1、电子表设计说明8电子钟程序设计电子表是单片机简单系统最典型的案例,本节学习使用动态扫描的方式实现6位数码管组成的电子钟的设计方法,主要目的是让读者掌握结构化程序设计方法,了解利用数组变量实现数码管数字显示技巧;并熟练掌握键盘的控制编程方式。本案例设计分阶段进行,首先使用定时器的方式,实现时钟的显示、调整,包括调整时数字的闪动,调整范围的界定等问题。然后实现单闹钟的功能,包括定时时间的显示,所定时间的调整,定时到时的响铃等功能。最后实现双闹钟的全部可调的设计目标。8.1设计任务电子表使用6位数码管实现显示时分秒,实现双定闹。使用4个键控制,按模式键以后调整数字加减、闹钟开启。长按加减键,快速调整
2、,停止闪烁。普通模式按下闹钟键显示定时时间,闹铃时按下则停止闹铃。进入调整模式后10秒任意键没有操作,将返回到正常显示模式。采用的电路见图8-1所示。六位数码管的段选连接在单片机的P0口,位选连接在单片机的P2口,从右至左分别从P2.0到P2.5。键盘连接在P3.2到P3.5口。蜂鸣器连接在单片机的P1.7口。图8-1 硬件电路图8.2系统功能分析 此处以使用定时器中断计时的单时钟闹铃为例进行讲解,后附双时钟DS1302时钟的完整程序,两程序结构相似,后者在功能上更为完整,走时更为准确,具有实用价值。在设计中主要功能可以划分为键盘控制模块、显示时间输出、定时等部分内容。时间的计数要使用定时器中
3、断来实现。一、键盘控制键盘控制是本设计中的重要部分,在键盘上实现全部的功能的调整,首先需要定义各个按键的主要功能,功能如表8-1所示。KEY2键负责调整模式的选择,带有去抖功能,每按下一次,改变一次状态,前三次修改时间,后三次修改闹钟时间。KEY3、KEY4在对应的模式下进行加或者减,也带有去抖功能。KEY1在正常显示时按下,显示所定闹钟的时间和开启与否,在闹铃响起时,按下起到停止闹铃的作用。由于一直按下时显示所定闹钟时间,故此键不能带有去抖功能。表8-1 键盘功能控制键名KEY1闹铃键KEY2模式键KEY3加键KEY4减键按下功能显示定时调时时加,23后为0时减,0后23闹铃时停止闹铃调分分
4、加,59后为0分减,0后59调秒秒加,59后为0秒减,0后59闹钟小时时加,23后为0时减,0后23闹钟分钟分加,59后为0分减,0后59定时开关打开关闭循环打开关闭循环二、 显示时间输出显示终端为6位数码管,从左到右分别显示时分秒,小时、分钟和秒各占2位数码管共6位。在定时状态下,只显示时分,右边第二位熄灭,右边第一位显示“F”表示闹铃关闭,“E”表示闹铃开启。在调整过程中,要求对应的调整位置以0.5的速度进行闪烁,以示区别。三、 定时输出当到所定时间时,闹铃响起,按下KEY1,闹钟停止。在正常显示时间模式下按下,显示定时时刻及状态。8.3设计流程 采用“自顶向下”的设计方法,根据对设计功能
5、的分析,可以规划出本程序的主要框图,如图8-2所示。由于使用结构化编程,程序层次清楚。程序开始运行以后,先进行初始化,然后就不断的检查键盘按下与否,到中断产生时,就进行显示,计时,加载数据等操作。图8-2 程序结构图一、 变量声明在程序中使用到多个变量,在编写程序前首先应对其进行定义。定义的内容主要包括三个数组的定义,这三个数组主要是用在显示函数中;程序使用的变量定义;程序硬件接口的定义三个部分。详细定义如下:/*/#include unsigned char code LEDDATA=0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff
6、,0x8e,0x86;/数码管显示的代码表,后三个为灭灯、“F”、“E”unsigned char code LEDBITDATA=0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f,; /数码管扫描代码表unsigned char LEDBuffer6; /定义显示缓冲区数组unsigned char Second; /秒单元unsigned char Minute; /分单元unsigned char Hour; /时单元unsigned char Beepflag; /定时响铃标志unsigned char Minuterom; /定时分单元unsigned
7、char Hourrom; /定时时单元unsigned char SETFlag=0; /模式标志unsigned char second_tick; /闪动标志unsigned char Time; /超时计数unsigned char ALMFlag; /定时开启标志sbit SET_KEY=P33; /模式键sbit DOWN_KEY=P34; /加计数键sbit UP_KEY=P35; /减计数键sbit ALM_KEY=P32; /显示定时时间按键sbit Beep=P17; /蜂鸣器接口引脚/*/二、 主程序流程程序设计采用模块化设计方式后,在主程序里面仅包含程序初始化,键盘模块
8、和中断几个部分。程序开始运行以后,首先进入初始化阶段,在对定时器进行初始化操作后进入到while死循环内部,反复检查键盘是否有操作、闹铃是否打开。在这一过程中,定时器时间到就进入中断服务函数,执行相应操作。对应的结构如图8-3所示。图8-3主程序流程此部分对应的程序代码如下:/*/void main(void) init(); /初始化while(1) key(); /调用键盘 if(ALMFlag=1) if(Minute!=Minuterom) Beepflag=1; /定时和现在不同,关闭蜂鸣器 if(Hour=Hourrom)&(Minute=Minuterom)&(Beepflag=
9、1) Beep=0; /时分相同并闹铃打开就响铃 /*/在定时部分,首先判断闹铃标志是否打开,只有当闹铃打开,小时、分钟都相等的情况下,蜂鸣器开始工作。Beepflag作为标志用来使蜂鸣器在到时间响起时按下KEY1使其复位,停止在这一分钟内继续鸣响。 三、初始化模块初始化的主要功能是指定定时器的工作方式,装载初值,打开中断。该模块的工作流程如图8-4所示。图8-4 初始化流程/*/void init() TMOD=0x01; /T0初始化方式1,定时TH0=(65536-2000)/256; /TH0,TL0装入定时2mS的初值TL0=(65536-2000)%256;TR0=1; /启动T0
10、工作ET0=1; /允许T0溢出中断EA=1; /CPU开中断/*/中断时间和机器周期单位为微秒,机器周期是单片机振荡周期12倍,如果单片机的晶体振荡频率为,则机器周期由于执行定时器初始化相关语句和晶体振荡频率误差会影响定时器精度,实际预置数需要进行调试调整。在本程序中,机器周期为1微秒,要求2000微秒即2毫秒中断1次,Timer0计数最大为0xffff,定时器预置数值按下面公式计算:预置数16=(2定时器位中断时间/机器周期10三、 显示模块显示部分主要作用是把显示暂存区的内容传输到数码管上。由于是6位数码管,因此必须要使用动态扫描的方式,动态扫描的方式有多种,在本例中是通过建立暂存区来实
11、现,建立暂存区的目的就是使显示模块独立出来,如何显示内容在编程的其他部分不用过多考虑,只需要把显示数据放入在暂存区内,起到数据传递的作用,基本的结构如图8-5所示。图8-5 显示的基本模块在本模块中,核心的语句就是:P2= LEDBITDATALEDScanCount; /送出位选数据P0=LEDDATALEDBufferLEDScanCount; /送出段选数据LEDScanCount+; /扫描指针加计数if(LEDScanCount=6)LEDScanCount=0 ; /扫描完从头开始语句中LEDBuffer就是所说的暂存区,实际是一个数组。LEDScanCount相当于扫描计数器,从
12、0到5 循环。LEDDATA为数码管的编码字符,LEDBITDATA是对应的数码管选中编码。现以右端显示两位数为例进行说明,要显示的数据为“32”,把个位“2”放入LEDBuffer0,把十位“3”放入LEDBuffer1。假设LEDScanCount初始为0。进入到该部分后,首先送位选数据,LEDScanCount=0,也就是LEDBITDATA0,LEDBITDATA0意味着此数组中的第一个数即0xFE,也就是P2=0xFE=11111110,由电路结构可知,使用的是共阳型数码管通过反向器驱动,最后一位为“0”,取反后即可驱动数码管,因此最右边的数码管被选中,位选功能已经实现。下面开始送段
13、选数据,LEDScanCount=0,即LEDBuffer0,LEDBuffer0=2,故P0=LEDDATA2=0xA4,就送出了段选数据。此时P2=0xFE=11111110 P0=LEDDATA2=0xA4 同理,LEDScanCount=1时P2= LEDBITDATALEDScanCount;= LEDBITDATA1;= 0xFD=11111101;P0=LEDDATALEDBufferLEDScanCount;=LEDDATALEDBuffer1;=LEDDATA3;=0xB0执行完后,LEDScanCount加一,当LEDScanCount为2时清零,通过对这几句的反复调用实现
14、动态显示功能。显示模块进入以后判定是否需要闪烁,然后送出位选数据,在送段选数据时需要结合当前的模式状态,也就是SETFlag的数值进行选择。由于调整时间和调整定时需要闪烁的时分秒位置对应,故SETFlag的1、4相同,2、5相同,3、6相同,0时为正常显示。当为1、4时,数码管的最左段两位需要闪烁,因此,当扫描到最左段两位即LEDScanCount=4,P0送i和所送数据的按位取或,当i为11111111时,不管所送为何值,P0送出的都是11111111,也就是关闭了该位的数码管显示。i隔0.5秒后变化为00000000,此时送出的就是原始数据,这样就实现了该位的闪烁功能。其他各位情况相同,不
15、再累述。当扫描到其他不需要闪烁的位置时,执行else语句,正常的送出数据,不再和i去或。每次进入display函数一次,点亮一位数码管,进入六次以后,也就是当扫描到最后一位时LEDScanCount清零复位。该模块的主要流程如图8-6所示。图8-6 显示流程 此模块代码如下:/* */void display(void) /显示暂存区内容对应的代码显示 unsigned char LEDScanCount,i; /位选扫描计数器P0=0xff; /适应仿真需要if(UP_KEY=0)|(DOWN_KEY=0) i=0x00; /加减键有操作放弃闪烁 else i = 0xff*second_t
16、ick; /设定闪烁变量P2= LEDBITDATALEDScanCount; /送出位选数据switch(SETFlag) case 0: P0=LEDDATALEDBufferLEDScanCount;break; /送出段选数据 case 1: if(LEDScanCount=4) /判断出最高两位 P0=i|LEDDATALEDBufferLEDScanCount; /使小时闪烁 else P0=LEDDATALEDBufferLEDScanCount;break; /低位正常显示 case 2: if(LEDScanCount=2)|(LEDScanCount=3) /判断出中间位
17、P0=i|LEDDATALEDBufferLEDScanCount; /使分钟闪烁 else P0=LEDDATALEDBufferLEDScanCount;break; /其他位正常显示 case 3: if(LEDScanCount=4) P0=i|LEDDATALEDBufferLEDScanCount; else P0=LEDDATALEDBufferLEDScanCount;break; case 5: if(LEDScanCount=2)|(LEDScanCount=3) P0=i|LEDDATALEDBufferLEDScanCount; else P0=LEDDATALEDBu
18、fferLEDScanCount;break; case 6: if(LEDScanCount=1) P0=i|LEDDATALEDBufferLEDScanCount; else P0=LEDDATALEDBufferLEDScanCount;break; LEDScanCount+; /扫描指针加计数if(LEDScanCount=6)LEDScanCount=0 ; /扫描完从头开始;/*/四、键盘扫描模块键盘的具体定义前面已经给出,按照定义,可以规划出该模块的流程图,如图8-7所示。程序进入此部分后循环对四个按键进行检测,如果有任意键被按下就执行该键的功能。图8-7 按键模块程序流程当
19、模式键被按下,在进行去抖检查后,模式标志增加,即SETFlag+。由于调整时间和调整定时都有显示位置的相似性,因此,从显示暂存区LEDBuffer中取出当前调整的数值较为方便。LEDBuffer中存储的都是1位十进制数据,需要进行合并,合并后进行加减操作,在更改数据后需要及时的把数据写回到对应变量,再由载入程序把新数据装载到暂存区,这样显示的数据就是已经更改过的新数据。在加减时需要考虑合适返回值的问题,如小时加到“23”后应为“0”,减到“0”是应为“23”这样的问题。闹铃键的功能相对比较独立,闹铃响时,按下此键更改闹铃标志,从而停止闹铃。在正常显示时,按下闹铃键的功能是显示所定的闹铃时间,此
20、键不需要去抖功能。显示闹铃的方式就是直接把所定的闹铃时间装载到显示暂存区里面。所定时间不包括“秒”,右边第二位进行灭灯处理,加载入0xFF。最右边根据闹铃标志装载“F”或者“E”。此部分的参考程序如下:/*/void Delay(unsigned int t) /延时子程序 while(t)t-;void key() /键盘操作子程序 unsigned char i; /缓冲数组位数标志 char Num; /临时数字,存储数组合并值 if(SET_KEY=0) /判断模式键是否按下 Delay(500); /去按键抖动 if(SET_KEY=0) /再判断是否真得按下了 SETFlag+;
21、/状态改变 if(SETFlag=7) SETFlag=0; /返回正常模式 if(SETFlag=1) i=4; /调节读取显示数组的位数 if(SETFlag=2) i=2; if(SETFlag=3) i=0; if(SETFlag=4) i=4; if(SETFlag=5) i=2; if(SETFlag=6) i=0; while(SET_KEY=0); /等按键释放 if(UP_KEY=0)&(SETFlag!=0) /判断加计数键是否按下 Delay(5000); /去按键抖动 if(UP_KEY=0) /再判断是否真得按下了 Num=(LEDBufferi+1*10+LEDBu
22、fferi); Num+; /时单元的数值加1 if(Num=60) Num=0; /加到60归0 if(Num=24)&(SETFlag=1)|(SETFlag=4) Num=0; /加到24归0 switch(SETFlag) /把修改值写回 case 0: ;break; case 1: Hour=Num;break; case 2: Minute=Num;break; case 3: Second=Num;break; case 4: Hourrom=Num;break; case 5: Minuterom=Num;break; case 6: ALMFlag=!ALMFlag;bre
23、ak; if(DOWN_KEY=0)&(SETFlag!=0) /判断减计数键是否按下 Delay(5000); /去按键抖动 if(DOWN_KEY=0) /再判断是否真得按下了 Num=(LEDBufferi+1*10+LEDBufferi); Num-; /时单元的数值减1 if(Num0)&(SETFlag=1)|(SETFlag=4) Num=23; /到24归0 if(Num= 10)SETFlag = 0;Time = 0; /*/六、中断模块中断模块的基本功能是要完成时间的计数。根据初始化的设定,每2ms进入中断一次,对进入次数进行计数,500次就是1秒,当然这种计数存在较大的误差,需要进行修正,使用专用的时钟芯片进行计
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1