任务6 电子秒表的设计与实现
本任务重点学习微处理器的定时/计数器,掌握CC2530定时/计数器的基本原理、功能和驱动方法,通过驱动CC2530的定时/计数器来实现秒表计时。
6.1 开发场景:如何实现电子秒表
在竞技类体育运动中,裁判员通常使用电子秒表来为运动员计时。打开多个手机,都调节到秒表部分,同时开始计时,在经过相同的时间段后,停止秒表,会发现各个手机上所计时间相同。为什么电子秒表的计时可信?为什么不同手机之间能做到同样时间内统计的时间是相同的?这是因为每个电子设备都使用了定时器来作为时间计时的时基。定时器通过精确的时钟来为秒表提供精确而稳定的时间,本项目将围绕这个场景展开微处理器定时/计数器的学习与实践。电子秒表如图6.1所示。
图6.1 电子秒表
6.2 开发目标
(1)知识要点:定时/计数器的工作原理。
(2)技能要点:掌握定时/计数器的基本原理;会使用CC2530微处理器定时/计数器。
(3)任务目标:使用CC2530微处理器模拟表/计数功能,通过编程使用CC2530微处理器的定时/计数器每秒产生一次脉冲信号,通过I/O接口连接信号灯的闪烁来表示定时/计数器秒脉冲的产生,同时使用模拟延时来比较定时1 s与延时1 s的准确性。
6.3 原理学习:定时/计数器
6.3.1 定时/计数器
定时/计数器是一种能够对时钟信号或外部输入信号进行计数的器件,当计数值达到设定要求时便向微处理器提出中断请求,从而实现定时或计数的功能。
定时/计数器的基本功能是定时和计数,且在整个工作过程中不需要微处理器进行过多参与,它将微处理器从相关任务中解放出来,提高了微处理器的使用效率。例如,任务4中实现信号灯控制采用的是软件延时方法,在延时过程中微处理器通过执行循环指令来消耗时间,在整个延时过程中会一直占用微处理器,降低了微处理器的工作效率。若使用定时/计数器来实现延时,则在延时过程中微处理器可以去执行其他工作任务。微处理器与定时/计数器之间的交互关系如图6.2所示。
图6.2 微处理器与定时/计数器之间的交互关系
1.微处理器中的定时/计数器功能
定时/计数器包含三个功能,分别是定时器功能、计数器功能和脉冲宽度调制(Pulse Width Modulation, PWM)输出功能,具体如下。
(1)定时器功能:对规定时间间隔的输入信号的个数进行计数,当计数值达到指定值时,说明定时时间已到。这是定时/计数器的常用功能,可用来实现延时或定时控制,其输入信号一般是微处理器内部的时钟信号。
(2)计数器功能:对任意时间间隔的输入信号的个数进行计数,一般用来对外界事件进行计数,其输入信号一般来自微处理器外部的开关型传感器,可用于生产线产品计数、信号数量统计和转速测量等方面。
(3)PWM输出功能:对规定时间间隔的输入信号的个数进行计数,根据设定的周期和占空比从I/O口输出控制信号,一般用来控制LED亮度或电机转速。
2.定时/计数器基本工作原理
无论使用定时/计数器的哪种功能,其最基本的工作原理是进行计数。定时/计数器的核心是一个计数器,可以进行加1(或减1)计数,每出现一个计数信号,计数器就自动加1(或自动减1),当计数值从最大值变成0(或从0变成最大值)溢出时,定时/计数器便向微处理器提出中断请求。计数信号的来源可以是周期性的内部时钟信号(如定时功能)或非周期性的外界输入信号(如计数功能)。
6.3.2 CC2530与定时器
1.CC2530定时器概述
CC2530一共有4个定时器,分别是定时器1、定时器2、定时器3和定时器4。这4个定时器根据硬件特性可分为三类,分别是16位定时器(定时器1)、MAC定时器(定时器2)、8位定时器(定时器3和定时器4),下面详细介绍每类定时器的特性。
(1)定时器1。定时器1是一个独立的16位定时器,支持输入捕获、输出比较和PWM输出功能。定时器有5个独立的捕获/比较通道,每个通道定时器使用一个I/O引脚。定时器广泛用于控制和测量领域,5个通道的正计数/倒计数模式可应用于诸如电机控制场合。定时器1的功能如下:
● 5个捕获/比较通道;
● 上升沿、下降沿或任何边沿的输入捕获;
● 设置、清除或切换输出比较;
● 自由运行、模或正计数/倒计数操作;
● 可作为被1、8、32或128整除的时钟分频器;
● 在每个捕获/比较和最终计数时生成中断请求;
● DMA触发功能。
(2)定时器2。定时器2主要用于为IEEE 802.15.4 CSMA-CA算法提供定时,以及为IEEE 802.15.4 MAC层提供一般的计时功能。当定时器2和睡眠定时器一起使用时,即使系统进入低功耗模式也会提供定时功能,定时器运行在CLKCONSTA.CLKSPD指定的速度上,如果定时器2和睡眠定时器一起使用,时钟速度必须设置为32 MHz,且必须使用一个频率为32 kHz的外部晶体振荡器(简称晶振)获得精确的结果。定时器2的主要特性如下:
● 16位定时器提供正计数功能,如16 μs、32 μs的符号/帧周期;
● 可变周期可精确到31.25 ns;
● 2×16位定时器比较功能;
● 24位溢出计数;
● 2×24位溢出计数比较功能;
● 帧首定界符捕获功能;
● 定时器启动/停止与外部32 kHz时钟同步,或由睡眠定时器提供定时;
● 比较和溢出时会产生中断;
● 具有DMA触发功能;
● 通过引入延迟可调整定时器值。
(3)定时器3与定时器4。定时器3和定时器4是2个8位的定时器,每个定时器有2个独立的捕获/比较通道,每个通道使用一个I/O引脚。定时器3和定时器4的特性如下:
● 2个捕获/比较通道;
● 可设置、清除或切换输出比较;
● 时钟分频器,可以被1、2、4、8、16、32、64、128整除;
● 在每次捕获/比较和最终计数事件发生时会产生中断请求;
● DMA触发功能。
通常情况下,在没有使用到CC2530的射频部分的模块时,基本上不会用到定时器2,而定时器1可以理解为定时器3与定时器4的增强版,因此本任务着重对定时器1进行详细分析。
2.CC2530定时器1
定时器1的16位计数器计数值的大小会在每个活动时钟边沿递增或递减,活动时钟边沿周期由寄存器位CLKCON.TICKSPD定义,它设置了全球系统时钟的划分,提供了0.25~32 MHz不同的时钟频率(可以使用32 MHz的XOSC作为时钟源)。在定时器1中,由T1CTL.DIV对分频器值进一步划分,分频器系数可以为1、8、32或128。因此,当使用32 MHz晶振作为系统时钟源时,定时器1可以使用的最低时钟频率是1953.125 Hz,最高时钟频率是32 MHz;当使用16 MHz的RC振荡器作为系统时钟源时,定时器1可以使用的最高时钟频率是16 MHz。
定时器1的计数器可以作为一个自由运行计数器、一个模计数器或一个正计数/倒计数器,以及用于中心对齐的PWM输出。
定时器1在获取计数器值时可以通过2个8位的SFR读取16位的计数器值,分别是T1CNTH和T1CNTL,这两个寄存器存储的计数器值分别表示计数器数值的高位字节和低位字节。当读取T1CNTL时,计数器的高位字节被缓冲到T1CNTH,以便高位字节从T1CNTH中读出,因此T1CNTL需要提前从T1CNTH读取。
对T1CNTL寄存器的所有写入内容进行访问操作时,将复位16位计数器,当达到最终计数值(溢出)时,计数器将产生一个中断请求。可以用T1CTL控制寄存器设置启动或者停止该计数器,当向T1CTL.MODE写入01、10或11时,计数器开始运行;如果向T1CTL.MODE写入00时,计数器将停止运行但计数值不会被清空。
3.CC2530定时器1的计数模式
CC2530定时器1拥有三种不同的计数模式,分别是自由运行模式、模模式、正计数/倒计数模式。自由运行模式适合产生独立的时间间隔,输出信号的频率;模模式适合周期不是0xFFFF的应用程序;正计数/倒计数模式适合周期必须是对称输出脉冲而不是固定值的应用程序。
(1)自由运行模式。计数器从0x0000开始,在每个活动时钟边沿增加1,当计数器达到0xFFFF(溢出)时,计数器载入0x0000,继续递增,如图6.3所示;当达到最终计数值0xFFFF时,将设置标志IRCON.T1IF和T1STAT.OVFIF。如果设置了相应的中断屏蔽位TIMIF.OVFIM和IEN1.T1EN,将产生一个中断请求。
图6.3 自由运行模式
(2)模模式。16位计数器从0x0000开始,在每个活动时钟边沿增加1,当计数器达到T1CC0(溢出)时,寄存器T1CC0H:T1CC0L保存的最终计数值,计数器将复位到0x0000,并继续递增。如果定时器开始于T1CC0以上的一个值,当达到最终计数值(0xFFFF)时,将设置标志IRCON.T1IF和T1CTL.OVFIF。如果设置了相应的中断屏蔽位TIMIF.OVFIM及IEN1.T1EN,将产生一个中断请求,模模式可以用于周期不是0xFFFF的应用程序,如图6.4所示。
图6.4 模模式
(3)正计数/倒计数模式。计数器反复从0x0000开始,当正计数直到达到T1CC0时保存的值,然后计数器将倒计数直到0x0000,如图6.5所示。这个定时器用于周期必须是对称的输出脉冲,而不是0xFFFF的应用程序,因此允许用于中心对齐的PWM输出应用。在正计数/倒计数模式,当达到最终计数值时,将设置标志IRCON.T1IF和T1CTL.OVFIF。如果设置了相应的中断屏蔽位TIMIF.OVFIM及IEN1.T1EN,将产生一个中断请求。
图6.5 正计数/倒计数模式
比较三种模式可以看出,自由运行模式的溢出值为0xFFFF,不可变;其他两种模式可通过对T1CC0赋值来精确控制定时器的溢出值。
4.CC2530定时器1中断
CC2530为定时器分配了一个中断矢量,当下列事件发生时,将产生一个中断请求。
● 计数器达到最终计数值(溢出或回到0);
● 输入捕获事件;
● 输出比较事件。
寄存器状态寄存器T1STAT包括最终计数值事件和5个通道比较/捕获事件的中断标志。仅当设置了相应的中断屏蔽位和IEN1.T1EN时,才能产生一个中断请求。中断屏蔽位是n个通道的T1CCTLn.IM和溢出事件TIMIF.OVFIM。如果有其他未决中断,必须在一个新的中断请求产生之前,通过软件清除相应的中断标志;而且如果设置了相应的中断标志,使能一个中断屏蔽位将产生一个新的中断请求。
5.CC2530定时器1寄存器
定时器1是16位定时器,在时钟上升沿或下降沿递增或递减,时钟边沿周期由寄存器CLKCON.TICKSPD定义,设置了系统时钟的划分,提供的频率范围为0.25~32 MHz。定时器1由T1CTL.DIV分频器进一步分频,分频值为1、8、32或128;具有定时器/计数器/脉宽调制功能,它有3个单独可编程输入捕获/输出比较信道,每个信道都可以当成PWM输出或捕获输入信号的边沿时间。
CC2530定时器1的配置寄存器共7个,这7个寄存器分别是:T1CNTH(定时器1计数高位寄存器)、T1CNTL(定时器1计数低位寄存器)、T1CTL(定时器1控制寄存器)、T1STAT(定时器1状态寄存器)、T1CCTLn(定时器1通道n捕获/比较控制寄存器)、T1CCnH(定时器1通道n捕获/比较高位值寄存器)、T1CCnL(定时器1通道n捕获/比较低位值寄存器),其中T1CCTLn、T1CCnH、T1CCnL均有多个且结构相同,这里只介绍通道1。下面分析定时器1的每个寄存器的各个位的配置含义。
(1)T1CTL:定时器1控制寄存器,D1D0用于控制运行模式,D3D2用于设置分频器划分值,表6.1所示为T1CTL功能表。
表6.1 T1CTL功能表
(2)T1STAT:定时器1状态寄存器,D4~D0为通道4~0的中断标志,D5为溢出标志位,当计数到最终计数值时将自动置1,表6.2所示为T1STAT功能表。
表6.2 T1STAT功能表
(3)T1CCTL0:D1、D0为捕获模式选择:00为不捕获,01为上升沿捕获,10为下降沿捕获,11为所有边沿都捕获;D2位为捕获或比较的选择,0为捕获模式,1为比较模式。D5~D3为比较模式的选择:000为发生比较式输出端置1,001为发生比较时输出端清0,010为比较时输出翻转,其他模式较少使用。T1CCTL0(定时器1通道0捕获/比较控制寄存器)如表6.3所示。
表6.3 定时器1通道0捕获/比较控制寄存器(T1CCTL0)
(4)T1CNTH(定时器1计数高位寄存器)如表6.4所示。
表6.4 定时器1计数高位寄存器
(5)T1CNTL(定时器1计数低位寄存器)如表6.5所示。
表6.5 定时器1计数低位寄存器
(7)T1CC0H(定时器1通道0捕获/比较高位寄存器)如表6.6所示。
表6.6 定时器1通道0捕获/比较高位寄存器
(8)T1CC0L(定时器1通道0捕获/比较低位寄存器)如表6.7所示。
表6.7 定时器1通道0捕获/比较低位寄存器
6.4 任务实践:电子秒表的软/硬件设计
6.4.1 开发设计
1.硬件设计
本任务的硬件架构设计如图6.6所示。
图6.6 硬件架构设计
使用CC2530微处理器定时器可以实现像秒表一样实现精确的秒脉冲,CC2530微处理器秒脉冲信号的精确与否取决于定时器的配置。根据CC2530微处理器定时器的性质,定时器无法产生1 s以上的延时,要实现1 s的延时就需要产生一个稳定的延时,这个延时乘以一个倍数就等于1 s,因此可以配置一个10 ms的延时,然后循环计数100次即可产生一个脉冲控制信号以此实现1 s的精确延时。
定时计算公式为:
式中,最大值为M, N为计数值,初值为X, f为晶振频率。
先配置定时器的工作模式为模模式,然后将系统时钟(32 MHz)进行8分频,8分频后系统时钟为4 MHz,要实现10 ms延时就需要在4 MHz的时钟下计数40000次,即1/4000000×40000=0.001 s,然后设置每完成一个定时周期触发一次中断使循环计数加1,循环加100次就可以实现10 ms延时。
2.软件设计
本任务程序设计思路如下。
(1)初始化系统时钟、LED引脚和定时器。
(2)初始化完成后程序进入主循环。
(3)软件延时1 s,对LED1的状态进行取反。
(4)同时定时器每经过10 ms就产生一次中断,并统计进入中断的次数,当达到100次时,也就是10 ms×100=1 s时,执行LED2状态的反转操作并将次数清0,重新开始计数。
程序设计流程如图6.7所示。
图6.7 程序设计流程图
6.4.2 功能实现
1.相关头文件模块
/**************************************************************************************** * 文件:led.h ****************************************************************************************/ #define D1 P1_1 //宏定义D1灯(即LED1)控制引脚P1_1 #define D2 P1_0 //宏定义D2灯(即LED2)控制引脚P1_0 #define ON 0 //宏定义打开状态为ON #define OFF 1 //宏定义关闭状态为OFF
2.主函数模块
主函数在完成初始化系统时钟、LED引脚和定时器后,进入主循环,通过软件延时1 s控制LED1闪烁。主函数程序如下。
/**************************************************************************************** * 名称:main() * 功能:主函数 ****************************************************************************************/ void main(void) { xtal_init(); //CC2530系统时钟初始化 led_io_init(); //LED引脚初始化 time1_init(); //定时器1初始化 while(1){ delay_ss(1); //软件延时1 s D1=! D1; //改变LED1的状态 } }
3.系统时钟初始化模块
CC2530系统时钟初始化源代码如下。
/**************************************************************************************** * 名称:xtal_init() * 功能:CC2530系统时钟初始化 ****************************************************************************************/ void xtal_init(void) { CLKCONCMD&=~0x40; //选择32 MHz的外部晶振 while(CLKCONSTA&0x40); //晶振开启且稳定 CLKCONCMD&=~0x07; //选择32 MHz系统时钟 CLKCONCMD&=~0x38; //选择32 MHz定时器时钟 }
4.定时器初始化模块
将定时器初始化为模模式,时钟8分频,重装载寄存高位写入0x90、低位写入0x40,配置中断模式,使能定时器中断,开总中断。定时器初始化源代码如下。
/**************************************************************************************** * 名称:time1_init() * 功能:定时器1初始化 ****************************************************************************************/ void time1_init(void) { T1CTL|=0x06; //8分频,模模式,从0计数到T1CC0 T1CC0L=0x40; //定时器1通道0捕获/比较值低位 T1CC0H=0x9C; //定时器1通道0捕获/比较值高位,定义10 ms产生一次中断 T1CCTL0|=0x44; //定时器1通道0捕获/比较控制 T1IE=1; //设定定时器1中断使能 EA=1; //设定总中断使能 }
5.LED引脚初始化模块
LED引脚初始化源代码如下。
/**************************************************************************************** * 名称:led_init() * 功能:LED引脚初始化 ****************************************************************************************/ void led_init(void) { P1SEL&=~0x03; //配置控制引脚(P1_0和P1_1)为GPIO模式 P1DIR|=0x03; //配置控制引脚(P1_0和P1_1)为输出模式 D1=OFF; //初始状态为关闭 D2=OFF; //初始状态为关闭 }
6.定时器中断服务函数模块
该模块有两个功能,分别完成1 s循环计数和控制LED2反转。定时器中断服务函数源代码如下。
/**************************************************************************************** * 名称:void T1_ISR(void) * 功能:中断服务子程序 ****************************************************************************************/ #pragma vector = T1_VECTOR __interrupt void T1_ISR(void) { EA=0; //关总中断 counter++; //统计进入中断的次数 if(counter>100){ //初始化中定义10ms产生一次中断,经过100次中断,10 ms×100=1 s counter=0; //统计的复位次数 D2=! D2; //改变LED2的状态,打开LED2延时1 s,关闭LED2延时1 s } T1IF=0; //中断标志位清0 EA=1; //开总中断 }
6.5 任务验证
使用IAR开发环境打开任务设计工程,程序通过编译后,由SmartRF下载到CC2530微处理器中,执行程序后,开发平台上的LED1和LED2同时开始闪烁,闪烁的时间大概为1 s,随着时间的推移,LED1和LED2的闪烁动作逐渐拉开,无法保持同步闪烁。
6.6 任务小结
本任务通过配置CC2530微处理器的定时/计数器实现每秒产生一次脉冲的功能,由信号灯闪烁来显示脉冲输出,从而基于CC2530定时器实现电子秒表的开发。
通过本任务的开发,理解CC2530定时/计数器的工作原理和功能特点,通过定时器1的学习,掌握其技术模式、寄存器配置,并掌握定时/计数器的中断初始化以及中断服务函数,理解秒脉冲发生的工作原理。
6.7 思考与拓展
(1)CC2530定时/计数器的功能和特点有哪些?
(2)定时/计数器的计数模式有哪些?
(3)CC2530有几个定时/计数器?分别有哪些寄存器?
(4)如何正确对CC2530定时/计数器进行中断初始化?
(5)定时/计数器除了能够实现1 s的精确延时,还可以产生PWM输出,可用来控制直流电机转速、屏幕亮度、旋转速度等,信号指示灯实验中的“呼吸灯”效果便是使用了模拟PWM输出的效果。但相比模拟PWM输出而言,通过定时中断实现的PWM输出具有更高的灵活性。尝试以“呼吸灯”效果为目的,通过使用定时/计数器产生PWM输出,由PWM输出控制LED1和LED2产生“呼吸灯”效果。