본문으로 바로가기

Timer Output compare Introduction

  • 이전 글에서는 타이머의 입력 기능(시간 베이스 생성, 입력 캡처 모드)을 다룸
  • 이번에는 Output Compare 모드를 학습
    • OC 모드는 PWM(펄스 폭 변조) 모드원펄스 모드(One Pulse Mode)와도 관련

 

실습 목표

  • Timer2를 활용하여 500Hz, 1kHz, 2kHz, 4kHz의 사각파(Square Wave) 생성
  • Timer2의 4개 채널을 모두 출력 채널로 사용
  • 타이머에 4개 입력, 4개 출력이 있다고 착각할 수 있지만 실제로는 총 4개의 채널입력 또는 출력으로 설정하는 것

 

출력 비교(OC) 모드 개념

  • Input capture mode 에서는 특정 신호가 감지될 때 타이머 값(카운트 레지스터 값)을 저장
  • Output compare mode 에서는 타이머가 특정 값(Capture Compare Register, CCR)과 일치할 때 출력을 토글.
  • 파형 생성 방식:
    1. CCR 값 설정 → 타이머가 해당 값에 도달하면 출력 상태 변경
    2. 인터럽트 발생 → CCR 값을 다시 변경하여 일정한 주기로 토글
    3. 반복 수행하여 주기적인 신호(사각파) 생성

 

구현 방식

  • 초기 CCR 값(Pulse Value) 설정 후 타이머 시작
  • 타이머가 해당 값에 도달할 때마다 출력 채널 상태 변경(High ↔ Low)
  • 인터럽트 핸들러에서 CCR 값을 변경하여 주기적인 출력 유지
  • 4개의 채널을 활용하여 다양한 주파수(500Hz, 1kHz, 2kHz, 4kHz)의 신호 생성

Output Compare Exercise

1) Initialize the TIMER output Compare Time base

HAL_TIM_OC_init(TIM_HandleTypeDef*htim)

이전 Timer2 초기화 방식과 동일

 

2) Configure output channel of the timer

HAL_TIM_OC_ConfigChannel(TIM_HandleTypeDef*htim, TIM_OC_InitTypeDef* sConfig, uint32_t Channel)

✅ Output Compare Process (TIM_OCMODE_TOGGLE)

  1. 카운터 시작(0부터 증가)
    • TIM2가 0부터 시작하여 계속 증가함.
  2. 비교 값(CCR) 도달
    • 예제에서 CCR1 = 0x003A라면, 카운터가 0x003A가 되는 순간 출력을 토글
    • LOW → HIGH 또는 HIGH → LOW.
  3. 다음 비교 값 설정 (인터럽트 발생 시 갱신 가능)
    • 인터럽트 핸들러에서 CCR1 값을 새롭게 설정
  • 그림에서 OC1에서 HIGH 부분의 길이가 Pulse 에 해당함
  • 각 채널에 대해 원하는 frequency square waves에 따라 “Pulse” 값을 계산

OC1REF vs. OC1

  • OC1REF : 타이머 내부에서 생성된 신호
    • CCR1과 TCNT를 값을 비교하여 생성
    • 실제 출력 핀과 연결되기 전 내부적으로 존재하는 가상의 신호
  • OC1 : OC1REF를 기준으로 최종적으로 출력되는 신호
    • TIMx_CH1 핀으로 나가는 실제 PWM 파형 또는 토글 신호
    • OC1REF 값이 변한다고 해서 바로 OC1이 바뀌지는 않고 output polarity 의 영향을 받음
      • Polarity = 0 (Active High)
        • 기본값 (Default)
        • OC1REF = 1이면 OC1 핀 출력도 HIGH
        • OC1REF = 0이면 OC1 핀 출력도 LOW
      • Polarity = 1 (Active Low)
        • 반전된 출력
        • OC1REF = 1이면 OC1 핀 출력은 LOW
        • OC1REF = 0이면 OC1 핀 출력은 HIGH

 

Calculate Pulse Value

타이머 설정 개요

  • 타이머의 클럭 주파수: 25MHz
  • 출력 채널에서 원하는 주파수: 500Hz
  • 500Hz의 주기: 0.002초 (2ms)

펄스 지속 시간 (Pulse Duration) 계산

  • 50% 듀티 사이클이라면, ON/OFF 시간이 같아야 함→ 즉, 펄스 지속 시간 = 0.001초 (1ms)
  • 0.001초의 주파수는 1kHz→ 즉, 토글 주파수는 1kHz가 되어야 함

타이머 카운트 값 계산

  • 타이머의 클럭 = 25MHz (1초에 25000000만큼 증가)
  • 토글 주파수 = 1kHz (1ms마다 토글링해야함)
  • 타이머가 1kHz에서 토글하려면,

Pulse Value = 25000 (1ms마다 타이머가 25000 증가)

원하는 출력 주파수 토글 주파수 계산된 펄스 값
500Hz 1kHz 25000
1kHz 2kHz 12500
2kHz 4kHz 6250
4kHz 8kHz 3125
TIM_HandleTypeDef htimer2;
UART_HandleTypeDef huart2;

uint32_t pulse1_value = 25000; // to produce 500Hz
uint32_t pulse2_value = 12500; // to produce 1kHz
uint32_t pulse3_value = 6250; // to produce 2kHz
uint32_t pulse4_value = 3125; // to produce 4kHz

