细说MCU定时器中断的实现方法

目录

一、硬件及工程

二、STM32G4系列MCU的定时器

三、定时器中断的实现过程

1、配置新工程.ioc

2、代码修改

(1)时钟初始化函数MX_TIM3_Init()

(2)使能定时器中断

(3)定时器中断服务函数

(4)重定义定时器回调函数

3、下载并运行

4、修改定时器参数


一、硬件及工程

文章依赖的硬件及工程配置同本文作者的其他文章:细说ARM MCU的串口接收数据的实现过程-CSDN博客 https://wenchm.blog.csdn.net/article/details/139541112

二、STM32G4系列MCU的定时器

STM32G4系列MCU的定时器功能比较强大,有下面几种定时器:

  • 1个高精度定时器(high-resolution timer);
  • 3个高级控制定时器(advanced-control timer);
  • 7个通用定时器(general-purpose timer);
  • 2个基本定时器(basic timer);
  • 2个看门狗定时器(watch-dog timer);
  • 1个SysTick定时器。

|-------------------------|-----------------|--------|------------------|--------------------------------|----------|----------|------|
| | 定时器 | 计数器 精度 | 计数器类型 | 预分频因子 | DMA 请求产生 | 捕捉/ 比较通道 | 互补输出 |
| 高精度 定时器 | HRTIM | 16位 | Up | 1/2/4(x2,x4, x8,x16,x32, 带DLL) | 是 | 12 | 有 |
| 高级 控制 | TIM1、TIM8、TIM20 | 16位 | Up、Down和 Up/Down | 1~65536 之间的整数 | 是 | 4 | 4 |
| 通用 | TIM2和TIM5 | 32位 | Up、Down和 Up/Down | 1~65536 之间的整数 | 是 | 4 | 无 |
| 通用 | TIM3和TIM4 | 16位 | Up、Down和 Up/Down | 1~65536 之间的整数 | 是 | 4 | 无 |
| 通用 | TIM15 | 16位 | Up | 1~65536 之间的整数 | 是 | 2 | 1 |
| 通用 | TIM16、TIM17 | 16位 | Up | 1~65536 之间的整数 | 是 | 1 | 1 |
| 基本 | TIM6、TIM7 | 16位 | Up | 1~65536 之间的整数 | 是 | 0 | 无 |

定时器最基本的功能是起到定时的作用,其中有一个关键模块:计数器(counter)。该计数器可以循环往复计数,计数的模式有三种类型:升、降和升/降。Up模式是从0到最大值递增计数,计到最大值后再从0重新开始计。

除了TIM2和TIM5以外,其余的定时器中,计数器都是16位,相应的计数最大值为65535。除了计数器的参数以外,定时器中的另一个比较重要的参数是预分频因子(prescaler factor),这个参数关系到两次计数之间的计时间隔(具体数值,还要结合定时器的时钟频率来计算)。此外,定时器还可用于输入捕捉,以及产生PWM波形(互补)输出;当然对这两个功能,不同的定时器是有差别的。

三、定时器中断的实现过程

本文利用 STM32G474RE上的通用定时器,以定时器中断的方式控制NUCLEO - G474RE板上的发光二极管LD2以不同的频率闪烁(该功能也可通过延时函数的方式实现)。

1、配置新工程.ioc

  • 配置GPIO:配置PA5为输出,用PA5控制开发板上的LD2。Level=High,mode=PP,Pull_up,speed=High,Label=LED;
  • 外部时钟,Serial Wire;
  • 配置定时器:打开TIM3的配置界面,在模式(Mode)区,将时钟源(Clock Source)选择为Internal Clock;然后,在配置区中,将参数设置(Parameter Settings)选项卡中的预分频因子(Prescaler)和计数器周期(Counter Period)分别设置为999和16999。这两个参数从0开始计数,分频因子为999,实际为分频999+1倍;计数器周期的计算与此相同。这里的计数周期实际就是计数器计数时的最大值 ,在时钟频率确定的情况下,****预分频因子决定着两次计数之间的时间间隔。****所以,根据这两个参数以及定时器的时钟频率,就可以计算出定时器计数的周期。此外,把计数模式(Counter Mode)设置为升模式(Up),并使能自动重载(auto-reload preload)。
  • 配置中断:使能TIM3的全局中断。将它的抢占式优先级设为1,响应优先级设为0。
  • 配置系统时钟:将系统时钟(SYSCLK)频率配置为170 MHz。定时器的时钟来自高级外设总线(APB,Advanced Peripheral Bus),APB的时钟也有自己的预分频因子,如果该因子为1,则定时器的时钟频率就与APB时钟相同,也与系统时钟相同,都是170 MHz。

2、代码修改

至此,硬件配置便完成了。保存,启动代码生成过程,系统会将刚才配置硬件的信息自动转换成代码。

(1)时钟初始化函数MX_TIM3_Init()

该函数自动生成。

cpp 复制代码
/**
  * @brief TIM3 Initialization Function
  * @param None
  * @retval None
  */
static void MX_TIM3_Init(void)
{

  /* USER CODE BEGIN TIM3_Init 0 */

  /* USER CODE END TIM3_Init 0 */

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

  /* USER CODE BEGIN TIM3_Init 1 */

  /* USER CODE END TIM3_Init 1 */
  htim3.Instance = TIM3;
  htim3.Init.Prescaler = 999;
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim3.Init.Period = 16999;
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM3_Init 2 */

  /* USER CODE END TIM3_Init 2 */

}

