一、简介
STM32f40x 芯片一共有 14 个定时器,分为 3 类
- 基本定时器
ST32F40x 基本定时器包括 TIM6 和 TIM7。基本定时器只提供基本的定时功能以及 DAC 触发功能。
- 通用定时器
ST32F40x 通用定时器包括 TIM2、 TIM3、 TIM4、 TIM5 和 TIM9、 TIM10、 TIM11、 TIM12、 TIM13 以及 TIM14。
通用定时包含基本定时器的功能,同时还具有输入捕获、输出比较, PWM 输入以及 PWM 输出。
- 高级定时器
ST32F40x 高级定时器包括 TIM1 和 TIM8。高级定时器包含通用定时器的功能,同时还具有 PW 互补输出、
死区功能以及刹车功能。
在【STM32F4资料\1.手册\STM32F407ZGT6.pdf】的P17时钟树中可知:
2、STM32F407ZGT6的基本定时器概述
STM32F407ZGT6基本定时器有两个,分别是TIM6和TIM7。基本定时器的特性:①16 位自动重载递增计数器;②16 位可编程预分频器,用于对计数器时钟频率进行分频(即运行时修改),分频系数介于 1 和 65536 之间;③发生计数器上溢时会生成中断(前提是使能更新中断)。
3、STM32F407ZGT6的基本定时器框图讲解
- 定时器的最大计数频率(时钟频率)是84MHZ,即图中的CK_INT。
- 基本定时器有一个预分频器,分频值1 和 65536,作用是:降低计数频率(时钟频率),分频后的计数频率(时钟频率)是上图中的CK_CNT
- 基本定时器的工作原理:编程时,先确定计数频率CK_CNT,然后给“自动重装载寄存器 ”赋值,然后“定时器计数器寄存器”在CK_CNT的频率下进行自增,如果增加到与“自动重装载寄存器 ”中的值相等时,则定时时间到达,同时会产生更新事件(如果使能中断,则会产生更新中断),简答得说:定时的过程就是“定时器计数器寄存器”的值和自动重装载寄存器 ”实时比较的一个过程。
- 时基单元:①计数器寄存器 (TIMx_CNT);②预分频器寄存器 (TIMx_PSC);③自动重载寄存器 (TIMx_ARR)
- 时基单元的每个寄存器都是16位。
自动重装载寄存器的“影子寄存器”:是否使用“影子寄存器”取决于TIMx_CR1 (x:6~7)寄存器中的自动重载预装载使能位 (ARPE)。如果使能自动重载预装载使能位,则影子寄存器有效;否则无效。①如果使能自动重载预装载使能位,则影子寄存器有效,计时的过程是“计数器寄存器 (TIMx_CNT)”和“自动重载寄存器 (TIMx_ARR)的影子寄存器”的一个比较过程。如果定时器时间没有到达,即还在计时中,此时修改了“自动重载寄存器 (TIMx_ARR)”,要在上一次定时时间到达后才会更新“影子寄存器”。(因此可对自动重载寄存器 (TIMx_ARR)进行任意更改。而新的自动重装载值将在下一更新事件发生时被采用)。举例:假如第一次定时时间为1000ms,定时时间还没到,当前已经计数计时到了350ms,此时,修改定时时间为500ms,定时时长设置不会马上生效,而是要等到定时1000ms并产生更新事件后才会有效。
②如果不使能自动重载预装载使能位,则影子寄存器无效,计时的过程是“计数器寄存器 (TIMx_CNT)”和“自动重载寄存器 (TIMx_ARR)”的一个比较过程。随时更新“自动重载寄存器 (TIMx_ARR)”的值,都可以马上改变定时时长。举例:假如第一次定时时间为1000ms,定时时间还没到,当前已经计数计时到了350ms,此时,修改定时时间为500ms,定时时长设置会马上生效,则在定时到500ms时就会产生更新事件。
- 预分频器寄存器 (TIMx_PSC)也有“缓冲”,“缓冲”是永远有效的,由于 TIMx_PSC 预分频寄存器有缓冲,因此可对预分频器进行任意更改。而新的预分频比将在下一更新事件发生时被采用。
- 计数器从 0 计数到自动重载值( TIMx_ARR 寄存器的值)并生成计数器上溢事件,如果TIMx_CR1寄存器的OPM位置1,则定时器重新又从 0 开始计数。每次发生计数器上溢时会生成更新事件。
4、相关寄存器
4.1、TIM6 和 TIM7 控制寄存器 1 (TIMx_CR1)
位 0 CEN 计数器使能 (Counter enable)
0:禁止计数器
1:使能计数器
位 1 UDIS 更新禁止 (Update disable)
0:使能更新 (UEV),更新事件可通过以下事件之一生成:①计数器上溢②将 UG 位置 1
1:禁止 更新UEV。定时到达后不会生成更新事件。
位 2 URS 选择更新请求源 (Update request source)
0:所有以下事件都会生成更新中断。此类事件包括:①计数器上溢;②将 UG 位置 1
1:只有计数器上溢才会生成更新中断。
位 3 OPM 单脉冲模式 (One-pulse mode)
0:计数器在发生更新事件时不会停止计数
1:计数器在发生下一更新事件时停止计数(将 CEN 位清零)。
位 7 ARPE 自动重载预装载使能 (Auto-reload preload enable)
0: TIMx_ARR 寄存器不进行缓冲(影子寄存器无效)。
1: TIMx_ARR 寄存器进行缓冲(影子寄存器有效)。
举例:定时器6使能ARR自动重装载寄存器的影子寄存器,重复定时,允许计数器上溢产生更新请求且允许UG位置1产生更新请求,使能定时器6:
TIM6->CR1 |= 1<<7;
TIM6->CR1&=~(1<<3);
TIM6->CR1&=~(1<<1);
TIM6->CR1&=~(1<<2);
TIM6->CR1|=1<<0;
4.2、TIM6 和 TIM7 DMA/中断使能寄存器 (TIMx_DIER)
位 0 UIE 更新中断使能 (Update interrupt enable)
0:禁止更新中断。
1:使能更新中断。所谓的“更新中断”可以理解为“定时器中断”。
举例:定时器6使能更新中断 TIM6->DIER |= 1<<0;
4.3、TIM6 和 TIM7 状态寄存器 (TIMx_SR)
位 0 UIF 更新中断标志 (Update interrupt flag)
该位有硬件置1,软件清零。
0:未发生更新(即:定时时间还没到)。
1:更新中断挂起,由以下两种因素导致该位硬件置1:
①定时器计数器上溢会导致该位置1(即该位置1表示定时时间到达)
②UG位置1也会导致该位置1(前提:TIMx_CR1 寄存器中 URS = 0 且 UDIS = 0)
总结:导致产生更新中断标志位置1并且产生更新中断的因素有两个
举例:定时器7的中断函数
void TIM7_IRQHandler(void)
{
if(TIM7->SR&(1<<0)) //判断中断标志是否置1
{
TIM7->SR &=~(1<<0);//中断标志清零
//中断代码
}
}
4.4、TIM6 和 TIM7 事件生成寄存器 (TIMx_EGR)
位 0 UG 更新生成 (Update generation)
该位可通过软件置 1,并由硬件自动清零。
0:不执行任何操作。
1:重新初始化定时器计数器(TIMx_CNT)并生成寄存器更新事件。
举例:使用UG位置1来初始化TIM6_CNT寄存器 TIM6->EGR |= 1<<0;
4.5、TIM6 和 TIM7 预分频器 (TIMx_PSC)
位 15:0 PSC[15:0] 预分频器值 (Prescaler value)
计数器时钟频率 CK_CNT 等于 fCK_PSC / (PSC[15:0] + 1)。
PSC 包含在每次发生更新事件时要装载到实际预分频器寄存器的值。
举例:已知TIM6的时钟频率 fCK_PSC是84MHZ,如何分频可以让CK_CNT的频率为10KHZ?即IM6->PSC = 8400 -1;
4.6、TIM6 和 TIM7 自动重载寄存器 (TIMx_ARR)
该寄存器16位有效,主要用于和“计数器寄存器”进行比较,如果“计数器寄存器”的值自增到与“自动重装载寄存器”的值相等,则溢出,即定时时间到达。
举例:已知TIM6的时钟频率 fCK_PSC是84MHZ,如何分频可以让CK_CNT的频率为10KHZ?如果CK_CNT为10KHZ,则自动重装载寄存器赋值多少可以实现1s的定时?
TIM6->PSC = 8400 -1;
计算公司Tout= ((arr+1)*(psc+1))/Tclk;故
((1+TIM_Prescaler )/84M)*(1+TIM_Period )=((8400)/84M)*(1+TIM_Period )=1秒*/
TIM_Period = 10000;
TIM6->ARR=10000;
4.7、APB1 外设时钟使能寄存器(RCC_APB1ENR)
该寄存器主要关注位8~位0,相应位置1则使能相应外设的时钟
举例:使能定时器6的时钟,RCC->APB1ENR |= 1<<4;
4.8、TIM6&TIM7的中断函数与中断端通道编号
该表格主要查阅startup_stm32f40_41xxx.s 和 stm32f4xx.h
5、编程思路
- 使能定时器的相应时钟,即RCC->APB1ENR的相应位置1
- TIMx_ARR 寄存器进行缓冲
- 计数器在发生更新事件时不会停止计数(循环计数,循环定时)
- 使能更新 (UEV)
- 选择更新请求源
- 使能更新中断
- UG位置1重新初始化定时器计数器(TIMx_CNT)
- 状态寄存器清零
- 配置预分频值
- 配置自动重装载值
- 使能定时器中断中断通道,调用NVIC_EnableIRQ函数,如果要调整抢占和响应的优先级,请参考第五章的内容。
- 使能定时器
6、软件设计
实现定时器7控制LED灯亮灭。每秒钟取反一次LED灯的状态。
寄存器版
void TIM7_Init(u16 psc, u16 arr)
{
RCC->APB1ENR |= 1<<5;//使能定时器7的时钟
TIM7->CR1 |= 1<<7; //TIM7_ARR 寄存器进行缓冲
TIM7->CR1&=~(1<<3);//计数器在发生更新事件时不会停止计数(循环计数,循环定时)
TIM7->CR1&=~(1<<1);//使能更新 (UEV)
TIM7->CR1&=~(1<<2);//选择更新请求源,允许①计数器上溢;②将 UG 位置 1 ,这两种情况产生更新事件
TIM7->DIER |= 1<<0;//使能更新中断
TIM7->EGR |= 1<<0;//UG位置1重新初始化定时器计数器(TIMx_CNT)
TIM7->SR = 0;//状态寄存器清零
TIM7->PSC = psc;//配置预分频值
TIM7->ARR = arr;//配置自动重装载值
NVIC_EnableIRQ(TIM7_IRQn);//使能定时器中断中断通道
TIM7->CR1|=1<<0;//使能定时器
}
void TIM7_IRQHandler(void)
{
if(TIM7->SR&(1<<0)) //判断中断标志是否置1
{
TIM7->SR &=~(1<<0);//中断标志清零
GPIOF->ODR ^= (1<<6);//使用异或操作,取反GPIOF->ODR的位6的值
}
}
int main(void)
{
LED_Init();
TIM7_Init(8400 -1,10000);
while(1)
{
}
}
库函数版
void TIM7_Init(u16 psc, u16 arr)
{
NVIC_InitTypeDef NVIC_InitStructure;
TIM_TimeBaseInitTypeDef TIM7_TimeBaseInitTypeDef;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7,ENABLE); //①使能 TIM7 时钟
TIM7_TimeBaseInitTypeDef.TIM_Period = arr; //自动重装载值
TIM7_TimeBaseInitTypeDef.TIM_Prescaler=psc; //定时器分频
TIM7_TimeBaseInitTypeDef.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
TIM7_TimeBaseInitTypeDef.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM7,&TIM7_TimeBaseInitTypeDef);// ②初始化定时器 TIM7
TIM_ITConfig(TIM7,TIM_IT_Update,ENABLE); //③允许定时器 7 更新中断
NVIC_InitStructure.NVIC_IRQChannel=TIM7_IRQn; //定时器 7 中断
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);// ④初始化 NVIC
TIM_Cmd(TIM7,ENABLE); //⑤使能定时器 7
}
void TIM7_IRQHandler(void)
{
if(TIM_GetITStatus(TIM7,TIM_IT_Update)==SET) //溢出中断
{
GPIO_ToggleBits(GPIOF,GPIO_Pin_6);
}
TIM_ClearITPendingBit(TIM7,TIM_IT_Update); //清除中断标志位
}
int main(void)
{
LED_Init();
TIM7_Init(8400 -1,10000);
while(1)
{
}
}