文章目录
- [一、 前言](#一、 前言)
- [二、 仿真](#二、 仿真)
-
- 1.001-灯闪烁
-
- [1.1 电路仿真](#1.1 电路仿真)
- [2.1 仿真程序](#2.1 仿真程序)
- 2.002-模拟开关灯
-
- [2.1 电路仿真](#2.1 电路仿真)
- [2.2 仿真程序](#2.2 仿真程序)
- 3.003-多路开关状态指示
-
- [3.1 电路仿真](#3.1 电路仿真)
- [3.2 仿真程序](#3.2 仿真程序)
- 4.004-广告灯的左移右移
-
- [4.1 电路仿真](#4.1 电路仿真)
- [4.2 仿真程序](#4.2 仿真程序)
- 5.005-广告灯(利用取表方式)
-
- [5.1 电路仿真](#5.1 电路仿真)
- [5.2 仿真程序](#5.2 仿真程序)
- 三、总结
一、 前言
汇编语言是二进制指令的文本形式,与指令是一一对应的关系。比如,加法指令00000011写成汇编语言就是 ADD。只要还原成二进制,汇编语言就可以被 CPU 直接执行,所以它是最底层的低级语言。
最近对汇编的仿真产生兴趣,虽然尚未系统学习,但搜集了一些资料尝试实践。学习汇编有助于更深入理解硬件工作原理,顺带记录一下学习过程,也欢迎交流指正。
二、 仿真
1.001-灯闪烁
/*
001-灯闪烁
LED 以约 0.2 秒亮、0.2 秒灭的频率闪烁,总周期约为 0.4 秒。
*/

1.1 电路仿真

2.1 仿真程序
c
/*
001-灯闪烁
LED 以约 0.2 秒亮、0.2 秒灭的频率闪烁,总周期约为 0.4 秒。
*/
ORG 0 ; ORG 0: 告诉编译器,程序从程序存储器(ROM)的地址 0 开始存放。
; 单片机复位后,程序计数器(PC)从 0 地址开始执行。
START: CLR P1.0 ; START: 标号,表示程序循环的起点。
; CLR P1.0: 清除 P1.0 引脚,即将其设置为低电平 (0V)。如果 LED 阴极接 P1.0,阳极接 VCC,则此时 LED 会亮。
LCALL DELAY ; LCALL: 长调用指令。调用位于 DELAY 标号处的延时子程序。
; 执行这条指令后,程序会跳转到 DELAY 子程序,执行完后再返回到这里。
SETB P1.0 ; SETB P1.0: 置位 P1.0 引脚,即将其设置为高电平 (5V)。此时 LED 会熄灭。
LCALL DELAY ; 再次调用延时子程序,使 LED 熄灭状态保持一段时间。
LJMP START ; LJMP: 长跳转指令。无条件跳转回 START 标号处。
; 这将形成一个无限循环:亮 -> 延时 -> 灭 -> 延时 -> 亮 ... 从而实现 LED 闪烁。
; ===== 延时子程序 DELAY =====
DELAY: MOV R5, #20 ; DELAY: 子程序入口标号。
; MOV R5, #20: 将立即数 20 送入寄存器 R5。这是外层循环计数器。
D1: MOV R6, #20 ; D1: 中层循环标号。将立即数 20 送入寄存器 R6。这是中层循环计数器。
D2: MOV R7, #248 ; D2: 内层循环标号。将立即数 248 送入寄存器 R7。这是内层循环计数器。
DJNZ R7, $ ; DJNZ R7, $: 寄存器 R7 减 1,如果不为 0,则跳转到当前地址('$' 表示当前地址)。
; 这条指令会执行 248 次,产生一个短暂的延时。
DJNZ R6, D2 ; 寄存器 R6 减 1,如果 R6 不为 0,则跳转回 D2 标号,重新初始化 R7 并循环。
; 该指令执行 20 次,所以内层循环(R7循环)总共执行 20 * 248 次。
DJNZ R5, D1 ; 寄存器 R5 减 1,如果 R5 不为 0,则跳转回 D1 标号,重新初始化 R6 并循环。
; 该指令执行 20 次,所以中层循环(R6循环)总共执行 20 * 20 * 248 次。
RET ; RET: 子程序返回指令。返回到 LCALL DELAY 的下一条指令继续执行。
; 整个延时子程序通过三层嵌套循环来消耗 CPU 时间,实现延时效果。
END ; END: 汇编结束指令,告诉编译器程序到此为止。
2.002-模拟开关灯
/*
002-模拟开关灯
查询式按键检测程序,按键按下→ LED 亮,按键释放→ LED 灭。
*/

2.1 电路仿真

2.2 仿真程序
c
/*
002-模拟开关灯
查询式按键检测程序,按键按下→ LED 亮,按键释放→ LED 灭。
*/
ORG 0 ; 程序起始地址为 0(通常省略,因为默认从 0 开始)
START: JB P3.0, LIG ; START: 程序主循环的起点
; JB P3.0, LIG: 判断 P3.0 引脚的电平
; - 如果 P3.0 为高电平 (1),则跳转到 LIG 标号处执行
; - 如果 P3.0 为低电平 (0),则顺序执行下一条指令
CLR P1.0 ; 将 P1.0 引脚设置为低电平 (0V)
; 如果 P1.0 接LED(阴极接P1.0,阳极接VCC),则LED亮
SJMP START ; 短跳转回 START 标号,形成循环,继续检测 P3.0 的状态
LIG: SETB P1.0 ; LIG: 标号(可能是 "LIGHT" 的缩写)
; SETB P1.0: 将 P1.0 引脚设置为高电平 (5V)
; 此时 LED 熄灭
SJMP START ; 短跳转回 START 标号,形成循环,继续检测 P3.0 的状态
END ; 程序结束标志
3.003-多路开关状态指示
/*
003-多路开关状态指示
这是一个4路独立的开关控制程序,
输入引脚(检测端) 输出引脚(控制端)
P1.4 (按键) → P1.0 (LED)
P1.5 (按键) → P1.1 (LED)
P1.6 (按键) → P1.2 (LED)
P1.7 (按键) → P1.3 (LED)
*/

3.1 电路仿真

3.2 仿真程序
c
/*
003-多路开关状态指示
这是一个4路独立的开关控制程序,
输入引脚(检测端) 输出引脚(控制端)
P1.4 (按键) → P1.0 (LED)
P1.5 (按键) → P1.1 (LED)
P1.6 (按键) → P1.2 (LED)
P1.7 (按键) → P1.3 (LED)
*/
ORG 00H ; 程序从地址 00H 开始执行
; ===== 检测 P1.4,控制 P1.0 =====
START: JB P1.4, NEXT1 ; 判断 P1.4 引脚电平
; 如果 P1.4 = 1(高电平),跳转到 NEXT1
; 如果 P1.4 = 0(低电平),顺序执行
CLR P1.0 ; 将 P1.0 设置为低电平 (0V)
SJMP NEX1 ; 跳转到 NEX1,跳过下面的 SETB 指令
NEXT1: SETB P1.0 ; 将 P1.0 设置为高电平 (5V)
NEX1: ; 第一个检测控制段的结束标号
; ===== 检测 P1.5,控制 P1.1 =====
JB P1.5, NEXT2 ; 判断 P1.5 引脚电平
; 如果 P1.5 = 1,跳转到 NEXT2
; 如果 P1.5 = 0,顺序执行
CLR P1.1 ; 将 P1.1 设置为低电平
SJMP NEX2 ; 跳转到 NEX2
NEXT2: SETB P1.1 ; 将 P1.1 设置为高电平
NEX2: ; 第二个检测控制段的结束标号
; ===== 检测 P1.6,控制 P1.2 =====
JB P1.6, NEXT3 ; 判断 P1.6 引脚电平
; 如果 P1.6 = 1,跳转到 NEXT3
; 如果 P1.6 = 0,顺序执行
CLR P1.2 ; 将 P1.2 设置为低电平
SJMP NEX3 ; 跳转到 NEX3
NEXT3: SETB P1.2 ; 将 P1.2 设置为高电平
NEX3: ; 第三个检测控制段的结束标号
; ===== 检测 P1.7,控制 P1.3 =====
JB P1.7, NEXT4 ; 判断 P1.7 引脚电平
; 如果 P1.7 = 1,跳转到 NEXT4
; 如果 P1.7 = 0,顺序执行
CLR P1.3 ; 将 P1.3 设置为低电平
SJMP NEX4 ; 跳转到 NEX4
NEXT4: SETB P1.3 ; 将 P1.3 设置为高电平
NEX4: ; 第四个检测控制段的结束标号
SJMP START ; 跳转回 START,形成无限循环,持续检测和控制
END ; 程序结束
4.004-广告灯的左移右移
/*
004-广告灯的左移右移
流水灯,
LED灯会以0.2秒的间隔依次点亮,形成从左到右、再从右到左的来回扫描效果,类似于跑马灯灯效。
*/

4.1 电路仿真

4.2 仿真程序
c
/*
004-广告灯的左移右移
流水灯,
LED灯会以0.2秒的间隔依次点亮,形成从左到右、再从右到左的来回扫描效果,类似于跑马灯灯效。
*/
ORG 0 ; 程序从地址0开始执行
START: MOV R2, #8 ; 设置循环计数器R2=8,控制8个LED的移动次数
MOV A, #0FEH ; 将初始值0FEH(1111 1110B)送入累加器A
; 最低位为0,对应P1.0引脚为低电平,第一个LED亮
SETB C ; 将进位标志C置1,为后面的循环移位做准备
; ===== 从左到右的流水灯效果 =====
LOOP: MOV P1, A ; 将累加器A的值输出到P1端口,控制LED显示
LCALL DELAY ; 调用延时子程序,保持当前显示状态一段时间
RLC A ; 带进位循环左移(Rotate Left through Carry)
; 例如:A=1111 1110, C=1 → 左移后:A=1111 1101, C=1
; 这样0的位置会向左移动,实现LED从左到右移动的效果
DJNZ R2, LOOP ; R2减1,如果不为0则跳转回LOOP继续循环
; 共循环8次,完成8个LED的依次点亮
; ===== 从右到左的流水灯效果 =====
MOV R2, #7 ; 重新设置循环计数器R2=7
LOOP1:
RRC A ; 先右移,再显示
MOV P1, A ; 将累加器A的值输出到P1端口
LCALL DELAY ; 调用延时子程序
DJNZ R2, LOOP1 ; 循环7次
LJMP START ; 跳转回START,重新开始流水灯效果,形成无限循环
; ===== 延时子程序 DELAY =====
DELAY: MOV R5, #20 ; 外层循环计数器R5=20
D1: MOV R6, #20 ; 中层循环计数器R6=20
D2: MOV R7, #248 ; 内层循环计数器R7=248
DJNZ R7, $ ; R7减1,不为0则跳转到当前地址($),形成短暂延时
DJNZ R6, D2 ; R6减1,不为0则跳转到D2
DJNZ R5, D1 ; R5减1,不为0则跳转到D1
RET ; 返回主程序
END ; 程序结束
5.005-广告灯(利用取表方式)
/*
005-广告灯(利用取表方式)
一个典型的多模式LED灯效控制器程序,通过查表法实现了复杂的显示效果。
LED会依次显示:
从左到右流水灯 × 2次
从右到左流水灯 × 2次
全亮全灭闪烁 × 2次
重复以上序列
*/

5.1 电路仿真

5.2 仿真程序
c
/*
005-广告灯(利用取表方式)
一个典型的多模式LED灯效控制器程序,通过查表法实现了复杂的显示效果。
LED会依次显示:
从左到右流水灯 × 2次
从右到左流水灯 × 2次
全亮全灭闪烁 × 2次
重复以上序列
*/
ORG 0 ; 程序从地址0开始执行
START: MOV DPTR, #TABLE ; 将数据指针DPTR指向TABLE表的起始地址
; ===== 主循环:读取表数据并控制LED =====
LOOP: CLR A ; 清空累加器A(A=0)
MOVC A, @A+DPTR ; 查表指令:从程序存储器读取数据
; 地址 = DPTR + A,即读取TABLE中的当前数据到A
CJNE A, #01H, LOOP1 ; 比较A是否等于01H
; 如果不等于01H,跳转到LOOP1
; 如果等于01H,顺序执行(表示遇到结束标志)
JMP START ; 跳回START,重新开始循环(从头播放灯效)
LOOP1: MOV P1, A ; 将表数据输出到P1端口,控制LED显示
MOV R3, #20 ; 设置延时循环计数器R3=20
LCALL DELAY ; 调用延时子程序
INC DPTR ; 数据指针加1,指向下一个表数据
JMP LOOP ; 跳回LOOP,继续读取下一个数据
; ===== 延时子程序 =====
DELAY: MOV R4, #20 ; 中层循环计数器R4=20
D1: MOV R5, #248 ; 内层循环计数器R5=248
DJNZ R5, $ ; R5减1,不为0则跳转到当前地址(短暂延时)
DJNZ R4, D1 ; R4减1,不为0则跳转到D1
DJNZ R3, DELAY ; R3减1,不为0则跳转回DELAY(外层循环)
RET ; 返回主程序
; ===== LED灯效数据表 =====
TABLE:
; 模式1:从左到右流水灯(2次)
DB 0FEH,0FDH,0FBH,0F7H ; 1111 1110, 1111 1101, 1111 1011, 1111 0111
DB 0EFH,0DFH,0BFH,07FH ; 1110 1111, 1101 1111, 1011 1111, 0111 1111
DB 0FEH,0FDH,0FBH,0F7H ; 重复一次
DB 0EFH,0DFH,0BFH,07FH
; 模式2:从右到左流水灯(2次)
DB 07FH,0BFH,0DFH,0EFH ; 0111 1111, 1011 1111, 1101 1111, 1110 1111
DB 0F7H,0FBH,0FDH,0FEH ; 1111 0111, 1111 1011, 1111 1101, 1111 1110
DB 07FH,0BFH,0DFH,0EFH ; 重复一次
DB 0F7H,0FBH,0FDH,0FEH
; 模式3:全亮全灭闪烁(2次)
DB 00H, 0FFH,00H, 0FFH ; 0000 0000(全亮), 1111 1111(全灭), 全亮, 全灭
; 结束标志
DB 01H ; 结束标志,遇到01H就重新开始
END ; 程序结束
三、总结
今天通过几个简单的51汇编仿真,我们亲眼看到了代码如何驱动硬件。算是为"更深层次了解单片机工作原理"这个目标迈出了一小步。感谢观看,后续继续分享!
再次感谢你的观看!