MX_TIM3_Init()函数主要完成对TIM3的模式和参数配置,如预分频因子、计数模式、计数周期等参数。在MX_TIM3_Init()函数的定义中,用到了一个结构体变量htim3,该结构体变量也被称为定时器句柄。这个变量是在自动代码生成过程中自动生成的,位于main.c文件的最前面:

cpp 复制代码
TIM_HandleTypeDef htim3;

在MX_TIM3_Init()函数的定义中,把设置的参数赋给了结构体变量htim3。其中一条if语句,在其条件表达式中调用了一个函数:HAL_TIM_Base_Init(&htim3);结构体变量htim3通过调用HAL_ TIM_Base_Init(&htim3)实现与实际硬件关联的。该函数只有一个参数。调用时,把刚配置的结构体变量htim3传递了过来。实际上,真正与硬件关联的,还不是HAL_TIM_Base_Init()函数,而是在HAL_TIM_Base_Init()函数中调用的TIM_Base_SetConfig()函数。正是通过TIM_Base_SetConfig()函数,才真正地把设置的参数传递给了相关寄存器。在库函数文件stm32g4xx_hal_tim.c中有对TIM_Base_Set-Config()函数的定义。

(2)使能定时器中断

虽然配置了TIM3的中断功能,但在默认情况下,中断不是开启的。所以,在使用时,还要开启该中断。开启定时器中断可以使用如下库函数:

cpp 复制代码
HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim);

该函数也只有一个参数,并且该参数也是一个结构体变量。对于TIM3来说,其实就可以用前面提到的htim3。开启定时器中断可以使用如下代码:

cpp 复制代码
/*USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim3);    //手动添加
/*USER CODE END 2*/

上述代码位于while(1)循环前面的注释对中。不过,需要将它放到TIM3初始化函数MX_TIM3_ Init()的后面。

(3)定时器中断服务函数

开启TIM3的中断后,当条件满足时,就会执行定时器中断服务函数TIM3_IRQHandler()。该函数是自动生成的,位置在stm32g4xx_it.c文件中有该函数的定义:

cpp 复制代码
/**
  * @brief This function handles TIM3 global interrupt.
  */
void TIM3_IRQHandler(void)
{
  /* USER CODE BEGIN TIM3_IRQn 0 */

  /* USER CODE END TIM3_IRQn 0 */
  HAL_TIM_IRQHandler(&htim3);
  /* USER CODE BEGIN TIM3_IRQn 1 */

  /* USER CODE END TIM3_IRQn 1 */
}

TIM3_IRQHandler()函数的定义中又调用了HAL_TIM_IRQHandler()函数,此函数的定义在stm32g4xxhal_tim.c中。实际上,在HAL_TIM_IRQHandler()函数中,还会调用TIM中断的回调函数HAL_TIM_PeriodElapsedCallback()。这个函数的定义也是在stm32g4xxhal_tim.c中,不过,被定义为一个弱函数。这种方式与串口中断接收过程是类似的。就像对串口接收中断回调函数的处理一样,在定时器中断的使用中,需要做的是在main.c中重新定义TIM中断的回调函数。

(4)重定义定时器回调函数

在main.c中重新定义回调函数HAL_TIM_PeriodElapsedCallback()。在其中让PA5的输出状态翻转。具体实现如下:

cpp 复制代码
/*USER CODE BEGIN 4 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
}
/*USER CODE END 4*/

本例中,定时器的预分频因子(Prescaler)和计数器周期(Counter Period)分别置为999和16999,定时器的时钟频率为170 MHz,最终TIM3中断的周期为

cpp 复制代码
(999+1)/(170×10⁶)×(16999+1)=0.1(s),即频率为10 Hz。

3、下载并运行

编译工程并下载到硬件中运行,会看到LD2灯以5 Hz的频率闪烁。为什么是5 Hz因为控制PA5用的是Toggle。

4、修改定时器参数

修改定时器的预分频因子(Prescaler)和计数器周期(CounPeriod),改变LD2灯的闪烁频率为1 Hz、0.5 Hz等。

cpp 复制代码
(19999+1)/(170×10⁶)×(16999+1)=2(s),即频率为2 Hz。
(9999+1)/(170×10⁶)×(16999+1)=1(s),即频率为1 Hz。
相关推荐
yutian06068 小时前
Keil MDK下载程序后MCU自动重启设置
单片机·嵌入式硬件·keil
析木不会编程11 小时前
【小白51单片机专用教程】protues仿真独立按键控制LED
单片机·嵌入式硬件·51单片机
枯无穷肉15 小时前
stm32制作CAN适配器4--WinUsb的使用
stm32·单片机·嵌入式硬件
不过四级不改名67715 小时前
基于HAL库的stm32的can收发实验
stm32·单片机·嵌入式硬件
嵌入式大圣15 小时前
单片机UDP数据透传
单片机·嵌入式硬件·udp
云山工作室16 小时前
基于单片机的视力保护及身姿矫正器设计(论文+源码)
stm32·单片机·嵌入式硬件·毕业设计·毕设
嵌入式-老费16 小时前
基于海思soc的智能产品开发(mcu读保护的设置)
单片机·嵌入式硬件
qq_3975623118 小时前
MPU6050 , 设置内部低通滤波器,对于输出数据的影响。(简单实验)
单片机
liyinuo201718 小时前
嵌入式(单片机方向)面试题总结
嵌入式硬件·设计模式·面试·设计规范
艺术家天选18 小时前
STM32点亮LED灯
stm32·单片机·嵌入式硬件