|
|
|
@ -0,0 +1,403 @@
|
|
|
|
|
[TOC]
|
|
|
|
|
|
|
|
|
|
# 1. 定时器概述
|
|
|
|
|
|
|
|
|
|
定时器的工作如同闹钟,单不只是闹钟这么简单。
|
|
|
|
|
|
|
|
|
|
## 1.1. 定时器工作原理
|
|
|
|
|
|
|
|
|
|
1. 计数模式:对引脚输人的外部脉冲信号进行计数(如何实现?)。
|
|
|
|
|
2. 定时模式:对处理器内部的周期性时钟信号进行计数(时钟信号的频率是确定的,设置一个数,一个时钟信号递减1,当减少到0的时候,这个过程的时间就是定时的时间)。
|
|
|
|
|
3. 定时时钟:在定时模式下,输人定时器的周期性时钟信号称为定时时钟(为定时器提供的稳定时钟信号)。
|
|
|
|
|
4. 计数时间:在定时模式下,定时器内部的计数单元记一次数所花费的时间称为计数时间,该值为定时时钟频率的倒数。
|
|
|
|
|
|
|
|
|
|
根据定时时钟和计数时间的定义,我们可以得到定时器的定时时间计算公式:
|
|
|
|
|
|
|
|
|
|
定时时间=计数值x计数时间
|
|
|
|
|
|
|
|
|
|
定时时间=计数值/定时时钟频率
|
|
|
|
|
|
|
|
|
|
## 1.2. STM32定时器介绍
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
专用定时器(略):
|
|
|
|
|
|
|
|
|
|
1. 开门狗(什么是看门狗?有什么用?);
|
|
|
|
|
2. 实时时钟:就是我们所说的时间和日期;
|
|
|
|
|
3. 低功耗定时器:低功耗模式(如休眠)下任然工作,用于休眠中唤醒的定时器。
|
|
|
|
|
|
|
|
|
|
常规定时器:
|
|
|
|
|
|
|
|
|
|
1. 基本定时器:几乎没有任何对外的输入/输出通道,常用作时间基准(时基),实现基本的定时功能。
|
|
|
|
|
2. 通用定时器:具备多路独立的捕获/比较通道,可以完成定时/计数、输入捕获、输出比较等功能还可以连接其他的传感器接口,如编码器和霍尔传感器。
|
|
|
|
|
3. 高级定时器:高级定时器的功能最为强大,除了具备通用定时器的功能外,还增加了重复计数器带死区控制的互补信号输出等功能,可用于电机控制等领域。
|
|
|
|
|
|
|
|
|
|
[视频-时钟树](https://www.bilibili.com/video/BV1ph4y1e7Ey/?spm_id_from=333.788&vd_source=3c8e333d6657680a469ddf0238f01d6a)
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
总线结构(中文手册25页)
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
[视频-定时器](https://www.bilibili.com/video/BV11u4y1A7gS/?spm_id_from=333.788&vd_source=3c8e333d6657680a469ddf0238f01d6a)
|
|
|
|
|
|
|
|
|
|
无论哪一种定时器,最基本的功能都是定时和计数,在这两个功能的基础上又衍生出其他的功能。在实际的工程应用中,最常用的定时器功能有以下三种:(P205)
|
|
|
|
|
① 定时/计数功能:用于产生时间基准以及测量外部脉冲的个数。
|
|
|
|
|
②输出比较功能:包括 PWM 输出、电平翻转、单脉冲输出以及强制输出等功能。
|
|
|
|
|
③ 输入捕获功能:用于测量输人信号的脉冲宽度。
|
|
|
|
|
|
|
|
|
|
# 2. HAL库外设模块设计方法
|
|
|
|
|
|
|
|
|
|
## 2.1. 外设句柄设计(略p205)
|
|
|
|
|
|
|
|
|
|
本质上是用一个结构体来描述一个外设(定时器、串口、I2C等寄存器等)。
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
## 2.2. 外设编程模型
|
|
|
|
|
|
|
|
|
|
HAL库根据微控制器和外设的数据传输方式,设计了轮询(一般在主程序中去读取外设的状态,从而进行相应的处理)、中断(通过中断服务进行外设状态变化的函数回调)和 DMA(类似中断,不过关联了DMA的功能) 三种编程模型,用于外设的数据传输。
|
|
|
|
|
以定时器的定时功能为例,三种编程模型下的接口函数分别是:
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
> 在下面的例子中,我们将会使用到中断方式启用外设。DMA方式将在下一章,串口中进行讲解。
|
|
|
|
|
|
|
|
|
|
## 2.3. 外设通用接口函数设计(p208)
|
|
|
|
|
|
|
|
|
|
1. 初始化函数;
|
|
|
|
|
2. I/O操作函数;
|
|
|
|
|
3. 控制函数;
|
|
|
|
|
4. 状态函数;
|
|
|
|
|
|
|
|
|
|
# 3. 定时/计数功能
|
|
|
|
|
|
|
|
|
|
## 3.1. 时钟源
|
|
|
|
|
|
|
|
|
|
时钟源主要有四种(p209),我们目前主要介绍内部时钟源(CK_INT)。注意,这里所说的内部时钟源是统称,表示总线上的是时钟,不是指MCU提供时钟的时钟源。为MCU提供时钟信号的也分为内部和外部,但最终都会形成总线上的时钟信号(AHB)。
|
|
|
|
|
|
|
|
|
|
通过设置相关的寄存器,选择对应的时钟源后,该时钟源将作为时基单元的预分频时钟CK_PSC送人时基单元。时钟源选择的示意图如图8-3所示。
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 3.2. 时基单元
|
|
|
|
|
|
|
|
|
|
时基单元是定时器的核心控制单元,负责时钟源的分频、计数和溢出重载等基本功能。它主要由三个模块组成:预分频模块、计数模块和自动重载模块。时基单元的功能框图如图 8-4所示。
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
### 3.2.1. 预分频模块
|
|
|
|
|
|
|
|
|
|
预分频模块的工作原理如下:定时器启动后,预分频计数器的初值为0。预分频时钟CK_PSC每输入一个脉冲,预分频计数器的计数值就自动加一。当计数值等于预分频寄存器中存放的预分频系数PSC时,计数值清零,并开始下一轮计数。
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
预分频时钟 CK_PSC 经过预分频模块后,得到计数时钟CK_CNT,它的计算公式如下:
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
注意:0表示不分频,1表示2分频,因此需要PSC+1,
|
|
|
|
|
|
|
|
|
|
### 3.2.2. 计数模块
|
|
|
|
|
|
|
|
|
|
计数模块由核心计数器和计数器寄存器TIMx_CNT组成:核心计数器用来对预分频模块输出的计数时钟 CK_CNT进行二次计数。计数时钟CK_CNT每输入一个脉冲,核心计数器的计数值就自动加一或减一(根据用户设置的不同计数模式来决定是加一还是减一)。计数器寄存器则用来存放核心计数器运行时的计数值,便于用户读取。
|
|
|
|
|
|
|
|
|
|
### 3.2.3. 自动重载模块
|
|
|
|
|
|
|
|
|
|
自动重载模块由自动重载寄存器TIMx_ARR构成,用来设置计数器的计数终值或计数初值,决定计数脉冲的多少(计数模式)或定时周期(定时模式)的长短。我们将TMx_ARR寄存器的内容记为自动重载值ARR。当定时器设置为递增计数模式时,ARR作为计数器的计数终值,表示记到ARR时发生溢出。当定时器设置为递减计数模式时,ARR作为计数器的计数初值,表示从ARR开始向下计数。
|
|
|
|
|
|
|
|
|
|
### 3.2.4. 计数模式
|
|
|
|
|
|
|
|
|
|
定时器的计数模块支持三种计数模式:递增计数、递减计数和中心对齐计数,并产生溢出事件,作为定时器的更新中断(定时中断)。
|
|
|
|
|
|
|
|
|
|
1. 递增计数:计数器从0开始向上计数,当计数值等于ARR时,产生计数器上溢事件,并从0开始新一轮的计数周期。
|
|
|
|
|
|
|
|
|
|
2. 递减计数:计数器从ARR开始向下计数,当计数值等于0时,产生计数器下溢事件,并从ARR开始新一轮的计数周期。
|
|
|
|
|
|
|
|
|
|
3. 中心对齐计数(递增/递减计数):计数器从0开始向上计数,当计数值等于ARR-1时,产生计数器上溢事件;然后从ARR 开始向下计数,当计数值等于1时,产生计数器下溢事件。之后再从0开始新一轮的计数周期。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
三种计数模式的计数过程如图 8-6所示。
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
> 预装载功能和影子寄存器(p212):
|
|
|
|
|
|
|
|
|
|
### 3.2.5. 定时时间计算公式
|
|
|
|
|
|
|
|
|
|
当定时器工作于定时模式时,预分频时钟 CK_PSC 等于定时时钟 TIMx_CLK。定时时间由 TIMx_CLK 的频率、预分频系数 PSC 和自动重载值 ARR 三者决定。
|
|
|
|
|
|
|
|
|
|
假设PSC=1,ARR=36,采用递增计数模式,计数器寄存器的初值为0。定时器的时序如图 8-7 所示。
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
预分频时钟 CK_PSC经过分频后,得到计数时钟CK_CNT,送入计数器。计数器的计数时间为1/C_KCNT,代入式(8-3),可以得到计数时间为(PSC+1)/CK_PSC。由于实际的计数值为 ARR+1,将计数值和计数时间代入式(8-1),可以得到定时时间的计算公式:
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
## 3.3. 外部脉冲计数(p214略)
|
|
|
|
|
|
|
|
|
|
对外部的脉冲信号进行计数,例如通过传感器统计生产线上物料个数。这是外部传感器在有物料通过时可以产生一个脉冲,输入到MCU,使用定时器的计数模式进行统计。
|
|
|
|
|
|
|
|
|
|
定时器的计数模式在硬件上有极性选择、分频、滤波等电路,可以实现较高频率的脉冲计数,同时可以对输入波形进行滤波,减少干扰的影响。
|
|
|
|
|
|
|
|
|
|
> 其实,如果对低频脉冲的统计,可以使用IO的外部中断即可(每个中断对全局变量进行累加);但是IO中断并不提供分频、滤波等功能,因此不适合高频率的脉冲,抗干扰能力也较弱。
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
> 定时器的ETR引脚和书上的不一致,请参考芯片手册。
|
|
|
|
|
|
|
|
|
|
## 3.4. 定时/计数功能的数据类型(略:p215)
|
|
|
|
|
|
|
|
|
|
在 stm32f1xx_hal_tim.h 的大约 46 行:
|
|
|
|
|
|
|
|
|
|
```c
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
uint32_t Prescaler; /*!< Specifies the prescaler value used to divide the TIM clock.
|
|
|
|
|
This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */
|
|
|
|
|
|
|
|
|
|
uint32_t CounterMode; /*!< Specifies the counter mode.
|
|
|
|
|
This parameter can be a value of @ref TIM_Counter_Mode */
|
|
|
|
|
|
|
|
|
|
uint32_t Period; /*!< Specifies the period value to be loaded into the active
|
|
|
|
|
Auto-Reload Register at the next update event.
|
|
|
|
|
This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF. */
|
|
|
|
|
|
|
|
|
|
uint32_t ClockDivision; /*!< Specifies the clock division.
|
|
|
|
|
This parameter can be a value of @ref TIM_ClockDivision */
|
|
|
|
|
|
|
|
|
|
uint32_t RepetitionCounter; /*!< Specifies the repetition counter value. Each time the RCR downcounter
|
|
|
|
|
reaches zero, an update event is generated and counting restarts
|
|
|
|
|
from the RCR value (N).
|
|
|
|
|
This means in PWM mode that (N+1) corresponds to:
|
|
|
|
|
- the number of PWM periods in edge-aligned mode
|
|
|
|
|
- the number of half PWM period in center-aligned mode
|
|
|
|
|
GP timers: this parameter must be a number between Min_Data = 0x00 and
|
|
|
|
|
Max_Data = 0xFF.
|
|
|
|
|
Advanced timers: this parameter must be a number between Min_Data = 0x0000 and
|
|
|
|
|
Max_Data = 0xFFFF. */
|
|
|
|
|
|
|
|
|
|
uint32_t AutoReloadPreload; /*!< Specifies the auto-reload preload.
|
|
|
|
|
This parameter can be a value of @ref TIM_AutoReloadPreload */
|
|
|
|
|
} TIM_Base_InitTypeDef;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
ClockDivision 用于计数和输入捕获的滤波(略)。
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
## 3.5. 定时/计数功的接口函数(p217)
|
|
|
|
|
|
|
|
|
|
### 3.5.1. 时基单元初始化函数(HAL_TIM_Base_Init)
|
|
|
|
|
|
|
|
|
|
该函数用于时基单元的初始化,将用户设定的配置参数写人对应的寄存器,具体描述如表 8-7 所示。
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
### 3.5.2. 定时器轮询方式启动函数(HAL_TIM_Base_Start)
|
|
|
|
|
|
|
|
|
|
该函数用于在轮询方式下启动定时器运行,具体描述如表 8-8所示。
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
### 3.5.3. 定时器中断方式启动函数(HAL_TIM_Base_Start_IT)
|
|
|
|
|
|
|
|
|
|
该函数用于在中断方式下启动定时器运行,具体描述如表8-9所示。
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
### 3.5.4. 定时器中断通用处理函数(HAL_TIM_IROHandler)
|
|
|
|
|
|
|
|
|
|
该函数是所有定时器中断发生后的通用处理程序。任何一个定时器的相关中断(如更新中断或捕获中断)发生后,都会通过中断向量表中的定时器中断服务程序TMxIROHandler()(x表示定时器编号1~11)调用该函数。在函数内部会进行中断类型的判断,并清除对应的中断标志,然后再根据不同的中断类型,调用不同的回调函数来完成具体的中断处理任务。具体描述如表 8-10所示。
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
### 3.5.5. 定时器更新中断回调函数(HAL_TIM_PeriodElapsedCallback)
|
|
|
|
|
|
|
|
|
|
该函数用于处理发生更新中断后的具体任务。任何一个定时器发生更新中断后,都会调用更新中断回调函数。因此,在函数内部需要判断是哪一个定时器产生的本次更新中断,然后再执行具体的中断处理任务。具体描述如表8-11所示。
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
> 注意:这个函数有缺省的实现,是用weak标识的,因此一般情况下需要重新定义该函数来完成用户逻辑的处理。
|
|
|
|
|
|
|
|
|
|
### 3.5.6. 定时器中断标志清除函数(__HAL_TIM_CLEAR_IT)
|
|
|
|
|
|
|
|
|
|
该函数用于清除定时器的各类中断标志,采用带参数的宏实现(宏函数),与普通函数相比,宏函数省去了函数调用的过程,执行效率较高。具体描述如表 8-12所示。
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
### 3.5.7. 定时器计数值读取函数(__HAL_TIM_GET_COUNTER)
|
|
|
|
|
|
|
|
|
|
该函数用于读取定时器的计数值,采用带参数的宏实现(宏函数),具体描述如表 8-13
|
|
|
|
|
所示。
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
## 3.6. 基础任务:定时闪烁指示灯
|
|
|
|
|
|
|
|
|
|
### 3.6.1. 实现过程
|
|
|
|
|
|
|
|
|
|
使用绿色LED(PA7)进行显示,定时器4以中断方式运行,每隔1秒钟切换一次电平。
|
|
|
|
|
|
|
|
|
|
1. 以前面讲的方式建立一个项目,名称是 TimerLED;
|
|
|
|
|
2. 设置PA7为输出模式,并设置标签为LED;
|
|
|
|
|
3. 使用TIM4作为定时器。检查TIM4挂接的总线([STM32定时器介绍](#STM32定时器介绍)),知道是挂接在APB1上;
|
|
|
|
|
4. 检查时钟配置:
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
知道APB1的定时器时钟是8M。注意:定时器的中断时间是需要按照定时器所在总线的时钟进行设置的。
|
|
|
|
|
|
|
|
|
|
5. 配置时钟:
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
勾选2位置,以便使用内部时钟(指APB总线时钟,不是外部时钟晶体和内部RC时钟源);在3位置设置PSC为7999;在4位置设置ARR为999;根据8-4公式,可以知道此时的时钟计时周期为1秒;
|
|
|
|
|
|
|
|
|
|
6. 开启TIM4的全局中断,并保存生成代码框架。
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
7. 在 main.c 的 /* USER CODE BEGIN 2 */ 位置插入下列代码:
|
|
|
|
|
|
|
|
|
|
```c
|
|
|
|
|
/* USER CODE BEGIN 2 */
|
|
|
|
|
HAL_TIM_Base_Start_IT(&htim4);
|
|
|
|
|
/* USER CODE END 2 */
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
8. 在 main.c 文件的 /* USER CODE BEGIN 0 */ 位置插入定时中断回调函数:
|
|
|
|
|
|
|
|
|
|
```c
|
|
|
|
|
/* USER CODE BEGIN 0 */
|
|
|
|
|
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
|
|
|
|
|
if (htim->Instance == TIM4)
|
|
|
|
|
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
|
|
|
|
|
}
|
|
|
|
|
/* USER CODE END 0 */
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
好了,下载到开发板后,绿色LED应该亮一秒,灭一秒,一直循环。
|
|
|
|
|
|
|
|
|
|
### 3.6.2. 代码分析
|
|
|
|
|
|
|
|
|
|
main.c 的 43 行定义了外设TIM4的句柄:
|
|
|
|
|
|
|
|
|
|
```c
|
|
|
|
|
TIM_HandleTypeDef htim4;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
这个书上没有讲,其实就是针对 TIM4 这个外设的结构体,包括配置信息和相关的操作函数指针(有点像Java中的类定义),有兴趣可以看看。
|
|
|
|
|
|
|
|
|
|
main.c 的 147 行左右是定时器的初始化:
|
|
|
|
|
|
|
|
|
|
```c
|
|
|
|
|
static void MX_TIM4_Init(void) {
|
|
|
|
|
|
|
|
|
|
/* USER CODE BEGIN TIM4_Init 0 */
|
|
|
|
|
|
|
|
|
|
/* USER CODE END TIM4_Init 0 */
|
|
|
|
|
|
|
|
|
|
TIM_ClockConfigTypeDef sClockSourceConfig = { 0 };
|
|
|
|
|
TIM_MasterConfigTypeDef sMasterConfig = { 0 };
|
|
|
|
|
|
|
|
|
|
/* USER CODE BEGIN TIM4_Init 1 */
|
|
|
|
|
|
|
|
|
|
/* USER CODE END TIM4_Init 1 */
|
|
|
|
|
htim4.Instance = TIM4;
|
|
|
|
|
htim4.Init.Prescaler = 7999;
|
|
|
|
|
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
|
|
|
|
|
htim4.Init.Period = 999;
|
|
|
|
|
htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
|
|
|
|
|
htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
|
|
|
|
|
if (HAL_TIM_Base_Init(&htim4) != HAL_OK) {
|
|
|
|
|
Error_Handler();
|
|
|
|
|
}
|
|
|
|
|
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
|
|
|
|
|
if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK) {
|
|
|
|
|
Error_Handler();
|
|
|
|
|
}
|
|
|
|
|
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
|
|
|
|
|
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
|
|
|
|
|
if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig)
|
|
|
|
|
!= HAL_OK) {
|
|
|
|
|
Error_Handler();
|
|
|
|
|
}
|
|
|
|
|
/* USER CODE BEGIN TIM4_Init 2 */
|
|
|
|
|
|
|
|
|
|
/* USER CODE END TIM4_Init 2 */
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
/* USER CODE BEGIN 2 */ 位置的代码:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
HAL_TIM_Base_Start_IT(&htim4);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
表示以中断方式启动TIM4;
|
|
|
|
|
|
|
|
|
|
stm32f1xx_it.c 的大约 204 行:
|
|
|
|
|
|
|
|
|
|
```c
|
|
|
|
|
void TIM4_IRQHandler(void)
|
|
|
|
|
{
|
|
|
|
|
/* USER CODE BEGIN TIM4_IRQn 0 */
|
|
|
|
|
|
|
|
|
|
/* USER CODE END TIM4_IRQn 0 */
|
|
|
|
|
HAL_TIM_IRQHandler(&htim4);
|
|
|
|
|
/* USER CODE BEGIN TIM4_IRQn 1 */
|
|
|
|
|
|
|
|
|
|
/* USER CODE END TIM4_IRQn 1 */
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
此代码是自动生成的,函数 TIM4_IRQHandler(void) 是 TIM4 溢出中断调用的函数,该函数调用了 HAL_TIM_IRQHandler(),这个函数,并传入了 htim4 这个结构体的指针,标识TIM4中断;接着看 stm32f1xx_hal_tim.c 的大约 3882 行开始的 HAL_TIM_IRQHandler() 这个函数;这个函数很复杂,在大约3959行调用了 HAL_TIM_PeriodElapsedCallback(htim); 这个函数。因此该中断的调用过程是:
|
|
|
|
|
|
|
|
|
|
TIM4_IRQHandler->HAL_TIM_IRQHandler->HAL_TIM_PeriodElapsedCallback
|
|
|
|
|
|
|
|
|
|
HAL_TIM_PeriodElapsedCallback 是我们在主函数中定义的用户逻辑函数。
|
|
|
|
|
|
|
|
|
|
## 3.7. 进阶任务:外部脉冲计数(略)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 4. PWM输出功能
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 4.1. 捕获/比较通道
|
|
|
|
|
|
|
|
|
|
## 4.2. PWM实现原理
|
|
|
|
|
|
|
|
|
|
## 4.3. PWM输出功能的数据类型
|
|
|
|
|
|
|
|
|
|
## 4.4. PWM输出功能的接口函数
|
|
|
|
|
|
|
|
|
|
## 4.5. 基础任务:输出PWM信号
|
|
|
|
|
|
|
|
|
|
## 4.6. 进阶任务:实现呼吸灯
|
|
|
|
|
|
|
|
|
|
# 5. 输入捕获功能
|
|
|
|
|
|
|
|
|
|
## 5.1. 输入捕获功能概述
|
|
|
|
|
|
|
|
|
|
## 5.2. 输入捕获功能的数据类型
|
|
|
|
|
|
|
|
|
|
## 5.3. 输入捕获功能的接口函数
|
|
|
|
|
|
|
|
|
|
## 5.4. 挑战任务:信号测量
|
|
|
|
|
|