본문으로 바로가기

 PWM Introduction

  • PWM(Pulse Width Modulation) : 펄스의 폭을 조정하는 방식
    • 신호의 주기를 조정하는 방식으로, 주로 출력 전압을 제어하거나 디지털/아날로그 통신, 로봇 공학 등에서 사용 (스테퍼 모터나 DC 모터의 제어)
  • 디지털 회로에서, 마이크로컨트롤러의 디지털 핀은 논리 0은 0V, 논리 1은 3.3V 또는 1.8V를 의미
    • STM32 마이크로컨트롤러의 GPIO 핀)
      • 논리 0 → GND, 논리 1 → VCC(3.3V)
      • 0V와 3.3V 사이의 출력을 얻으려면 PWM 필요
  • 주기(period)가 1초인 신호에서, 신호가 50%의 시간 동안은 3.3V로, 나머지 50%는 0V로 유지
    • T ON(켜짐 시간) / T OFF(꺼짐 시간) ( Duty cycle : 50%)
    • Duty cycle : ON 시간의 비율, 여기서는 평균 전압이 3.3V의 절반인 1.65V
    • Duty cycle 을 늘리면, 예를 들어 ON 시간이 75%로 증가하면 평균 전압도 증가
    • PWM을 이용해서 Duty cycle을 조절할 수 있음

PWM Exercise

목표 : PWM mode를 사용하여 TIMER2 channel에 대해 PWM signal(25%, 45%, 75%, 90% duty cycle) 생성하기

 

1) Initialize the TIMER output Compare Time base

HAL_TIM_PWM_init(TIM_HandleTypeDef*htim)

이전 Timer2 초기화 방식과 동일

 

2) Configure output channel of the timer

HAL_TIM_**PWM**_ConfigChannel(TIM_HandleTypeDef*htim, TIM_**PWM**_InitTypeDef* sConfig, uint32_t Channel)

 

Duty cycle Configuration

<가정>

  • 40% 듀티 사이클 신호를 채널 1에서 생성
  • 주기 = 1sec (update events occur every second)

 

1초의 주기를 얻기 위한 ARR 값 계산

  • ARR 값이 10000일 때, 1초의 주기를 생성

 

40% duty cycle을 위한 pulse value 계산

  • 40% 듀티 사이클은 전체 주기의 40%가 ON인 상태이므로, Pulse 값은 ARR 값의 40%
  • 10000 × 40% = 4000
    • Pulse 필드에 4000을 입력하면, 타이머의 카운터가 4000에 도달할 때 출력 신호가 토글
    • 90% duty cycle ⇒ Pulse value = 9000

Polarity of signal

극성에 따라 출력 신호의 ON/OFF 상태 달라짐

  • Polarity HIGH ⇒ 카운트 값이 CCR1보다 작을 때, 출력 HIGH
  • Polarity LOW ⇒ 카운트 값이 CCR1보다 작을 때, 출력 LOW

 

TIM_HandleTypeDef htimer2;
UART_HandleTypeDef huart2;

int main(void)
{
	HAL_Init();
	SystemClockConfig();
	GPIO_Init();
	UART2_Init();
	TIMER2_Init();

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

	while(1)
	return 0;
}

void TIMER2_Init(void)
{
	TIM_OC_InitTypeDef tim2PWM_init;

	htimer2.Instance = TIM2;
	htimer2.Init.Period = 10000-1;
	htimer2.Init.Prescaler = 4999;
	if(HAL_TIM_PWM_Init(&htimer2) != HAL_OK)Error_handler();
	
	memset(&tim2PWM_init, 0, sizeof(tim2PWM_init));

	tim2PWM_init.OCMode = TIM_OCMODE_PWM1;
	tim2PWM_init.OCPolarity = TIM_OCPOLARITY_HIGH;

	tim2PWM_init.Pulse = (htimer2.Init.Period * 25) / 100;
	if(HAL_TIM_PWM_ConfigChannel(&htimer2, &tim2PWM_init, TIM_CHANNEL_1) != HAL_OK) Error_handler();

	tim2PWM_init.Pulse = (htimer2.Init.Period * 45) / 100;
	if(HAL_TIM_PWM_ConfigChannel(&htimer2, &tim2PWM_init, TIM_CHANNEL_2) != HAL_OK) Error_handler();

	tim2PWM_init.Pulse = (htimer2.Init.Period * 75) / 100;
	if(HAL_TIM_PWM_ConfigChannel(&htimer2, &tim2PWM_init, TIM_CHANNEL_3) != HAL_OK) Error_handler();

	tim2PWM_init.Pulse = (htimer2.Init.Period * 95) / 100;
	if(HAL_TIM_PWM_ConfigChannel(&htimer2, &tim2PWM_init, TIM_CHANNEL_4) != HAL_OK) Error_handler();
}
  • msp.c 는 이전에 작성했던 HAL_TIM_OC_MspInit(TIM_HandleTypeDef *htim) 를 HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim) 로 함수명만 변경해 주면 됨

LED brightness control using PWM signal

목표 : LED가 어두운 상태에서 점점 밝아졌다가 다시 어두워지는 효과 구현. PWM을 활용하여 LED에 공급되는 평균 전압 조절

  • 듀티 사이클 증감 로직 구현
    • 초기 듀티 사이클을 0%에서 시작하여 100%까지 증가.
    • 이후 다시 0%로 감소하도록 설정.
    • CCR1(Capture Compare Register)에 값을 지속적으로 업데이트.
  • 코드 구성
    • brightness 변수 생성하여 CCR1에 저장.
    • HAL_TIM_SET_COMPARE() 함수 사용하여 타이머 핸들, 채널 번호, 비교 값 전달.
    • 1ms 지연을 추가하여 프로세서 속도를 조절.
  • 하드웨어 및 타이머 주기 고려
    • 타이머 주파수: 10MHz, 프로세서 주파수: 50MHz
    • CCR1 레지스터는 16비트 값 사용 → uint16_t 적용.
    • 주기값(Period) 1ms = 9999 (10,000 - 1).
  • PWM 신호 제어 로직

int main(void)
{
	uint32_t brightness = 0;
	HAL_Init();
	SystemClockConfig();
	GPIO_Init();
	UART2_Init();
	TIMER2_Init();

	if(HAL_TIM_PWM_Start(&htimer2, TIM_CHANNEL_1) != HAL_OK) Error_handler();

	while(1)
	{
		while(brightness < htimer2.Init.Period)
			{
				brightness+=10;
				__HAL_TIM_SET_COMPARE(&htimer2, TIM_CHANNEL_1, brightness);
				HAL_Delay(1);
			}

			while(brightness > 0)
			{
				brightness-=10;
				__HAL_TIM_SET_COMPARE(&htimer2, TIM_CHANNEL_1, brightness);
				HAL_Delay(1);
			}
	}

	return 0;
}
void HAL_TIM_PWM_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_5; // NUCLEO 보드 내부 LED
	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);

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