你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32F7普通定时器的使用(定时+中断+PWM)

[复制链接]
STMCU小助手 发布时间:2021-12-12 22:03
此篇博客记录的是自己通过CubeMX学习F7系列定时器功能的过程,献给有过标准库开发经验的同学。

基本计时功能
最简单的,定时器嘛,基本的定时器就是定时功能,简单来说就是TIMx->CNT会跟随着输入时钟的脉冲而计数。
初始化定时器的参数,大家都好理解,因为TIM2的输入时钟是108Mhz,这里进行10800分频,输入频率为10K,重装载值设置为20K,每2秒溢出一次。
在HAL_TIM_Base_Init的执行过程中,会先调用HAL_TIM_Base_MspInit再进行其他参数的配置,即先开时钟。

  1. TIM_HandleTypeDef TIM2_Handler;
  2. static void MX_TIM2_Init(void)
  3. {
  4.     TIM2_Handler.Instance = TIM2;
  5.     TIM2_Handler.Init.Prescaler = 10800;
  6.     TIM2_Handler.Init.CounterMode = TIM_COUNTERMODE_UP;
  7.     TIM2_Handler.Init.Period = 20000;
  8.     TIM2_Handler.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;

  9.     HAL_TIM_Base_Init(&TIM2_Handler);
  10.         HAL_TIM_Base_Start(&TIM2_Handler);
  11. }

  12. void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
  13. {
  14.                 __HAL_RCC_TIM2_CLK_ENABLE();            //使能TIM3时钟  
  15. }
复制代码

主函数中每秒打印一次定时器的值:

  1.    while (1)
  2.     {
  3.                 printf("cnt:%d\r\n",TIM2->CNT);
  4.         delay_ms(1000);
  5.     }
复制代码

显示效果如下:

2020042111095122.png


定时器中断
通过HAL_TIM_Base_Start可以开启基本计时功能,但要实现定时器中断功能,就需要开启相应的标志位,即使用HAL_TIM_Base_Start_IT进行定时的开启;
在配置定时器之前,除了要开启时钟,还需要先设置中断优先级,和使能中断向量;
在定时器中断TIM2_IRQHandler服务函数中调用HAL库提供的定时器中断处理函数HAL_TIM_IRQHandler,解析到的定时器超时中断会自动跳转到HAL_TIM_PeriodElapsedCallback;

  1. TIM_HandleTypeDef TIM2_Handler;
  2. static void MX_TIM2_Init(void)
  3. {
  4.     TIM2_Handler.Instance = TIM2;
  5.     TIM2_Handler.Init.Prescaler = 10800;
  6.     TIM2_Handler.Init.CounterMode = TIM_COUNTERMODE_UP;
  7.     TIM2_Handler.Init.Period = 20000;
  8.     TIM2_Handler.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;

  9.     HAL_TIM_Base_Init(&TIM2_Handler);
  10.     //HAL_TIM_Base_Start(&TIM2_Handler);
  11.         HAL_TIM_Base_Start_IT(&TIM2_Handler);
  12. }

  13. void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
  14. {
  15.     __HAL_RCC_TIM2_CLK_ENABLE();            //使能TIM2时钟
  16.     HAL_NVIC_SetPriority(TIM2_IRQn, 1, 3);  //设置中断优先级,抢占优先级1,子优先级3
  17.     HAL_NVIC_EnableIRQ(TIM2_IRQn);          //开启ITM2中断
  18. }

  19. void TIM2_IRQHandler(void)
  20. {
  21.     HAL_TIM_IRQHandler(&TIM2_Handler);
  22. }


  23. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  24. {
  25.     if (htim == (&TIM2_Handler))
  26.     {
  27.         printf("enter irq\r\n");
  28.     }
  29. }
复制代码

显示效果如下:

20200421113641141.png


