[ZigBee] 5、ZigBee基础实验——图文与代码详解定时器1(16位定时器)(长文)
1、定时器1概述
定时器1 是一个支持典型的定时/计数功能的独立16 位定时器,支持输入捕获,输出比较和PWM等功能。定时器有五个独立的捕获/比较通道。每个通道定时器要使用一个I/O 引脚。定时器用于范围广泛的控制和测量应用,可用的五个通道的正计数/倒计数模式将允许诸如电机控制应用的实现。
定时器1 的功能如下:
• 五个捕获/比较通道
• 上升沿、下降沿或任何边沿的输入捕获
• 设置、清除或切换输出比较
• 自由运行、模或正计数/倒计数操作
• 可被1,8,32 或128 整除的时钟分频器
• 在每个捕获/比较和最终计数上生成中断请求
• DMA 触发功能
2、16 位计数器
【关于分频,时钟】定时器包括一个16 位计数器,在每个活动时钟边沿递增或递减。活动时钟边沿周期由寄存器位CLKCON.TICKSPD定义,它设置全球系统时钟的划分,提供了从0.25MHz 到32MHz 的不同的时钟标签频率(可以使用32 MHz XOSC 作为时钟源)。这在定时器1 中由T1CTL.DIV设置的分频器值进一步划分。这个分频器值可以从1、8、32 或128。因此当32 MHz 晶振用作系统时钟源时,定时器1 可以使用的最低时钟频率是1953.125Hz,最高是32 MHz。当16MHz RC 振荡器用作系统时钟源时,定时器1 可以使用的最高时钟频率是16MHz。
【读取定时器当前值】计数器可以作为一个自由运行计数器,一个模计数器或一个正计数/倒计数器运行,用于中心对齐的PWM。可以通过两个8 位的SFR 读取16 位的计数器值:T1CNTH 和T1CNTL,分别包含在高位字节和低位字节中。当读取T1CNTL 时,计数器的高位字节在那时被缓冲到T1CNTH,以便高位字节可以从T1CNTH 中读出。因此!!!T1CNTL 必须总是在读取T1CNTH 之前首先读取。对T1CNTL 寄存器的所有写入访问将复位16 位计数器。当达到最终计数值(溢出)时,计数器产生一个中断请求。
【控制定时器】可以用T1CTL 控制寄存器设置启动并停止该计数器。当一个不是00 值的写入到T1CTL.MODE 时,计数器开始运行。如果00 写入到T1CTL.MODE,计数器停止在它现在的值上。
3、定时器1 操作
一般来说控制寄存器T1CTL 用于控制定时器操作。状态寄存器T1STAT 保存中断标志。各种操作模式如下所述。
4、自由运行模式
在自由运行操作模式下,计数器从0x0000 开始,每个活动时钟边沿增加1。当计数器达到0xFFFF(溢出),计数器载入0x0000,继续递增它的值,如图1 所示。当达到最终计数值0xFFFF,设置标志IRCON.T1IF 和T1STAT.OVFIF。如果设置了相应的中断屏蔽位TIMIF.OVFIM 以及IEN1.T1EN,将产生一个中断请求。自由运行模式可以用于产生独立的时间间隔,输出信号频率。(0xFFFF型)
图1 自由运行模式
5、模模式
当定时器运行在模模式,16 位计数器从0x0000 开始,每个活动时钟边沿增加1。当计数器达到T1CC0(溢出),寄存器T1CC0H:T1CC0L 保存的最终计数值,计数器将复位到0x0000,并继续递增。如果定时器开始于T1CC0 以上的一个值,当达到最终计数值(0xFFFF)时,设置标志IRCON.T1IF 和T1CTL.OVFIF。如果设置了相应的中断屏蔽位TIMIF.OVFIM 以及IEN1.T1EN,将产生一个中断请求。模模式可以用于周期不是0xFFFF的应用程序。计数器的操作展示在图2 中。(自定义上限型)
图2 模模式
6、正计数/倒计数模式
在正计数/倒计数模式,计数器反复从0x0000 开始,正计数直到达到T1CC0H:T1CC0L 保存的值。然后计数器将倒计数直到0x0000,如图3 所示。这个定时器用于周期必须是对称输出脉冲而不是0xFFFF 的应用程序,因此允许中心对齐的PWM 输出应用的实现。在正计数/倒计数模式,当达到最终计数值时,设置标志IRCON.T1IF 和T1CTL.OVFIF。如果设置了相应的中断屏蔽位TIMIF.OVFIM 以及IEN1.T1EN,将产生一个中断请求。(增减交替型)
图3 正计数/倒计数模式
7、通道模式控制
通道模式随着每个通道的控制和状态寄存器T1CCTLn 设置。设置包括输入捕获和输出比较模式。
8、 输入捕获模式
当一个通道配置为输入捕获通道,和该通道相关的I/O 引脚必须被配置为输入。(什么是输入捕获模式,运作机制)在启动定时器之后,输入引脚的一个上升沿、下降沿或任何边沿都将触发一个捕获,即把16 位计数器内容捕获到相关的捕获寄存器中。因此定时器可以捕获一个外部事件发生的时间。(捕获外部事件发生时间)
注意:在定时器可以使用一个输入/输出引脚之前,所需的I/O 引脚必须配置为定时器1 的外设引脚。通道输入引脚和内部系统时钟是同步的。因此输入引脚上的脉冲的最低持续时间必须大于系统时钟周期。
16 位捕获寄存器的内容从寄存器T1CCnH:T1CCnL 中读出。当捕获发生时,要设置IRCON.T1IF 标志和该通道的中断标志T1STAT.CHnIF(n 是通道号码)。如果分别设置了相应的中断屏蔽位T1CCTLn.IM,以及IEN1.T1EN,将产生一个中断请求。
9、输出比较模式(最难理解的一个,下面的边沿对其和中心对其其实是基于输出比较的运作核心,用上面讲的几种简单的模式实现PWM,中心对齐是利用模模模式实现的)
在输出比较模式中,与通道相关的IO要设置为输出模式。(什么是输出比较模式,运作机制)在定时器启动后, 计数器的内容会和通道比较寄存器中的内容T1CCnH:T1CCnL做比较。如果这两个内容(值)相等,输出引脚根据比较输出模式T1CCTLn.CMP的设置进行设置、复位或切换。(核心就是将T1CC0的值和每个通道寄存器中的值比较,相等了则产生中断等)
注意:输出引脚运行在一个给定输出比较模式下时,它上面的所有边沿都是无故障运行的。
写入比较寄存器T1CCnL 将被缓冲,这样写入到T1CCnL 的值不起作用,直到相应的高位寄存器T1CCnH 被写入。写入比较寄存器T1CCnH:T1CCnL 对于输出比较值不起作用,直到定时器达到0x00。
注意:通道0 的输出比较模式较少,因为T1CC0H:T1CC0L 在模式6 和7 有一个特殊功能,这意味着这些模式对于通道0 是不能使用的。
当发生一个比较时,设置IRCON.T1IF 标志和该通道的中断标志T1STAT.CHnIF(n 是通道号码)。如果分别设置了相应的中断屏蔽位T1CCTLn.IM,以及IEN1.T1EN,将产生一个中断请求。
不同定时器模式下输出比较模式的例子给定在以下图中。
边沿对齐:PWM 输出信号可以使用定时器在自由运行模式下,通道1 和2 在输出比较模式6 或7(第6和第7条线)下生成(由T1CCTLn.CMP 位定义,其中n 是1 或2),如图4 所示。PWM 信号的周期通过设置T1CC0 确定,通道输出的占空比由T1CCn 确定,其中n 是PWM 通道1 或2。也可以使用定时器自由运行模式。在这种情况下,T1CTL.DIV 位中的CLKCON.TICKSPD 和分频器值设置PWM 信号的周期。PWM 信号的极性由使用的是输出比较模式6 还是7 确定。PWM 输出信号还可以使用图4 所示的输出比较模式4 和5,或通过使用图5 所示的模模式生成。对于简单的PWM,最好使用使用输出比较模式4 和5 来生成。
图4 输出比较模式,定时器自由运行模式
图中0、1、2...6是7种模式,左边的波形对应每种模式
图5 输出比较模式,定时器模模式
中心对齐:PWM 输出可以通过选择定时器正计数/倒计数模式生成。根据PWM 信号所需的极性选择通道输出比较模式4 或5(由T1CCTLn.CMP 位定义,其中n 是1 或2)。PWM 信号的周期由T1CC0 确定,通道输出的占空比由T1CCn 确定,其中n 是PWM 通道1 或2。某些类型的电机驱动应用程序会需要中心对齐的PWM 模式,一般地这比边沿对齐的PWM 模式产生的噪音更少,因为I/O 引脚传输不集中在同一个时钟边沿上。
图6 输出比较模式,定时器正计数/倒计数模式
(附加小姿势)在一些类型的应用程序中,需要在输出之间定义一个延迟或死亡的时间。典型地,这用于输出驱动一个H桥配置,以避免H 桥的一边交叉传导失控。延迟或死亡时间可以通过使用T1CCn 在PWM 输出中获得,如下所示:
假定通道1 和通道2 使用定时器正计数/倒计数模式,用于驱动输出,且这两个通道分别使用输出比较模式4 和5,那么定时器周期(定时器1 的时钟周期)是:
tP = T1CC0 x 2
死亡时间,即两个输出都为低电平的时间,(定时器1 的时钟周期)是:
tD = T1CC1 – T1CC2
当下列情况发生,比较输出引脚初始化为表9-1 所列的值:
一个值被写入T1CNTL(所有定时器1 通道)
0x7 被写到T1CCTLn.CMP(通道n)
表9-1 初始的比较输出值(比较模式)
10、IR 信号产生和线性化 (*)
本节描述了CC253x 设备只需最少的SW 参与即可产生IR 的功能。
10.1、简介(*)
为远程控制产生IR 信号一般以下面两种方式之一完成:
调制码
非调制码(C 码,闪存代码)
CC253x 包括灵活的定时器功能,以最少的CPU 参与,来执行这两种类型的IR 信号的产生和线性化。大多数IR 协议每个命令只需一个CPU 干预即可实现。
10.2、调制码(*)
调制码可以使用定时器1(16 位)和定时器3(8 位)生成。处于调制模式的定时器3 用于产生载波。定时器3 有一个单独的分频器,用于它的输入。它的周期使用T3CC0 设置。定时器3 通道1 用于PWM 输出。载波的占空比使用T3CC1 设置。通道1 使用比较模式:“在比较设置输出,在0xFF 清除”(T3CCTL1.CMP = 101)。
表9-2 显示了定时器3 的38kHz 载波的频率误差计算。(用定时器3产生载波)
38kHz 载波的频率误差计算
IRCTL.IRGEN 寄存器位使得IR 产生模式处于定时器1。当设置了IRGEN 位,定时器1采用定时器3通道1的输出比较信号作为标记,而不是采用系统标记。定时器1周期是使用T1CC0 设置的,定时器1处于调制模式(T1CTL.MODE = 10),通道0处于比较模式(T1CCTL0.MODE = 1)。通道1比较模式“在比较设置输出,在0x0000 清除”(T1CCTL1.CMP = 011)用于输出门控信号。
标记载波的个数由T1CC1. T1CC1 设置,需要每个定时器1 周期由DMA 或CPU 更新一次。注意T1CC1的一个更新被缓冲,在定时器1 达到0x0000 之前不起作用。
空间载波的个数由T1CC0 设置。其值必须设置为标记和空间载波周期希望的总数。比较值被缓冲直到定时器达到0x0000。
定时器1 通道1 的输出进行定时器3 通道1 的输出和运算,作为IR 的输出,如图。
图7 定时器在IR 产生模式的方框图
定时器3 通道1 输出和定时器1 通道1 输出信号的时序是同步的,这样在IR 输出信号上就没有故障。当设置了IRGEN 位,IR 输出信号被送到引脚,而不是送到一般的定时器1 通道1 输出。下图显示了定时器3 被初始化为33%的占空比(T3CC0 = 3× T3CC1)的例子。定时器1 已经被初始化为3。(如果仅仅要实现一个空间周期,T1CC1 必须设置为0x00。)
图8 调制的波形示例
10.3 非调制码(*)
要产生非调制IR 码,定时器1 要处于模模式。信号的周期由T1CC0 给定,脉冲宽度由T1CC1 给定。T1CC1给出标记周期的长度,T1CC0 给出标记和空间周期的总数。比较值被缓冲,直到定时器达到0x0000。如果比较值不能保持不变,必须在每个周期由DMA 或CPU 更新。
10.4 学习(*)
学习通过使用定时器1(16 位)和定时器3(8 位)的捕获功能完成。定时器3 可以处理载波频率检测,定时器1 可以处理调制信代码的学习。电路应该按照图9 所述安装。
图9 IR 学习的方框图
10.4.1 载波频率检测(*)
定时器3 用于捕获和检测直接从IR 引脚二极管输入的载波频率。定时器必须对载波进行一定次数的采样。如果载波被检测,被检测出的频率必须提供平均数,这会存储在数据库中。
10.4.2 解调码学习(*)
IR 引脚二极管的输出由一个合适的电路解调。这一电路的输出用作处于捕获模式的定时器1 其中一个通道的输入。
10.5 其他注意事项(*)
IR 输出引脚在复位期间必须处于三态或下拉状态,以避免点亮IR LED 这一不必要的功耗。注意只有定时器1 通道1 的输出P1.1 是三态的,在复位期间和复位后没有上拉。
11、定时器1 中断
为定时器分配了一个中断向量。当下列定时器事件之一发生时,将产生一个中断请求:
● 计数器达到最终计数值(溢出或回到零)
● 输入捕获事件
● 输出比较事件
寄存器状态寄存器T1STAT 包括最终计数值事件和五个通道比较/捕获事件的中断标志。仅当设置了相应的中断屏蔽位和IEN1.T1EN 时,才能产生一个中断请求。中断屏蔽位是n 个通道的T1CCTLn.IM 和溢出事件TIMIF.OVFIM。如果有其它未决中断,必须在一个新的中断请求产生之前,通过软件清除相应的中断标志。而且,如果设置了相应的中断标志,使能一个中断屏蔽位将产生一个新的中断请求。
12、定时器1 DMA 触发
有三种DMA 触发与定时器1 有关。这些是DMA 触发T1_CH0,T1_CH1 和T1_CH2,分别在以下定时器比较事件上产生:
● T1_CH0 – 通道0 比较
● T1_CH1 – 通道1 比较
● T1_CH2 – 通道2 比较
通道3 和4 没有相关的触发。
13、定时器1 寄存器
本节描述了定时器1 的寄存器,由以下寄存器组成:
● T1CNTH – 定时器1 计数高位
● T1CNTL – 定时器1 计数低位
● T1CTL – 定时器1 控制
● T1STAT –定时器1 状态
● T1CCTLn – 定时器1 通道n 捕获/比较控制
● T1CCnH – 定时器1 通道n 捕获/比较高位值
● T1CCnL – 定时器1 通道n 捕获/比较低位值
TIMIF.OVFIM 寄存器位驻留在TIMIF 寄存器,和定时器3 和定时器4 寄存器一起描述。
14、作为数组访问定时器1 寄存器
定时器1 捕获/比较通道寄存器可以在XDATA 存储空间中作为一个连续的区域被访问。这使得可以作为一个简单的索引结构方便地访问寄存器。5 个捕获/比较控制寄存器映射到0x62A0 - 0x62A4。16 位捕获/比较值映射到0x62A6 - 0x62AF。0x62A5 不使用。
15、代码示例
15.1、定时器轮训方式
下面代码展示了通过设置定时器1计数来控制LED闪烁,这里没有采用中断方式,而是采用普通的轮训。关于LED的初始化我在前几节已经介绍了。这里重点看和定时器1相关的部分:
① 第33行将T1CTL设置为0x0d=0000 1101,参考下面寄存器表可知是通过设置定时器1的控制和状态寄存器来将定时器设置为:128分频,自由运行,从0x0000到0xFFFF反复计数。由于系统在没配置工作频率时默认是2分频,即32MHz/2=16MHz,因此定时器每次溢出的时间T=1/(16MHz/128)*65536=0.524s
② 第34行将T1STAT设置为0x21=0010 0001,参考下面的寄存器表知为使用定时器1的通道0,且中断有效。
③ 在49行一旦IRCON>0表明发生了某个中断,在该例程中我们仅仅用了TIMER1的中断,因此表明TIMER1中断发生:
因此下面整个程序实现的功能是:利用定时器1的通道0产生0.524s的周期性中断,每次发生中断系统会自动置IRCON的第1位为1,在main函数的轮训中一旦检测到该信息,则让计数变量count加1,一旦count计数大于1时,就让LED状态翻转,实现1SLED闪烁控制。
1 /**************************************************************************** 2 * 文 件 名: main.c 3 * 版 本: 1.0 4 * 描 述: 定时器T1通过查询方式控制LED1周期性闪烁 5 ****************************************************************************/ 6 #include <ioCC2530.h> 7 8 typedef unsigned char uchar; 9 typedef unsigned int uint; 10 11 #define LED1 P1_0 // P1.0口控制LED1 12 13 /**************************************************************************** 14 * 名 称: InitLed() 15 * 功 能: 设置LED灯相应的IO口 16 * 入口参数: 无 17 * 出口参数: 无 18 ****************************************************************************/ 19 void InitLed(void) 20 { 21 P1DIR |= 0x01; //P1.0定义为输出 22 LED1 = 1; //使LED1灯上电默认为熄灭 23 } 24 25 /**************************************************************************** 26 * 名 称: InitT1() 27 * 功 能: 定时器初始化,系统不配置工作时钟时默认是2分频,即16MHz 28 * 入口参数: 无 29 * 出口参数: 无 30 ****************************************************************************/ 31 void InitT1() 32 { 33 T1CTL = 0x0d; //128分频,自动重装 0x0000-0xFFFF 34 T1STAT= 0x21; //通道0,中断有效 35 } 36 37 /**************************************************************************** 38 * 程序入口函数 39 ****************************************************************************/ 40 void main(void) 41 { 42 uchar count=0; 43 44 InitLed(); //调用初始化函数 45 InitT1(); 46 47 while(1) 48 { 49 if(IRCON > 0) 50 { 51 IRCON=0; 52 if(count++ >= 1) //约1s周期性闪烁,示波器测大约为1025MS 53 { 54 count=0; 55 LED1 = !LED1; //LED1闪烁 56 } 57 } 58 } 59 }
15.2、定时器中断方式
上面采用轮训的方法是比较占资源的,类似我们按键那节中介绍的,除了轮训我们还可以用中断方式!在黄色初始化部分比采用轮训方式多了使能T1中断和总中断的部分(35、36行代码)。我之前没有加这两句代码,结果橙色部分的定时器1中断回调函数无法被触发!查datasheet发现Interrupt Overview:如果想触发回调函数,需要将EA开关和T1IE开关打开才行!
1 /**************************************************************************** 2 * 文 件 名: main.c 3 * 版 本: 1.0 4 * 描 述: 定时器T1通过查询方式控制LED1周期性闪烁 5 ****************************************************************************/ 6 #include <ioCC2530.h> 7 8 typedef unsigned char uchar; 9 typedef unsigned int uint; 10 11 #define LED1 P1_0 // P1.0口控制LED1 12 13 /**************************************************************************** 14 * 名 称: InitLed() 15 * 功 能: 设置LED灯相应的IO口 16 * 入口参数: 无 17 * 出口参数: 无 18 ****************************************************************************/ 19 void InitLed(void) 20 { 21 P1DIR |= 0x01; //P1.0定义为输出 22 LED1 = 1; //使LED1灯上电默认为熄灭 23 } 24 25 /**************************************************************************** 26 * 名 称: InitT1() 27 * 功 能: 定时器初始化,系统不配置工作时钟时默认是2分频,即16MHz 28 * 入口参数: 无 29 * 出口参数: 无 30 ****************************************************************************/ 31 void InitT1() 32 { 33 T1CTL = 0x0d; //128分频,自动重装 0x0000-0xFFFF 34 T1STAT= 0x21; //通道0,中断有效 35 T1IE = 1; //开总中断和T1中断 36 EA = 1; //开总中断 37 } 38 39 //定时器T1中断处理函数 40 #pragma vector = T1_VECTOR 41 __interrupt void T1_ISR(void) 42 { 43 static uchar count=0; 44 IRCON = 0x00; //清中断标志, 也可由硬件自动完成 45 if(count++ >= 1) //约1s周期性闪烁,示波器测大约为1025MS 46 { 47 count=0; 48 LED1 = !LED1; //LED1闪烁 49 } 50 } 51 52 /**************************************************************************** 53 * 程序入口函数 54 ****************************************************************************/ 55 void main(void) 56 { 57 InitLed(); //调用初始化函数 58 InitT1(); 59 60 while(1){} 61 }
Zigbee系列文章:
[ZigBee] 3、ZigBee基础实验——GPIO输出控制实验-控制Led亮灭
PS:如果您觉得还不错,点个赞,让更多人受益~
@beautifulzzzz 2016-07-13 continue~
e-mail:beautifulzzzz@qq.com
sina:http://weibo.com/beautifulzzzz?is_all=1