文章目录
- 一、前言
- 二、仿真
-
- 1.011-00-59秒计时器(利用软件延时)
-
- [1.1 电路仿真](#1.1 电路仿真)
- [1.2 仿真程序](#1.2 仿真程序)
- 2.012-可预置可逆4位计数器
-
- [2.1 电路仿真](#2.1 电路仿真)
- [2.2 仿真程序](#2.2 仿真程序)
- 3.013-动态数码显示技术
-
- [3.1 电路仿真](#3.1 电路仿真)
- [3.2 仿真程序](#3.2 仿真程序)
- 4.014-4×4矩阵式键盘识别技术
-
- [4.1 电路仿真](#4.1 电路仿真)
- [4.2 仿真程序](#4.2 仿真程序)
- 5.015-定时计数器T0作定时应用技术(一)
-
- [5.1 电路仿真](#5.1 电路仿真)
- [5.2 仿真程序](#5.2 仿真程序)
- 三、总结
一、前言
汇编语言是二进制指令的文本形式,与指令是一一对应的关系。比如,加法指令00000011写成汇编语言就是 ADD。只要还原成二进制,汇编语言就可以被 CPU 直接执行,所以它是最底层的低级语言。
最近对汇编的仿真产生兴趣,虽然尚未系统学习,但搜集了一些资料尝试实践。学习汇编有助于更深入理解硬件工作原理,顺带记录一下学习过程,也欢迎交流指正。
二、仿真
1.011-00-59秒计时器(利用软件延时)
/*
011-00-59秒计时器(利用软件延时)
数码管会以1秒为间隔显示:
00 → 01 → 02 → ... → 59 → 00 → 01 ... 循环不断
*/

1.1 电路仿真

1.2 仿真程序
c
/*
011-00-59秒计时器(利用软件延时)
数码管会以1秒为间隔显示:
00 → 01 → 02 → ... → 59 → 00 → 01 ... 循环不断
*/
Second EQU 30H ; 定义秒计数器变量,使用内部RAM的30H单元
ORG 0 ; 程序从地址0开始执行
START: MOV Second, #00H ; 初始化秒计数器为0
; ===== 主计时循环 =====
NEXT: MOV A, Second ; 将秒数送入累加器A
MOV B, #10 ; 除数10送入B寄存器
DIV AB ; A除以B:A=商(十位数),B=余数(个位数)
; 例如:Second=25 → A=2(十位), B=5(个位)
; ===== 显示十位数 =====
MOV DPTR, #TABLE ; 数据指针指向七段码表
MOVC A, @A+DPTR ; 查表获取十位数的七段码
MOV P0, A ; 十位数送到P0口(十位数码管)
; ===== 显示个位数 =====
MOV A, B ; 将个位数从B寄存器送入A
MOVC A, @A+DPTR ; 查表获取个位数的七段码
MOV P2, A ; 个位数送到P2口(个位数码管)
LCALL DELY1S ; 调用1秒延时子程序
INC Second ; 秒计数器加1
MOV A, Second ; 秒数送入A
CJNE A, #60, NEXT ; 比较秒数是否等于60
; 如果A≠60,跳转到NEXT继续计时
; 如果A=60,顺序执行(需要归零)
LJMP START ; 跳回START,秒表归零重新开始
; ===== 1秒延时子程序 =====
DELY1S: MOV R5, #100 ; 外层循环计数器(100次)
D2: MOV R6, #20 ; 中层循环计数器(20次)
D1: MOV R7, #248 ; 内层循环计数器(248次)
DJNZ R7, $ ; R7减1,不为0则跳转到当前地址
DJNZ R6, D1 ; R6减1,不为0则跳转到D1
DJNZ R5, D2 ; R5减1,不为0则跳转到D2
RET ; 返回
; ===== 七段数码管码表 =====
TABLE:
DB 3FH ; 数字0: 0011 1111 → 显示0
DB 06H ; 数字1: 0000 0110 → 显示1
DB 5BH ; 数字2: 0101 1011 → 显示2
DB 4FH ; 数字3: 0100 1111 → 显示3
DB 66H ; 数字4: 0110 0110 → 显示4
DB 6DH ; 数字5: 0110 1101 → 显示5
DB 7DH ; 数字6: 0111 1101 → 显示6
DB 07H ; 数字7: 0000 0111 → 显示7
DB 7FH ; 数字8: 0111 1111 → 显示8
DB 6FH ; 数字9: 0110 1111 → 显示9
END ; 程序结束
2.012-可预置可逆4位计数器
/*
012-可预置可逆4位计数器
一个典型的可逆计数器程序
双功能按键:一个键加计数,一个键减计数
可设置初始值:通过DIP开关设置起始计数值
循环计数:0-15循环计数,不会溢出
实时显示:4个LED实时显示当前二进制数值
*/

2.1 电路仿真

2.2 仿真程序
c
/*
012-可预置可逆4位计数器
一个典型的可逆计数器程序
双功能按键:一个键加计数,一个键减计数
可设置初始值:通过DIP开关设置起始计数值
循环计数:0-15循环计数,不会溢出
实时显示:4个LED实时显示当前二进制数值
*/
COUNT EQU 30H ; 定义计数器变量,使用内部RAM的30H单元
ORG 00H ; 程序从地址00H开始
START:
MOV A, P3 ; 读取P3端口的状态
ANL A, #0FH ; 保留P3.0-P3.3的低4位(DIP开关输入)
; DIP开关接VCC→输入1,接GND→输入0
MOV COUNT, A ; 将开关设置值作为计数器初始值
MOV P1, A ; 输出到P1口控制LED显示
; ===== 主循环:检测两个按键 =====
SK2: JB P3.6, SK1 ; 检测P3.6键(加键)是否按下
; 如果P3.6=1(未按下),检测P3.7键
; 如果P3.6=0(按下),执行加操作
; ===== P3.6键处理(加按键) =====
LCALL DELY10MS ; 调用10ms延时去抖动
JB P3.6, SK1 ; 再次检测,如果是抖动则跳转
INC COUNT ; 计数器加1
MOV A, COUNT ; 将计数器值送入累加器A
CJNE A, #16, NEXT ; 比较计数器值是否等于16(0-15循环)
; 如果A≠16,跳转到NEXT
; 如果A=16,顺序执行(需要处理溢出)
; 计数器达到16,重新从P3口的低4位读取初始值
MOV A, P3
ANL A, #0FH
MOV COUNT, A
SJMP DISPLAY ; 跳转到显示部分
NEXT: ; 计数器未溢出,正常显示
DISPLAY:
MOV P1, A ; 将计数器值输出到P1口显示LED
WAIT: JNB P3.6, WAIT ; 等待加键释放(P3.6变为高电平)
LJMP SK2 ; 返回继续检测按键
; ===== P3.7键处理(减按键) =====
SK1: JB P3.7, SK2 ; 检测P3.7键(减键)是否按下
; 如果P3.7=1(未按下),跳转回SK2
; 如果P3.7=0(按下),执行减操作
LCALL DELY10MS ; 调用10ms延时去抖动
JB P3.7, SK2 ; 再次检测,如果是抖动则跳转
DEC COUNT ; 计数器减1
MOV A, COUNT ; 将计数器值送入累加器A
CJNE A, #0FFH, NEX ; 比较计数器值是否等于0FFH(-1)
; 如果A≠0FFH,跳转到NEX
; 如果A=0FFH(减到-1),顺序执行(需要处理下溢)
; 计数器下溢(减到-1),重新从P3口的低4位读取初始值
MOV A, P3
ANL A, #0FH
MOV COUNT, A
SJMP DISPLAY2 ; 跳转到显示部分
NEX: ; 计数器未下溢,正常显示
DISPLAY2:
MOV P1, A ; 将计数器值输出到P1口显示LED
WAIT2: JNB P3.7, WAIT2 ; 等待减键释放(P3.7变为高电平)
LJMP SK2 ; 返回继续检测按键
; ===== 10ms延时子程序 =====
DELY10MS:
MOV R6, #20 ; 外层循环计数器
MOV R7, #248 ; 内层循环计数器
D1: DJNZ R7, $ ; R7减1,不为0则跳转到当前地址
DJNZ R6, D1 ; R6减1,不为0则跳转到D1
RET ; 返回
END ; 程序结束
3.013-动态数码显示技术
/*
013-动态数码显示技术
一个典型的8位数码管动态扫描显示程序
P1.7接GND时显示"1 2 3 4 5",接VCC时显示"H E L L O"
硬件连接:P0口接段选,P2口接位选,数码管为共阴类型
*/

3.1 电路仿真
P1.7接GND时显示"1 2 3 4 5"

P1.7接VCC时显示"H E L L O"

3.2 仿真程序
c
/*
013-动态数码显示技术
一个典型的8位数码管动态扫描显示程序
P1.7接GND时显示"1 2 3 4 5",接VCC时显示"H E L L O"
硬件连接:P0口接段选,P2口接位选,数码管为共阴类型
*/
ORG 0000H
LJMP MAIN
ORG 0030H
MAIN:
MOV SP, #60H ; 设置堆栈指针
MOV P0, #00H ; 初始化P0口
MOV P2, #0FFH ; 初始化P2口(位选全关)
MAIN_LOOP:
; 检查模式选择引脚P1.7
JB P1.7, MODE_HELLO
MODE_12345:
MOV DPTR, #TABLE1 ; 指向"1 2 3 4 5"段码表
SJMP START_SCAN
MODE_HELLO:
MOV DPTR, #TABLE2 ; 指向"H E L L O"段码表
START_SCAN:
MOV R0, #00H ; 当前扫描的数码管位置(0-7)
MOV R1, #01H ; 位选模式(从最低位开始)
SCAN_LOOP:
; 设置位选信号(共阴数码管:低电平选中)
MOV A, R1
CPL A ; 取反,使对应位为低电平
MOV P2, A
; 根据当前位置确定显示内容
MOV A, R0 ; 获取当前扫描位置
; 判断当前位置(0-4显示字符,5-7熄灭)
CJNE A, #00H, CHECK_POS1
; 位置0: 显示第一个字符
MOV A, #00H
SJMP GET_SEGMENT
CHECK_POS1:
CJNE A, #01H, CHECK_POS2
; 位置1: 显示第二个字符
MOV A, #01H
SJMP GET_SEGMENT
CHECK_POS2:
CJNE A, #02H, CHECK_POS3
; 位置2: 显示第三个字符
MOV A, #02H
SJMP GET_SEGMENT
CHECK_POS3:
CJNE A, #03H, CHECK_POS4
; 位置3: 显示第四个字符
MOV A, #03H
SJMP GET_SEGMENT
CHECK_POS4:
CJNE A, #04H, TURN_OFF_DISPLAY
; 位置4: 显示第五个字符
MOV A, #04H
SJMP GET_SEGMENT
TURN_OFF_DISPLAY:
; 位置5-7: 熄灭数码管
MOV P0, #00H ; 段选全灭
SJMP DELAY_DISPLAY
GET_SEGMENT:
MOVC A, @A+DPTR ; 从表中获取段码
MOV P0, A ; 输出到P0口(段选)
DELAY_DISPLAY:
; 延时保持显示(约1ms)
LCALL DELAY_1MS
; 关闭当前数码管(消隐处理,避免鬼影)
MOV P0, #00H ; 段选全灭
MOV P2, #0FFH ; 关闭所有位选
; 更新扫描位置
INC R0 ; 移动到下一个数码管
MOV A, R0
CJNE A, #08H, UPDATE_BIT_SELECT
; 如果扫描完8位数码管,重新开始
MOV R0, #00H
UPDATE_BIT_SELECT:
; 更新位选信号(左移一位)
MOV A, R1
RL A
MOV R1, A
; 检查是否完成一轮扫描(8位数码管)
CJNE A, #01H, CONTINUE_SCAN
; 如果位选信号回到01H,重新检查模式
LJMP MAIN_LOOP
CONTINUE_SCAN:
LJMP SCAN_LOOP
; 1ms延时子程序(用于动态扫描)
DELAY_1MS:
MOV R6, #4
DELAY_LOOP1:
MOV R7, #250
DELAY_LOOP2:
DJNZ R7, DELAY_LOOP2
DJNZ R6, DELAY_LOOP1
RET
; 共阴数码管段码表(标准编码)
; 段顺序: dp g f e d c b a
; 对应位: 7 6 5 4 3 2 1 0
TABLE1:
DB 06H ; 1: 0000 0110 (bc段亮)
DB 5BH ; 2: 0101 1011 (abged段亮)
DB 4FH ; 3: 0100 1111 (abgcd段亮)
DB 66H ; 4: 0110 0110 (fgbc段亮)
DB 6DH ; 5: 0110 1101 (afgcd段亮)
TABLE2:
DB 76H ; H: 0111 0110 (fgebc段亮)
DB 79H ; E: 0111 1001 (afged段亮)
DB 38H ; L: 0011 1000 (fed段亮)
DB 38H ; L: 0011 1000 (fed段亮)
DB 3FH ; O: 0011 1111 (abcdef段亮)
END
4.014-4×4矩阵式键盘识别技术
/*
014-4×4矩阵式键盘识别技术
一个典型的4x4矩阵键盘扫描程序
数码管显示对应矩阵按键按下的值
*/

4.1 电路仿真

4.2 仿真程序
c
/*
014-4×4矩阵式键盘识别技术
一个典型的4x4矩阵键盘扫描程序
数码管显示对应矩阵按键按下的值
*/
KEYBUF EQU 30H ; 定义键盘缓冲区地址为30H,用于存储按键值
ORG 00H ; 程序从00H地址开始执行
START:
MOV KEYBUF, #8 ; 初始化键盘缓冲区的值为8(默认显示数字8)
WAIT: ; 主循环,开始扫描4行键盘
; ********** 扫描第一行(P3.4)**********
MOV P3, #0FFH ; 设置P3口为输入模式(高电平)
CLR P3.4 ; 将P3.4置为低电平,选中第一行
; 读取列值(P3.0-P3.3)
MOV A, P3 ; 读取P3口状态
ANL A, #0FH ; 屏蔽高4位,只保留低4位(列值)
XRL A, #0FH ; 异或操作,如果有按键按下,结果不为0
JZ NOKEY1 ; 如果结果为0,说明没有按键按下,跳转到NOKEY1
; 按键去抖延时
LCALL DELY10MS ; 调用10ms延时去抖动
; 再次读取列值,确认按键
MOV A, P3
ANL A, #0FH
XRL A, #0FH
JZ NOKEY1 ; 如果第二次检测无按键,说明是抖动,跳转
; 确定具体哪个按键被按下
MOV A, P3
ANL A, #0FH ; 获取列值
; 根据列值判断具体按键
CJNE A, #0EH, NK1 ; 如果列值不是1110(P3.0=0),跳转到NK1
MOV KEYBUF, #0 ; 按键(1,1) -> 键值0
LJMP DK1
NK1:
CJNE A, #0DH, NK2 ; 如果列值不是1101(P3.1=0),跳转到NK2
MOV KEYBUF, #1 ; 按键(1,2) -> 键值1
LJMP DK1
NK2:
CJNE A, #0BH, NK3 ; 如果列值不是1011(P3.2=0),跳转到NK3
MOV KEYBUF, #2 ; 按键(1,3) -> 键值2
LJMP DK1
NK3:
CJNE A, #07H, NK4 ; 如果列值不是0111(P3.3=0),跳转到NK4
MOV KEYBUF, #3 ; 按键(1,4) -> 键值3
LJMP DK1
NK4:
NOP ; 无操作占位
DK1: ; 显示按键值
MOV A, KEYBUF ; 获取键值
MOV DPTR, #TABLE ; 设置数据指针指向段码表
MOVC A, @A+DPTR ; 查表获取对应的段码
MOV P0, A ; 将段码输出到P0口(控制数码管显示)
DK1A: ; 等待按键释放
MOV A, P3
ANL A, #0FH ; 读取列值
XRL A, #0FH ; 检查是否有按键按下
JNZ DK1A ; 如果仍有按键按下,继续等待
NOKEY1: ; 第一行无按键
; ********** 扫描第二行(P3.5)**********
MOV P3, #0FFH
CLR P3.5 ; 将P3.5置为低电平,选中第二行
MOV A, P3
ANL A, #0FH
XRL A, #0FH
JZ NOKEY2 ; 无按键则跳转
LCALL DELY10MS ; 去抖动
MOV A, P3
ANL A, #0FH
XRL A, #0FH
JZ NOKEY2
MOV A, P3
ANL A, #0FH
; 扫描第二行的按键
CJNE A, #0EH, NK5
MOV KEYBUF, #4 ; 按键(2,1) -> 键值4
LJMP DK2
NK5:
CJNE A, #0DH, NK6
MOV KEYBUF, #5 ; 按键(2,2) -> 键值5
LJMP DK2
NK6:
CJNE A, #0BH, NK7
MOV KEYBUF, #6 ; 按键(2,3) -> 键值6
LJMP DK2
NK7:
CJNE A, #07H, NK8
MOV KEYBUF, #7 ; 按键(2,4) -> 键值7
LJMP DK2
NK8:
NOP
DK2: ; 显示第二行按键
MOV A, KEYBUF
MOV DPTR, #TABLE
MOVC A, @A+DPTR
MOV P0, A
DK2A: ; 等待按键释放
MOV A, P3
ANL A, #0FH
XRL A, #0FH
JNZ DK2A
NOKEY2: ; 第二行无按键
; ********** 扫描第三行(P3.6)**********
MOV P3, #0FFH
CLR P3.6 ; 将P3.6置为低电平,选中第三行
MOV A, P3
ANL A, #0FH
XRL A, #0FH
JZ NOKEY3
LCALL DELY10MS
MOV A, P3
ANL A, #0FH
XRL A, #0FH
JZ NOKEY3
MOV A, P3
ANL A, #0FH
; 扫描第三行的按键
CJNE A, #0EH, NK9
MOV KEYBUF, #8 ; 按键(3,1) -> 键值8
LJMP DK3
NK9:
CJNE A, #0DH, NK10
MOV KEYBUF, #9 ; 按键(3,2) -> 键值9
LJMP DK3
NK10:
CJNE A, #0BH, NK11
MOV KEYBUF, #10 ; 按键(3,3) -> 键值10
LJMP DK3
NK11:
CJNE A, #07H, NK12
MOV KEYBUF, #11 ; 按键(3,4) -> 键值11
LJMP DK3
NK12:
NOP
DK3: ; 显示第三行按键
MOV A, KEYBUF
MOV DPTR, #TABLE
MOVC A, @A+DPTR
MOV P0, A
DK3A: ; 等待按键释放
MOV A, P3
ANL A, #0FH
XRL A, #0FH
JNZ DK3A
NOKEY3: ; 第三行无按键
; ********** 扫描第四行(P3.7)**********
MOV P3, #0FFH
CLR P3.7 ; 将P3.7置为低电平,选中第四行
MOV A, P3
ANL A, #0FH
XRL A, #0FH
JZ NOKEY4
LCALL DELY10MS
MOV A, P3
ANL A, #0FH
XRL A, #0FH
JZ NOKEY4
MOV A, P3
ANL A, #0FH
; 扫描第四行的按键
CJNE A, #0EH, NK13
MOV KEYBUF, #12 ; 按键(4,1) -> 键值12
LJMP DK4
NK13:
CJNE A, #0DH, NK14
MOV KEYBUF, #13 ; 按键(4,2) -> 键值13
LJMP DK4
NK14:
CJNE A, #0BH, NK15
MOV KEYBUF, #14 ; 按键(4,3) -> 键值14
LJMP DK4
NK15:
CJNE A, #07H, NK16
MOV KEYBUF, #15 ; 按键(4,4) -> 键值15
LJMP DK4
NK16:
NOP
DK4: ; 显示第四行按键
MOV A, KEYBUF
MOV DPTR, #TABLE
MOVC A, @A+DPTR
MOV P0, A
DK4A: ; 等待按键释放
MOV A, P3
ANL A, #0FH
XRL A, #0FH
JNZ DK4A
NOKEY4: ; 第四行无按键
LJMP WAIT ; 跳回主循环,重新开始扫描
; ********** 10ms延时子程序 **********
DELY10MS:
MOV R6, #10 ; 外层循环10次
D1:
MOV R7, #248 ; 内层循环248次
DJNZ R7, $ ; R7减1不为0则跳转到当前地址(空循环)
DJNZ R6, D1 ; R6减1不为0则跳转到D1
RET
; ********** 数码管段码表 **********
; 段码顺序:0-9, A, b, C, d, E, F
; 段位顺序:dp g f e d c b a
TABLE:
DB 3FH, 06H, 5BH, 4FH, 66H, 6DH, 7DH, 07H ; 0,1,2,3,4,5,6,7
DB 7FH, 6FH, 77H, 7CH, 39H, 5EH, 79H, 71H ; 8,9,A,b,C,d,E,F
END
5.015-定时计数器T0作定时应用技术(一)

5.1 电路仿真

5.2 仿真程序
查询法
c
/*
015-定时计数器T0作定时应用技术(一)
两个共阳数码管显示秒数(00-59)
*/
SECOND EQU 30H ; 定义秒计数变量地址为30H
TCOUNT EQU 31H ; 定义定时中断计数变量地址为31H
ORG 00H ; 程序从00H地址开始执行
START:
MOV SECOND, #00H ; 初始化秒计数为0
MOV TCOUNT, #00H ; 初始化定时中断计数为0
; 配置定时器0为16位定时器模式(模式1)
MOV TMOD, #01H ; TMOD=0000 0001,定时器0模式1,定时模式
; 设置定时器0初值,定时50ms
; 晶振频率假设为12MHz,机器周期1μs
; 定时50ms需要50000个机器周期
; 初值 = 65536 - 50000 = 15536 = 3CB0H
MOV TH0, #(65536-50000) / 256 ; 设置定时器高8位初值
MOV TL0, #(65536-50000) MOD 256 ; 设置定时器低8位初值
SETB TR0 ; 启动定时器0
DISP: ; 显示子程序:在数码管上显示秒数
MOV A, SECOND ; 将秒数加载到累加器A
; 将秒数拆分为十位和个位
MOV B, #10 ; 除数10
DIV AB ; A/B,商在A(十位),余数在B(个位)
; 显示十位数
MOV DPTR, #TABLE ; 设置数据指针指向段码表
MOVC A, @A+DPTR ; 查表获取十位数的段码
MOV P0, A ; 将十位数段码输出到P0口(控制十位数码管)
; 显示个位数
MOV A, B ; 将个位数加载到累加器A
MOVC A, @A+DPTR ; 查表获取个位数的段码
MOV P2, A ; 将个位数段码输出到P2口(控制个位数码管)
WAIT: ; 等待定时器溢出中断标志
JNB TF0, WAIT ; 如果TF0=0(定时器未溢出),继续等待
; 定时器溢出处理
CLR TF0 ; 清除定时器0溢出标志
; 重新设置定时器初值,准备下一次50ms定时
MOV TH0, #(65536-50000) / 256
MOV TL0, #(65536-50000) MOD 256
INC TCOUNT ; 定时中断计数加1
MOV A, TCOUNT ; 将中断计数值加载到累加器A
CJNE A, #20, NEXT ; 如果中断计数不等于20,跳转到NEXT
; 如果中断计数达到20次(20×50ms=1000ms=1秒)
MOV TCOUNT, #00H ; 重置中断计数
INC SECOND ; 秒数加1
MOV A, SECOND ; 将秒数加载到累加器A
CJNE A, #60, NEX ; 如果秒数不等于60,跳转到NEX
; 如果秒数达到60,重置为0
MOV SECOND, #00H ; 重置秒计数
NEX: ; 秒数更新后跳转点
LJMP DISP ; 跳转到显示子程序,更新显示
NEXT: ; 未满1秒跳转点
LJMP WAIT ; 跳转回等待,继续计数
; ********** 数码管段码表 **********
; 段码顺序:0,1,2,3,4,5,6,7,8,9
; 段位顺序:dp g f e d c b a
TABLE:
DB 3FH, 06H, 5BH, 4FH, 66H, 6DH, 7DH, 07H, 7FH, 6FH
; 对应显示:0,1,2,3,4,5,6,7,8,9
END
中断法
c
SECOND EQU 30H ; 定义秒计数变量地址为30H
TCOUNT EQU 31H ; 定义定时中断计数变量地址为31H
ORG 00H ; 程序从00H地址开始执行
LJMP START ; 跳转到主程序开始处
ORG 0BH ; 定时器0中断向量地址
LJMP INT0X ; 跳转到定时器0中断服务程序
START:
MOV SECOND, #00H ; 初始化秒计数为0
; 初始化显示:显示00
MOV A, SECOND ; 将秒数加载到累加器A
MOV B, #10 ; 除数10
DIV AB ; A/B,商在A(十位),余数在B(个位)
MOV DPTR, #TABLE ; 设置数据指针指向段码表
MOVC A, @A+DPTR ; 查表获取十位数的段码
MOV P0, A ; 将十位数段码输出到P0口(控制十位数码管)
MOV A, B ; 将个位数加载到累加器A
MOVC A, @A+DPTR ; 查表获取个位数的段码
MOV P2, A ; 将个位数段码输出到P2口(控制个位数码管)
; 初始化定时器相关变量
MOV TCOUNT, #00H ; 初始化定时中断计数为0
; 配置定时器0为16位定时器模式(模式1)
MOV TMOD, #01H ; TMOD=0000 0001,定时器0模式1,定时模式
; 设置定时器0初值,定时50ms
; 晶振频率假设为12MHz,机器周期1μs
; 定时50ms需要50000个机器周期
; 初值 = 65536 - 50000 = 15536 = 3CB0H
MOV TH0, #(65536-50000) / 256 ; 设置定时器高8位初值
MOV TL0, #(65536-50000) MOD 256 ; 设置定时器低8位初值
SETB TR0 ; 启动定时器0
SETB ET0 ; 允许定时器0中断
SETB EA ; 开启总中断允许
SJMP $ ; 原地循环,等待中断发生
INT0X: ; 定时器0中断服务程序
; 重新设置定时器初值,准备下一次50ms定时
MOV TH0, #(65536-50000) / 256
MOV TL0, #(65536-50000) MOD 256
INC TCOUNT ; 定时中断计数加1
MOV A, TCOUNT ; 将中断计数值加载到累加器A
CJNE A, #20, NEXT ; 如果中断计数不等于20,跳转到NEXT
; 如果中断计数达到20次(20×50ms=1000ms=1秒)
MOV TCOUNT, #00H ; 重置中断计数
INC SECOND ; 秒数加1
MOV A, SECOND ; 将秒数加载到累加器A
CJNE A, #60, NEX ; 如果秒数不等于60,跳转到NEX
; 如果秒数达到60,重置为0
MOV SECOND, #00H ; 重置秒计数
NEX: ; 秒数更新处理
; 更新数码管显示
MOV A, SECOND ; 将秒数加载到累加器A
MOV B, #10 ; 除数10
DIV AB ; A/B,商在A(十位),余数在B(个位)
MOV DPTR, #TABLE ; 设置数据指针指向段码表
MOVC A, @A+DPTR ; 查表获取十位数的段码
MOV P0, A ; 将十位数段码输出到P0口
MOV A, B ; 将个位数加载到累加器A
MOVC A, @A+DPTR ; 查表获取个位数的段码
MOV P2, A ; 将个位数段码输出到P2口
NEXT: ; 中断计数未满20次跳转点
RETI ; 中断返回
; ********** 数码管段码表(共阳数码管) **********
; 段码顺序:0,1,2,3,4,5,6,7,8,9
; 段位顺序:dp g f e d c b a
TABLE:
DB 3FH, 06H, 5BH, 4FH, 66H, 6DH, 7DH, 07H, 7FH, 6FH
; 对应显示:0,1,2,3,4,5,6,7,8,9
END
三、总结
今天通过几个简单的51汇编仿真,我们亲眼看到了代码如何驱动硬件。算是为"更深层次了解单片机工作原理"这个目标迈出了一小步。感谢观看,后续继续分享!
再次感谢你的观看!
