资源预览内容
第1页 / 共5页
第2页 / 共5页
第3页 / 共5页
第4页 / 共5页
第5页 / 共5页
亲,该文档总共5页全部预览完了,如果喜欢就下载吧!
资源描述
-工程4 简易数字钟的设计2计算机专业有门必修课程叫软件工程,这门课程告诉软件学习者们如何系统性的、标准化的、可定量的过程化方法去开发和维护软件。我们在学习单片机编程的过程当中,也应该借鉴软件工程课程当中的讲述的方法和手段,去维护和标准我们的单片机程序。在本单元当中,我们安排了4个任务。任务1介绍了一种基于状态机的程序框架,通过状态机的学习,初学者可以写出思路清晰、多任务运行流畅的程序。任务2介绍了程序的风格和可移植性,标准了变量和函数等的命名,并简单介绍了C51中提高程序可移植性的方法。任务3介绍了程序模块化的实现方法,让初学者学会合理的管理程序。任务4中运用本单元所讲的知识,结合前一单元,完成简易数字钟的设计。【内容安排】4.1 基于状态机的程序框架4.2 程序的风格和可移植性4.3 程序的模块化4.4 简易数字钟的设计 任务4.1 基于状态机的程序框架4.1.1 任务介绍上一单元中已经屡次提到多任务运行时,延时函数(DelayMs()对程序的危害性,堵塞CPU,系统任务的实时性得不到有效的保证。在3.4节中,提到中断可以提高任务的实时性,但是单片机的中断数量是有限的,不可能每一个任务都有中断。在3.5节中,通过定时器中断效劳函数提供的时标信号,定时扫描LED和数码管,可以消除延时函数,时标信号给我们提供了一种新的思路来消除延时函数本质上还是借助于中断。但是LED闪烁和动态数码管扫描都是属于状态时间分配均匀的LED闪烁有两个状态,亮和灭分配时间相等;数码管每个位扫描的时间也相等,程序易于实现。对于像按键检测这样的时间分配不均匀的任务,怎样来消除程序中的延时呢.本节任务是:利用本节所讲状态机,改写独立按键程序,并增加长按、连击等功能。4.1.2 知识准备1、 状态机的思想网络上经常报道特级象棋大师车和多人一起下象棋,采用的方式是车轮战。车轮战有两种方式:1象棋大师先和甲开场下象棋,直到有了结果,然后才轮到乙和象棋大师对阵,下完了之后,然后是丙.,一直到和最后一个人下完。2象棋大师先和A下一步棋,然后再和B下一步棋,然后再和C,和.,和所有人下完一遍后,再回头从A开场,一个人接一个人。很显然车轮战的第1种方式效率不如第2种方式效率高,报道上的车轮战也是指的第2种方式。原因在于象棋大师的水平远远高于其他人,如果采用第一种方式,象棋大师下一步棋很快,甲需要考虑很长时间才能落子,象棋大师在和甲下棋的过程中,其他人只能等待。如果采用第二种方式,象棋大师和甲只下一步棋,然后再和乙也下一步棋,和所有人下完一步棋之后,再从甲开场,这样看起来是所有人都在下象棋,效率自然远高于第一种方式。车轮战的第2种方式,实际上就是程序中状态机的根本原理。程序中的多个任务可以看成是其他棋手,CPU是象棋大师,CPU在执行多个任务时,不再是先执行任务1,执行完任务1后,再执行任务2,而是把每个任务又划分出多个小任务小任务中没有时间等待,CPU每次只执行每个任务中的小任务,执行完任务1中的一个小任务后,然后快速转向任务2中的小任务,按照这种模式轮询下去,由于CPU很快象棋大师,整个程序中的任务都得到了实时的执行。任务中的小任务是按照任务的状态来划分的,故称为状态机。上一单元中,利用定时器的时标信号扫描动态数码管的程序实际上也是采用了状态机的原理。时标信号到来后,CPU扫描1位数码管,然后去执行别的任务,下一个时标信号到来后,再扫描下一位数码管。6位数码管的扫描被分成了6个子任务,CPU每一次只执行一个子任务。2、 任务的划分数码管扫描的任务划分非常简单,因为每一个子任务执行的时间时均匀的,而且任务很相似。但是程序中大多数任务划分出来的子任务时间分布不均匀,而且划分出来的子任务不相似。举个例子,2个LED,第一个LED按照亮1秒,灭2秒的方式闪烁,第2个LED按照亮2秒,灭1秒的方式闪烁,要求不用延时函实现。我们先给出程序,通过程序学习状态机的实现方法。*include*define uchar unsigned char*define uint unsigned int sbit LED1=P10; /第一个LED1接口定义sbit LED2=P11; /第二个LED2接口定义bit FlagSystem1Ms=0; /系统1ms时标信号 /定时器0初始化void Timer0Init() TMOD=0*02; /GATE=0,C/T=0,M1M0=02; TH0=56; /高8位RAM赋值 TL0=56; /低8位RAM赋值,200us定时 ET0=EA=1; /开定时器中断和总中断 TR0=1; /开启定时器/第一个LED以亮1秒,灭2秒的方式闪烁void Led1Twinkle() static uchar Led1State=0; /状态机变量1 static uchar Led1t=0; /计数变量1 swithch(Led1State) case 0: /LED1亮状态 LED1=0; if(+Led1t=1000) /LED1亮1秒后,跳转到灭状态 if(+Led1t=2000) /LED1灭2秒后,返回到亮状态 Led1t=0; Led1State=0; break; Led1t=0; Led1State=1; break; case 1: /LED1灭状态 LED1=1; if(+Led1t=2000) /LED1灭2秒后,返回到亮状态 Led1t=0; Led1State=0; break; /第二个LED以亮2秒,灭1秒的方式闪烁void Led2Twinkle() static uchar Led2State=0; /LED2状态机变量 static uchar Led2t=0; /LED2计数变量 swithch(Led2State) case 0: /LED2亮状态 LED2=0; if(+Led2t=2000) /LED2亮2秒后,跳转到灭状态 Led2t=0; Led2State=1; break; case 1: /LED2灭状态 LED2=1; if(+Led2t=1000) /LED2灭1秒后,返回到亮状态 Led2t=0; Led2State=0; Led2State=1; break; case 1: /LED2灭状态 LED2=1; if(+Led2t=1000) /LED2灭1秒后,返回到亮状态 Led2t=0; Led2State=0; break; /主函数void main() TimeriInit(); /定时器0初始化 while(1) if(FlagSystem1Ms=1) /间隔1ms轮询系统任务 FlagSystem1Ms=0; Led1Twinkle(); /任务1 Led2Twinkle(); /任务2 /定时器0中断效劳函数,提供2ms的时标信号void timer0_intr(void) interrupt 1 static uchar t200us=0; /200us计数变量if(+t200us=5) /0.2ms*5=1ms t200us=0; /清空计数变量FlagSystem1Ms=1; /1ms时标信号置位 整个程序的框架思路清晰,定时器0产生1ms时标信号供主函数使用,主函数每间隔1ms执行任务1和任务2。任务1和任务2实现内容类似,下面我们分析任务1的内容。任务1中的Led1Twinkle()函数是通过状态机完成的,状态机由switch-case语句构造。在程序中先定义状态机变量Led1State,静态局部变量,并赋初值0,以及时间计数变量Led1t。LED1的状态为亮1秒,灭2秒,分2个状态,所以switch-case语句中对应LED1的两个状态有两个case分支, 状态机变
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号