void TIMER2_Init(void)
{
	TIM_OC_InitTypeDef tim2OC_init;

	htimer2.Instance = TIM2;
	htimer2.Init.Period = 0XFFFFFFFF;
	htimer2.Init.Prescaler = 1;
	if(HAL_TIM_OC_Init(&htimer2) != HAL_OK) Error_handler();

	tim2OC_init.OCMode = TIM_OCMODE_TOGGLE;
	tim2OC_init.OCPolarity = TIM_OCPOLARITY_HIGH;

	tim2OC_init.Pulse = pulse1_value;
	if(HAL_TIM_OC_ConfigChannel(&htimer2, &tim2OC_init, TIM_CHANNEL_1) != HAL_OK) Error_handler();

	tim2OC_init.Pulse = pulse2_value;
	if(HAL_TIM_OC_ConfigChannel(&htimer2, &tim2OC_init, TIM_CHANNEL_2) != HAL_OK) Error_handler();

	tim2OC_init.Pulse = pulse3_value;
	if(HAL_TIM_OC_ConfigChannel(&htimer2, &tim2OC_init, TIM_CHANNEL_3) != HAL_OK) Error_handler();

	tim2OC_init.Pulse = pulse4_value;
	if(HAL_TIM_OC_ConfigChannel(&htimer2, &tim2OC_init, TIM_CHANNEL_4) != HAL_OK) Error_handler();
}

 

msp.c Configuration

PA2와 PA3는 이미 UART(시리얼 통신)로 할당되어 있기 때문에 사용할 수 없으므로 TIM2 채널 3, 4를 다른 핀으로 대체

  • 채널 3: PB10 사용
  • 채널 4: PB2 사용
void HAL_TIM_OC_MspInit(TIM_HandleTypeDef *htim)
{
	GPIO_InitTypeDef tim2OC_ch_gpios;

	// 1) Enable the peripheral clock for the timeer2 peripheral
	__HAL_RCC_TIM2_CLK_ENABLE();
	__HAL_RCC_GPIOA_CLK_ENABLE();

	// 2) Configure a gpios to behave as timer 2 channel 1, 2, 3, and 4
	tim2OC_ch_gpios.Pin = GPIO_PIN_0 | GPIO_PIN_1;
	tim2OC_ch_gpios.Mode = GPIO_MODE_AF_PP;
	tim2OC_ch_gpios.Speed = GPIO_SPEED_FREQ_LOW;
	tim2OC_ch_gpios.Alternate = GPIO_AF1_TIM2;
	HAL_GPIO_Init(GPIOA, &tim2OC_ch_gpios);

	tim2OC_ch_gpios.Pin = GPIO_PIN_2 | GPIO_PIN_10;
	tim2OC_ch_gpios.Mode = GPIO_MODE_AF_PP;
	tim2OC_ch_gpios.Speed = GPIO_SPEED_FREQ_LOW;
	tim2OC_ch_gpios.Alternate = GPIO_AF1_TIM2;
	HAL_GPIO_Init(GPIOB, &tim2OC_ch_gpios);

	// 3) NVIC settings
	HAL_NVIC_SetPriority(TIM2_IRQn, 15, 0);
	HAL_NVIC_EnableIRQ(TIM2_IRQn);
}

 

Callback Implementation

  • HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
  • CCR1 값 설정:
    • HAL_TIM_ReadCapturedValue()로 CCR1 값을 읽고, HAL_TIM_SET_COMPARE()로 CCR1 값을 설정
int main(void)
{
	HAL_Init();
	SystemClockConfig();
	GPIO_Init();
	UART2_Init();
	TIMER2_Init();

	if(HAL_TIM_OC_Start_IT(&htimer2, TIM_CHANNEL_1) != HAL_OK) Error_handler();
	if(HAL_TIM_OC_Start_IT(&htimer2, TIM_CHANNEL_2) != HAL_OK) Error_handler();
	if(HAL_TIM_OC_Start_IT(&htimer2, TIM_CHANNEL_3) != HAL_OK) Error_handler();
	if(HAL_TIM_OC_Start_IT(&htimer2, TIM_CHANNEL_4) != HAL_OK) Error_handler();

	while(1)
	return 0;
}

uint32_t ccr_content;
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
{
	/* TIM3_CH1 toggling with frequency = 500 Hz */
	if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
	{
		ccr_content = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
		__HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_1, ccr_content + pulse1_value);
	}

	/* TIM3_CH2 toggling with frequency = 1000 Hz */
	if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)
	{
		ccr_content = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2);
		__HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_2, ccr_content + pulse2_value);
	}

	/* TIM3_CH3 toggling with frequency = 2000 Hz */
	if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_3)
	{
		ccr_content = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_3);
		__HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_3, ccr_content + pulse3_value);
	}

	/* TIM3_CH4 toggling with frequency = 4000 Hz */
	if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_4)
	{
		ccr_content = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_4);
		__HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_4, ccr_content + pulse4_value);
	}
}

 

HAL_GPIO_TogglePin() 과 같은 토글 코드를 사용하지 않고 타이머가 직접 CCR register 값과 비교하여 토글링

반응형