原创 单片机实现交通灯的一些感想

2007-2-15 17:04 3922 7 11 分类: MCU/ 嵌入式

       上午别人拿给我一个程序,关于单片机模拟实现城市交通灯的,也可以称的上是MCU中一个很经典的程序吧。朋友写了很久终于搞定的,而且用KEIL生成了.HEX文件,但当烧到片子里的时候出向了问题:有时候无缘无故地不能执行!下面就把他的程序贴出来,大伙可以讨论讨论。芯片是AT89S52,22.1184MHz,整体图片:2bfc1c67-5c40-48da-9c30-7be9002ef39b.jpg,也就是32个I/O口都接了一个LED,并利用它们来模拟交通灯。具体构思为:


P1口的低四位             东西方向的红灯<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />


P1口的高四位             东西方向的绿灯


P3                     东西方向的黄灯


P0口的低四位             南北方向的红灯


P0口的高四位             南北方向的绿灯


P2                     南北方向的黄灯


 


他的程序是:


 


ORG 0000H                ;从地址0开始执行程序
LJMP START               ;跳转到START处
START: MOV P1,#0FFH      ;关闭P1口的所有LED
MOV P2,#0FFH             ;关闭P2口的所有LED
MOV P3,#0FFH             ;关闭P3口的所有LED
MOV P0,#0FFH             ;关闭P0口的所有LED
LOOP:
LCALL STATUS_1           ;调用STATUS-1子程序
LCALL STATUS_2           ;调用STATUS-2子程序
LCALL STATUS_3           ;调用STATUS-3子程序
LCALL STATUS_4           ;调用STATUS-4子程序
LJMP LOOP                ;跳转到LOOP处形成一个循环
//**************以下为STATUS-1的模块***************//
STATUS_1:
MOV R0,#5                ;将5赋给R0
LOOP_1:MOV A,#00H        ;将0赋给A
MOV P3,A                 ;将A中的数值赋给P3口
MOV P2,A                 ;将A中的数值赋给P2口
LCALL DELAY_5            ;调用DELAY-5延迟子程序
CPL A                    ;将A中的数值取反
MOV P3,A                 ;将A中的数值赋给P3口
MOV P2,A                 ;将A中的数值赋给P2口
LCALL DELAY_5            ;调用DELAY-5延迟子程序
DJNZ R0,LOOP_1           ;跳回LOOP-1处,循环5次
RET                      ;返回
//***************以下是STATUS-2的模块***************//
STATUS_2:
MOV P1,#0FH              ;P1口的高4位亮,即绿灯亮
MOV P0,#0F0H             ;P0口的低4位亮,即红灯亮
LCALL DELAY_30           ;调用DELAY-30程序
RET                      ;返回
//***************以下是STATUS-3的模块*******************//
STATUS_3:
MOV R1,#5                ;将5赋给R1
LOOP_2:MOV A,#00H        ;将0赋给A
MOV P3,A                 ;将A中的数值赋给P3口
MOV P2,A                 ;将A中的数值赋给P2口
LCALL DELAY_5            ;调用DELAY-5子程序
CPL A                    ;将A中的数值取反
MOV P3,A                 ;将A中的数值赋给P3口
MOV P2,A                 ;将A中的数值赋给P2口
LCALL DELAY_5            ;调用DELAY-5子程序
DJNZ R1,LOOP_2           ;跳回LOOP-2处,循环5次
RET                      ;返回
//****************以下是STSTUS-4的模块****************//
STATUS_4:
MOV P1,#0F0H             ;P1口的低4位亮,即红灯亮
MOV P0,#0FH              ;P0口的高4位亮,即绿灯亮
LCALL DELAY_30           ;调用DELAY-30子程序
RET                      ;返回
//****************以下是DELAY-5模块******************//
DELAY_5:
MOV R2,#5                ;
LOOP_3:MOV R3,#184       ;
LOOP_4:MOV R4,#200       ;
DJNZ R4,$                ;
DJNZ R3,LOOP_4           ;
DJNZ R2,LOOP_3           ;
RET                      ;
//****************以下是DELAY-30模块****************** //
DELAY_30:
MOV R5,#6                ;
LOOP_6:MOV R6,#247       ;
LOOP_5:MOV R7,#249       ;
DJNZ R7,$                ;
DJNZ R6,LOOP_5           ;
DJNZ R5,LOOP_6           ;
RET                      ;
//**************结束****************  //
END


 


