STM32 PWM 모드 사용
(feat. open103V)
PWM(Pulse Width Modulation)이란, 펄스폭 변조로 만들어지는 신호로서, 한 주기동안 H와 L의 비율(Duty rate)을 변화시켜 변조하는 방식이다.
PWM은 신호의 주기, 펄스 폭(Duty rate), 전압 레벨로 정의 할 수 있다.
PA0 핀에 1kHz 에 듀티비 50%의 PWM 파형을 생성한다.
TIM2는 APB1 Clock에 따라서 72MHz를 Source로 입력받고 Counter Period 가 999이고 Prescaler가 71이기때문에 1kHz로 동작한다.
Pulse 값을 500으로 설정하였기 때문에 Duty Ratio는 500/(999+1) = 50%가 된다.
주기설정 계산은 아래와 같다.
STM32CubeMx TIM2 설정
PWM 코드 일부분
#include "main.h"
#include "cmsis_os.h"
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the CPU, AHB and APB busses clocks */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
Error_Handler();
}
/** Initializes the CPU, AHB and APB busses clocks */
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|
RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) {
Error_Handler();
}
}
static void MX_TIM2_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
htim2.Instance = TIM2;
htim2.Init.Prescaler = 72-1;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 1000-1;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK) {
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK) {
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htim2) != HAL_OK) {
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK) {
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 500;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK) {
Error_Handler();
}
HAL_TIM_MspPostInit(&htim2);
}
int main(void)
{
HAL_Init();
SystemClock_Confi();
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_TIM2_Init();
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128);
defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);
INIT_Thread();
osKernelStart();
while (1) {}
}
PWM 파형을 출력하기 위해서 main 함수에서HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
함수를 호출해야 한다.
아래와 같이 1msec 주기로 생성되는 파형을 스코프로 측정할 수 있다.
HAL_TIM_ReadCapturedVallue(&htim2, TIM_CHANNEL_1);
함수를 사용하여 듀티 값을 획득한다. 아래 코드와 같이 Pulse 값이 0이므로 듀티 값은 0으로 획득된다.
__HAL_TIM_GET_AUTORELOAD(&htim2);
함수를 사용하여 최대 Period 값을 획득한다. 아래 코드와 같이 Period는 1000-이므로 999값을 획득한다.
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, duty);
함수를 사용하여 듀티값을 설정하고 듀티 값은 0 ~ 999 값을 설정할 수 있다.
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the CPU, AHB and APB busses clocks */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
Error_Handler();
}
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|
RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) {
Error_Handler();
}
}
static void MX_TIM2_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
htim2.Instance = TIM2;
htim2.Init.Prescaler = 72-1;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 1000-1;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK) {
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK) {
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htim2) != HAL_OK) {
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK) {
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK) {
Error_Handler();
}
HAL_TIM_MspPostInit(&htim2);
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
MX_SPI3_Init();
MX_FSMC_Init();
MX_SPI1_Init();
MX_TIM2_Init();
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
duty = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_1);
autoReload = __HAL_TIM_GET_AUTORELOAD(&htim2);
printf("duty : %d\r\n", duty);
printf("autoReload : %d\r\n", autoReload);
osThreadDef(defaultTask, StartDefaultTask, osPriorityRealtime, 0, 128);
defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);
INIT_Thread();
osKernelStart();
while (1) {
while(duty < autoReload) {
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, ++duty);
HAL_Delay(1);
}
while(duty > 0) {
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, --duty);
HAL_Delay(1);
}
}
}
[개발 노트] TLC5955 LED 드라이버 밝기 오류 (0) | 2020.04.17 |
---|---|
[개발노트] SPI Slave 모드에서 BSY Flag 에러 (0) | 2020.04.17 |
STM32 TIMER Counter Mode (0) | 2019.03.06 |
TLC5947 24-Channel, 12-Bit PWM LED Driver (0) | 2019.02.21 |
애노드와 캐소드 차이점(Anode Cathod) (0) | 2019.02.20 |