STM32F4-普通计时器TIM9-回调函数执行过程详解

乐活   2025-02-05 14:03   内蒙古  

这个定时器是倒数的,数字大的,功能少。

我找到了一段比我形象的解读。

定时器的基本功能是计时功能,如同闹铃一般,设定好对应的时间后,会在设定的时刻响起铃声。

不管多复杂,都是这三个寄存器

分频(PSC):时钟上不同的指针需要有不同的速度,也就是不同的频率,从而精确的表示时间,比如秒针,分针,时针,这三者相邻的频率之比都是 60:1,即秒针每转过 60 格分针转动 1 格,分针转动 60 格时针转动 1 格,所以分针对于秒针的分频为 60。
计数(CNT):时钟所对应的值都是与工作时间成正比的,比如秒针转动 10 格,意味着过了 10秒,同样定时器中的计数也是和计数时间成正比的值,频率越高增长速度越快。
重载(ARR):时、分、秒的刻度都是有上限的,一个表盘最多记 12 小时, 60 分钟, 60 秒,如果继续增加的话就会回到 0。同样的在定时器中也需要重载,当定时器中的计数值达到重载值时,计数值就会被清零。
时钟源处的时钟信号经过预分频寄存器,按照预分频寄存器内部的值进行分频。比如时钟源的频率为 16MHz,而预分频寄存器中设置的值为 16:1,那么通过预分频后进入定时器的时钟频率就下降到了 1MHz。
在已经分频后的定时器时钟驱使下, TIMx_CNT 根据该时钟的频率向上计数,直到TIMx_CNT 的值增长到与设定的自动重装载寄存器 TIMx_ARR 相等时, TIMx_CNT 被清空,并重新从 0 开始向上计数, TIMx_CNT 增长到 TIMx_ARR 中的值后被清空时产生一个定时中断触发信号。综上定时器触发中断的时间是由设定的 TIMx_PSC 中的分频比和TIMx_ARR 中的自动重装载值共同决的。

可以看到是和TIM1的中断重叠的,需要程序里面判断

在NVIC里面确实是这样的

这个是使能

Bit0就是管这个的

每一种中断都有对应的中断函数,当中断发生时,程序会自动跳转到处理函数处运行,而不需要人为进行调用。
如第一节中,当定时器的计数值增长到重载值时,在清空计数值的同时,会触发一次定时器中断,即定时器更新中断。只要设定好定时器的重载值,就可以保证定时器中断以固定的频率被触发。

输入模式是使用这个内置的这个时钟

参数没几个,下面是时钟进来太快,会分频一下

其实就是配置这个结构体

GUI选好的

就把这个htim9传给这个函数来启动

这些是TIM可以传入的定时器源,在这里

时钟也OK

有着丰富的中断事件

也就是这个图里面最小的定时器部分,进来以后到CK_PSC,往后:

就是要这些事件

注意,HAL库里面这个定时器的中断没有写,需要自己写上

开始运行以后,有事件产生,然后就可以编程控制了。

这些就是我们感兴趣的中断事件

在HAL库

所有的回调函数,在这里都是弱定义

需要复制一个函数头过来重新写

每个回调函数在特定的定时器事件发生时被调用。

HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef htim)
功能: 当定时器的计数器达到自动重装载值(即定时器溢出)时调用。
应用场景: 通常用于周期性任务,如定时器中断。

就是这里

HAL_TIM_PeriodElapsedHalfCpltCallback(TIM_HandleTypeDef htim)
功能: 当定时器的计数器达到自动重装载值的一半时调用。
应用场景: 适用于需要在中途执行某些操作的场景。
HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef htim)
功能: 当输出比较(Output Compare)延迟时间到达时调用。
应用场景: 用于需要精确控制输出信号延迟的应用。
HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef htim)
功能: 当输入捕获(Input Capture)事件发生时调用。
应用场景: 用于测量外部信号的频率或脉冲宽度。
HAL_TIM_IC_CaptureHalfCpltCallback(TIM_HandleTypeDef htim)
功能: 当输入捕获事件完成一半时调用。
应用场景: 适用于需要在中途处理捕获数据的场景。
HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef htim)
功能: 当PWM脉冲完成时调用。
应用场景: 用于PWM信号生成,处理脉冲结束后的操作。
HAL_TIM_PWM_PulseFinishedHalfCpltCallback(TIM_HandleTypeDef htim)
功能: 当PWM脉冲完成一半时调用。
应用场景: 适用于需要在中途处理PWM信号的场景。
HAL_TIM_TriggerCallback(TIM_HandleTypeDef htim)
功能: 当定时器触发事件发生时调用。
应用场景: 用于同步多个定时器或触发其他外设操作。
HAL_TIM_TriggerHalfCpltCallback(TIM_HandleTypeDef htim)
功能: 当定时器触发事件完成一半时调用。
应用场景: 适用于需要在中途处理触发事件的场景。
HAL_TIM_ErrorCallback(TIM_HandleTypeDef htim)
功能: 当定时器发生错误时调用。
应用场景: 用于错误处理,如计数器溢出错误等。