PWM输出
硬件PWM输出是不需要使用定时器中断的,但同样需要基本的定时参数配置,初始化也不再是使用HAL_TIM_Base_Init而是使用HAL_TIM_PWM_Init进行初始化了;
配置完定时器为PWM模式,那么相应的输出通道也需要通过HAL_TIM_PWM_ConfigChannel进行配置;
同样的启动也不是HAL_TIM_Base_Start或者HAL_TIM_Base_Start_IT了,而是HAL_TIM_PWM_Start了;

  1. TIM_HandleTypeDef TIM2_Handler;
  2. TIM_OC_InitTypeDef TIM2_CH2Handler;
  3. static void MX_TIM2_Init(void)
  4. {
  5.     TIM2_Handler.Instance = TIM2;
  6.     TIM2_Handler.Init.Prescaler = 10800;
  7.     TIM2_Handler.Init.CounterMode = TIM_COUNTERMODE_UP;
  8.     TIM2_Handler.Init.Period = 20000;
  9.     TIM2_Handler.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  10.     HAL_TIM_PWM_Init(&TIM2_Handler);

  11.     TIM2_CH2Handler.OCMode = TIM_OCMODE_PWM1;
  12.     TIM2_CH2Handler.Pulse = 10000;
  13.     TIM2_CH2Handler.OCPolarity = TIM_OCPOLARITY_HIGH;
  14.     TIM2_CH2Handler.OCFastMode = TIM_OCFAST_DISABLE;
  15.     HAL_TIM_PWM_ConfigChannel(&TIM2_Handler, &TIM2_CH2Handler, TIM_CHANNEL_2) ;

  16.     HAL_TIM_PWM_Start(&TIM2_Handler, TIM_CHANNEL_2); //开启PWM通道2
  17. }

  18. void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
  19. {
  20.     GPIO_InitTypeDef GPIO_InitStruct = {0};
  21.                
  22.         __HAL_RCC_TIM2_CLK_ENABLE();           
  23.     __HAL_RCC_GPIOA_CLK_ENABLE();
  24.     GPIO_InitStruct.Pin = GPIO_PIN_1;
  25.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  26.     GPIO_InitStruct.Pull = GPIO_NOPULL;
  27.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  28.     GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
  29.     HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  30. }
复制代码


通过万用表可以发现PA1口的电压在0和3.3V中大概每秒变动一次(疫情期间在家学习,没有示波器真是太惨了……为了确定确实有PWM波形,只能出此下策了T_T),串口打印数据如下:

20200421120810839.png


PWM + 定时器溢出中断
我们都知道,PWM模式是在比较值处翻转,在溢出的时候再次翻转,而溢出的时候,我们也是可以产生中断的;所以同一个定时器,在这种定时时长和周期相同的时候,是可以即做硬件PWM输出,又做溢出中断的。
这里我们观察一下两种初始化函数:

  1. /**
  2.   * @brief  Initializes the TIM Time base Unit according to the specified
  3.   *         parameters in the TIM_HandleTypeDef and create the associated handle.
  4.   * @param  htim: pointer to a TIM_HandleTypeDef structure that contains
  5.   *                the configuration information for TIM module.
  6.   * @retval HAL status
  7.   */
  8. HAL_StatusTypeDef HAL_TIM_Base_Init(TIM_HandleTypeDef *htim)
  9. {
  10.   /* Check the TIM handle allocation */
  11.   if(htim == NULL)
  12.   {
  13.     return HAL_ERROR;
  14.   }

  15.   /* Check the parameters */
  16.   assert_param(IS_TIM_INSTANCE(htim->Instance));
  17.   assert_param(IS_TIM_COUNTER_MODE(htim->Init.CounterMode));
  18.   assert_param(IS_TIM_CLOCKDIVISION_DIV(htim->Init.ClockDivision));

  19.   if(htim->State == HAL_TIM_STATE_RESET)
  20.   {  
  21.     /* Init the low level hardware : GPIO, CLOCK, NVIC */
  22.     HAL_TIM_Base_MspInit(htim);
  23.   }

  24.   /* Set the TIM state */
  25.   htim->State= HAL_TIM_STATE_BUSY;

  26.   /* Set the Time Base configuration */
  27.   TIM_Base_SetConfig(htim->Instance, &htim->Init);

  28.   /* Initialize the TIM state*/
  29.   htim->State= HAL_TIM_STATE_READY;

  30.   return HAL_OK;
  31. }


  32. HAL_StatusTypeDef HAL_TIM_PWM_Init(TIM_HandleTypeDef *htim)
  33. {
  34.   /* Check the TIM handle allocation */
  35.   if(htim == NULL)
  36.   {
  37.     return HAL_ERROR;
  38.   }

  39.   /* Check the parameters */
  40.   assert_param(IS_TIM_INSTANCE(htim->Instance));
  41.   assert_param(IS_TIM_COUNTER_MODE(htim->Init.CounterMode));
  42.   assert_param(IS_TIM_CLOCKDIVISION_DIV(htim->Init.ClockDivision));

  43.   if(htim->State == HAL_TIM_STATE_RESET)
  44.   {
  45.     /* Allocate lock resource and initialize it */
  46.     htim->Lock = HAL_UNLOCKED;  
  47.     /* Init the low level hardware : GPIO, CLOCK, NVIC and DMA */
  48.     HAL_TIM_PWM_MspInit(htim);
  49.   }

  50.   /* Set the TIM state */
  51.   htim->State= HAL_TIM_STATE_BUSY;  

  52.   /* Init the base time for the PWM */  
  53.   TIM_Base_SetConfig(htim->Instance, &htim->Init);

  54.   /* Initialize the TIM state*/
  55.   htim->State= HAL_TIM_STATE_READY;

  56.   return HAL_OK;
  57. }  
