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 }