其实,说真的,朋友做事很认真,程序写的很工整,让人看起来很舒服。但美中不足的是,这个程序是不能用的。


 


我修改后的程序:


 


ORG 0000H                ;从地址0开始执行程序


LJMP START               ;跳转到START


START: MOV P1,#0FFH      ;关闭P1口的所有LED


MOV P2,#0FFH             ;关闭P2口的所有LED


MOV P3,#0FFH             ;关闭P3口的所有LED


MOV P0,#0FFH             ;关闭P0口的所有LED


NOP


NOP


LCALL DELAY_5           ;调用延迟子程序,准备主程序的运行


<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />LOOP:


NOP


NOP


LCALL STATUS_1           ;调用STATUS-1子程序


NOP


NOP


LCALL STATUS_2           ;调用STATUS-2子程序


NOP


NOP


LCALL STATUS_3           ;调用STATUS-3子程序


NOP


NOP


LCALL STATUS_4           ;调用STATUS-4子程序


NOP


NOP


LJMP LOOP                ;跳转到LOOP处形成一个循环


//**************以下为STATUS-1的模块***************//


STATUS_1:


MOV P0,  #0FFH           ;关闭P0


MOV P1, #0FFH             ;关闭P1


MOV R0,#5                ;5赋给R0


LOOP_1:MOV A,#00H        ;0赋给A


MOV P3,A                 ;A中的数值赋给P3


MOV P2,A                 ;A中的数值赋给P2


NOP


NOP


LCALL DELAY_5            ;调用DELAY-5延迟子程序


CPL A                    ;A中的数值取反


MOV P3,A                 ;A中的数值赋给P3


MOV P2,A                 ;A中的数值赋给P2


NOP


NOP


LCALL DELAY_5            ;调用DELAY-5延迟子程序


DJNZ R0,LOOP_1           ;跳回LOOP-1处,循环5


RET                      ;返回


//***************以下是STATUS-2的模块***************//


STATUS_2:


MOV P1,#0FH              ;P1口的高4位亮,即绿灯亮


MOV P0,#0F0H             ;P0口的低4位亮,即红灯亮


NOP


NOP


LCALL DELAY_30           ;调用DELAY-30程序


RET                      ;返回


//***************以下是STATUS-3的模块*******************//


STATUS_3:


MOV P0, #0FFH              ;关闭P0


MOV P1, #0FFH             ;关闭P1


MOV R1,#5                ;5赋给R1


LOOP_2:MOV A,#00H        ;0赋给A


MOV P3,A                 ;A中的数值赋给P3


MOV P2,A                 ;A中的数值赋给P2


NOP


NOP


LCALL DELAY_5            ;调用DELAY-5子程序


CPL A                    ;A中的数值取反


MOV P3,A                 ;A中的数值赋给P3


MOV P2,A                 ;A中的数值赋给P2


NOP


NOP


LCALL DELAY_5            ;调用DELAY-5子程序


DJNZ R1,LOOP_2           ;跳回LOOP-2处,循环5


RET                      ;返回


//****************以下是STSTUS-4的模块****************//


STATUS_4:


MOV P1,#0F0H             ;P1口的低4位亮,即红灯亮


MOV P0,#0FH              ;P0口的高4位亮,即绿灯亮


NOP


NOP


LCALL DELAY_30           ;调用DELAY-30子程序


RET                      ;返回


//****************以下是DELAY-5模块******************//


DELAY_5:


MOV R2,#12                ;


LOOP_3:MOV R3,#191       ;


LOOP_4:MOV R4,#200       ;


DJNZ R4,$                ;


DJNZ R3,LOOP_4           ;


DJNZ R2,LOOP_3           ;


RET                      ;


//****************以下是DELAY-30模块****************** //


DELAY_30:


MOV R5,#75                ;


LOOP_6:MOV R6,#247       ;


LOOP_5:MOV R7,#249       ;


DJNZ R7,$                ;


DJNZ R6,LOOP_5           ;


DJNZ R5,LOOP_6           ;


RET                      ;


//**************结束****************  //


END


 


其中的红色部分是我修改的地方,可能有朋友要问了,你不就写了三个字母么?有什么了不起!呵呵,好像也对,但如果拿到那三个字母你再看看?程序还能正常运行么?其实,当初我在学习单片机的时候也对NOP很不以为然,但又来慢慢才意识到INTRL不是吃白饭的!人家总共就造了111个指令,怎么可能弄一个没用的上去呢?从指令的运行来讲:NOP这条指令是没有任何含义的,它唯一的作用是消耗时间,但从保护程序的正确执行来讲,它却是一个大功臣!去搜一搜很多别人写好的程序,总会发现在LCALL,JB,JNB,JC,JNC等指令的前后有它们的身影。为什么呢?


 


