单片机汇编程序100例?单片机程序设计范例汇总
大家好,今天小编来为大家解答单片机汇编程序100例这个问题,单片机程序设计范例汇总很多人还不知道,现在让我们一起来看看吧!
8051单片机的汇编程序编程问题!!!
既然向我求助,那我就写一下大概的思路和C语言程序,至于汇编语言,兄弟可自己由C语言翻译,我今天的确没太有时间.
周期为2个ms的方波,也就是说半个周期为1ms,也就是说,P1.2每1ms翻转一次;
同理,P1.3每3.5ms翻转一次,因为只能用一个定时器,所以,取1ms和3.5ms的最大公约数,是0.5ms,也就是说,让定时器0.5ms中断一次,然后每两个中断,翻转一次P1.2,每7个中断翻转一次P1.3;
这就是编程思路,那么我们来看具体的编程细节;
6Mhz,也就是指令周期是2us,0.5ms需要250个计数,250<256,为了速度和效率,我们让定时器1工作在自动重载模式下,也就是模式2;
C语言整个程序编程如下;
#include<reg52.h>
bit P1_2_Turn_Cnt= 0;//----用于P1.2的计数翻转,因为只有两次,大可不必用unsigned char类型的
unsigned char P1_3_Turn_Cnt= 0;//----用于P1.3的计数翻转,因为要7次,所以用unsigned char
sbit P1_3= P1^3;
sbt P1_2= P1^2;
void main(void)
{
//----初始化定时器,工作在模式2,自动重载,开中断,每0.5ms中断一次
TH1= 256- 250;//----设置定时器,250 X 2us= 500us= 0.5ms
TL1= 256-250;
TMOD= 0x20;//----定时器/计数器1工作在模式2下;
ET1= 1;//----开定时器1中断
TR1= 1;//-----启动定时器
EA= 1;//----开总中断
while(1);//----定时器初始化完成,方波工作在定时器内部完成;
}
void ISR_Timer1(void) interrupt 3
{
P1_2_Turn_Cnt=~P1_2_Turn_Cnt;//---P1_2翻转计数,由于初始化为0,所以第1次会翻转为1;
//---由此推论,奇数次翻转后状态为1,偶数次中断会翻转为
//---0,所以当P1_2_Turn_Cnt= 0时,P1_2翻转.
if(P1_2_Turn_Cnt== 0)
{
P1_2=~P1_2;
}
P1_3_Turn_Cnt++;
if(P1_3_Turn_Cnt>= 7)
{
P1_3_Turn_Cnt= 0;
P1_3=~P1_3;
}
}
以上就是这个程序实现的全部过程,你可以自己手动翻译成汇编语言,也可以用Keil的Debug模式自动翻译,因为我时间有限,所以就不给你翻译了,请见谅!
单片机汇编语言的几个疑问
1、如果程序代码空间不紧张的话,建议你全部使用LJMP,或者如果跳转的目标段程序相隔很近,也可以使用SJMP。个人认为AJMP最麻烦:其机器码所标注的地址位从A10到A0,计11位,000H~7FFH,也就是说跳转到的地址与当前地址的A10到A0必须一样,比如:
原来AJMP地址为:07F8H,目标地址:07F0H,如果调试软件修改后,变为
AJMP地址为:0800H,目标地址:07F8H,A11不一样,不在一页范围,此时还得将其改成LJMP,否则编译出错。其实就省了一个字节,执行时间上都是2个机器周期,没区别,所以犯不上。如果像此例,使用SJMP倒是个较好的选择,只是要求你对代码间的间隔有数才行。
总之,尽量用LJMP吧。
2、二者区别其实就是转移的目标是绝对地址,还是针对当前转移指令所在地址的相对偏移量
【绝对转移】51中只有AJMP、LJMP是绝对转移指令,都是直接跳转到固定地址。我们平时写的比如LJMP MAIN语句,源程序中并未表示出这一点,假设MAIN标志的程序段是从0102H开始的,此句编译后的机器码是02 01 02,02代表LJMP跳转,后两个字节0102则为目标地址,由于使用了2个字节指明目标地址,所以其范围就是整个64K的空间,没有限制;AJMP在这一点上就要差些,只能在所在的2K页内跳转。
【相对转移】剩下的转移指令都是相对转移指令,包括SJMP,它们是以当前语句的下一条语句所在地址为准,PC值向前或向后偏移数个字节地址开始执行,偏移量范围从-128到+127,以补码方式存在。比如
ORG 0100H
NO1: CLR A
SJMP NO1
编译后地址及机器码:
0100H: 0E4H(CLR A)
0101H: 80H 0FDH(SJMP NO1,从0103跳回0100,倒退3字节,3的补码即取反+1=FDH
0103H: 00H
3、要想能高效的调试大的汇编程序,首先要求程序本身的编制应该规范。关于这一点我有些感触,就算是经验吧,供参考(我曾经用汇编做过50多K机器码的程序)。
单片机的执行其实是单一线程的,一般只有两种情况会偏离原来的流程,一种是中断服务子程序,注意别忘了使用RETI;还有就是普通子程序,别忘了使用RET;再加上注意出栈、入栈操作,而且该操作因成对并保证先进后出原则,这样的话才真正做到模块化编程,调试时才能不理睬其它部份,单独调试某个模块,提高效率。这一点C语言就比较好,以函数方式实现,少写括号编译通不过,不像汇编,少些个RET照样能通过。
另外,编写程序时应养成一种习惯:对程序体中的寄存器操作尽量不要直接使用其地址,比如:MOV 30H,A,这里直接写30H不好,因为大的汇编程序对寄存器地址的调整是很常见的现象,一旦修改,程序中每个地方都要改,容易遗漏,一旦遗漏,非常难找原因的。可以用EQU伪指令对其定义,程序体中使用。同样,这一点C语言要好的多,至少不需要去分配寄存器了。
还有一个最容易出错的地方,就是关于标志位C:比如CJNE指令,它的判断是会影响C标志位的,如果程序后面有对C标志位判断的语句又没有考虑这种情况,问题极难查。很多人包括我自己会忽略这种细节。这类问题只能靠细致了。
最后谈谈调试
调试过程一般都是逐个模块(子程序)分别调试,完成一个再检查下一个,不能指望一上来就正确。调试完后还要多次、多人测试,自己有时形成了固定思维,发现不了问题。
终极方法:分部仿真设断点甚至于单步执行。对一些外部输入可以采用到断电后修改寄存器数值的方法来模拟。当然最理想的情况是不用,只是初期恐怕是免不了的。比如对上面所说的C标志位问题,我个人当初几乎都是这样找出的问题
大的汇编程序的调试确实比较麻烦,首先要求你对软件流程很清晰,对单片机的运行模式也要很了解。至于你现在的程序如果有问题,我建议还是采用仿真设断点的方式来找问题,尤其是如果搞单片机的时间不长的话,这样其实可能更快些。
希望所说能对你有所帮助。
单片机交通灯的汇编程序
NAME T3;I/O口扩展实验一
PORT EQU 0CFA0H;片选地址CS0
CSEG AT 0000H
LJMP START
CSEG AT 4100H
START: MOV A,#11H;两个红灯亮,黄灯、绿灯灭
ACALL DISP;调用273显示单元(以下雷同)
ACALL DE3S;延时3秒
LLL: MOV A,#12H;东西路口绿灯亮;南北路口红灯亮
ACALL DISP
ACALL DE10S;延时10秒
MOV A,#10H;东西路口绿灯灭;南北路口红灯亮
ACALL DISP
MOV R2,#05H;R2中的值为黄灯闪烁次数
TTT: MOV A,#14H;东西路口黄灯亮;南北路口红灯亮
ACALL DISP
ACALL DE02S;延时0.2秒
MOV A,#10H;东西路口黄灯灭;南北路口红灯亮
ACALL DISP
ACALL DE02S;延时0.2秒
DJNZ R2,TTT;返回TTT,使东西路口;黄灯闪烁五次
MOV A,#11H;两个红灯亮,黄灯、绿灯灭
ACALL DISP
ACALL DE02S;延时0.2秒
MOV A,#21H;东西路口红灯亮;南北路口绿灯亮
ACALL DISP
ACALL DE10S;延时10秒
MOV A,#01H;东西路口红灯亮;南北路口绿灯灭
ACALL DISP
MOV R2,#05H;黄灯闪烁五次
GGG: MOV A,#41H;东西路口红灯亮;南北路口黄灯亮
ACALL DISP
ACALL DE02S;延时0.2秒
MOV A,#01H;东西路口红灯亮;南北路口黄灯灭
ACALL DISP
ACALL DE02S;延时0.2秒
DJNZ R2,GGG;返回GGG,使南北路口;黄灯闪烁五次
MOV A,#03H;两个红灯亮,黄灯、绿灯灭
ACALL DISP
ACALL DE02S;延时0.2秒
JMP LLL;转LLL循环
DE10S: MOV R5,#100;延时10秒
JMP DE1
DE3S: MOV R5,#30;延时3秒
JMP DE1
DE02S: MOV R5,#02;延时0.2秒
DE1: MOV R6,#200
DE2: MOV R7,#126
DE3: DJNZ R7,DE3
DJNZ R6,DE2
DJNZ R5,DE1
RET
DISP: MOV DPTR,#PORT;273显示单元
CPL A
MOVX@DPTR,A
RET
END
你看看行不行如果不行你自己再稍微改一下吧反正大概就这些了
希望对你能有所帮助。
关于单片机汇编程序100例的内容到此结束,希望对大家有所帮助。