因为我们上面写了,TIM1的中断和9的在一起,所以要判断

当定时器计数器溢出时,STM32 HAL 库会自动调用 HAL_TIM_PeriodElapsedCallback 函数。
在函数内部,代码会检查触发事件的定时器实例是否是 htim9(TIM9 定时器)。

有点像Python里面的self的感觉

使用一个静态变量 tim_delay 来计数定时器溢出的次数。
tim_delay 只在当前函数中使用,可以保持为静态变量。如果有其他函数需要访问它,可以考虑将其定义为全局变量。
每次定时器溢出时,tim_delay 的值会加 1。
条件判断:
当 tim_delay 的值达到 500 时,表示定时器已经溢出了 500 次。
此时,tim_delay 会被重置为 0,并执行 HAL_GPIO_TogglePin() 函数。

通过定时器 TIM9 的溢出事件,每 500ms 切换一次 GPIO 引脚的状态。

如果写程序的话,其实已经完美了。但是我们靠这个东西吃饭,那就需要继续挖。

寄存器这些都没有看见,怎么做到的?

这个函数

上面函数的返回值是这个的时候才证明可以继续执行

确定上面OK,就是用一个宏打开我们感兴趣的中断

正是我们感兴趣的

使用宏的效率更高

继续看看这个初始化的函数

如果定时器的状态是 HAL_TIM_STATE_RESET,即定时器还没有被初始化过,那么执行初始化过程:
初始化定时器的锁资源(htim->Lock = HAL_UNLOCKED;)。

如果启用了回调机制(USE_HAL_TIM_REGISTER_CALLBACKS == 1),则会使用回调函数来初始化硬件。

否则,直接调用 HAL_TIM_Base_MspInit 函数进行硬件初始化。

htim->State = HAL_TIM_STATE_BUSY;

在初始化过程中,定时器的状态设置为 BUSY,表示当前正在执行初始化操作。

这行代码是核心操作,它调用 TIM_Base_SetConfig 函数来配置定时器的基本设置,传入定时器实例和定时器初始化参数(包括计数模式、周期、时钟分频等)。

  1. 检查定时器句柄和配置参数的有效性。
  2. 如果定时器还未初始化,进行硬件和回调函数的初始化。
  3. 配置定时器的基本设置(如计数模式、时钟分频等)。
  4. 初始化 DMA 和通道状态。
  5. 设置定时器为就绪状态,表示定时器已经准备好进行工作。

以上就是初始化过程。

其实说了这么多,中断的寄存器这部分还是没有摸到。

在这里

TIM1_BRK_TIM9_IRQHandler 是定时器1的断路中断和定时器9的全局中断的统一处理函数。
函数的核心是调用 HAL_TIM_IRQHandler(&htim9) 来处理定时器9的中断事件。
在中断发生时,开发者可以通过 USER CODE 区域插入自定义代码,处理定时器1的断路中断或定时器9的中断逻辑。

函数核心在此

HAL_TIM_IRQHandler 是一个通用的中断处理函数,处理与定时器相关的所有中断事件。

它会根据不同的中断标志来调用对应的回调函数,从而完成特定的任务,例如捕获比较(input capture)、输出比较(output compare)、PWM脉冲完成等。

  1. itsource 表示定时器的中断使能寄存器(DIER),它包含了各个中断源的使能位。
  2. itflag 表示定时器的状态寄存器(SR),它包含了各个中断的标志位,指示哪种事件发生了。

如果定时器溢出或达到了自动重载值(即定时器周期),则会触发更新事件,调用 PeriodElapsedCallback

在代码中,回调函数的调用依赖于宏 USE_HAL_TIM_REGISTER_CALLBACKS 的值:
如果宏定义为 1,使用用户注册的回调函数(例如 htim->IC_CaptureCallback(htim))。
否则,调用库函数提供的默认回调(如 HAL_TIM_IC_CaptureCallback(htim))

上面是回调,下面是非常细节的细节。

用于处理中断标志 TIM_FLAG_UPDATE,它检查是否发生了定时器的 更新事件(比如定时器溢出或者达到自动重载值)。当这种事件发生时,代码会清除该中断标志,并调用一个回调函数 PeriodElapsedCallback

检查定时器是否发生了 更新事件(如溢出或计数器到达自动重载值)。

确保 更新中断 被使能(即相关的中断被允许触发)。

清除中断标志,防止中断重复触发。

STM32 的定时器有一个状态寄存器,存储了每个中断源的标志位。为了避免重复触发中断,必须在中断处理程序中手动清除该标志。
__HAL_TIM_CLEAR_FLAG 宏会清除定时器中指定的标志位,确保该事件只处理一次。

调用用户定义的回调函数 PeriodElapsedCallback,用于处理定时器周期结束后的任务(例如,周期性的任务或超时操作)。

htim 是定时器句柄,包含定时器的配置和状态。回调函数通过句柄传递,以便访问和操作相关的定时器信息。

void (*PeriodElapsedCallback)(struct __TIM_HandleTypeDef *htim);

在这里

大概就是这样了。

云深之无迹
纵是相见,亦如不见,潇湘泪雨,执念何苦。
 最新文章