stm32F407定时器Sample

stm32的定时器,顾名思义用于定时任务,难点是需要根据定时器的时钟进行计算预分频和计数器。 本例程是以两个定时器,分配控制两个LED灯定时点亮,但是这两个LED点亮间隔时间不同。

一、系统定时器

stm32F407拥有14个定时器,

  1. 高级定时器:TIM1,TIM8,APB2
  2. 基本定时器:TIM6,TIM7,APB1
  3. 通用定时器:TIM2-TIM5,TIM9-TIM14,APB1(TIM9-11,APB2)

二、硬件

任务:通使用定时器控制间隔时间,通过外部中断控制LED灯的闪烁间隔时间

  1. LED1<-->PF7:0.5秒(500ms)闪烁一次
  2. LED2<-->PF8:1秒(1000ms)闪烁一次

三、定时器原理

stm32的定时器有两个核心的概念:分频器(Prescaler)和计数器(Counter Period),当分频器溢出后会触发计数器,计数器溢出后,即定时的时间到了,触发外部中断。

  1. 分频器是16位,即数值在0-65535之间;
  2. 计数器有16位,也有32位,图形化的配置工具中有提示,如TIM2的计数器是32位的,TIM3的计数器是16位的。
  3. 间隔时间计算公式: (Prescaler+1 ) X (Counter Period+1) X 1/ 定时器时钟频率,输入的是1khz。
  4. 如:1khz*32000*500/32M=500ms(32M是设置的时钟输出频率,如果不是这个,需要另外计算,原理一样)

四、配置

  • 时钟(RCC):时钟全部输出频率为32M(初步这样设置,还不了解具体的计时器是挂在那个APB下
  • GPIO:PF7/PF8全部设置为gpio_output,初始输出电频为low
  • 定时器:Timers标签下,计时器参数在Parameter Settings下设置,同时为了使计时器能驱动LED,要启用计时器的NVIC功能,在NVIC Settings下设置开启
      1. TIM2:
          * Clock source:internal clock
          * Prescaler:32000-1=31999
          * Counter Mode:up
          * Counter Period:500-1=499
      2. TIM3:
          * Clock source:internal clock
          * Prescaler:32000-1=31999
          * Counter Mode:up
          * Counter Period:1000-1=999
    

五、调用流程

定时器的调用流程同外部冲断类似,同时通过调用回调函数实现处理功能。

main.c-->tim.c-->stm32f4xx_it.c-->stm32f4xx_hal_tim.c-->main.c

六、代码

tim.c

负责定义计时器,及TIM的初始化,配置定时器的参数。这里定义和初始化的计时器,在main.c中进行调用。

tim.c文件在项目的src目录下

  1. 定义计时器(这里定义两个计时器)

    TIM_HandleTypeDef htim2;
    TIM_HandleTypeDef htim3;
    
  2. 定时器初始化(这个代码是TIM2的初始化代码)

void MX_TIM2_Init(void)
{

  /* USER CODE BEGIN TIM2_Init 0 */

  /* USER CODE END TIM2_Init 0 */

  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  /* USER CODE BEGIN TIM2_Init 1 */

  /* USER CODE END TIM2_Init 1 */
  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 31999;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 499;
  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();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM2_Init 2 */

  /* USER CODE END TIM2_Init 2 */

}

stm32f4xx_it.c

stm32f4xx_it.c文件在项目src目录下

这里的功能是计时器调用外部中断

  1. 引入在tim.c定义的定时器变量

    extern TIM_HandleTypeDef htim2;
    extern TIM_HandleTypeDef htim3;
    
  2. 生成定时器的中断调用函数,此函数调用定时器中断处理函数(这里是TIM2定时器)

    void TIM2_IRQHandler(void)
    {
    /* USER CODE BEGIN TIM2_IRQn 0 */
    
    /* USER CODE END TIM2_IRQn 0 */
    HAL_TIM_IRQHandler(&htim2);
    /* USER CODE BEGIN TIM2_IRQn 1 */
    
    /* USER CODE END TIM2_IRQn 1 */
    }
    

stm32f4xx_hal_tim.c

stm32f4xx_hal_tim.c文件在driver的src目录下

  1. 在此文件中找到上面调用的函数void HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim)
  2. HAL_TIM_IRQHandler函数中找到时间间隔回调函数HAL_TIM_PeriodElapsedCallback
  3. 找到HAL_TIM_PeriodElapsedCallback的函数原型,这是一个虚函数,我们需要实现的功能,需要在main.c文件中,重载这个函数

    __weak void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
    {
    /* Prevent unused argument(s) compilation warning */
    UNUSED(htim);
    
    /* NOTE : This function should not be modified, when the callback is needed,
             the HAL_TIM_PeriodElapsedCallback could be implemented in the user file
    */
    }
    

main.c

在main文件中,重写这个函数,实现我们要用的功能,这里是TIM定时器反转LED的电平。

  1. 重写HAL_TIM_PeriodElapsedCallback函数,实现反转LED的电平

    void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
     if(htim->Instance==TIM2) {
         HAL_GPIO_TogglePin(GPIOF, GPIO_PIN_7);
     }
     if(htim->Instance==TIM3) {
         HAL_GPIO_TogglePin(GPIOF, GPIO_PIN_8);
     }
    }
    
  2. 启动定时器

    在mian.c文件的main方法中,系统各项功能初始化完毕后,通过定时器启动函数启动定时器。 启动函数传入的定时器的指针,变量需要用到&符号

  HAL_TIM_Base_Start_IT(&htim2);
  HAL_TIM_Base_Start_IT(&htim3);

七、其他

  1. 此例程是按照教程的,全部输出时钟设置的32M,暂时没有使用其他时钟频率
  2. STM32CubeIDE功能非常强大,配置后会自动生成代码,只需要在main.c文件的中重写定时器回调函数和启用定时器即可

八、补充(定时器间隔时间的计算)

前面的定时器配置完全是按照教程来的,把系统时钟频率全部设置为32M,但是,STM32F407开发板的芯片时钟最高频率是168M,而且实际应用中,也不可能把系统所有输出的时钟频率设置为32M,重新研究时钟配置和定时器间隔时间的计算。

  1. 系统时钟设置为168M,如:sysclk,hclk的时钟全部配置为168M,其他各输出时钟由图形化工具自动计算;
  2. apb1的预分频系数配置为/4,apb2的预分频系数配置为/2
  3. 前文中已经提到TIM2-TIM5定时器是挂在apb1下的,因此需要根据apb1的预分频系数来计算输出的时钟
  4. 查看时钟树或者查看IDE的时钟图形化配置,可以知道apb1外设时钟是42M(168/4=42),apb1的定时器时钟是84M(168/4*2=84M);
  5. 抄录一段文字 当 APB1 的时钟分频数为 1 的时候,TIM2~7 以及 TIM12~14 的时钟为 APB1 的时钟,而如果 APB1 的时钟分频数不为 1,那么 TIM2~7 以及 TIM12~14 的时钟频率将为 APB1时钟的两倍。

  6. 根据第4、5点,可以知道TIM2,TIM3的时钟频率是84M,这个时钟频率是我们计算间隔定时器频率

  7. 定时器间隔0.5秒(500ms)计算:1khz*(4999+1)*(8399+1)/84Mhz=500ms
  8. 定时器间隔1秒(1000ms)计算:1khz*(9999+1)*(8399+1)/84Mhz=1000ms
  9. 在实际应用中,可以按照以上方法根据系统配置的时钟,计算定时器的间隔时间

results matching ""

    No results matching ""

    results matching ""

      No results matching ""