背景
上一讲 STM32 CubeMX 学习:外部中断的使用 介绍了如何配置以及操作GPIO外部中断。
这一讲我们介绍定时器的有关概念,并对其中一种进行示范。
HOST-OS : Windows-10
STM32 Cube :v5.6
MCU : STM32F429
LIB : stm32cube_fw_f4_v1250
知识
STM32中,有基本定时器(Basic timer)、通用定时器(General-purpose timer)、高级定时器(Advanced-control timer)三类TIM定时器。
- 基本定时器 是16位的只能向上计数的定时器,只能用于定时。
- 通用定时器和高级定时器有更多的功能,如还可以进行输出比较、输入捕捉等功能。
定时器种类 | 位数 | 计数器模式 | 产生DMA请求 | 捕获/比较通道 | 互补输出 | 特殊应用场景 |
---|---|---|---|---|---|---|
高级定时器 | 16 | 向上,向下,向上/下 | 可以 | 4 | 有 | 带可编程死区的互补输出 |
通用定时器 | 32 | 向上,向下,向上/下 | 可以 | 4 | 无 | 通用。定时计数,PWM输出,输入捕获,输出比较 |
通用定时器 | 16 | 向上,向下,向上/下 | 可以 | 4 | 无 | 通用。定时计数,PWM输出,输入捕获,输出比较 |
通用定时器 | 16 | 向上 | 没有 | 2 | 无 | 通用。定时计数,PWM输出,输入捕获,输出比较 |
基本定时器 | 16 | 向上,向下,向上/下 | 可以 | 0 | 无 | 主要应用于驱动DAC(TIM 6,7 独有) |
如何知道STM32什么芯片拥有多少个定时器,每一个定时器对应了什么什么种类的定时器。
- 在ST官网查找有关的芯片手册,根据下载的芯片手册中的目录(Contents)可以看到:
- SMT32F4系列共有15个定时器:高级定时器(TIM1、TIM8);通用定时器(TIM 2 ~ 5、TIM 9 ~ 14);基本定时器(TIM6、TIM7)。
Contents STM32F427xx STM32F429xx
3.22 Timers and watchdogs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
3.22.1 Advanced-control timers (TIM1, TIM8) . . . . . . . . . . . . . . . . . . . . . . . 36
3.22.2 General-purpose timers (TIMx) . . . . . . . . . . . . . . . . . . . . . . . . . . 36
3.22.3 Basic timers TIM6 and TIM7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
3.22.4 Independent watchdog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
3.22.5 Window watchdog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
3.22.6 SysTick timer. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
定时器的有关功能
基本定时器就是单纯的定时计数器;通用定时器多了四个通道,相对应的增加了功能;高级定时器具有基本,通用定时器的所有的功能,并且添加了其他功能。
基本定时器功能(TIM6、TIM7):
- 16位向上、向下、向上/下自动装载计数器
- 16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65535之间的任意数值
- 触发DAC的同步电路 (此项是TIM6/7独有功能)
- 位于APB1总线上
通用定时器(TIM2~TIM5)的主要功能:
- 16位向上、向下、向上/下自动装载计数器
- 16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65535之间的任意数值
- 4 个独立通道(TIMx_CH1~4)可以用作:
- 测量输入信号的脉冲长度( 输入捕获)
- 输出比较
- 单脉冲模式输出
- PWM输出(边缘或中间对齐模式)
- 支持针对定位的增量(正交)编码器和霍尔传感器电路
- 如下事件发生时产生中断/DMA:
- 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
- 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
- 输入捕获
- 输出比较
- 位于APB1总线上
高级定时器(TIM1,TIM8)的主要功能:
- 高级定时器具有基本,通用定时器的所有的功能,
- 还具有控制交直流电动机所有的功能,
- 输出6路互补带死区的信号,刹车功能等等
- 位于APB2总线上
STM32 APB1和APB2的区别如下:(要结合时钟树来理解APB, Advanced Peripheral Bus)
一般APB1和APB2上的时钟都是系统时钟经过 AHB(Advanced High performance Bus) Prescaler 分频得到 HCLK(High performance Bus Clock 高级高性能总线时钟)。
- HCLK 经过APB1 Prescaler 得到APB1时钟,而总线下TIMER的时钟源为APB1的2倍
- HCLK 经过APB2 Prescaler 得到APB2时钟,而总线下TIMER的时钟源为APB2的2倍
AHB,是Advanced High performance Bus的缩写,译作高级高性能总线,这是一种“系统总线”,主要用于高性能模块(如CPU、DMA和DSP等)之间的连接。
1)所负责端口不同:
- APB2负责AD,I/O,高级TIM,串口1。
- APB1负责DA,USB,SPI,I2C,CAN,串口2345,普通TIM。
2)所支持速度不同:APB2支持高速状态下的工作,APB1支持低速状态下的工作。
时钟源
时钟需要时钟源来驱动。计数器时钟可以由下列时钟源提供:
- 内部时钟(CK_INT)
- 外部时钟模式1:外部捕捉比较引脚(TIx)
- 外部时钟模式2:外部触发输入(ETR) (仅适用于TIM 2,3,4)
- 内部触发输入(ITRx):使用一个定时器作为另外一个定时器的预分频器(例如配置TimerA作为TimerB的预分频器)
定时器有关框图
基本定时器主要由下面三个寄存器组成。
- 计数器寄存器 (TIMx_CNT)
- 预分频器寄存器 (TIMx_PSC)
- 自动重载寄存器 (TIMx_ARR)
关于定时器配置的一些参数
虽然有3类定时器但,不管怎样,只要是配置作为定时器,那么便 总是 与 基本定时器是类似的。
在下文配置的时候要注意:我们配置的是进入定时器中断的频率,然后要定的时间要跟据这个频率来定时的。
Prescaler(PSC): 定时器预分频设置,用来设置TIMx_PSC的值,16位,设置 0~65535 以达到 1 至 65536 的分频。
Couter Mode : 定时器的计数方式,基本定时器只能向上(UP)计数(即TIMx_CNT只能从0开始递增,无须初始化)
http://www.waveshare.net/study/article-642-1.html
定时器有如下3种计数模式:
- 递增计数模式:计数器从 0 计数到自动重载值(TIMx_ARR寄存器的值),然后重新从 0 开始计数并生成计数器上溢事件。
- 递减计数模式:计数器从自动重载值(TIMx_ARR)开始递减到 0,然后重新从自动重载值开始计数并生成计数器下溢事件。
- 中心对齐模式:也叫 向上向下计数,计数器从 0 开始计数到自动重载值(TIMx_ARR) – 1 ,生成计数器上溢事件;然后从自动重载值开始向下计数到 1 并生成计数器下溢事件。之后从0 开始重新计数。
Couter Period : 定时器周期,16位,设置 0~65535 以达到 1 至 65536 的周期。每当定时器达到 设定值时,置位。
假设现在HCLK为 168 MHZ,设置了下面的值:
预分频(PSC):167,那么分频以后的时钟是: 168 MHz / (167 + 1 ) = 1 MHz
如果 定时器周期(Period) 的 值 设置为999,定时器产生中断的频率 为:1MHz/1000 = 1Khz,即 1 ms 的定时周期。
那么也就有:[发生中断的时间 = (PSC + 1) * ( Period + 1)/定时器的频率 ]
InternalCLock Division(CKD): 内部时钟分频因子,与PWM输出实验其实是没关系的,指设置定时器时钟(CK_INT)频率与数字滤波器(ETR,TIx)使用的采样频率之间的分频比例的(与输入捕获相关)。
Auto-reload preload :自动重载。一般每次触发以后需要重新填充。
ARM中,有的逻辑寄存器在物理上对应2个寄存器,一个是程序员可以写入或读出的寄存器,称为preload register(预装载寄存器),另一个是程序员看不见的、但在操作中真正起作用的寄存器,称为shadow register(影子寄存器);
设计preload register和shadow register的好处是:所有真正需要起作用的寄存器(shadow register)可以在同一个时间(发生更新事件时)被更新为所对应的preload register的内容,这样可以保证多个通道的操作能够准确地同步。如果没有shadow register,或者preload register和shadow register是直通的,即软件更新preload register时,同时更新了shadow register,因为软件不可能在一个相同的时刻同时更新多个寄存器,结果造成多个通道的时序不能同步,如果再加上其它因素(例如中断),多个通道的时序关系有可能是不可预知的。
Trigger Output Parameters : 触发输出 (TRGO) ,当定时器的定时时间到达的时候输出一个信号(如:定时器更新产生TRGO信号来触发ADC的同步转换)
Repetition Counter:重复计数器(RCR -8 bits),属于高级控制寄存器专用寄存器位,利用它可以非常容易控制输出 PWM 的个数。
Master/Slave Mode(MSM bit) :主从模式
定时器一般是通过软件设置而启动,STM32的每个定时器也可以通过外部信号触发而启动,还可以通过另外一个定时器的某一个条件被触发而启动。这里所谓某一个条件可以是定时到时间、定时器超时、比较成功等许多条件。
这种通过一个定时器触发另一个定时器的工作方式称为定时器的同步,发出触发信号的定时器工作于主模式,接受触发信号而启动的定时器工作于从模式。
定时器的四种主从机模式:外部触发模式1、IRC重置模式、门控模式、触发模式。
CubeMx 对 定时器的配置 (以TIM6 为例)
1)在Pinout & Configuration中,选择一个 定时器 (例如 TIM6)
2)"Mode":勾选 "Activated"
3)"Configuration " - "Parameter Settings"中 :
- PSC : 167
- Counter Mode: Up
- Counter Period: 999
- auto-reload preload: Enable
- Trigger Event Selection : Reset
- "Configuration " - "NVIC Settings"中 : 勾选 "Enabled" (开启中断)
5)右上角,"GENERATE CODE"
源码分析
通过配置以后,在main函数中会有对应的定时器初始化函数,在新版的CubeMx中如果配置了中断,那么响应的中断函数HAL_TIM_Base_Start_IT
也可以看到。
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM6_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim6);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
9 static void TIM_DMAPeriodElapsedCplt(DMA_HandleTypeDef *hdma)
8 {
7 TIM_HandleTypeDef *htim = (TIM_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;
6
5 htim->State = HAL_TIM_STATE_READY;
4
3 #if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
2 htim->PeriodElapsedCallback(htim);
1 #else
5747 HAL_TIM_PeriodElapsedCallback(htim);
1 #endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
2 }
由于 DMA的机制这里还未涉及,所以不讨论 DMA 中断的机制。有关文件:Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c
、Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c
HAL_TIM_DMABurst_WriteStart 中注册了 TIM_DMAPeriodElapsedCplt 作为 hdma->XferCpltCallback的回调函数
HAL_DMA_IRQHandler() -> hdma->XferCpltCallback(hdma); -> TIM_DMAPeriodElapsedCplt() -> HAL_TIM_PeriodElapsedCallback()
添加代码
/* USER CODE BEGIN 0 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM6){
// do_something();
}
}
/* USER CODE END 0 */
运行,即可