
타이머(Timer)는 클럭 신호를 세는 하드웨어 카운터입니다. 일정한 주기로 증가 또는 감소하며, 특정 값에 도달하면 이벤트를 발생시킵니다.
타이머의 주요 용도:
Clock Source → Prescaler → Counter → Auto-Reload Register
↓
Compare/Match
↓
Interrupt
주요 구성 요소:
Clock Source (클럭 소스)
Prescaler (프리스케일러)
Counter (카운터)
Auto-Reload Register (ARR)
Capture/Compare Register (CCR)
카운터가 ARR에 도달한 후 다음 클럭이 오면 Update 이벤트가 발생합니다. 이 때 Update 인터럽트 플래그가 셋(Set)되면서 설정에 따라 CPU에 인터럽트를 요청하게 되고, 동시에 카운터는 0으로 돌아가 처음부터 다시 카운팅을 시작합니다.
Up-counting 모드:
Counter Value
↑
ARR |------------------------→ (오버플로우, 인터럽트 발생)
| ↓
| |
| |
0 |______________________|→ Time
타이머 주기 계산:
Timer Update Frequency = Timer Clock / ((Prescaler + 1) * (ARR + 1))
예시:
- Timer Clock: 84 MHz
- Prescaler: 8399 (분주비 8400)
- ARR: 9999 (카운트 10000)
Update Frequency = 84,000,000 / (8400 * 10000) = 1 Hz (1초 주기)
STM32F4는 여러 종류의 타이머를 제공합니다:
Advanced-control Timer (TIM1, TIM8)
General-purpose Timer (TIM2~TIM5)
General-purpose Timer (TIM9~TIM14)
Basic Timer (TIM6, TIM7)
다음은 각 타이머들의 주요 사용처입니다.
TIM1/TIM8: 주로 고성능 AC/DC 모터 제어, 드론 변속기(ESC), 태양광 인버터 등 TIM2~TIM5: 거리센서(초음파) 신호 분석, 서보 모터 제어, 엔코더 읽기. TIM9~TIM14: 단순한 LED 밝기 조절(PWM), 단순 센서 신호 대기, 시스템의 보조 타이머 TIM6/TIM7: DAC(디지털-아날로그 변환) 타이밍 제어, 정확인 1ms/1s 인터럽트 발생
| 용도 | 추천 타이머 | 이유 |
|---|---|---|
| 간단한 주기 인터럽트 | TIM6, TIM7 | 리소스 절약 |
| 긴 시간 측정 | TIM2, TIM5 | 32비트 카운터 |
| PWM (4채널) | TIM2~TIM5 | 많은 채널 |
| 모터 제어 | TIM1, TIM8 | 보완 출력, 데드타임 |
| Encoder | TIM2~TIM5 | Encoder 모드 지원 |
STM32F4의 타이머 클럭은 두 가지 버스에서 공급됩니다:
APB1 Timer Clock (TIM2~TIM7, TIM12~TIM14)
APB2 Timer Clock (TIM1, TIM8~TIM11)
HSE (8MHz) → PLL → SYSCLK (168MHz)
↓
AHB Prescaler (/1)
↓
HCLK (168MHz)
↓
+-----------+-----------+
↓ ↓
APB1 Prescaler APB2 Prescaler
(/4) (/2)
↓ ↓
APB1 (42MHz) APB2 (84MHz)
↓ ↓
Timer Clock Timer Clock
Multiplier (x2) Multiplier (x2)
↓ ↓
84 MHz 168 MHz
중요: APB Prescaler가 1이 아니면 타이머 클럭은 자동으로 2배가 됩니다.
예제 1: 1ms 주기 타이머 (TIM6 사용)
목표: 1ms마다 인터럽트 발생
Timer Clock: 84 MHz
방법 1: Prescaler = 83, ARR = 999
Update Frequency = 84,000,000 / ((83 + 1) * (999 + 1))
= 84,000,000 / (84 * 1000)
= 1,000 Hz = 1ms
방법 2: Prescaler = 8399, ARR = 9
Update Frequency = 84,000,000 / ((8399 + 1) * (9 + 1))
= 84,000,000 / (8400 * 10)
= 1,000 Hz = 1ms
어떤 방법을 선택할까?
| 비교 항목 | A: PSC 작게 / ARR 크게 | B: PSC 크게 / ARR 작게 |
|---|---|---|
| 카운팅 속도 | 매우 빠름 (촘촘함) | 느림 (듬성듬성함) |
| 제어 정밀도 | 매우 높음 (섬세함) | 낮음 (거칠음) |
| PWM 해상도 | 우수 (예: 10,000단계) | 부족 (예: 100단계) |
| 주파수 범위 | 저주파~중주파 유리 | 고주파 생성 가능 |
| 추천 용도 | 정밀 모터 제어, 오디오, 밝기 제어 | 단순 신호 발생, 아주 긴 시간 측정 |
Step 1: 타이머 활성화
Step 2: 타이머 설정
Configuration → TIM6:
Prescaler: 8399 (분주비 8400)
Counter Mode: Up
Counter Period (ARR): 9999 (카운트 10000)
Auto-reload preload: Enable
계산: 84,000,000 / (8400 * 10000) = 1 Hz (1초 주기)
Step 3: 인터럽트 활성화
NVIC Settings:
TIM6 global interrupt: Enabled
Priority: 0
Step 4: 코드 생성
Project Manager → Generate Code
// 타이머 시작 (인터럽트 모드)
HAL_StatusTypeDef HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim);
// 타이머 정지
HAL_StatusTypeDef HAL_TIM_Base_Stop_IT(TIM_HandleTypeDef *htim);
// 타이머 시작 (폴링 모드)
HAL_StatusTypeDef HAL_TIM_Base_Start(TIM_HandleTypeDef *htim);
// 타이머 정지 (폴링 모드)
HAL_StatusTypeDef HAL_TIM_Base_Stop(TIM_HandleTypeDef *htim);
// 카운터 값 읽기
uint32_t __HAL_TIM_GET_COUNTER(TIM_HandleTypeDef *htim);
// 카운터 값 설정
#define __HAL_TIM_SET_COUNTER(htim, value) ((htim)->Instance->CNT = (value))
// Auto-Reload 값 설정
#define __HAL_TIM_SET_AUTORELOAD(htim, value) ((htim)->Instance->ARR = (value))
/* USER CODE BEGIN 0 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM6) {
// TIM6 인터럽트 발생 시 처리
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);
}
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
// 타이머 시작
HAL_TIM_Base_Start_IT(&htim6);
/* USER CODE END 2 */
/* USER CODE BEGIN 0 */
uint32_t seconds_counter = 0;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM6) {
seconds_counter++;
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12); // LED 토글
// 10초마다 다른 LED 토글
if(seconds_counter % 10 == 0) {
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_13);
}
}
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim6);
/* USER CODE END 2 */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
// 메인 루프는 다른 작업 수행 가능
}
/* USER CODE END 3 */
HAL_Delay()보다 정밀한 마이크로초 단위 지연:
/* USER CODE BEGIN 0 */
void delay_us(uint16_t us)
{
// TIM2 사용 (1MHz, 1us 해상도)
// Prescaler: 83 (84MHz / 84 = 1MHz)
__HAL_TIM_SET_COUNTER(&htim2, 0);
while(__HAL_TIM_GET_COUNTER(&htim2) < us);
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
// TIM2 시작 (폴링 모드)
HAL_TIM_Base_Start(&htim2);
/* USER CODE END 2 */
/* USER CODE BEGIN 3 */
// 사용 예
delay_us(100); // 100 마이크로초 지연
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);
delay_us(100);
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);
/* USER CODE END 3 */
TIM2 설정 (STM32CubeMX):
Prescaler: 83 (84MHz / 84 = 1MHz)
Counter Period: 0xFFFFFFFF (최대값, 32비트)
/* USER CODE BEGIN 0 */
#define TIMEOUT_MS 1000
uint8_t Wait_For_Button_With_Timeout(uint32_t timeout_ms)
{
uint32_t start_time = HAL_GetTick();
while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_SET) {
if((HAL_GetTick() - start_time) > timeout_ms) {
return 0; // 타임아웃
}
}
return 1; // 버튼 눌림
}
/* USER CODE END 0 */
/* USER CODE BEGIN 3 */
if(Wait_For_Button_With_Timeout(5000)) {
// 5초 이내에 버튼 눌림
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_SET);
} else {
// 타임아웃
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_13, GPIO_PIN_SET);
}
/* USER CODE END 3 */
/* USER CODE BEGIN 0 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM6) {
// TIM6: 1초마다
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);
}
else if(htim->Instance == TIM7) {
// TIM7: 100ms마다
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_13);
}
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
// TIM6 설정: 1초 주기
// Prescaler: 8399, ARR: 9999
// TIM7 설정: 100ms 주기
// Prescaler: 8399, ARR: 999
HAL_TIM_Base_Start_IT(&htim6);
HAL_TIM_Base_Start_IT(&htim7);
/* USER CODE END 2 */
/* USER CODE BEGIN 0 */
#define DEBOUNCE_TIME_MS 50
typedef struct {
GPIO_TypeDef* port;
uint16_t pin;
uint32_t last_change_tick;
GPIO_PinState stable_state;
GPIO_PinState last_raw_state;
} TimerDebounceButton_t;
TimerDebounceButton_t btn = {
.port = GPIOA,
.pin = GPIO_PIN_0,
.last_change_tick = 0,
.stable_state = GPIO_PIN_SET,
.last_raw_state = GPIO_PIN_SET
};
// TIM6 인터럽트에서 10ms마다 호출
void Button_TimerDebounce_Process(TimerDebounceButton_t* btn)
{
static uint32_t tick_counter = 0;
GPIO_PinState current_state = HAL_GPIO_ReadPin(btn->port, btn->pin);
if(current_state != btn->last_raw_state) {
btn->last_raw_state = current_state;
btn->last_change_tick = tick_counter;
}
// 일정 시간 동안 상태가 안정적이면 확정
if((tick_counter - btn->last_change_tick) >= (DEBOUNCE_TIME_MS / 10)) {
if(btn->stable_state != current_state) {
btn->stable_state = current_state;
// 버튼 눌림 이벤트
if(current_state == GPIO_PIN_RESET) {
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);
}
}
}
tick_counter++;
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM6) {
// 10ms마다 버튼 상태 확인
Button_TimerDebounce_Process(&btn);
}
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
// TIM6 설정: 10ms 주기
// Prescaler: 8399, ARR: 99
HAL_TIM_Base_Start_IT(&htim6);
/* USER CODE END 2 */
TIMx_CR1 (Control Register 1)
TIM6->CR1 |= TIM_CR1_CEN; // 타이머 활성화
TIM6->CR1 &= ~TIM_CR1_CEN; // 타이머 비활성화
TIM6->CR1 |= TIM_CR1_ARPE; // Auto-reload preload 활성화
TIMx_PSC (Prescaler)
TIM6->PSC = 8399; // Prescaler 설정
TIMx_ARR (Auto-Reload Register)
TIM6->ARR = 9999; // ARR 설정
TIMx_CNT (Counter)
uint16_t counter = TIM6->CNT; // 현재 카운터 값 읽기
TIM6->CNT = 0; // 카운터 리셋
TIMx_DIER (DMA/Interrupt Enable Register)
TIM6->DIER |= TIM_DIER_UIE; // Update 인터럽트 활성화
TIM6->DIER &= ~TIM_DIER_UIE; // Update 인터럽트 비활성화
TIMx_SR (Status Register)
if(TIM6->SR & TIM_SR_UIF) {
// Update 인터럽트 플래그 확인
TIM6->SR &= ~TIM_SR_UIF; // 플래그 클리어
}
/* USER CODE BEGIN 0 */
void TIM6_Custom_Init(void)
{
// 클럭 활성화
__HAL_RCC_TIM6_CLK_ENABLE();
// Prescaler 설정 (84MHz / 8400 = 10kHz)
TIM6->PSC = 8399;
// ARR 설정 (10kHz / 10000 = 1Hz)
TIM6->ARR = 9999;
// Auto-reload preload 활성화
TIM6->CR1 |= TIM_CR1_ARPE;
// Update 인터럽트 활성화
TIM6->DIER |= TIM_DIER_UIE;
// NVIC 설정
HAL_NVIC_SetPriority(TIM6_DAC_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM6_DAC_IRQn);
// 타이머 시작
TIM6->CR1 |= TIM_CR1_CEN;
}
void TIM6_DAC_IRQHandler(void)
{
if(TIM6->SR & TIM_SR_UIF) {
TIM6->SR &= ~TIM_SR_UIF; // 플래그 클리어
// 인터럽트 처리
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);
}
}
/* USER CODE END 0 */
/* USER CODE BEGIN 0 */
typedef struct {
uint16_t milliseconds;
uint8_t seconds;
uint8_t minutes;
uint8_t running;
} Stopwatch_t;
Stopwatch_t stopwatch = {0, 0, 0, 0};
void Stopwatch_Start(void)
{
stopwatch.running = 1;
HAL_TIM_Base_Start_IT(&htim6);
}
void Stopwatch_Stop(void)
{
stopwatch.running = 0;
HAL_TIM_Base_Stop_IT(&htim6);
}
void Stopwatch_Reset(void)
{
stopwatch.milliseconds = 0;
stopwatch.seconds = 0;
stopwatch.minutes = 0;
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM6 && stopwatch.running) {
// 10ms마다 호출 (Prescaler: 8399, ARR: 99)
stopwatch.milliseconds += 10;
if(stopwatch.milliseconds >= 1000) {
stopwatch.milliseconds = 0;
stopwatch.seconds++;
// LED로 초 표시
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);
if(stopwatch.seconds >= 60) {
stopwatch.seconds = 0;
stopwatch.minutes++;
}
}
}
}
void Stopwatch_Display(void)
{
// LED로 시간 표시 (간단한 예)
// 실제로는 7-segment나 LCD 사용
uint8_t total_seconds = stopwatch.minutes * 60 + stopwatch.seconds;
LEDs_SetBinary(total_seconds & 0x0F); // 하위 4비트만 표시
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
// TIM6 설정: 10ms 주기
// Prescaler: 8399, ARR: 99
/* USER CODE END 2 */
/* USER CODE BEGIN 3 */
// 버튼으로 제어
if(Start_Button_Pressed) {
Stopwatch_Start();
}
if(Stop_Button_Pressed) {
Stopwatch_Stop();
}
if(Reset_Button_Pressed) {
Stopwatch_Reset();
}
Stopwatch_Display();
/* USER CODE END 3 */
/* USER CODE BEGIN 0 */
#define SENSOR_READ_INTERVAL_MS 100
uint16_t sensor_values[10];
uint8_t sensor_index = 0;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM6) {
// 100ms마다 센서 읽기
// ADC를 사용한다고 가정 (다음 강의에서 학습)
sensor_values[sensor_index] = Read_Sensor();
sensor_index = (sensor_index + 1) % 10;
// 평균 계산
uint32_t sum = 0;
for(int i = 0; i < 10; i++) {
sum += sensor_values[i];
}
uint16_t average = sum / 10;
// 평균값에 따라 LED 제어
if(average > 500) {
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_SET);
} else {
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_RESET);
}
}
}
/* USER CODE END 0 */
/* USER CODE BEGIN 0 */
typedef struct {
void (*task_function)(void);
uint32_t period_ms;
uint32_t last_run_tick;
} Task_t;
void Task_LED1(void) {
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);
}
void Task_LED2(void) {
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_13);
}
void Task_LED3(void) {
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_14);
}
void Task_LED4(void) {
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_15);
}
Task_t tasks[] = {
{Task_LED1, 100, 0}, // 100ms마다
{Task_LED2, 200, 0}, // 200ms마다
{Task_LED3, 500, 0}, // 500ms마다
{Task_LED4, 1000, 0} // 1초마다
};
#define NUM_TASKS (sizeof(tasks) / sizeof(Task_t))
uint32_t system_tick_ms = 0;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM6) {
// 10ms마다 호출
system_tick_ms += 10;
// 각 태스크의 실행 시간 확인
for(uint8_t i = 0; i < NUM_TASKS; i++) {
if((system_tick_ms - tasks[i].last_run_tick) >= tasks[i].period_ms) {
tasks[i].task_function();
tasks[i].last_run_tick = system_tick_ms;
}
}
}
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
// TIM6 설정: 10ms 주기
HAL_TIM_Base_Start_IT(&htim6);
/* USER CODE END 2 */
여러 타이머를 사용할 때 우선순위를 적절히 설정해야 합니다.
/* USER CODE BEGIN 2 */
// TIM6: 높은 우선순위 (긴급 작업)
HAL_NVIC_SetPriority(TIM6_DAC_IRQn, 0, 0);
// TIM7: 낮은 우선순위 (일반 작업)
HAL_NVIC_SetPriority(TIM7_IRQn, 1, 0);
HAL_TIM_Base_Start_IT(&htim6);
HAL_TIM_Base_Start_IT(&htim7);
/* USER CODE END 2 */
/* USER CODE BEGIN 0 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM6) {
// 높은 우선순위 - 빠른 처리
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_SET);
// 짧은 작업만 수행
for(volatile int i = 0; i < 100; i++);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_RESET);
}
else if(htim->Instance == TIM7) {
// 낮은 우선순위 - 시간이 걸리는 작업
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_13, GPIO_PIN_SET);
// 긴 작업 (TIM6 인터럽트가 중첩 가능)
for(volatile int i = 0; i < 10000; i++);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_13, GPIO_PIN_RESET);
}
}
/* USER CODE END 0 */
중요: 인터럽트 내에서는 가능한 한 짧게 처리하고, 플래그만 설정한 뒤 메인 루프에서 실제 작업을 수행하는 것이 좋습니다.
/* USER CODE BEGIN 0 */
void Debug_Timer_Status(void)
{
// 타이머가 실행 중인지 확인
if(TIM6->CR1 & TIM_CR1_CEN) {
// 타이머 실행 중
}
// 현재 카운터 값
uint16_t counter = TIM6->CNT;
// Prescaler 값
uint16_t prescaler = TIM6->PSC;
// ARR 값
uint16_t arr = TIM6->ARR;
// 인터럽트 활성화 상태
if(TIM6->DIER & TIM_DIER_UIE) {
// 인터럽트 활성화됨
}
}
/* USER CODE END 0 */
/* USER CODE BEGIN 0 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM6) {
// GPIO를 HIGH로 설정
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_15, GPIO_PIN_SET);
// 실제 작업
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);
// GPIO를 LOW로 설정
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_15, GPIO_PIN_RESET);
}
}
/* USER CODE END 0 */
오실로스코프로 PD15 핀을 측정하면 인터럽트 실행 시간을 확인할 수 있습니다.
32비트 타이머(TIM2, TIM5)를 사용하여 오버플로우를 최소화:
/* USER CODE BEGIN 0 */
uint32_t Get_Microseconds(void)
{
// TIM2: 1MHz로 설정 (Prescaler: 83)
return __HAL_TIM_GET_COUNTER(&htim2);
}
uint32_t Get_Elapsed_Time_us(uint32_t start_time)
{
uint32_t current_time = Get_Microseconds();
if(current_time >= start_time) {
return current_time - start_time;
} else {
// 오버플로우 발생
return (0xFFFFFFFF - start_time) + current_time + 1;
}
}
/* USER CODE END 0 */
/* USER CODE BEGIN 3 */
uint32_t start = Get_Microseconds();
// 작업 수행
Some_Function();
uint32_t elapsed = Get_Elapsed_Time_us(start);
// elapsed: 작업 소요 시간 (마이크로초)
/* USER CODE END 3 */
요구사항:
1. 타이머로 1초마다 시간 업데이트
2. 시, 분, 초 카운터 구현
3. 4개 LED로 초의 하위 4비트 표시
4. 버튼으로 시간 설정 기능
요구사항:
1. TIM6: 100ms마다 LED 패턴 변경
2. TIM7: 1초마다 패턴 모드 변경
3. 최소 3가지 패턴 구현 (순차, 교차, 랜덤 등)
요구사항:
1. 랜덤한 시간 후 LED 켜기
2. 사용자가 버튼을 누르는 시간 측정
3. 반응 시간을 ms 단위로 측정
4. LED 개수로 반응 속도 등급 표시
힌트:
uint32_t start_time_ms = 0;
uint8_t waiting = 0;
// LED 켜기
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_SET);
start_time_ms = system_tick_ms;
waiting = 1;
// 버튼 인터럽트에서
if(waiting) {
uint32_t reaction_time = system_tick_ms - start_time_ms;
waiting = 0;
// reaction_time에 따라 등급 표시
}
Update Frequency = Timer Clock / ((Prescaler + 1) * (ARR + 1))
예제:
- 1ms 주기: PSC=83, ARR=999 (84MHz 기준)
- 10ms 주기: PSC=8399, ARR=99
- 100ms 주기: PSC=8399, ARR=999
- 1초 주기: PSC=8399, ARR=9999
| 용도 | 타이머 | 설정 |
|---|---|---|
| 간단한 주기 인터럽트 | TIM6, TIM7 | Basic Timer |
| 마이크로초 지연 | TIM2, TIM5 | 32비트, 1MHz |
| 다중 작업 스케줄러 | TIM6 | 10ms 주기 |
| PWM (다음 강의) | TIM2~TIM5 | 고급 기능 |