上午别人拿给我一个程序,关于单片机模拟实现城市交通灯的,也可以称的上是MCU中一个很经典的程序吧。朋友写了很久终于搞定的,而且用KEIL生成了.HEX文件,但当烧到片子里的时候出向了问题:有时候无缘无故地不能执行!下面就把他的程序贴出来,大伙可以讨论讨论。芯片是AT89S52,22.1184MHz,整体图片:,也就是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等...
呵呵,闲暇时间,略微唠叨唠叨,期待与朋友们的交流和探讨.
用户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
用户66632 2007-3-2 18:57