Microprocessor/STM32

STM32 PWM 모드 사용하기

Dexter_- 2019. 3. 7. 15:46
728x90
반응형

 

STM32 PWM 모드 사용

 

(feat. open103V)


 

 

PWM 이란 ?

PWM(Pulse Width Modulation)이란, 펄스폭 변조로 만들어지는 신호로서, 한 주기동안 H와 L의 비율(Duty rate)을 변화시켜 변조하는 방식이다.

 

PWM은 신호의 주기, 펄스 폭(Duty rate), 전압 레벨로 정의 할 수 있다.

 

 

PWM 모드

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 주기로 생성되는 파형을 스코프로 측정할 수 있다.

 

 

 

PWM을 이용한 LED 점멸

 

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);
        }
    }
}
 

 

 

 

 

 

 

 

728x90
반응형