复制代码

两者除了底层的HAL_TIM_Base_MspInit和HAL_TIM_PWM_MspInit,其他地方一模一样,所以我们随便调用一个初始化就可以了,但是要注意,我们在HAL_TIM_Base_MspInit里配置了NVIC,在HAL_TIM_PWM_MspInit里配置了GPIO,所以只调用一个的话,必须手动把底层的粘贴到另一个函数里去,如果是想两个都调用一次,这里就需要考虑htim->State这个变量了,因为在调用了一次之后,其值就从HAL_TIM_STATE_RESET变成了HAL_TIM_STATE_READY,后面的一个就得不到执行了。
再来看启动代码:

  1. /**
  2.   * @brief  Starts the TIM Base generation.
  3.   * @param  htim: pointer to a TIM_HandleTypeDef structure that contains
  4.   *                the configuration information for TIM module.
  5.   * @retval HAL status
  6.   */
  7. HAL_StatusTypeDef HAL_TIM_Base_Start(TIM_HandleTypeDef *htim)
  8. {
  9.   /* Check the parameters */
  10.   assert_param(IS_TIM_INSTANCE(htim->Instance));

  11.   /* Set the TIM state */
  12.   htim->State= HAL_TIM_STATE_BUSY;

  13.   /* Enable the Peripheral */
  14.   __HAL_TIM_ENABLE(htim);

  15.   /* Change the TIM state*/
  16.   htim->State= HAL_TIM_STATE_READY;

  17.   /* Return function status */
  18.   return HAL_OK;
  19. }


  20. /**
  21.   * @brief  Starts the TIM Base generation in interrupt mode.
  22.   * @param  htim: pointer to a TIM_HandleTypeDef structure that contains
  23.   *                the configuration information for TIM module.
  24.   * @retval HAL status
  25.   */
  26. HAL_StatusTypeDef HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim)
  27. {
  28.   /* Check the parameters */
  29.   assert_param(IS_TIM_INSTANCE(htim->Instance));

  30.   /* Enable the TIM Update interrupt */
  31.   __HAL_TIM_ENABLE_IT(htim, TIM_IT_UPDATE);

  32.   /* Enable the Peripheral */
  33.   __HAL_TIM_ENABLE(htim);

  34.   /* Return function status */
  35.   return HAL_OK;
  36. }


  37. /**
  38.   * @brief  Starts the PWM signal generation.
  39.   * @param  htim: pointer to a TIM_HandleTypeDef structure that contains
  40.   *                the configuration information for TIM module.
  41.   * @param  Channel: TIM Channels to be enabled.
  42.   *          This parameter can be one of the following values:
  43.   *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
  44.   *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
  45.   *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
  46.   *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
  47.   * @retval HAL status
  48.   */
  49. HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel)
  50. {
  51.   /* Check the parameters */
  52.   assert_param(IS_TIM_CCX_INSTANCE(htim->Instance, Channel));

  53.   /* Enable the Capture compare channel */
  54.   TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);

  55.   if(IS_TIM_ADVANCED_INSTANCE(htim->Instance) != RESET)  
  56.   {
  57.     /* Enable the main output */
  58.     __HAL_TIM_MOE_ENABLE(htim);
  59.   }

  60.   /* Enable the Peripheral */
  61.   __HAL_TIM_ENABLE(htim);

  62.   /* Return function status */
  63.   return HAL_OK;
  64. }