首先需要明确的是:在单片机中,无论是程序还是数字,反映到物理设备上,他们仅仅是一串高低电平而已!


其次还得从指令的格式说起,一般来说汇编语言是单字节指令的,换个话说:有的指令是双字节指令,甚至三字节指令的。当执行单字节指令的时候,指令的格式为:操作码,;而双字节指令则为:操作码,操作数;三字节指令的格式为:操作码,操作数,操作数,操作数;当程序执行单字节指令的时候,PC(program counter)可以保证程序正确地执行,即:先取操作码,后取操作数。而当程序执行到双字节或三字节指令的时候,有时候就会误将第二个操作数当成操作码(注意前面红色部分),从而就出现了程序跑飞的发生。而单字节指令NOP的使用恰好可以保证程序从错误的“轨道”上“偏”回来。


呵呵,具体的做法是在一些对程序的流向起控制作用的重要指令前插入两条NOP指令。这类指令有:RET,RETI,ACALL,LCALL,LJMP,JZ,JNZ,JC,JNC,DJNZ,CJNE,SETB等...


 


呵呵,闲暇时间,略微唠叨唠叨,期待与朋友们的交流和探讨.


 

 

文章评论4条评论)

登录后参与讨论

用户66632 2007-5-1 17:00

你的要求并不高,只要你仔细看了我的这个程序,稍微修改一下你的程序完全可以实现了.程序不是写出来的,而是修改出来的,写程序可能只需要10%的时间,但修改程序却需要90%的时间,但今天你花90%的时间去修改是为了明天只需要花10%就可以完成了.

刚开始我学汇编的时候写个10行的程序也感觉很痛苦,但到现在,随便200,300行完全没有问题,而且几乎不用修改.我个人感觉做电子行业是从理论---->错误的实践------>理论------>正确的实践.

你可以考虑一下

用户66632 2007-4-11 22:03

差两个I/O口,什么意思?

其实我的设计仅仅是提出一种实现方法,在模拟试验的时候只要有6个LED就完全可以用了。

用户587325 2007-3-14 17:21

受益了!不过写程序一直很少用NOP,除非是延时。但也从未发生过问题。

用户66632 2007-3-2 18:57

建议你看看汇编语言的格式,尤其是程序计数器(program counter,简称PC)的工作方式,在各个指令中PC的值是不一样的,有的是PC<------PC+1,有的是PC<-----PC+2,而有的是PC<-------PC+3……
相关推荐阅读
用户66632 2007-04-13 20:14
最近有点迷失目标了
又很久没有来BLOG了,说实在的,最近确实有点乱.在别人眼里看来我挺忙的,整天见不到人.的确,我和我的班级已经脱节了,甚至有很多人很久都没有见到了.       下午的时候忽然间想反省一下自己,总觉的...
用户66632 2007-03-02 19:42
用单片机来做门铃?
其实单片机的功能是很强大的,而这里,我倒很愿意同各位朋友一起分享一个单片机来作门铃的试验程序。一般的单片机学习板上都有嗡鸣器(也就是我们常说的喇叭),所以利用它来模拟门铃的功能其实挺简单的。废话少说,...
用户66632 2007-02-21 00:13
生活随想
        很久没有写东西了,以前都是些日记的,不过,如今大伙都流行写博客。为了不让自己落伍,我也得改改“陋习”了,呵呵     最近一直很郁闷,说不上为什么?总感觉很烦,做事情没有激情。主要是自...
用户66632 2007-02-06 13:37
单片机的一点感想
  昨天有个朋友问我一个关于单片机ADC和DAC的问题,解释了半天,他才明白为什么要有OC门,什么是缓冲器,它的作用是什么.呵呵,其实这些都是一些很基础的知识了,如果这些东西都搞懂了,你就可以称得上是...
用户66632 2007-02-03 20:24
有点郁闷
 忽然之间发现自己最近挺没目标的,已经差不多十来天没有认真做事情了。上午的时候一直在忙着装软件,CAD, 3D MAX, ADAMS,PRO/E ,唉,说实在的,到现在为止也就一个CAD装好了,其余的...
我要评论
4
7
关闭 站长推荐上一条 /2 下一条