TIM简介
本节课我们将介绍互联型产品CKS32F107xx系列的定时器使用,CKS32F107xx的定时器资源比较丰富,包含2个高级定时器,10个通用定时器,2个基本定时器,以及两个看门狗定时器和一个系统定时器,共达17个定时器之多。关于定时器部分内容的讲解我们将分4个部分展开,本节将围绕定时器的定时原理展开,并以简单定时配置操作演示。
首先介绍下CKS32F107xx系列定时器的分类,详见下图:
图1 CKS32F107xx系列定时器的分类
1、计数器分辨率
指定时器的一个计数周期,例如:当TIM1的工作时钟配置为72MHz,则分辨率的范围为:1*(1000ns/ 72)~(2^16)*(1000ns/72)。
2、计数器类型(按计数的方向来划分)
向上计数:指的是从0开始到1,2...直到自己设置的计数上限值N,达到后再次从0开始计数,周而复始;
向下计数:指的是从设置的计数上限值N开始到N-1,N-2,...直到0,达到后再次从N开始计数,周而复始;
向上/下计数:指的是从0,1,2...N,然后再从N,N-1,N-2...0,周而复始。
3、预分频系数
可以通过设置该项系数来配置时基,如定时器工作在72MHz下,配置不分频则一个计数时基为13.9ns,配置成2分频则一个计数时基为27.8ns。
4、产生DMA
定时器(除TIM9~TIM14)的更新会发出DMA请求,这是因为在DMA通道中为定时器预留了一个通道。
5、捕获/比较通道:捕获就是定时器可以捕捉到通道的上升沿或者下降沿信号,比较就是定时器可以将计数器的值和装载值做比较,关于这部分将会在下后续章节展开。
6、互补输出:互补输出指的是输出的两个通道两个波形完全相反,通常运用在桥式电路中的互补PWM输出,这一部分将在后续章节展开。
定时原理
前面提到,定时器有三个分类,分别是高级、通用和基本,但核心都是时基,以下通过基本定时器的功能框图来阐述时基原理,后续三个章节将不再赘述。
图2 基本定时器框图
从上图可以看出,时基单元由计数器(CNT)、预分频器(PSC)和自动重装载寄存器(ARR)组成。定时器时钟TIMxCLK经过PSC预分频后,即为CK_CNT,而PSC是16位的预分频器,所以可对TIMxCLK进行1~65536分频。而自动重装载寄存器和计数器都为16位,当计数器向上计数到自动重装载寄存器的数值会产生更新事件,若使能了中断,则会产生溢出中断,同时计数器清零并从头开始计数。
关于定时器的定时时长取决于定时器进入一次中断的时长和进入中断的次数。若用变量Cnt_Period来记录中断次数,则定时时长为:(PSC+1)*(ARR+1)*Cnt_Period / TIMxCLK。
定制配置操作
搞清楚定时原理后,下面我们以基本定时器TIM6为例,进行相应的定时配置操作,实现500ms的定时,并以LED的翻转来进行演示,编程的要点如下所示。
1、TIM6时钟使能
TIM6时钟来自于APB1域,我们通过APB1总线下的时钟使能函数来使能TIM6的时钟。调用的函数为:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
2、TIM6时基结构体初始化
在标准库中,定时器时基的初始化是通过TIM_TimeBaseInit实现的:
TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);
参数结构体指针,结构体类型为TIM_TimeBaseInitTypeDef,下面是结构体的定义:
typedef struct
{
uint16_t TIM_Prescaler; // 预分频器
uint16_t TIM_CounterMode; // 计数模式
uint32_t TIM_Period; // 定时器周期
uint16_t TIM_ClockDivision; // 时钟分频
uint8_t TIM_RepetitionCounter; // 重复计算器
} TIM_TimeBaseInitTypeDef;
这个结构体一共有5个成员变量,需要强调的是,对于基本和通用定时器只有前面四个参数有用,最后参数TIM_RepetitionCounter仅针对高级定时器才有效的,后续章节会详解,在此不赘述。
参数一TIM_Prescaler是用来设置分频系数的,对应上表中的预分频系数。
参数二TIM_CounterMode是用来设置计数方式,如上表所述,可以设置为向上计数,向下计数方式还有向上\下计数(中央对齐计数)方式,比较常用的是向上计数TIM_CounterMode_Up和向下计数 TIM_CounterMode_Down。
参数三是设置自动重载计数周期值,可以通俗的理解成要定时的次数,这个是根据定时时间和时基做除法换算得到的,比如定时器现在计数1次,时间经过了500ms,要定时1s,那自动重载计数周期值为2。
参数四是用来设置时钟分频因子,这个参数与定时器的其他功能有密切,本节操作先按照TIM_CKD_DIV1来配置(不分频)。
针对TIM6时基初始化示例代码格式如下:
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_TimeBaseStructure.TIM_Period = 4999; TIM_TimeBaseStructure.TIM_Prescaler = 7199; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);
根据上文定时时间的描述,当TIM6工作时钟配置为72MHz时,那么示例代码中配置的中断次时间为:(7199+1)*(4999+1) / 72000000 = 500ms。
3、设置TIM6_DIER允许更新中断
为了实现到达指定时间后,方便用户能有一个后续操作,这需要我们开启TIM6的更新中断,在库函数里面定时器中断使能是通过TIM_ITConfig函数来实现的:
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
参数一是选择定时器号,取值为 TIM1~TIM17。参数二非常关键,是用来指明我们使能的定时器中断的类型,定时器中断的类型有很多种,包括更新中断TIM_IT_Update,触发中断TIM_IT_Trigger,以及输入捕获中断等等。参数三就很简单了,就是失能还是使能。例如我们要使能TIM6的更新中断,格式如下:
TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE );
4、TIM6中断优先级设置
在定时器中断使能之后,因为要产生中断,必不可少的要设置NVIC(向量中断控制器)来设置中断优先级。关于NVIC_Init函数实现中断优先级的设置可参照先前课程中的讲述,这里不再赘述。
5、使能TIM6
配置好定时器后,用户可以自行选择在合适的位置开启,且只有开启后,定时器才会进入工作状态,在标准库中使能定时器的函数是通过TIM_Cmd函数来实现的:
TIM_Cmd(TIM6, ENABLE);
6、编写终端服务函数
当中断产生后,程序会跳转至定时器TIM6的中断服务函数中,在这个函数中可通过状态寄存器的值来判断此次产生的中断属于什么类型,进而执行相关的操作,我们这里使用的是更新(溢出)中断,在处理完中断之后应该向TIM6_SR的最低位写0,来清除该中断标志。
在标准库中,通过读取中断状态寄存器的值来判断中断所属类型的函数是:ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t);因此若要判断定时器6是否发生更新(溢出)中断,可通过如下语句判断:
if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)
每当TIM6的更新(溢出)中断发生后,需要清除中断标志位,通过如下函数实现:
TIM_ClearITPendingBit(TIM6, TIM_IT_Update);
所以,针对TIM6的中断服务函数,示例代码如下:
void TIM6_IRQHandler(void) { if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM6, TIM_IT_Update);//Clear TIM6 Update IT Flag GPIO_TogglePin(GPIOB, GPIO_Pin_12);//LED Toggle } }
至此,本节课中简单定时操作例程已讲述完毕,程序编译后下载至开发板,用户可观测到LED每隔500ms翻转一次。
来源:中科芯MCU
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。