复制代码


其中的核心代码分别是:

  1. __HAL_TIM_ENABLE(htim);
复制代码
  1.   __HAL_TIM_ENABLE_IT(htim, TIM_IT_UPDATE);
  2.   __HAL_TIM_ENABLE(htim);
复制代码
  1.   TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);
  2.   __HAL_TIM_ENABLE(htim);
复制代码
那么我们可以不用hal库提供给我们的启动函数,直接写三句话,就实现了定时器中断+PWM功能了。

  1.   __HAL_TIM_ENABLE_IT(htim, TIM_IT_UPDATE);
  2.   TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);
  3.   __HAL_TIM_ENABLE(htim);
复制代码

完整代码如下:

  1. TIM_HandleTypeDef TIM2_Handler;
  2. TIM_OC_InitTypeDef TIM2_CH2Handler;


  3. static void MX_TIM2_Init(void)
  4. {
  5.     __HAL_RCC_TIM2_CLK_ENABLE();
  6.     HAL_NVIC_SetPriority(TIM2_IRQn, 1, 3);
  7.     HAL_NVIC_EnableIRQ(TIM2_IRQn);

  8.     TIM2_Handler.Instance = TIM2;
  9.     TIM2_Handler.Init.Prescaler = 10800;
  10.     TIM2_Handler.Init.CounterMode = TIM_COUNTERMODE_UP;
  11.     TIM2_Handler.Init.Period = 20000;
  12.     TIM2_Handler.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;

  13.     HAL_TIM_PWM_Init(&TIM2_Handler);

  14.     TIM2_CH2Handler.OCMode = TIM_OCMODE_PWM1;
  15.     TIM2_CH2Handler.Pulse = 10000;
  16.     TIM2_CH2Handler.OCPolarity = TIM_OCPOLARITY_HIGH;
  17.     TIM2_CH2Handler.OCFastMode = TIM_OCFAST_DISABLE;
  18.     HAL_TIM_PWM_ConfigChannel(&TIM2_Handler, &TIM2_CH2Handler, TIM_CHANNEL_2) ;

  19.     __HAL_TIM_ENABLE_IT(&TIM2_Handler, TIM_IT_UPDATE);
  20.     TIM_CCxChannelCmd(TIM2_Handler.Instance, TIM_CHANNEL_2, TIM_CCx_ENABLE);
  21.     __HAL_TIM_ENABLE(&TIM2_Handler);
  22. }


  23. void TIM2_IRQHandler(void)
  24. {
  25.     HAL_TIM_IRQHandler(&TIM2_Handler);
  26. }


  27. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  28. {
  29.     if (htim == (&TIM2_Handler))
  30.     {
  31.         printf("enter irq\r\n");
  32.     }
  33. }


  34. void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
  35. {
  36.     GPIO_InitTypeDef GPIO_InitStruct = {0};

  37.     __HAL_RCC_TIM2_CLK_ENABLE();
  38.     __HAL_RCC_GPIOA_CLK_ENABLE();
  39.     GPIO_InitStruct.Pin = GPIO_PIN_1;
  40.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  41.     GPIO_InitStruct.Pull = GPIO_NOPULL;
  42.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  43.     GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
  44.     HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  45. }
复制代码


收藏 评论0 发布时间:2021-12-12 22:03

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版