타이머의 세 가지 핵심 레지스터. 주파수와 듀티를 결정한다.
- PSC (Prescaler) — — 입력 클럭을 나눈다
- ARR (Auto-Reload) — — 카운터가 여기까지 세고 0으로 돌아간다 → 주기
- CCR (Capture/Compare)— 이 값에서 출력이 바뀐다 → 듀티
구조
타이머 클럭 ──[÷(PSC+1)]──► 카운터 클럭
│
▼
0 ─────────► ARR ──(오버플로 → 0으로)
↑
CCR ← 여기를 지날 때 출력 토글
계산식
- 카운터 클럭 — = 타이머 클럭 / (PSC + 1)
- PWM 주파수 — = 카운터 클럭 / (ARR + 1)
- = 타이머 클럭 / ((PSC+1) × (ARR+1))
- 듀티 사이클 — = (CCR + 1) / (ARR + 1) (모드에 따라 CCR/(ARR+1))
+1이 붙는 이유 — 카운터가 0부터 세기 때문이다.
ARR=99면 0~99까지 100개를 센다.
예제 1 — 1kHz PWM, 듀티 25%
- 타이머 클럭 84MHz
| PSC = 83 | → 카운터 클럭 = 84MHz / 84 = 1MHz | (1μs 단위) |
|---|---|---|
| ARR = 999 | → 주기 = 1000μs = 1ms → 1kHz | ✓ |
| CCR = 250 | → 듀티 = 250/1000 = 25% | ✓ |
PSC로 편리한 시간 단위를 만들고, ARR로 주기를 세는 것이 요령이다. 1MHz(1μs)나 10kHz(0.1ms)처럼 계산하기 쉬운 값을 고른다.
예제 2 — 1초 주기 인터럽트
84MHz / ((PSC+1) × (ARR+1)) = 1Hz (PSC+1) × (ARR+1) = 84,000,000
- PSC = 8399 — → 84MHz / 8400 = 10kHz
- ARR = 9999 — → 10kHz / 10000 = 1Hz ✓
16비트 타이머는 PSC와 ARR이 각각 최대 65535다. 큰 값이 필요하면 곱으로 분배해야 한다.
타이머 클럭을 잘못 알면 전부 틀린다 — 가장 흔한 실수
- APB1 분주비가 1이면 — → 타이머 클럭 = APB1 클럭 APB1 분주비가 1이 아니면 → 타이머 클럭 = APB1 클럭 × 2 ★
SYSCLK 168MHz, APB1 분주 4 → PCLK1 = 42MHz
- TIM2 클럭 = 84MHz (42가 아니다!)
★ 이것을 놓치면 주파수가 정확히 2배로 나온다. "계산은 맞는데 주파수가 두 배"라면 이것이다.
uint32_t timclk = HAL_RCC_GetPCLK1Freq();
if ((RCC->CFGR & RCC_CFGR_PPRE1) != RCC_CFGR_PPRE1_DIV1) timclk *= 2;
런타임에 계산하면 클럭 설정이 바뀌어도 안전하다.
프리로드 (섀도 레지스터)
htim.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
켜면 ARR·CCR에 쓴 값이 즉시 반영되지 않고 다음 주기 시작 시 적용된다.
- 프리로드 없이 듀티를 바꾸면
- 현재 카운터가 이미 새 CCR을 지나쳤을 수 있다
- 그 주기에서 펄스가 사라지거나 비정상적으로 길어진다
- 모터가 순간적으로 튄다
동적으로 듀티를 바꾸는 용도라면 반드시 켜야 한다.
관련 레지스터
- CNT — — 현재 카운터 값 (읽기·쓰기 가능)
- CR1 — — 제어 (활성화, 방향, 정렬 모드)
- DIER — 인터럽트·DMA 활성화
- SR — — 상태 플래그 (UIF: 업데이트, CCxIF: 비교 일치)
- EGR — — 소프트웨어 이벤트 생성 (UG 비트로 강제 갱신)
설정 후 TIM->EGR = TIM_EGR_UG; 를 써서 프리로드 값을
즉시 반영시키는 것이 관례다.
인터럽트에서 플래그 클리어
void TIM2_IRQHandler(void) {
if (TIM2->SR & TIM_SR_UIF) {
TIM2->SR = ~TIM_SR_UIF; // ★ &= ~ 가 아니라 = ~ 로 쓴다
// …
}
}
&= 를 쓰면 read-modify-write가 되어 그 사이에 발생한
다른 플래그를 지워 버릴 수 있다. 타이머 SR은 0을 써야 클리어되는
레지스터라 = ~FLAG 형태가 안전하다.
타이머의 다른 용도
입력 캡처 : 외부 신호의 에지에서 CNT 값을 CCR에 저장 → 주파수·펄스 폭 측정 엔코더 모드: A/B 상 입력으로 CNT가 자동 증감 → 회전 위치 측정 원샷 모드 : 한 번만 실행하고 정지 → 정밀 지연
- 트리거 — : 다른 타이머나 ADC를 동기 구동
타이머로 ADC를 트리거하면 정확히 일정한 간격으로 샘플링할 수 있다. 소프트웨어로 하면 지터가 생겨 신호 처리 결과가 나빠진다.