此前已有案例演示了单只或多只LED的闪烁效果,它们均使用延时函数使LED按一定时间间隔闪烁显示。与已演示的案例不同的是,图3-15所示仿真电路中LED的闪烁延时控制使用定时器实现。图中示波器显示由TIMER0溢出中断控制RC5的输出,脉冲宽度为522.5-262.5=260ms。
图3-15 TIMER0控制单只LED闪烁
(一)PIC16F630定时器TIMER0简介
PIC16F630单片机有1个8位定时/计数器TIMER0和一个16位定时/计数器TIMER1。图3-16描述了PIC16F630的TIMER0/WDT预分频模块结构,PIC16F630的TIMER0定时器具有如下特征。
(1)8位的定时/计数器(8-Bit Timer/Counter)
TMR0寄存器的计数范围为:0x00~0xFF。
(2)可读写(Readable and Writable)
可设置TMR0的递增计数初值,也可读取TMR0的当前计数值。
(3)可选择内部或外部时钟信号(Internal or external clock select)
TIMER0的时钟源选择位T0CS(TMR0 Clock Source Select bit)为0时选择内部系统时钟(fosc/4),为1时选择RA2/T0CKI引脚输入的外部时钟源。
(4)带有8位的可编程预分频器(8-bit software programmable prescaler)
PSA=0时预分频器分配给TIMER0,PSA=1时,预分频器分配给WDT。
预分频器分配给TIMER0,PS2:PS0取值000~111可分别提供1:2、1:4、1:8、1:16、1:32、1:64、1:128、1:256共8种分频比;对于WDT则可分别提供1:1、1:2、1:4、1:8、1:16、1:32、1:64、1:128共8种分频比。
(5)溢出中断(Interrupt on overflow)
计数寄存器TMR0的计数值在累加过程中,由0xFF跳变到0x00时即产生溢出中断,硬件置位T0IF标志位。
(6)可选择外部时钟信号边缘方式(Edge select for external clock)
TIMER0的时钟信号边缘选择位T0SE(TMR0 Source Edge Select bit)为1时,RA2/T0CKI由高到低地跳变使TMR0递增计数,反之则是由低到高的跳变时TMR0递增计数。
图3-16 PIC16F630的TIMER0/WDT预分频模块结构图
(二)PIC16F630定时器TIMER0溢出中断程序设计
本例使用TIMER0定时/计数器溢出中断控制LED闪烁,下面归纳一下TIMER0溢出中断程序设计需要完成的几项工作。
(1)设置T0时钟选择位T0CS选择时钟源,主程序设T0CS=0,选择内部时钟(4 MHz/4)。
(2)设置预分频器,首先设PSA=0,将预分频器分配给TIMER0,然后设置分频比,主程序设PS2:PS0=111,选择256分频,设置语句为:PS2=1;PS1=1;PS0=1。
(3)设置定时初值,主程序设置TIMER0溢出时间为65ms,设置初值语句为:
(4)软件清零TIMER0中断标志位T0IF。
(5)使能TMR0溢出中断,置T0IE=1。
(6)开全局中断,置GIE=1。
(7)编写定时器溢出中断例程,当TMR0计数溢出时触发中断,所编写的中断例程将被自动调用,为使中断程序能按固定时间间隔持续触发,中断程序内应清零T0IF,并重置TMR0定时初值。
(三)定时/计数器初值的计算方法
对于8位的TMR0,在不使用预分频器时(分频器分配给WDT),每个计数脉冲使TMR0寄存器递增1,累加到255(0xFF)后再加1则溢出;使用预分频器时,多个脉冲使TMR0累加1,例如设置8分频时,每8个计数脉冲才使TMR0加1,从0累加到溢出时需要256×8=2 048个计数脉冲。
仿真电路中晶振为4MHz,指令周期为Tcy=1/(4M/4)=1μs。设分频值为K,可得TIMER0溢出时间(计时长度)为:256×K×Tcy。
当K取最小预分频值2时256×K×Tcy,可得最小的溢出时间为256×2×1μs=512μs;
当K取最大预分频值256时,可得最大溢出时间为256×256×1μs≈65ms。
在选择了分频值K以后,计算TMR0初值的公式为:
其中溢出时间的单位为s。
为得到仿真电路图中虚拟示波器所示的、控制LED闪烁所需要的约260ms的延时值,可有多种实现方法,例如:
(1)设置PS2=1;PS1=1;PS0=1,选择最大分频配置111,即1:256分频,K=256,可得TIMER0最大溢出时间约为65ms,以65ms为单位,通过计数变量累加4次,可得65×4=260ms。显然,采用这种配置时,所需要的延时值必须是最大溢出时间的整数倍。
(2)设置PS2=0;PS1=0;PS0=0,选择最小分频配置000,即1:2分频,K=2,可得TIMER0最小溢出时间为512μs,以此为基础,再通过溢出计数变量T_Count累加260/0.512≈508次,可得延时约为 260ms。显然,采用这种配置时,如果所需要的延时值较大,中断函数内通过软件方法需要累加溢出时间的次数将很大,这样产生的误差也是显而易见的,因为中断函数内的T_Count累加也需要计算时间。
根据公式TMR0=(INT8U)(256−_XTAL_FREQ/4/K×溢出时间)可知,为得到512μs溢出时间和65ms(实际为65.535ms)溢出时间,在分频值分别取K=2和K=256时,TMR0的初值都为0x00。
下面讨论一下根据TIMER0溢出时间(计时长度)公式256×K×Tcy,且满足溢出计时倍数要求的分频值K的推算方法。
(1)现假设需要实现300ms延时,由于TIMER0在4MHz、256分频配置下,最大溢出时间为65ms,以最大溢出时间65ms为单位,显然无法直接通过T_Count累加实现300ms的延时。
(2)进一步假定选择将TIMER0溢出时间配置为小于65ms,但又可以被300ms除尽的最大溢出时间为50ms,根据溢出时间计算公式有:
256×K×Tcy=50 000μs,得K=195.312 5
如果能为TIMER0配置出1:195.312 5这样的“特殊分频比”,则TMR0的初值如前面一样,可直接设为0x00,显然这是不可能的。
(3)根据PIC16F630技术手册可知,TIMER0的分频值K只能是2、4、8、16、32、64、128、256共8个值之一,考虑到当前计算结果,K值要求满足条件为:K≥195.312 5,故取K=256。由于直接将K值195.312 5代入公式时有:
计算可得TMR0=0x00,如果K取小于195.312 5的值,例如64、128等,TMR0将取得负数,这显然将导致错误的延时结果。
根据上述分析计算,为实现300ms的延时,可配置TMR0的溢出时间为50ms,设置TMR0初始的语句为:
计算结果为60.687 5,TMR0可取值为60,如果要四舍五入,应在上述语句中+0.5,即:
实际应用时不需要在源程序中写入TMR0=61,上述语句可直接编写到源程序中。
以50ms为单位,在溢出中断函数内通过6次累加即可得到50ms×6=300ms的延时。
(四)对K值的补充讨论
还有一个问题需要考察:假设某程序需要TIMER0实现10ms(0.01s)的延时,由256×K×Tcy=10 000μs,可得K值为39.062 5,K所取的分频值应满足条件:K≥39.062 5,而可能的选择有64、128、256这三项,将它们分别代入TMR0的初值公式如下所述。
(1)K=64:TMR0=(INT8U)(256−_XTAL_FREQ/4/64×0.01),可得TMR0=99;
(2)K=128:TMR0=(INT8U)(256−_XTAL_FREQ/4/128×0.01),可得TMR0=177;
(3)K=256:TMR0=(INT8U)(256−_XTAL_FREQ/4/256×0.01),可得TMR0=216;
显然,这些取值都可以得到正常的配置与正确的延时。
① 用TIMER0溢出中断控制单只数码管按一定时间间隔滚动显示数字0~9。
② 在电路中加入蜂鸣器,用定时器实现指定频率声音的输出。