TIM2 / PWM User Description
현재 구현에서 TIM2 time-base, CCR1 PWM duty, CC1E gate, CCR2 compare event가 PWM_OUT과 ADC 변환으로 이어지는 과정을 연결합니다.
TIM2/PWM Datasheet 현재 Trainer 실행 예제 RCC Datasheet GPIO User Description ADC User Description Lab Guide
1. 그림에서 먼저 보기
Figure 1의 번호는 아래 표의 같은 번호 행으로 이어집니다. 노란 경로는 현재 구현에서 보이는 CCR1→PA2/PWM_OUT과 CCR2→TIM2_CC2→ADC입니다. ADC 변환은 ADC1 설정까지 완료되어야 시작됩니다.
1
2
3
4
5
Figure 1. TIM2 clock, compare, PWM_OUT, and ADC trigger route
RM0008 Rev 21 Figure 100을 현재 구현 범위에 맞게 재구성했습니다. CC1I/CC2I는 공식 interrupt/DMA request output이고 U는 update event signal이며, 이 request/flag side effect는 현재 구현에서 Reference-only입니다. ADC 시작 신호는 TRGO가 아니라 TIM2_CC2입니다. 전체 trigger/slave block과 사용하지 않는 channel lane은 생략했습니다.
2. 블록 → 레지스터 비트
읽는 순서: 지원되는 항목을 먼저 확인하고, 미지원 항목은 문서 하단의 지원하지 않는 항목에서 확인합니다.
| 그림 블록 | 레지스터 | 필드 / bit | 그림에서 하는 일 / 값 의미 | 지원 여부 |
|---|---|---|---|---|
| 1Figure 1 TIM2 clock | ||||
| TIM2CLK gate | RCC_APB1ENR | TIM2EN[0] | 그림에서: APB1 clock을 TIM2 peripheral path에 공급합니다.0 = TIM2 clock 차단1 = TIM2 clock 공급TIM2 register effect와 counter 진행의 clock gate입니다. | 지원됨 |
| 2Figure 1 prescaler | ||||
| Counter divider | TIM2_PSC | PSC[15:0] | 그림에서: TIM2 입력 clock을 PSC+1로 나눠 counter clock을 만듭니다.PSC = 0 TIM2CLK / 1PSC = n TIM2CLK / (n + 1)현재 timing 계산과 Docs / Log frequency 메시지에 반영됩니다. | 지원됨 |
| 3Figure 1 counter and period | ||||
| Counter enable | TIM2_CR1 | CEN[0] | 그림 밖에서: Figure 1에는 CEN field가 직접 표시되지 않지만 CNT와 현재 PWM 경로의 실행을 제어합니다.0 = counter 정지1 = counter 시작CEN을 0으로 내려도 공식 하드웨어의 CNT 값은 자동으로 지워지지 않습니다. | 지원됨 |
| Counter value | TIM2_CNT | CNT[15:0] | 그림에서: 현재 counter 값을 보여 줍니다.CNT = n 현재 period 안의 count 위치TIM2_CNT는 read/write(RW) register입니다. software가 현재 count를 읽을 수도 있고, 0 또는 원하는 시작값을 직접 쓸 수도 있습니다. TIM2_CCR2 crossing과 PWM sample phase도 이 값을 기준으로 합니다. | 지원됨 |
| Auto-reload | TIM2_ARR | ARR[15:0] | 그림에서: counter가 포함해서 세는 마지막 값, 즉 CNT limit를 정합니다.ARR = 0 공식 하드웨어에서 counter blockARR = n CNT는 0부터 n까지 센 뒤 0으로 rollover, period counts = n + 1ARR에 100을 쓰면 CNT는 0부터 100까지 센 뒤 다음 counter clock에서 0으로 돌아갑니다. 0도 한 count이므로 총 101 counts입니다. 현재 구현은 저장된 ARR 값을 바로 timing 계산에 사용합니다. | 지원됨 |
| 4Figure 1 PWM compare and output gate | ||||
| Duty compare | TIM2_CCR1 | CCR1[15:0] | 그림에서: CNT와 비교하여 PWM 내부 파형이 바뀌는 경계값을 정합니다.CCR1 = n active-high PWM mode 1에서 CNT < n 동안 active, CNT = n부터 inactive예: ARR=99, CCR1=40이면 CNT 0~39는 active이고 40~99는 inactive라 duty는 40%입니다. 이 compare 결과가 OC1REF 방식의 내부 기준 파형이 되며, 현재 구현 고정 경로에서는 PA2/PWM_OUT으로 이어집니다. | 지원됨 |
| Output gate | TIM2_CCER | CC1E[0] | 그림에서: CCR1 compare 출력을 현재 PWM_OUT 경로로 내보내는 gate입니다.0 = 출력 차단1 = 출력 허용CEN도 1이어야 하며, 공식 PA2/TIM2_CH3 gate는 CC3E입니다. | 지원됨 |
| PA2 pad prerequisite | GPIOA_CRL | CNF2[11:10] / MODE2[9:8] | 그림 밖에서: Figure 1에는 GPIOA_CRL field가 직접 표시되지 않으며, PA2 pad를 출력 가능한 상태로 준비합니다.CRL[11:8] = 0xB 공식 AF push-pull 50 MHzMODE2 != 00 현재 구현이 허용하는 단순화된 output-capable 조건현재 구현은 TIM2_CCR1을 PA2/PWM_OUT에 연결한 고정 경로를 사용하며, 이는 공식 PA2/TIM2_CH3 mux와 다릅니다. | 지원됨 |
| 5Figure 1 TIM2_CC2 → ADC trigger route | ||||
| Sample compare | TIM2_CCR2 | CCR2[15:0] | 그림에서: ADC sample trigger를 보낼 compare 위치를 정합니다.CCR2 = n CNT가 n에 도달하거나 한 simulator tick 안에서 지나면 TIM2_CC2 event 발생이 값만으로 ADC 변환이 시작되지는 않습니다. | 지원됨 |
| ADC trigger acceptance | ADC_CR2 | EXTSEL[19:17] / EXTTRIG[20] | 그림에서: TIM2_CC2를 regular trigger로 선택하고 external trigger를 허용합니다.EXTSEL = 011 TIM2_CC2 선택EXTTRIG = 0 외부 trigger 비활성화EXTTRIG = 1 외부 trigger 활성화실제 변환에는 ADC1 clock, PA1 analog mode, ADC_SQR3 SQ1=1도 필요하며 나머지 ADC 설정은 ADC User Description에서 이어집니다. | 지원됨 |
3. 표 기준 단순 코드
| 현재 코드 / 값 | 데이터시트 필드로 해석 | 화면에서 확인 |
|---|---|---|
| RCC_APB1ENR_TIM2EN | RCC_APB1ENR.TIM2EN[0] = 1: TIM2 clock gate 공급 | Registers → Clock |
| TIM2->PSC = 11999u | TIM2_PSC.PSC[15:0] = 11999: 72 MHz / (11999 + 1) = 6 kHz counter clock | Registers, Docs / Log timing |
| TIM2->ARR = 99u | TIM2_ARR.ARR[15:0] = 99: period count = 100 | Registers, Docs / Log timing |
| pwm_write(30u) | TIM2_CCR1.CCR1[15:0] = 30: fixed PWM mode 1 rule에서 30 / (99 + 1) = 30% duty | Registers, Board Lab pin routes, Graph |
| TIM2->CCER = TIM2_CCER_CC1E | TIM2_CCER.CC1E[0] = 1: channel-1 output gate 허용. 현재 구현에서는 PA2/PWM_OUT 고정 alias gate | Registers, Board Lab pin routes |
| TIM2->CR1 = TIM2_CR1_CEN | TIM2_CR1.CEN[0] = 1: counter 시작 | Registers, Code Watch |
| TIM2->CCR2 = 30u | TIM2_CCR2.CCR2[15:0] = 30: TIM2_CC2 compare event 위치 | Registers, Docs / Log, Sample log |
| ADC1->CR2 = ADC_CR2_EXTSEL_TIM2_CC2 | ADC_CR2_EXTTRIG | ADC_CR2.EXTSEL[19:17] = 011과 EXTTRIG[20] = 1: TIM2_CC2 event를 ADC regular trigger로 허용 | Registers, Graph Sample log, ADC User Description |
4. 현재 Trainer 실행 예제
FW EDIT에 그대로 붙여넣어 PA2/PWM_OUT의 60 Hz, 30% duty와 TIM2 counter를 확인합니다. 각 주석의 번호는 위 블록 → 레지스터 비트 표와 연결되며, 현재 Trainer가 제공하는 CCR1→PA2 고정 경로를 사용합니다.
#include "training_api.h"
static void gpio_init(void)
{
GPIOA->CRL = 0x40422B00u; /* TIM User Description 4번: PA2 PWM pad 전제, GPIO UD 4번 */
GPIOB->CRL = 0x44444440u; /* P02 공통 Port API: PB0 analog, GPIO UD 4번 */
GPIOA->ODR &= ~(GPIOA_DRV_EN_BIT | GPIOA_DRV_DIR_BIT); /* GPIO UD 2번: motor output 안전 시작 */
}
static void pwm_init(void)
{
TIM2->PSC = 11999u; /* TIM User Description 2번: 72 MHz / 12000 = 6 kHz */
TIM2->ARR = 99u; /* TIM User Description 3번: 100 counts = 60 Hz period */
TIM2->CCR1 = 0u; /* TIM User Description 4번: duty compare 안전 시작값 */
TIM2->CCER = TIM2_CCER_CC1E; /* TIM User Description 4번: PWM output gate 허용 */
TIM2->CR1 = TIM2_CR1_CEN; /* TIM User Description 3번: counter 시작 */
}
static void pwm_write(uint32_t duty_percent)
{
if (duty_percent > 100u) duty_percent = 100u;
TIM2->CCR1 = duty_percent; /* TIM User Description 4번: CCR1 duty compare 갱신 */
}
void user_init(void)
{
trainer_clock_init_72mhz(); /* RCC User Description 1~4번: 72 MHz clock 경로 */
RCC->APB2ENR = RCC_APB2ENR_AFIOEN; /* RCC User Description 5번: AFIO gate */
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; /* TIM User Description 4번: PA2 pad용 GPIOA gate */
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; /* P02 공통 Port API의 GPIOB gate */
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; /* P02 공통 Port API의 ADC1 gate */
RCC->APB1ENR = RCC_APB1ENR_TIM2EN; /* TIM User Description 1번: TIM2 clock gate */
gpio_init(); /* TIM User Description 4번: PA2 output 전제 */
pwm_init(); /* TIM User Description 2~4번 설정 */
pwm_write(30u); /* TIM User Description 4번: CCR1=30, duty 30% */
}
void user_loop(void)
{
watch_u32("TIM2_PSC", TIM2->PSC); /* 2번 prescaler */
watch_u32("TIM2_ARR", TIM2->ARR); /* 3번 period limit */
watch_u32("TIM2_CNT", TIM2->CNT); /* 3번 current counter */
watch_u32("TIM2_CCR1", TIM2->CCR1); /* 4번 duty compare */
}
5. 사이트에서 확인
바로 위 실행 예제의 PSC·ARR·CCR1·CC1E·CEN 결과를 아래 화면에서 확인합니다.
- Registers: Clock 그룹의 TIM2EN, GPIO mode의 PA2, TIM2_CR1, TIM2_PSC, TIM2_ARR, TIM2_CNT, TIM2_CCR1, TIM2_CCR2, TIM2_CCER를 확인합니다. SR/EGR/CCMR1은 현재 focused Registers 목록에 없습니다.
- Board · Lab pin routes: PA2/PWM_OUT을 선택하면 Official device functions, Current fixed route, 공식 PA2=TIM2_CH3와 CCR1 고정 alias를 구분할 수 있고 Graph trace 목록에 자동으로 추가됩니다.
- Graph: PA2/PWM_OUT trace와 평균 전압을 CCR1/(ARR+1) duty와 비교합니다. P02 실행 예제의 TIM2_CNT와 CCR1 변화를 함께 확인합니다.
- Code Watch: TIM2_CNT 진행을 확인합니다.
- Docs / Log: TIM2_* updated to ..., TIM2 PWM timing: ..., TIM2_CC2 event: ... 메시지 계열을 확인합니다.
6. 부록: ARR·CNT·COMPARE로 PWM 완성하기
Figure A와 Figure B는 위 표의 TIM2_CNT, TIM2_ARR, TIM2_CCR1, TIM2_CCER를 한 흐름으로 읽기 위한 학습 그림입니다. 공식 필드 정의는 TIM2_CNT, TIM2_ARR, TIM2_CCR1 항목과 연결됩니다.
6.1 ARR는 CNT가 포함해서 세는 마지막 값
Figure A. ARR=100일 때 CNT의 0→100→0 rollover와 read/write 의미
ARR는 count 개수 자체가 아니라 마지막으로 포함되는 CNT 값입니다. 따라서 ARR=100이면 CNT=0, 1, 2, …, 99, 100까지 진행하고, 다음 counter clock에서 0으로 돌아갑니다. 0도 한 count이므로 한 period는 총 ARR+1=101 counts입니다.
TIM2_CNT는 read/write(RW) register입니다. 실행 중 현재 위치를 읽을 수 있고, software가 0이나 원하는 시작값을 직접 쓸 수도 있습니다. CEN=0으로 정지해도 CNT가 자동으로 0이 되지는 않습니다.
uint32_t now = TIM2->CNT; /* 현재 count 읽기 */ TIM2->CNT = 0u; /* 0부터 다시 시작 */ TIM2->CNT = 40u; /* 40을 시작 위치로 쓰기 */
6.2 CCR1 compare가 OC1REF와 PWM_OUT을 만드는 방법
Figure B. ARR=99, CCR1=40에서 compare 경계, OC1REF, CC1E, PA2/PWM_OUT 흐름
compare 위치는 CNT가 CCR1과 같아지면서 PWM 상태가 바뀌는 경계입니다. 위 예에서 CCR1=40이므로 CNT=0~39에서는 CNT<CCR1이 참이고, CNT=40~99에서는 거짓입니다. 그래서 active-high PWM mode 1 기준으로 100 counts 중 40 counts가 HIGH가 되어 duty는 40%입니다.
- OC1REF: CNT/CCR1 compare와 PWM mode가 만든 timer 내부 기준 신호입니다. 아직 물리 핀 출력은 아닙니다.
- CC1E: 현재 구현에서 OC1REF 방식의 compare 결과를 PWM_OUT 경로로 내보내는 output gate입니다.
- GPIOA_CRL: PA2 pad를 출력 가능한 상태로 준비합니다. 공식 설정 CRL[11:8]=0xB는 AF push-pull 50 MHz입니다.
- PA2/PWM_OUT: 현재 구현은 CCR1→OC1REF 방식→CC1E→PA2 고정 경로를 사용합니다.
6.3 PWM이 실제로 나오기 위한 전체 조건
| 순서 | 레지스터 / 필드 | 역할 |
|---|---|---|
| 1 | RCC_APB1ENR.TIM2EN / RCC_APB2ENR.IOPAEN | TIM2와 GPIOA에 clock을 공급합니다. |
| 2 | GPIOA_CRL[11:8] = 0xB | PA2를 공식 AF push-pull output으로 준비합니다. 현재 구현은 단순화되어 MODE2 != 00인 output-capable 상태를 허용합니다. |
| 3 | TIM2_PSC / TIM2_ARR | counter 속도와 한 period의 마지막 CNT 값을 정합니다. |
| 4 | TIM2_CCR1 | CNT와 비교할 경계값을 정하여 duty를 만듭니다. |
| 5 | TIM2_CCER.CC1E / TIM2_CR1.CEN | 현재 PWM_OUT gate를 열고 counter를 시작합니다. |
| 확인 | TIM2_CNT / Graph / PA2/PWM_OUT | CNT 진행, HIGH/LOW 파형, 평균 전압을 확인합니다. |
공식 STM32와 현재 구현의 차이: 공식 STM32F103에서 PA2는 TIM2_CH3이므로 CCR3→OC3REF→CC3E→PA2 경로를 사용합니다. 현재 구현은 학습용 고정 alias인 CCR1→OC1REF 방식→CC1E→PA2/PWM_OUT 경로를 사용합니다.
7. 지원하지 않는 항목
공식 TIM 기능 중 현재 구현에서 실행되지 않는 필드와 확장 기능을 아래에서 구분합니다.
7.1 그림 번호와 연결되는 미지원 항목
| 그림 블록 | 레지스터 | 필드 / bit | 공식 의미와 현재 구현 범위 | 지원 여부 |
|---|---|---|---|---|
| PWM mode decoder | TIM2_CCMR1 | OC1M[6:4] | 그림 밖에서: Figure 1에는 OC1M field가 직접 표시되지 않으며, 공식 하드웨어에서 compare 결과가 OC1REF 파형으로 바뀌는 방식을 고릅니다.000 = Frozen, 비교 결과가 출력에 영향 없음001 = match 때 active로 set010 = match 때 inactive로 reset011 = match 때 toggle100 = 강제 inactive101 = 강제 active110 = PWM mode 1, up-counting에서 CNT < CCR1 동안 active111 = PWM mode 2, up-counting에서 CNT < CCR1 동안 inactive현재 구현은 이 필드를 학생 코드에 노출하지 않고 고정 PWM mode 1 방식으로 CCR1 duty를 계산합니다. | 미지원 |
| CCR1 preload | TIM2_CCMR1 | OC1PE[3] | 그림 밖에서: Figure 1에는 OC1PE field가 직접 표시되지 않으며, 공식 하드웨어에서 CCR1 write의 preload 사용 여부를 정합니다.0 = preload 비활성화, CCR1 write가 즉시 반영1 = preload 활성화, update event 때 반영현재 구현은 preload transfer를 모델링하지 않고 CCR1 저장값을 바로 duty 계산에 사용합니다. | 미지원 |
| Update event / status boundary | TIM2_SR | UIF[0] | 그림 밖에서: Figure 1의 U는 update event signal이며, UIF는 그 event 발생을 기록하는 별도 status flag입니다.0 = update event pending 없음1 = update event pending공식 하드웨어에서는 overflow/underflow, UG, trigger reset 등으로 set되고 software가 clear합니다. 현재 구현은 rollover timing만 보이며 UIF flag side effect는 다루지 않습니다. | 미지원 |
| Software update command | TIM2_EGR | UG[0] | 그림 밖에서: UG는 Figure 1의 U update event를 software로 생성하는 write command이며, U signal 자체가 아닙니다.0 write = 변화 없음1 write = counter 재초기화와 update event 생성현재 구현은 UG write에 따른 preload transfer, UIF set, counter 재초기화를 실행하지 않습니다. | 미지원 |
| Compare request / status boundary | TIM2_SR | CC2IF[2] | 그림 밖에서: Figure 1의 CC2I는 interrupt/DMA request output이고, CC2IF는 channel 2 event를 기록하는 별도 status flag입니다.0 = compare/capture event pending 없음1 = output compare match 또는 input capture event pending현재 구현은 TIM2_CC2 event를 Docs / Log와 Sample log로 보여 주지만 CC2I request와 CC2IF flag side effect는 모델링하지 않습니다. | 미지원 |
7.2 추가 지원 경계
| 항목 | 공식 STM32 의미 | 현재 구현 경계 | 지원 여부 |
|---|---|---|---|
| TIM2_DIER / TIM2_SR | CC1I/CC2I interrupt/DMA request와 UIF/CC1IF/CC2IF status flag | focused Registers 목록과 training header에서 제외되며 request/flag side effect는 모델링하지 않습니다. | 미지원 |
| TIM2_CCMR1 | OC1M/OC1PE 등 PWM mode와 preload 설정 | 현재 실행 예제는 mode/preload를 조절 가능한 항목처럼 보이지 않게 제외합니다. PA2/PWM_OUT은 현재 고정 경로입니다. | 미지원 |
| TIM2_EGR | UG로 만드는 U update event 등 software event generation | counter rollover timing은 보이지만 UG/preload/UIF side effect는 모델링하지 않습니다. | 미지원 |
| TIM2_CCR3 | 공식 PA2 TIM2_CH3 PWM은 channel 3 compare register를 사용합니다. | 현재 구현의 PA2/PWM_OUT은 TIM2_CCR1 고정 alias입니다. | 미지원 |
| TIM1 / TIM8 advanced-control blocks | Repetition counter, dead-time/break, complementary output, trigger/slave controller | 현재 구현의 실행 가능한 timer register/header/UI 경로는 TIM2 focused block뿐이며 TIM1/TIM8 출력 side effect는 모델링하지 않습니다. | 미지원 |