目录
一、通用定时器功能概述
二、细说2通道定时器的功能
1.时钟信号和触发控制器
2.时基单元工作原理
(1)计数寄存器(CNT)
(2)预分频寄存器(PSC)
(3)自动重载寄存器(ARR)
(4)时基单元的控制位
3.捕获/比较通道
三、生成PWM波
1.生成PWM波的原理
2.与生成PWM波相关的HAL函数
四、输出比较
1.输出比较的原理
2.输出比较相关的HAL函数
五、输入捕获
1.输入捕获的原理
2.输入捕获相关的HAL函数
六、PWM输入模式
1.测量PWM波参数的原理
2.测量PWM波参数的相关HAL函数
七、定时器同步
八、通用定时器中断事件和回调函数
STM32F407的通用定时器数量较多,TIM2~TIM5以及TIM9~TIM14都是通用定时器。 通用定时器的功能基本相同,只是计数器有的是32位,有的是16位,捕获/比较通道有4通道、 2通道或1通道。通用定时器除了基本的定时功能,还具有输入捕获、输出比较、PWM输出等功能。TIM1和TIM8是高级控制定时器,除了具有通用定时器的功能,还有带可编程死区的互补输出、重复计数器等功能,高级控制定时器一般用于电机的控制。
一、通用定时器功能概述
通用定时器的区别主要在于计数器的位数、捕获/比较通道的个数不同。通用定时器具有以下特性。
- 16位或32位自动重载计数器。
- 16位可编程预分频器,分频系数为1~65536。分频系数可在运行时修改。
- 有1、2或4个独立通道,可用于:输入捕获、输出比较、 PWM生成(边沿对齐或中心对齐)、单脉冲模式输出。
- 可使用外部信号控制定时器,可实现多个定时器互连的同步电路。
- 发生如下事件时产生中断或DMA请求:
-
(1)更新——计数器上溢/下溢、计数器初始化(通过软件或内部/外部触发); (2)触发事件(计数器启动、停止、初始化或通过内部/外部触发计数); (3)输入捕获; (4)输出比较。
TIM2~TIM5的功能更多一些,TIM2~TIM5可以使用外部时钟信号驱动计数器,而TIM9~ TIM14只能使用内部时钟信号。
通用定时器有1、2或4个捕获/比较通道,只是4通道的定时器有外部时钟输入通道,2通道和1通道定时器只能使用内部时钟。1通道定时器没有内部触发输入,也就是不能工作于从模式(slave mode)与其他定时器串联。
二、细说2通道定时器的功能
2通道定时器的功能比较典型,下图是2通道的TIM9和TIM12的内部功能结构图。
1.时钟信号和触发控制器
定时器可以使用内部时钟(CK_INT)驱动,内部时钟来源于APB1或APB2总线的定时器时钟信号。如果定时器设置为从模式,还可以使用其他定时器输出的触发信号作为时钟信号,比如,ITR0、ITR1等。
触发控制器用于选择定时器的时钟信号,并且可以控制定时器的复位、使能、计数等。触发控制器输出时钟信号CK_PSC,若选择使用内部时钟,则CK_PSC就等同于CK_INT。
2.时基单元工作原理
定时器的主要模块是一个16位的计数器(TIM2和TIM5是32位计数器)及其相关的自动重载寄存器,计数器以递增方式计数(TIM2和TIM5还能递减计数,或递增/递减计数)。一个16位的预分频器对输入时钟信号CK_PSC进行分频,分频后的时钟信号CK_CNT驱动计数器。计数器内部还有时钟分频功能,可以对CK_CNT时钟信号进一步地进行1分频(不分频)、2分频或4分频。
时基单元包括3个寄存器,其功能描述如下。
(1)计数寄存器(CNT)
这个寄存器存储计数器当前的计数值,可以在运行时被读取。
(2)预分频寄存器(PSC)
这个寄存器的数值范围为0~65535,对应于分频系数1~65536。 在定时器运行时修改PSC寄存器的值,需要在下一个UEV事件时才会写入预分频器缓冲区并生效。例如,PSC寄存器初始值为0,预分频器缓冲区是实际用于分频的值。在某个时刻将PSC的值改为1,这个值并未立刻传送到预分频器缓冲区,仍按照原来的值分频和计数。到下一个UEV事件发生时,PSC寄存器里的值才更新到预分频器缓冲区,分频系数变为2,计数器也使用新的时钟频率计数。
(3)自动重载寄存器(ARR)
这个寄存器存储的是定时器计数周期。自动重载寄存器(Auto-Reload Register,ARR)是预装载的,它具有影子寄存器。影子寄存器用于底层的实时工作。在定时器工作时改变ARR的值,根据控制寄存器TIMx_CR1中ARPE 位的设置不同,ARR的值更新到影子寄存器的方式不同。
控制寄存器TIMx_CR1中的自动重载预装载(Auto-Reload Preload,ARPE)位为0时,即禁用预装载功能时,ARR的值立刻生效。例如,ARR的初始值为FF,在某个时刻更新ARR的值为36,计数器的值达到36时就产生UEV事件,而不是按照原来的值FF产生UEV事件。所以,APRE=0时,给ARR设置的新值立刻生效。
当APRE=1时,修改ARR的值,例如,ARR的初始值为F5,在某个时刻将ARR的值改为36,那么计数器还是计数到F5时才产生UEV事件,然后才将新的值36写入ARR的影子寄存器。所以,APRE=1时,ARR设置的新值要在下一个UEV事件时才生效。
(4)时基单元的控制位
时基单元的控制位主要有以下两个。
- TIMx_CR1寄存器中的CEN(Counter Enable)位使能计数器,当CEN设置为1时,计数器经过一个CK_CNT周期后开始计数,当CEN设置为0时,计数器停止工作。
- TIMx_EGR寄存器中的UG(Update Generation)位是用软件方式产生一次更新事件,使定时器复位,例如,定时器没有预分频,软件设置UG位为1后,经过1个CK_CNT脉冲后产生UEV事件,并自动将UG位复位,计数器寄存器清零,预分频计数器也清零,重新开始计数。
3.捕获/比较通道
通用定时器有1、2或4个捕获/比较通道,每个通道是独立工作的。TIM9和TIM12的定时器有2 个捕获/比较通道:一个通道要么作为捕获输入通道,要么作为比较输出通道;每个通道有一个复用的引脚,如TIM9_CH1、TIM9_CH2复用引脚。
捕获/比较通道由输入阶段、比较阶段和输出阶段组成。
- 输入阶段。通道作为输入引脚,例如,图中左侧的TIMx_CH1,从复用引脚输入时钟信号TI1,输入阶段可以对输入信号TI1进行滤波和边沿检测,再经过选择器和预分频器后得到时钟信号IC1PS。
- 捕获/比较阶段。有一个具有预装载功能的捕获/比较寄存器(Capture/Compare Register),以及相关的影子寄存器,可以读写CCR。在捕获模式下,捕获实际发生在影子寄存器中,然后将影子寄存器的内容复制到CCR中。在比较模式下,CCR的内容将复制到影子寄存器中,然后将影子寄存器的内容与计数器值进行比较。
- 输出阶段:输出阶段就是根据设置的工作模式和控制逻辑,控制输出引脚的电平。 使用捕获/比较通道,通用定时器可以实现如下功能。
- 输入捕获,可用于测量一个时钟信号的频率,脉冲宽度等。
- 输出比较,将计数器CNT的值与CCR的值比较,控制输出引脚的电平。
- PWM生成,通过设置ARR和CCR的值,在计数器的值CNT变化过程中输出PWM 波。PWM波的频率由ARR决定,占空比由CCR决定。
- 单脉冲模式输出。
三、生成PWM波
1.生成PWM波的原理
PWM(Pulse Width Modulation)就是脉冲宽度调制,是一种对模拟信号电平进行数字编码的方法。PWM波就是具有一定占空比的方波信号,通过定时器的设置可以控制方波的频率和占空比,从而对模拟电压进行数字编码。理论上,只要带宽足够(PWM波的频率足够高),任何模拟值都可以使用PWM进行编码。使用定时器生成PWM波的工作原理如下图,这里定时器是递增计数,PWM波是边沿对齐方式。
其基本工作原理描述如下。
- 设置自动重载寄存器ARR的值,这个值决定了PWM波一个周期的长度,比如PWM一个周期是100ms。
- 设置捕获比较寄存器CCR的值,在一个ARR计数周期内,当计数器值CNT <CCR 时,PWM参考信号OCxREF(x表示定时器编号)为高电平;当CNT ≥CCR时, OCxREF为低电平,并可产生CC(捕获/比较)事件。所以,CCR的值决定了占空比, 例如,设置CCR的值,使一个PWM波周期内高电平时长为70ms,则占空比为70%。
- 在计数器的值达到ARR值时,产生UEV事件。CCR具有预装载功能,修改的CCR 值需要在下一个UEV事件时才生效。
通用定时器都具有生成PWM波的功能,PWM波可以输出到定时器的通道引脚,也可以不输出到引脚。某些定时器输出PWM波还具有中心对齐模式。
2.与生成PWM波相关的HAL函数
与生成PWM波相关的HAL函数如下表。还有以DMA方式启动和停止PWM的函数,但是定时器基本不使用DMA方式。
函数名 | 功能描述 |
HAL_TIM_PWM_Init() | 生成PWM波的配置初始化,需先执行HAL_TIM_Base_Init()进行定时器初始化 |
HAL_TIM_PWM_ConfigChannel() | 配置PWM输出通道 |
HAL_TIM_PWM_Start() | 启动生成PWM波,需要先执行HAL_TIM_Base_Start()启动定时器 |
HAL_TIM_PWM_Stop() | 停止生成PWM波 |
HAL_TIM_PWM_Start_IT() | 以中断方式启动生成PWM波,需要先执行HAL_TIM_Base_Start_IT()启动定时器 |
HAL_TIM_PWM_Stop_IT() | 停止生成PWM波 |
HAL_TIM_PWM_GetState() | 返回定时器状态,与HAL_TIM_Base_GetState()功能相同 |
__HAL_TIM_ENABLE_OCxPRELOAD() | 使能CCR的预装载功能,为CCR设置的新值要等到下一个UEV事件发生时才更新到CCR |
__HAL_TIM_DISABLE_OCxPRELOAD() | 禁止CCR的预装载功能,为CCR设置的新值会立刻更新到CCR |
__HAL_TIM_ENABLE_OCxFAST() | 启用一个通道的快速模式 |
__HAL_TIM_DISABLE_OCxFAST() | 禁用一个通道的快速模式 |
HAL_TIM_PWM_PulseFinishedCallback() | 当计数器的值等于CCR的值时,产生输出比较事件,这是对应的回调函数 |
四、输出比较
1.输出比较的原理
输出比较(output compare)用于控制输出波形,或指示经过了某一段时间。它的工作原理是:用捕获/比较寄存器的值CCR与计数器值CNT比较,如果两个寄存器的值匹配,产生输出比较结果OCyREF,这个值由比较模式和输出极性决定,这个比较结果可以输出到通道的引脚。比较匹配时,可以产生中断或DMA请求,可以引起输出引脚发生如下几种变化:
- 冻结(Frozen),即保持其电平。
- 有效电平(Active level),有效电平由设置的通道极性决定。
- 无效电平(Inactive Level)。
- 翻转(Toggle)。
如果将捕获/比较模式寄存器TIMx_CCMR1或TIMx_CCMR2中的OCyPE(输出比较预装载使能)位设置为0,则捕获/比较寄存器TIMx_CCRy无预装载功能,对TIMx_CCRy寄存器的修改立刻生效;如果设置OCyPE位为1,对TIMx_CCRy寄存器的修改需要在下一个UEV事件时才生效。
这里,寄存器名称或位的名称表示中使用了x和y,例如寄存器TIMx_CCRy, 其中x表示定时器编号,y表示通道编号。某些定时器有2个或4个通道,相应的有寄存器TIMx_CCR1、TIMx_CCR2等。
例如,设置输出极性为高电平、匹配时输出翻转、TIMx_CCR1寄存器无预装载功能时的时序如下图。TIMx_CCR1初始设定值为003A,输出参考OC1REF初始为低电平。当TIMx_CNT 寄存器与TIMx_CCR1寄存器值第1次匹配时(值为003A时),输出参考OC1REF翻转为高电平,此时,如果使能了输出比较中断,会产生CClIF中断标志。
在中间,修改了TIMx_CCR1寄存器的值为0xB201,因为没有使用预装载功能,所以写入TIMx_CCR1寄存器的值立即生效。当TIMx_CNT寄存器与TIMx_CCR1寄存器值第2次匹配时(值为0xB201时),输出参考OC1REF再翻转为低电平,并且产生CCIIF中断标志。
2.输出比较相关的HAL函数
输出比较相关的HAL函数如下表。仅列出了相关函数名,简要说明其功能。
函数名 | 功能描述 |
HAL_TIM_OC_Init() | 输出比较初始化,需先执行HAL_TIM_Base_Init()进行定时器初始化 |
HAL_TIM_OC_ConfigChannel() | 输出比较通道配置 |
HAL_TIM_OC_Start() | 启动输出比较,需要先执行HAL_TIM_Base_Start()启动定时器 |
HAL_TIM_OC_Stop() | 停止输出比较 |
HAL_TIM_OC_Start_IT() | 以中断方式启动输出比较,需要先执行HAL_TIM_Base_Start_IT()启动定时器 |
HAL_TIM_OC_Stop_IT() | 停止输出比较 |
HAL_TIM_OC_GetState() | 返回定时器状态,与HAL_TIM_Base_GetState()功能相同 |
__HAL_TIM_ENABLE_OCxPRELOAD() | 使能CCR的预装载功能,为CCR设置的新值在下个UEV事件发生时才更新到CCR寄存器 |
__HAL_TIM_DISABLE_OCxPRELOAD() | 禁止CCR的预装载功能,为CCR设置的新值立刻更新到CCR |
__HAL_TIM_SET_COMPARE() | 设置比较寄存器CCR的值 |
__HAL_TIM_GET_COMPARE() | 读取比较寄存器CCR的值 |
HAL_TIM_OC_DelayElapsedCallback() | 产生输出比较事件时的回调函数 |
五、输入捕获
1.输入捕获的原理
输入捕获(input capture)就是检测输入通道输入方波信号的跳变沿,并将发生跳变时的计 数器的值锁存到CCR。使用输入捕获功能可以检测方波信号周期,从而计算方波信号的频率,也可以检测方波信号的占空比。
使用输入捕获功能检测方波信号周期的工作原理示意图如下图所示,设置捕获极性是上跳沿,定时器在ARR的控制下周期性地计数。
图中,假设输入方波的脉冲宽度小于定时器的周期,输入捕获测定脉冲周期的工作原理描述如下:
- 在一个上跳沿时,状态寄存器TIMx_SR中的捕获/比较标志位CCyIF会被置1,表示发生了捕获事件,会产生相应的中断。计数器的值自动锁存到CCR,假设锁存的值为CCR1。可以在程序里读取出CCR的值,并清除CCyIF标志位。
- 在下一个上跳沿时,计数器的值也会锁存到CCR,假设锁存的值为CCR2。如果在上次发生捕获事件后,CCR的值没有及时读出,则CCyIF位依然为1,且TIMx_SR中的重复捕获标志位CCyOF会被置1。
如果像图中那样,两个上跳沿的捕获发生在定时器的一个计数周期内,两个计数值分别为CCR1和CCR2,则方波的周期为CCR2-CCR1个计数周期。根据定时器的时钟周期就可以计算出方波周期和频率。
如果方波周期超过定时器的计数周期,或两次捕获发生在相邻两个定时周期里,如图中的CCR2和CCR3,则只需将计数器的计数周期和UEV事件发生次数考虑进去即可,根据CCR2和CCR3计算的脉冲周期应该是ARR-CCR2+CCR3。
输入捕获还可以对输入设置滤波,滤波系数0~15,用于输入有抖动时的处理。输入捕获还可以设置预分频器系数N,数值N的取值为1、2、4或8,表示发生N个事件时才执行一次捕获。
2.输入捕获相关的HAL函数
输入捕获相关的HAL函数如下表。仅列出了相关函数名,简要说明其功能。
函数名 | 功能描述 |
HAL_TIM_OC_Init() | 输出比较初始化,需先执行HAL_TIM_Base_Init()进行定时器初始化 |
HAL_TIM_OC_ConfigChannel() | 输出比较通道配置 |
HAL_TIM_OC_Start() | 启动输出比较,需要先执行HAL_TIM_Base_Start()启动定时器 |
HAL_TIM_OC_Stop() | 停止输出比较 |
HAL_TIM_OC_Start_IT() | 以中断方式启动输出比较,需要先执行HAL_TIM_Base_Start_IT()启动定时器 |
HAL_TIM_OC_Stop_IT() | 停止输出比较 |
HAL_TIM_OC_GetState() | 返回定时器状态,与HAL_TIM_Base_GetState()功能相同 |
__HAL_TIM_ENABLE_OCxPRELOAD() | 使能CCR的预装载功能,为CCR设置的新值在下个UEV事件发生时才更新到CCR寄存器 |
__HAL_TIM_DISABLE_OCxPRELOAD() | 禁止CCR的预装载功能,为CCR设置的新值立刻更新到CCR |
__HAL_TIM_SET_COMPARE() | 设置比较寄存器CCR的值 |
__HAL_TIM_GET_COMPARE() | 读取比较寄存器CCR的值 |
HAL_TIM_OC_DelayElapsedCallback() | 产生输出比较事件时的回调函数 |
六、PWM输入模式
1.测量PWM波参数的原理
PWM输入模式是输入捕获模式的一个特例,可用于测量PWM输入信号的周期和占空比。其原理如下:
- 将两个输入捕获信号IC1和IC2映射到同一个TI1输入上。
- 设置这两个捕获信号IC1和IC2在边沿处有效,但是极性相反。
- 选择TI1FP或TI2FP信号之一作为触发输入,并将从模式控制器配置为复位模式。例如图中所述,是测量TI1(输入通道CH1上的输入PWM波)的周期和占空比的示意图,其初始配置和工作原理描述如下。
- 将TIMx_CCR1和TIMx_CCR2的输入都设置为TI(即通道TIMx_CH1)。
- 设置TIMx_CCR1的极性为上跳沿有效,设置TIMx_CCR2的极性为下跳沿有效。
- 选择TI1FP1为有效触发输入。
- 将从模式控制器设置为复位模式。
- 同时使能TIMx_CCR1和TIMx_CCR2输入捕获。
- 在图中,在第1个上跳沿处,TIMx_CCR1锁存计数器的值,并且使计数器复位;在接下来的下跳沿处,TIMx_CCR2锁存计数器的值(为0002),这个值就是PWM的高电平宽度;在下一个上跳沿处,TIMx_CCR1锁存计数器的值(为0004),这个值就是PWM的周期。
2.测量PWM波参数的相关HAL函数
PWM输入模式就是输入捕获模式的一个特例,使用的就是前表中输入捕获相关的HAL函数。
七、定时器同步
两个或多个定时器可以内部连接,实现定时器同步或串联。某个工作于主模式的定时器,可以对另一个工作于从模式的定时器执行复位、启动、停止操作,或为其提供时钟。定时器之间的连接可以实现如下一些功能。
- 将一个定时器用作另一个定时器的预分频器。
- 使用一个定时器使能另外一个定时器。
- 使用一个定时器启动另外一个定时器。
- 使用一个外部触发信号同步启动两个定时器。
例如,TIM1和TIM2串联工作的示意图中。TIM1工作于主模式,TIM2工作于从模式,TIM1作为TIM2的预分频器,其各种设置和工作原理如下:
- 设置TIM1工作于主模式,其触发输出(trigger output)信号TRGO1的事件源选择UEV 事件,则每次UEV事件时TRGO1输出一个上升沿的脉冲信号。
- 将TIM2从模式设置为外部时钟模式,触发信号源选择ITR0,这样TIM1的触发输出信号TRGO1就成了TIM2的时钟信号,相当于TIM1作为TIM2的预分频器。
- 启动TIM1和TIM2,这两个定时器就开始串联工作。
八、通用定时器中断事件和回调函数
所有定时器的中断ISR里调用一个相同的函数HAL_TIM_IRQHandler(),这是定时器中断处理通用函数。在这个函数里,程序会判断中断事件类型,并调用相应的回调函数。例如,基础定时器只有一个UEV事件,对应的回调函数是HAL_TIM_PeriodElapsedCallback(htim)。通用定时器和高级控制定时器有更多的中断事件和回调函数,文件stm32f4xx hal_tim.h定义了定时器所有中断事件类型的宏:
#define TIM_IT_UPDATE TIM_DIER_UIE //更新中断(Update interrupt) #define TIM_IT_CC1 TIM_DIER_CC1IE //捕获/比较1中断(Capture/Compare 1 interrupt)#define TIM_IT_CC2 TIM_DIER_CC2IE //捕获/比较2中断(Capture/Compare 2 interrupt)#define TIM_IT_CC3 TIM_DIER_CC3IE //捕获/比较3中断(Capture/Compare 3 interrupt)#define TIM_IT_CC4 TIM_DIER_CC4IE //捕获/比较4中断(Capture/Compare 4 interrupt)#define TIM_IT_COM TIM_DIER_COMIE //换相中断(Commutation interrupt)#define TIM_IT_TRIGGER TIM_DIER_TIE //触发中断(Trigger interrupt) #define TIM_IT_BREAK TIM_DIER_BIE //断路中断(Break interrupt)
函数HAL_TIM_IRQHandler()会根据中断事件标志位和中断事件使能标志位,判断具体发生了哪个中断事件,从而调用相应的回调函数。
函数HAL_TIM_IRQHandler()会根据中断事件标志位和中断事件使能标志位,判断具体发生了哪个中断事件,从而调用相应的回调函数。
中断事件类型与回调函数的对应关系如下表。 这些回调函数都需要一个定时器对象指针htim作为输入参数。表中最后两个事件类型是高级控制定时器的,一般在电机控制中用到;TIM_IT_TRIGGER事件是定时器作为从定时器时,TRGI (触发输入)信号产生有效边沿跳变时的事件。
中断事件类型 | 事件名称 | 回调函数 |
TIM_IT_CC1 | CC1通道输入捕获 | HAL_TIM_IC_CaptureCallback() |
CC1通道输出比较 | HAL_TIM_OC_DelayElapsedCallback() | |
TIM_IT_CC2 | CC2通道输入捕获 | HAL_TIM_IC_CaptureCallback() |
CC2通道输出比较 | HAL_TIM_OC_DelayElapsedCallback() | |
TIM_IT_CC3 | CC3通道输入捕获 | HAL_TIM_IC_CaptureCallback() |
CC3通道输出比较 | HAL_TIM_OC_DelayElapsedCallback() | |
TIM_IT_CC4 | CC4通道输入捕获 | HAL_TIM_IC_CaptureCallback() |
CC4通道输出比较 | HAL_TIM_OC_DelayElapsedCallback() | |
TIM_IT_UPDATE | 更新事件(UEV) | HAL_TIM_PeriodElapsedCallback() |
TIM_IT_TRIGGER | TRGI触发事件 | HAL_TIM_TriggerCallback() |
TIM_IT_BREAK | 断路输入事件 | HAL_TIMEx_BreakCallback() |
TIM_IT_COM | 换相事件 | HAL_TIMEx_CommutCallback() |
对于捕获/比较通道,输入捕获和输出比较使用一个中断事件类型,如TIM_IT_CC1表示通道CC1的输入捕获或输出比较事件,程序会根据捕获/比较模式寄存器TIMx_CCMR1的内容判断到底是输入捕获,还是输出比较。如果是输出比较,会连续调用两个回调函数,这两个函数只是意义不同,根据使用场景实现其中一个即可。