Microprocessor/STM32 STM32 PWM 모드 사용하기
  • 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
    반응형
상단으로