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

基于STM32F7使用定时器经验分享

[复制链接]
STMCU小助手 发布时间:2023-3-13 22:40
基本计时功能
最简单的,定时器嘛,基本的定时器就是定时功能,简单来说就是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.   
  16.   /* Check the parameters */
  17.   assert_param(IS_TIM_INSTANCE(htim->Instance));
  18.   assert_param(IS_TIM_COUNTER_MODE(htim->Init.CounterMode));
  19.   assert_param(IS_TIM_CLOCKDIVISION_DIV(htim->Init.ClockDivision));
  20.   
  21.   if(htim->State == HAL_TIM_STATE_RESET)
  22.   {  
  23.     /* Init the low level hardware : GPIO, CLOCK, NVIC */
  24.     HAL_TIM_Base_MspInit(htim);
  25.   }
  26.   
  27.   /* Set the TIM state */
  28.   htim->State= HAL_TIM_STATE_BUSY;
  29.   
  30.   /* Set the Time Base configuration */
  31.   TIM_Base_SetConfig(htim->Instance, &htim->Init);
  32.   
  33.   /* Initialize the TIM state*/
  34.   htim->State= HAL_TIM_STATE_READY;
  35.   
  36.   return HAL_OK;
  37. }


  38. HAL_StatusTypeDef HAL_TIM_PWM_Init(TIM_HandleTypeDef *htim)
  39. {
  40.   /* Check the TIM handle allocation */
  41.   if(htim == NULL)
  42.   {
  43.     return HAL_ERROR;
  44.   }

  45.   /* Check the parameters */
  46.   assert_param(IS_TIM_INSTANCE(htim->Instance));
  47.   assert_param(IS_TIM_COUNTER_MODE(htim->Init.CounterMode));
  48.   assert_param(IS_TIM_CLOCKDIVISION_DIV(htim->Init.ClockDivision));

  49.   if(htim->State == HAL_TIM_STATE_RESET)
  50.   {
  51.     /* Allocate lock resource and initialize it */
  52.     htim->Lock = HAL_UNLOCKED;  
  53.     /* Init the low level hardware : GPIO, CLOCK, NVIC and DMA */
  54.     HAL_TIM_PWM_MspInit(htim);
  55.   }

  56.   /* Set the TIM state */
  57.   htim->State= HAL_TIM_STATE_BUSY;  
  58.   
  59.   /* Init the base time for the PWM */  
  60.   TIM_Base_SetConfig(htim->Instance, &htim->Init);
  61.    
  62.   /* Initialize the TIM state*/
  63.   htim->State= HAL_TIM_STATE_READY;
  64.   
  65.   return HAL_OK;
  66. }  
复制代码

两者除了底层的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.   
  12.   /* Set the TIM state */
  13.   htim->State= HAL_TIM_STATE_BUSY;
  14.   
  15.   /* Enable the Peripheral */
  16.   __HAL_TIM_ENABLE(htim);
  17.   
  18.   /* Change the TIM state*/
  19.   htim->State= HAL_TIM_STATE_READY;
  20.   
  21.   /* Return function status */
  22.   return HAL_OK;
  23. }


  24. /**
  25.   * @brief  Starts the TIM Base generation in interrupt mode.
  26.   * @param  htim: pointer to a TIM_HandleTypeDef structure that contains
  27.   *                the configuration information for TIM module.
  28.   * @retval HAL status
  29.   */
  30. HAL_StatusTypeDef HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim)
  31. {
  32.   /* Check the parameters */
  33.   assert_param(IS_TIM_INSTANCE(htim->Instance));
  34.   
  35.   /* Enable the TIM Update interrupt */
  36.   __HAL_TIM_ENABLE_IT(htim, TIM_IT_UPDATE);
  37.       
  38.   /* Enable the Peripheral */
  39.   __HAL_TIM_ENABLE(htim);
  40.       
  41.   /* Return function status */
  42.   return HAL_OK;
  43. }


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

  60.   /* Enable the Capture compare channel */
  61.   TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);
  62.   
  63.   if(IS_TIM_ADVANCED_INSTANCE(htim->Instance) != RESET)  
  64.   {
  65.     /* Enable the main output */
  66.     __HAL_TIM_MOE_ENABLE(htim);
  67.   }
  68.    
  69.   /* Enable the Peripheral */
  70.   __HAL_TIM_ENABLE(htim);
  71.   
  72.   /* Return function status */
  73.   return HAL_OK;
  74. }
复制代码

其中的核心代码分别是:
  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.    
  14.     HAL_TIM_PWM_Init(&TIM2_Handler);

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

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


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


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


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

  38.     __HAL_RCC_TIM2_CLK_ENABLE();
  39.     __HAL_RCC_GPIOA_CLK_ENABLE();
  40.     GPIO_InitStruct.Pin = GPIO_PIN_1;
  41.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  42.     GPIO_InitStruct.Pull = GPIO_NOPULL;
  43.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  44.     GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
  45.     HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  46. }
复制代码
————————————————
版权声明:小盼你最萌哒


收藏 评论0 发布时间:2023-3-13 22:40

举报

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