电子说
这一课我们将介绍CKS32F4XX系列产品的定时器使用,CKS32F4XX的定时器功能十分强大,包含2个高级控制定时器,8个普通定时器,2个基础定时器,以及两个看门狗定时器和一个系统定时器,总共15个定时器之多。关于定时器部分内容的讲解我们将分3个部分展开,本节将介绍定时器的基本特征和定时操作。
1、计数器分辨率:指定时器一个计数周期,例如同样是84Mhz的工作时钟,
对于TIM2,其分辨率的范围为:1*(1000ns/ 84)~(2^32)*(1000ns/84);
对于TIM3,其分辨率的范围为:1*(1000ns/84)~(2^16)*(1000ns/84)。
2、计数器类型:这个参数按照计数的方向来划分:
向上计数指的是从0开始到1,2...直到自己设置的计数上限值N,达到后再次从0开始计数,周而复始;
向下计数指的是从设置的计数上限值N开始到N-1,N-2,...直到0,达到后再次从N开始计数,周而复始;向上向下计数指的是从0,1,2...N,然后再从N,N-1,N-2...0,周而复始。
3、预分频系数:可以通过设置该系数来配置时基,如定时器工作在84Mhz下,配置不分频则一个计数时基为11.9ns,配置成2分频则一个计数时基为23.8ns。
4、产生DMA请求:定时器的更新会发出DMA请求,这是因为在DMA通道中为Timer预留了一个通道。
5、捕获比较通道:捕获就是定时器可以捕捉到通道的上升沿或者下降沿信号,比较就是定时器可以将计数器的值和装载值做比较,关于这部分将会在下后续章节展开。
6、互补输出:互补输出指的是输出的两个通道两个波形完全相反,通常运用在桥式电路中的互补PWM输出,这一部分将在后续章节展开。
7、最大接口时钟和最大工作时钟:定时器的时钟来源是APB,通过APB预分频器的配置,最大工作时钟可以是PCLKx的两倍。
CKS32F4XX定时器的定时操作
定时器的定时操作原理其实很简单,就像家里用的微波炉一样,需要加热食物时,先设定一个加热时间,然后按下开关,开始计时,当达到我们设置的定时时间以后,微波炉就会停止工作,并会有一个声音提示我们,定时时间到了。当然,在完成定时操作之前,必须要对Timer进行一些配置,下面我们以timer3为例,为大家演示。
1、Timer3时钟使能
TIM3时钟来自于APB1域,我们通过APB1总线下的时钟使能函数来使能TIM3的时钟。调用的函数是:
//>>使能 TIM3 时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);typedefstruct
2、定时器参数初始化
在库函数中定时器的初始化通过TIM_TimeBaseInit实现的:
void TIM_TimeBaseInit(TIM_TypeDef *TIMx, TIM_TimeBaseInitTypeDef * TIM_TimeBaseInitStruct);
参数结构体指针,结构体类型为TIM_TimeBaseInitTypeDef,下面是结构体的定义:
typedef struct { uint16_t TIM_Prescaler; uint16_t TIM_CounterMode; uint16_t TIM_Period; uint16_t TIM_ClockDivision; uint8_t TIM_RepetitionCounter; } TIM_TimeBaseInitTypeDef;typedefstruct
这个结构体一共有5个成员变量,要说明的是,对于通用定时器只有前面四个参数有用,最后参数TIM_RepetitionCounter是高级定时器才有用的,后续章节会详解,在此不赘述。
第一个参数TIM_Prescaler是用来设置分频系数的,对应上表中的预分频系数。
第二个参数TIM_CounterMode是用来设置计数方式,如上表所述,可以设置为向上计数,向下计数方式还有向上向下计数(中央对齐计数)方式,比较常用的是向上计数TIM_CounterMode_Up和向下计数 TIM_CounterMode_Down。
第三个参数是设置自动重载计数周期值,可以通俗的理解成要定时的次数,这个是根据定时时间和时基做除法换算得到的,比如定时器现在计数1次,时间经过了250ns,要定时100us,那自动重载计数周期值为400。
第四个参数是用来设置时钟分频因子,这个参数与定时器的其他功能有密切,本节操作先按照TIM_CKD_DIV1来配置(不分频)。
针对TIM3初始化范例代码格式:
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_TimeBaseStructure.TIM_Period=5000; TIM_TimeBaseStructure.TIM_Prescaler=7199; TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, TIM_TimeBaseStructure);
3、设置TIM3_DIER允许更新中断
因为我们要达到定时时间到后有一个到时提醒的效果,这就需要用到TIM3的更新中断,在库函数里面定时器中断使能是通过TIM_ITConfig函数来实现的:
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
第一个参数是选择定时器号,取值为 TIM1~TIM17。
第二个参数非常关键,是用来指明我们使能的定时器中断的类型,定时器中断的类型有很多种,包括更新中断TIM_IT_Update,触发中断TIM_IT_Trigger,以及输入捕获中断等等。
第三个参数就很简单了,就是失能还是使能。
例如我们要使能TIM3的更新中断,格式为:
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE );
4、TIM3中断优先级设置
在定时器中断使能之后,因为要产生中断,必不可少的要设置NVIC(向量中断控制器)来设置中断优先级。关于NVIC_Init函数实现中断优先级的设置请到NVIC章节参考,这里就不重复讲解。
5、使能TIM3
配置好定时器后,不要忘记开启定时器,就像按下微波炉的开关一样,定时器才会进入工作状态,在固件库里面使能定时器的函数是通过TIM_Cmd函数来实现的:
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)
这个函数非常简单,比如我们要使能定时器3,方法为:
//>>使能 TIMx 外设 TIM_Cmd(TIM3, ENABLE);
6、编写中断服务函数
最后,要编写定时器中断服务函数,类似于听到微波炉结束工作的声音后,我们需要进行把加热的食物取出或者继续加热等操作,通过该函数来处理定时器产生的相关中断。在中断产生后,通过状态寄存器的值来判断此次产生的中断属于什么类型。然后执行相关的操作,我们这里使用的是更新(溢出)中断,在处理完中断之后应,该向TIM3_SR的最低位写0,来清除该中断标志,在固件库函数里面,用来读取中断状态寄存器的值判断中断类型的函数是:ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t)
该函数的作用是,判断定时器TIMx的中断类型 TIM_IT是否发生中断。比如,我们要判断定时器3 是否发生更新(溢出)中断,方法为:
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET){} IT_Update) != RESET){}
固件库中清除中断标志位的函数是:
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT)
该函数的作用是,清除定时器TIMx的中断TIM_IT 标志位。使用起来非常简单,比如我们在TIM3的溢出中断发生后,我们要清除中断标志位,方法是:
TIM_ClearITPendingBit(TIM3, TIM_IT_Update );
这里需要说明一下,固件库还提供了两个函数用来判断定时器状态以及清除定时器状态标
志位的函数TIM_GetFlagStatus 和TIM_ClearFlag,他们的作用和前面两个函数的作用类似。只 是在TIM_GetITStatus函数中会先判断这种中断是否使能,使能了才去判断中断标志位,而TIM_GetFlagStatus 直接用来判断状态标志位。通过以上几个步骤,我们就可以达到我们的目的了,使用通用定时器的更新中断,来实现定时并产生定时中断信号。
代码实例
/**通用定时器3中断初始化
>>arr:自动重装值。psc:时钟预分频数
>>定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.
>>Ft=定时器工作频率,单位:Mhz
这里使用的是定时器3**/
void TIM3_Int_Init(u16 arr,u16 psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; NVIC_InitTypeDef NVIC_InitStructure; //>> ①使能 TIM3 时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); //>>自动重装载值 TIM_TimeBaseInitStructure.TIM_Period = arr; //>>定时器分频 TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //>>向上计数模式 TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; // ②初始化定时器 TIM3 TIM_TimeBaseInit(TIM3, TIM_TimeBaseInitStructure); //③允许定时器 3 更新中断 TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); //定时器 3 中断 NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQn; //抢占优先级 1 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x01; //响应优先级 3 NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x03; NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; // ④初始化 NVIC NVIC_Init( NVIC_InitStructure); //⑤使能定时器 3 TIM_Cmd(TIM3,ENABLE); } //⑥定时器 3 中断服务函数 void TIM3_IRQHandler(void) { //>>溢出中断 if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET) { } //>>清除中断标志位 TIM_ClearITPendingBit(TIM3,TIM_IT_Update); }
这里列出了一个中断服务函数和一个定时器3中断初始化函数,中断服务函数中,在每次中断后,判断 TIM3的中断类型,如果中断类型正确,则执行自己需要执行的操作并清除中断标志,TIM3_Int_Init 函数就是执行我们上面介绍的那5个步骤,使得 TIM3开始工作,并开启中断。这里我们分别用标号①~⑤来标注定时器初始化的五个步骤。该函数的2 个参数用来设置TIM3的溢出时间。假设系统初始化 SystemInit函数里面已经初始化APB1的时钟为 4分频,所以APB1的时钟为42M,这也是timer3的最大接口时钟,而从CKS32F4的内部时钟树图得知:当APB1的时钟分频数为1的时候,TIM2~7以及TIM12~14的时钟为APB1的时钟,而如果APB1的时钟分频数不为1,那么TIM2~7以及TIM12~14的时钟频率将为APB1时钟的两倍。因此,TIM3的时钟为84M,再根据我们设计的arr和psc的值,就可以计算中断时间了。计算公式如下:
Tout=((arr+1)*(psc+1))/Tclk;
其中:
Tclk:TIM3的输入时钟频率(单位为Mhz)。
Tout:TIM3溢出时间(单位为us)。
本节我们介绍了CKS32F4XX各定时器概况,以及如何设置最基础的定时功能,包括开启定时器的时钟,配置定时器的时基,定时次数以及计数的方向等,此外定时器其他的功能如输入捕获,比较输出和PWM输出等功能将会在后续章节展开。
来源:中科芯MCU
审核编辑:汤梓红
全部0条评论
快来发表一下你的评论吧 !