【51单片机】基于SDCC和Edsim51的代码仿真
1.实验目标
1.1. 点灯
安装SDCC和Edsim51,采用汇编语言与C语言两种方式,在Edsim51虚拟开发板上点亮一个Led灯,并思考尝试让Led灯周期性闪烁。写作详细操作过程。
1.2. 指令比对
参考附件中8051指令集大全手册,在Edsim51,把汇编语言点亮Led1灯的代码,手工翻译转换为机器指令,与Edsim51 编译生成的机器码指令进行对比,查看是否一致。写作详细操作过程。
1.3.Proteus仿真
在Proteus中画出一个与Edsim51虚拟电路原理基本相同的电路,用Proteus仿真完成Led灯的点亮和闪烁,须分别采用以下方式:
1.3.1采用Proteus+SDCC 的C程序编程;
1.3.2采用Proteus+Keil C51 的C程序编程;
1.3.3采用Proteus 内置汇编语言编译的方式;
2.实验过程
2.1.1 Edsim点亮1个LED灯
点亮1个接到P1端口的LED灯如P1.0,则需要1111 1110(低电平点亮LED)。
由于P1的低电平地址为90H(0x90),而我们需要传入的二进制1111 1110对应的十六进制数为FE、十进制数254。
因此,汇编语言可以写
MOV 90H,#254
;or
MOV 90H,#0xFE
;or
MOV 0x90,#254
;or
MOV 0x90,#0xFE
当然,不记得的话也可以写
MOV P1,#254 ;或许还有其他写法,可以自己尝试一下
2.1.2 周期性闪烁
想要实现周期性闪烁,我们可以让P1端口的八个灯依次逐个点亮。然后在最后加入DJNZ指令跳转LOOP实现周期循环。
DJNZ 将寄存器的值减1。如果寄存器的初始值为 0,则减少该值将使其重置为 255(0xFF Hex)。如果寄存器的新值 不为 0,程序将跳转到相对 addr指示的地址。如果寄存器的新值为0,则程序流程继续执行 DJNZ 指令后面的指令。
MOV R1,#0FEH ;给寄存器Rn赋值254,这个值不是固定的,根据需求设置,越大延迟时间越长
LOOP:
MOV P1,#0FEH ;点亮LED1
MOV P1,#253 ;0FDH,点亮LED2
MOV P1,#251 ;0FBH,点亮LED3
MOV P1,#247 ;0F7H,点亮LED4
MOV P1,#239 ;0EFH,点亮LED5
MOV P1,#223 ;0DFH,点亮LED6
MOV P1,#191 ;0BFH,点亮LED7
MOV P1,#127 ;07FH,点亮LED8
DJNZ R1,LOOP ;R1!=0时,R--;R1==0时,跳出循环
;以上代码在Edsim中低速运行,所以有流水灯效果,现实中微不可见,需要加入延时函数
效果如下:
2.1.3 SDCC
//1个LED
#include<mcs51/8051.h>
void main()
{
while(1){
P1=0b11111110;
}
}
#include<mcs51/8051.h>
void main()
{
while(1){
P1=0b11111110;
P1=0b11111101;
P1=0b11111011;
P1=0b11110111;
P1=0b11101111;
P1=0b11011111;
P1=0b10111111;
P1=0b01111111;
}
}
值得注意的是,使用sdcc将.c文件依照以下指令转为hex文件后加载到Edsim后,需要将Update Freq.的值设为10000,此时运行代码会呈现相对流畅的LED灯周期变化。
2.2. 代码比对
以下为Edsim根据导入的hex文件生成的汇编语言。
ORG 0000H
;程序起始和跳转
LJMP 0008H
LCALL 0064H
SJMP 0FEH
;数据移动和条件跳转
MOV 81H,#07H
LCALL 007EH
MOV A,82H
JZ 03H
LJMP 0003H
;寄存器操作和循环
MOV R1,#00H
MOV A,R1
ORL A,#00H
JZ 1BH
MOV R2,#00H
MOV DPTR, #0082H
MOV R0,#00H
;内存清零和循环结束检查
MOV 0A0H,#00H
CLR A
MOVC A,@A+DPTR
MOVX @R0,A
INC DPTR
INC R0
CJNE R0,#00H,02H
INC 0A0H
DJNZ R1,0F4H
DJNZ R2,0F2H
;更多的条件跳转和内存操作
MOV 0A0H,#0FFH
CLR A
MOV R0,#0FFH
MOV @R0,A
DJNZ R0,0FDH
MOV R0,#00H
MOV A,R0
ORL A,#00H
JZ 0AH
MOV R1,#00H
MOV 0A0H,#00H
CLR A
MOVX @R1,A
INC R1
DJNZ R0,0FCH
MOV R0,#00H
MOV A,R0
ORL A,#00H
JZ 0CH
MOV R1,#00H
MOV DPTR, #0000H
CLR A
MOVX @DPTR,A
INC DPTR
DJNZ R0,0FCH
DJNZ R1,0FAH
LJMP 0003H
MOV 90H,#0FEH
MOV 90H,#0FDH
MOV 90H,#0FBH
MOV 90H,#0F7H
MOV 90H,#0EFH
MOV 90H,#0DFH
MOV 90H,#0BFH
MOV 90H,#7FH
SJMP 0E6H
MOV 82H,#00H
RET
END
通过对比可以发现,Edsim生成的代码相较于我们写的汇编代码更长更复杂。
2.3.1 采用Proteus+SDCC 的C程序编程
Edsim51电路原理图
#include <8051.h> //经过测试,2.1.3的代码并不好用,经过更改后如下,但是以下代码无法在Edsim中仿真
void delay(unsigned int count) {
while(count--);
}
void main() {
while(1) {
P1=0b11111110;
delay(1000);
P1=0b11111101;
delay(1000);
P1=0b11111011;
delay(1000);
P1=0b11110111;
delay(1000);
P1=0b11101111;
delay(1000);
P1=0b11011111;
delay(1000);
P1=0b10111111;
delay(1000);
P1=0b01111111;
delay(1000);
}
}
2.3.2采用Proteus+Keil C51 的C程序编程
//51单片机编程常用的头文件
#include <reg51.h>
#include <intrins.h>
//延迟函数
void delay_ms(int a)
{
int i,j;
for(i=0;i<a;i++)
{
for(j=0;j<1000;j++) _nop_();
}
}
void main(void)
{
while(1)
{
P0=0xfe;
delay_ms(50);
P0=0xfd;
delay_ms(50);
P0=0xfb;
delay_ms(50);
P0=0xf7;
delay_ms(50);
P0=0xef;
delay_ms(50);
P0=0xdf;
delay_ms(50);
P0=0xbf;
delay_ms(50);
P0=0x7f;
delay_ms(50);
}
}
//https://blog.csdn.net/qq_43279579/article/details/108908566
2.3.3 采用Proteus 内置汇编语言编译的方式
ORG 0000H
LOOP:
MOV P1,#0FEH ; 点亮LED
MOV P1,#253
MOV P1,#251
MOV P1,#247
MOV P1,#239
MOV P1,#223
MOV P1,#191
MOV P1,#127
DJNZ R1,LOOP ;随便写个寄存器Rn都是可以的
END
3.实验总结
SDCC可以将.C文件转为hex文件,再由Edsim生成汇编语言,但是此时的汇编指令是很复杂的,比起了解汇编指令原理并直接书写要长很多。
C语言作为高级语言,比起汇编语言可读性更高,更容易理解。C语言是汇编语言的升级,汇编语言是C语言的基础,二者是相互兼容,理解汇编语言有利于我们更加了解C