51汇编仿真proteus8.15学习篇三(附源码)

文章目录


一、前言

汇编语言是二进制指令的文本形式,与指令是一一对应的关系。比如,加法指令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汇编仿真,我们亲眼看到了代码如何驱动硬件。算是为"更深层次了解单片机工作原理"这个目标迈出了一小步。感谢观看,后续继续分享!

再次感谢你的观看!

相关推荐
爱吃大芒果2 小时前
Flutter for OpenHarmony核心组件学习: MaterialApp、Scaffold 两大基础组件以及有无状态组件
开发语言·学习·flutter
rannn_1112 小时前
【Javaweb学习|Day11】SpringBoot原理|配置优先级、Bean的管理、原理及源码分析
java·spring boot·后端·学习·javaweb
微软Dynamics 365培训2 小时前
长沙爱码士IT学院Dynamics 365 &Power Platform部分学习模块拆解
学习
马猴烧酒.2 小时前
智能协图云图库学习笔记day5
java·jvm·spring boot·笔记·学习·mvc
2501_933513042 小时前
Java后端开发者的AGI时代学习与职业路径策略
java·学习·agi
救救孩子把2 小时前
60-机器学习与大模型开发数学教程-5-7 学习率调度(warmup、余弦退火、OneCycle)
人工智能·学习·机器学习
程序员_小兵2 小时前
STM32之中断详解
c语言·stm32·单片机·嵌入式硬件·mcu
臭东西的学习笔记10 小时前
论文学习——机器学习引导的蛋白质工程
人工智能·学习·机器学习
List<String> error_P11 小时前
STM32窗口看门狗WWDG详解
stm32·单片机·嵌入式硬件·定时器