集成式数码管显示一般采用动态扫描刷新显示方法,即在发送段码与位码完成一位数码显示后,调用延时函数delay_ms,在短暂延时后显示下一位数码,如此循环快速扫描,实现刷新显示。在图3-19所示电路中,改用了新的动态扫描刷新显示方法,数码管刷新程序由定时器溢出中断控制,同样实现了集成式数码管的动态显示。为实现更丰富的演示功能,仿真电路中对两组数据(年、月、日,时、分、秒)实现了切换显示。
图3-19 T1控制数码管动态显示电路
本案例使用STC15的T1,工作于11.059 2MHz时钟频率下,通过4ms定时中断,驱动数码管动态扫描以刷新显示。编程时可通过STC-ISP工具生成初始化代码,所生成的初始化代码中,AUXR &=0xBF(即10111111)将AUXR寄存器的第6位T1x12置1,表示T1选择计数驱动时钟为12分频;TMOD &=0x0F(00001111)将TMOD的高4位全部置0,其中高4位的后两位00将T1配置为模式0。
下面再来分析一下应如何设置T1定时/计数初值。在STC-ISP工具所生成的代码中,定时/计数初值为0xF19A,这是如何得到的呢?
本案例系统时钟频率为11.059 2MHz,由于配置为12分频(与传统8051单片机的默认分频值相同),故计数驱动时钟为11.059 2MHz/12=0.921 6MHz(小于上一个案例中的1MHz计数驱动时钟)。也就是说,其计数时钟周期 T cy =1/(11.059 2MHz/12)≈1.0851≈s,即每1.08510s将计数值累加1。
为实现4 000 s定时(计数),可设置定时/计数初值为65 536-4 000/1.0851≈65 536-4 000×(11.059 2MHz/12)≈65 536-3 686=61 850=0xF19A。可见,在0xF19A(61 850)基础上,经过3 686次累加将达65 536,使T1产生溢出,也就是经过3 683×1.08516s≈4 0000s后溢出,触发T1中断。
通过上述分析,对于源程序中的对应语句就很好理解了:
对于1T(1分频)模式下的定时/计数初值计算,可参考本案例程序,这里不再赘述。
用T1控制集成式数码管刷新显示,定时/计数初值的设置很重要。该初值设置得不好将导致数码管显示闪烁、亮度不足或字符滚动。本案例8个数码管每隔4ms切换显示下一个字符。由于视觉惰性,其快速切换使人不会察觉到它们是逐个出现并在4ms后消失,而会感到所有字符是同时稳定地显示在数码管上。
设置好定时/计数初值以后,使TR0 T 1即可启动T0,T0将在4ms后溢出。由于IE=0x88许可了T1中断,T1中断函数将被调用,完成对一位数码管的刷新显示,如此不断以4ms间隔触发T1中断,即可实现整个集成式数码管每一个数位的动态扫描刷新显示。
T1中断函数内控制的变量i与j分别是二维数组Table_OF_Digits的行与列索引。T1中断每隔4ms被触发,数组第i行第j列字符被显示,同时j递增,4ms后T1中断再次被触发,下一字符被显示,依次下去,第i行的8个字符被反复刷新显示在8只数码管上。
二维数组中一行8个字符的持续刷新显示时间由变量t控制。增加t值会延长一行字符显示在数码管上的时间。程序中的t为350,要注意将t定义为u16类型。在一行8个字符显示一段时间后,i的增加会使数码管更新显示出下一行字符。
细心研究时会发现,每一趟刷新要显示8个字符,如果t为350时开始切换到下一行,由于350/8=43余6,这表示t增加到350时,数码管刚刚完成第43趟刷新,开始进入第44趟第6个字符的刷新,在第44趟8个字符中还剩2个字符未被刷新显示时,变化i值而切换到另一行,这样会不会出现显示错误呢?
实测结果是,不管所取的t值是否能整除8,显示结果都是正常的。例如,当t为350时,将t清零,数码管上前6个字符仍是数组当前行的,i值变更后,后续显示的将是新行的第7和第8个字符,这时数码管上前6个字符是一行,后2个字符是另一行,这样显然会出现两行混合显示的情况。但由于每个字符仅停留4ms即被刷新,前面6个异常的字符会在极短的时间内,即在第45趟(或称为新开始的第0趟)被刷新为新行的前6个字符,因此用户是根本看不到这种混合显示的现象的。
如果希望切换新行时不出现可能的瞬间混合显示现象,要么将t值取为可被8整除,或者直接在变更i值的同时将j值归0,这样可保证输出新的一组数据时,输出的起始位码为P2=~(1<<j)=~(1<<0)=~0x01=0xFE。
除了可以使用T1中断函数实现数码管刷新显示以外,还可以不启用T1中断,并删除T1中断函数,然后在主程序while循环语句内通过查询T1溢出标志位TF1(TIMER1 Overflow Flag)是否被置位来判断是否出现定时/计数溢出,TF1为1时表示定时4ms已到达,此时将TF1清零即可刷新数码管显示。定时/计数初值的重新装载在模式0下是自动完成的,与TH1、TL1对应的是隐藏的同地址重装载寄存器RL_TH1、RL_TL1。
① 修改程序,改用非反相驱动器7407驱动数码管显示。
② 重新设计程序,配置T0工作于1T(1分频)模式0控制3组以上数据自动循环显示。
③ 在仿真电路中添加按键,每次按下按键时切换显示下一组数据。
在图3-20所示仿真电路中,同时使用了TIMER0、TIMER1及INT0中断。按下K1时14个音符组成的音阶将被逐一输出,输出控制由T0中断函数实现。如果输出端连接了虚拟示波器,可观察到输出信号脉宽逐步缩小、频率不断升高的变化过程。如果CPU因连接虚拟示波器而过载,导致声音播放失真,建议断开虚拟示波器再播放。K2用于播放/停止当前音乐片段;K3用于音乐片段选择。在播放音乐片段时,所设计程序中添加了节拍控制,实现了较为逼真的演播效果。
图3-20 TIMER0、TIMER1及INT0控制音阶及多段音乐输出电路
本案例的T0、T1均工作于12T模式0。程序分别使用T0、T1中断控制音阶及音乐片断输出。无论是音阶输出还是音乐片段输出,均要先获取各音符信号频率对应的定时器延时(定时)值。音符信号频率及对应的定时器12T模式0下的定时/计数初值(16位自动重装载)如表3-7所示。
音符信号周期: p >1/ f >1 000 000
音符信号半周期: P > p /2=1/ f >1 000 000/2
12T模式0定时/计数初值:TxMS=65 536- P >(FOSC/12/1 000 000)
定时器每两次连续中断触发才会形成一次完整的蜂鸣器振荡信号输出(1次输出高电平,1次输出低电平,可通过取反、取非、异或1实现),针对给出的音符信号频率对应的周期,实际中断触发周期(定时时长)应设定为音符信号周期的一半(半周期)。
在12MHz系统时钟下仿真输出音符时,可能会有卡顿现象,为此本案例将FOSC下调为6MHz,通过Excel表格计算,可得各音符定时/计数初值对应的常量数组定义如下:
code u16 TONE1_LIST[]={ 0, 65058, 65110, 65156, 65177, 65217, 65251, 65283, 65297, 65323, 65346, 65357, 65376, 65394, 65408 };
数组中除最前面填充的0以外,后面越大的初值意味着越快的累加至溢出,从而对应于越高的信号频率输出。为便于编程选择,表3-7对两种频率均给出了各音符的定时/计数初值。
表3-7 音符信号频率及定时器在12T模式0下的定时/计数初值(16位自动重装载)
① 控制音阶。
当按下K1时,for循环语句控制14个音符逐一输出,循环控制变量Tone_i同时也是当前音符序号变量。for循环语句内首先将TR0置1,启动T0。T0持续计数累加直至溢出,自动触发中断。在每趟for循环的400ms延时期间,T0中断将被持续反复触发。T0中断子程序Timer0_INT中的语句SPK ^=1输出音频信号。在PRE_i变量的控制下,每个音频信号仅在首次被软件重置定时/计数初值。在同一音频信号持续400ms的播放过程中,T0的每次溢出重载都由其模式0的16位自动重载功能实现。400ms后,主程序将TR0置0,使当前第i个音符信号输出停止,随后延时语句使音频输出停顿50ms,从而形成了14个音符信号每个音符输出达400ms便停顿50ms的演奏效果。
② 控制多段音乐输出。
当按下K2时,主程序将TR1置1,启动T1。在定时/计数溢出时,T1中断子程序Timer1_INT被调用。T1中断子程序内部代码工作原理与T0控制音阶输出相似,其差别主要有两个:一是主程序内不是固定400ms延时,而是用delay_ms(500 * Len[Song_idx][Tone_idx])使延时动态变化,从而形成不同节拍;二是重装载定时/计数初值不是直接从各音符信号定时/计数初值表中读取,而是先通过i=Song[Song_idx][Tone_idx]获取当前音符(节拍)索引,然后再去读取对应的定时/计数初值并重装。
另外,由于3段音乐长度不同,不宜用for循环语句控制。为此本案例程序在各段音乐数组末尾添加循1(0xFF)作为结束标志。当while循环语句遇到该标志时即认为一段音乐播放结束。while循环语句的条件中还添加了K2==1 && TR1==1,这样可使得按下K2时(指播放/停止按键被按下时),音乐播放可提前停止。另外,当按下K3选择播放音乐片段时,触发的中断使TR1置0,可见按下K3选择音乐片段时也能使音乐播放停止。关于K3触发的INT0中断子程序编写这里不再赘述。
① 另添加一段自编音乐数据并测试将其播放效果。
② 添加一组LED,使点亮的LED个数与当前输出音符信号频率同步变化。
③ 添加键盘矩阵,实现简易电子琴演奏功能。