STM32基于HAL库的PWM

作者: lissettecarlr | 来源:发表于2019-04-16 19:14 被阅读0次

    1 裸机

    1.1工程建立

    使用STM32CubeMX来生成工程,基本使用也可见链接

    • 选择外部时钟


      image.png
    • 选择SW调试


      image.png
    • 串口1作为打印


      image.png
    • 配置时钟,8M外部输入,32M系统时钟


      image.png
    • 选择PA2为PWM的通道


      image.png
    • 配置对于的时钟TIM2,选择通道3


      image.png
    • 配置该PWM通道,系统时钟32M


      image.png
    • 生产工程的配置


      image.png
      image.png
    • 最后生成的工程(GENERATE CODE)

    1.2 分析生产的代码

    主函数中初始化了GPIO和TIM,前者仅仅打开了GPIO的时钟,主要初始化在TIM中。

    //main.h
      MX_GPIO_Init();
      MX_TIM2_Init();
    

    在tim.c则进行了对于通道引脚初始化和定时器PWM初始化

    //初始化PA2对于定时器TIM2的CH3
    void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)
    {
    
      GPIO_InitTypeDef GPIO_InitStruct = {0};
      if(timHandle->Instance==TIM2)
      {
        __HAL_RCC_GPIOA_CLK_ENABLE();
        GPIO_InitStruct.Pin = GPIO_PIN_2;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
      }
    }
    

    定时器初始化的时候将进行时钟配置,这决定了PWM的频率,Prescaler表示分频数,表示如果最终分频到TIM的时钟是32M,那么实际使用时钟则是32/Prescaler。Period代表定时器计数达到多少后重新装填,对于PWM来说就代表了一个周期,所以PWM的频率就等于48M/Prescaler/Period。Pulse就对应占空比了,他代表当前计数,占空比 = Pulse/Period

    void MX_TIM2_Init(void)
    {
      TIM_MasterConfigTypeDef sMasterConfig = {0};
      TIM_OC_InitTypeDef sConfigOC = {0};
    
      htim2.Instance = TIM2;
      htim2.Init.Prescaler = 32-1;//分频
      htim2.Init.CounterMode = TIM_COUNTERMODE_UP;//向上计数
      htim2.Init.Period = 1000-1;//阈值
      htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
      if (HAL_TIM_PWM_Init(&htim2) != HAL_OK)
      {
        Error_Handler();
      }
      sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
      sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
      if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
      {
        Error_Handler();
      }
      sConfigOC.OCMode = TIM_OCMODE_PWM1;
      sConfigOC.Pulse = 0;//PWM初始计数值  0/1000
      sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
      sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
      if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
      {
        Error_Handler();
      }
      HAL_TIM_MspPostInit(&htim2);
    
    }
    

    之后的启动和占空比修改就需要自己调用stm32l1xx_hal.time.c中的函数来执行了

    函数 说明
    HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel) 启动对应通道的PWM
    HAL_StatusTypeDef HAL_TIM_PWM_Stop(TIM_HandleTypeDef *htim, uint32_t Channel) 停止对应通道的PWM
    说明
    __HAL_TIM_SET_COMPARE(HANDLE, CHANNEL, COMPARE) 配置对于通道占空比

    下面是在tim.c中添加的函数

    void USR_TIM_PWM_OpenTim2Ch3()
    {
        HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_3);
    }
    
    void USR_TIM_PWM_CloseTim2Ch3()
    {
      HAL_TIM_PWM_Stop(&htim2,TIM_CHANNEL_3);
    }
    
    //duty == 0~1000
    void USR_TIM_PWM_SetCompare(uint16_t duty)
    {
       if(duty >1000) //这里1000是因为计数阈值被设置成了1千
         {
              duty=1000;
         }
         __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_3, duty);
       
    }
    

    最后在主函数中调用进行测试
    先添加打印定向,使其能够printf输出

    #include "stdio.h"
    #ifdef __GNUC__
    #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
    #else
    #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
    #endif /* __GNUC__ */
    PUTCHAR_PROTOTYPE
    {
        HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);
        return ch;
    }
    

    然后启动PWM,设置占空比50%

     USR_TIM_PWM_OpenTim2Ch3();
    USR_TIM_PWM_SetCompare(500); //50%
    

    1.3 STM32L系列定时器通道对应关系

    引脚号 定时器通道
    PA0 TIM2_CH1_ETR
    PA1 TIM2_CH2
    PA2 TIM2_CH3
    PA3 TIM2_CH4
    PA6 TIM3_CH1
    PA7 TIM3_CH2
    PB0 TIM3_CH3
    PB1 TIM3_CH4
    PB6 TIM4_CH1
    PB7 TIM4_CH2
    PB8 TIM4_CH3
    PB9 TIM4_CH4

    2 RT-thread

    2.1 相关文件导入

    这里不使用scons,而是手动添加,第一步是加入HAL库文件
    stm32l1xx_hal_gpio.c
    stm32l1xx_hal_gpio_ex.c
    stm32l1xx_hal_tim.c
    stm32l1xx_hal_time_ex.c
    然后在stm32l1xx_hal_conf.h中打开宏定义,关于这里,如果是使用cube生成的工程,这些是根据配置自动生成的。

    #define HAL_GPIO_MODULE_ENABLED
    #define HAL_TIM_MODULE_ENABLED
    

    第二步,引入rtthread的PWM框架,该文件位于rt-thread-master\components\drivers\misc的rt_drv_pwm.c,无需修改,也没有头文件。底层驱动将会根据该框架来实现。
    第三步是添加PWM的底层驱动,这部分使用STM系列新品是无需编写的,在libraries/HAL_Drivers中drv_pwm.c,该文件有一个pwm_config.h用来基本配置,里面可以看出设备名和定时器之间的关系,例

    #ifdef BSP_USING_PWM2
    #ifndef PWM2_CONFIG
    #define PWM2_CONFIG                             \
        {                                           \
           .tim_handle.Instance     = TIM2,         \
           .name                    = "pwm2",       \
           .channel                 = 0             \
        }
    #endif /* PWM2_CONFIG */
    #endif /* BSP_USING_PWM2 */
    

    关于设备的注册是在rtconfig.h中用宏来决定的,例如需要初始化定时器3的PWM,则需要定义

    #define BSP_USING_PWM
    #define BSP_USING_PWM3
    

    实际上该宏决定了stm32_pwm_obj数组的内容,在stm32_pwm_init函数中,会根据该数组来注册PWM设备。
    文件末尾能够看到初始化被自启动,也表明pwm3这个设备我们可以直接find了

    INIT_DEVICE_EXPORT(stm32_pwm_init);
    

    3 下载

    裸机代码9zqy
    rt_thread代码

    相关文章

      网友评论

        本文标题:STM32基于HAL库的PWM

        本文链接:https://www.haomeiwen.com/subject/pikrvqtx.html