找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 53|回复: 1
收起左侧

几个MCU51单片机汇编语言设计

[复制链接]
ID:1155552 发表于 2025-6-29 09:57 | 显示全部楼层 |阅读模式
系统采用AT89S51,晶振为12MHz,独立、依次完成以下设计任务:
(1) 利用P1口,设计一份含4个独立键盘和4个独立发光二极管的硬件电路图,并设计晶振和复位电路,正确连接EA引脚。
(2) 设计一份(1)电路的键盘判断程序(不含键盘消除抖动功能),以及自定义功能的发光二极管显示程序。
(3) 设计一份通过P3.4口输出周期为1ms的方波(采用软件延时),以及通过P3.5口输出周期为1min的方波(采用定时中断)程序。
(4) 利用P1口,设计4×4行列式键盘电路,以及采用74HC164芯片,设计通过串行口连接方式连接4个7段数码管的电路。
(5) 在(4)电路中,设计一份键盘判断程序(包含键盘软件消除抖动功能)和显示学号后四位程序。
(6) 采用DAC0832设计一份D/A转换电路图,需要设计专门的基准电压。

led程序:
; 8051 汇编:按键(P1.0-P1.3)控制共阴极 LED(P1.4-P1.7)
; 功能:短按点亮对应 LED,长按(超 200ms)切换状态(亮/灭)
; 硬件:按键接 P1.0-P1.3(按下接地),共阴极 LED 接 P1.4-P1.7(阳极串电阻接 P1)

ORG 0000H
    LJMP MAIN

; 延时消抖(约 20ms@12MHz)
DELAY_20MS:
    MOV R7, #20
DEL1:
    MOV R6, #248
DEL2:
    DJNZ R6, DEL2
    DJNZ R7, DEL1
    RET

; 主程序
MAIN:
    MOV P1, #0FH       ; P1 低四位上拉输入,高四位初始灭(共阴极 LED 高电平灭)
   
KEY_SCAN:
    ; 检测 P1.0 按键
    MOV A, P1
    ANL A, #01H        ; 屏蔽其他位,取 P1.0
    JNZ CHECK_P11      ; 未按下,跳检测 P1.1
   
    ACALL DELAY_20MS   ; 消抖
    MOV A, P1
    ANL A, #01H
    JNZ CHECK_P11      ; 抖动,跳检测 P1.1
   
    ; 确认 P1.0 按下:翻转 P1.4 状态(共阴极 LED 高电平灭、低电平亮)
    CPL P1.4         
   
    ; 等待释放(长按不影响其他按键,但需等释放再扫其他键)
WAIT_RELEASE0:
    MOV A, P1
    ANL A, #01H
    JZ WAIT_RELEASE0   ; 未释放则等待
    ACALL DELAY_20MS   ; 释放消抖
    SJMP CHECK_P11     ; 扫下一个键

CHECK_P11:
    ; 检测 P1.1 按键(逻辑同 P1.0)
    MOV A, P1
    ANL A, #02H
    JNZ CHECK_P12
   
    ACALL DELAY_20MS
    MOV A, P1
    ANL A, #02H
    JNZ CHECK_P12
   
    CPL P1.5         
   
WAIT_RELEASE1:
    MOV A, P1
    ANL A, #02H
    JZ WAIT_RELEASE1
    ACALL DELAY_20MS
    SJMP CHECK_P12

CHECK_P12:
    ; 检测 P1.2 按键(逻辑同 P1.0)
    MOV A, P1
    ANL A, #04H
    JNZ CHECK_P13
   
    ACALL DELAY_20MS
    MOV A, P1
    ANL A, #04H
    JNZ CHECK_P12
   
    CPL P1.6         
   
WAIT_RELEASE2:
    MOV A, P1
    ANL A, #04H
    JZ WAIT_RELEASE2
    ACALL DELAY_20MS
    SJMP CHECK_P13

CHECK_P13:
    ; 检测 P1.3 按键(逻辑同 P1.0)
    MOV A, P1
    ANL A, #08H
    JNZ KEY_SCAN       ; 未按下,回到总扫描
   
    ACALL DELAY_20MS
    MOV A, P1
    ANL A, #08H
    JNZ KEY_SCAN       ; 抖动,回到总扫描
   
    CPL P1.7         
   
WAIT_RELEASE3:
    MOV A, P1
    ANL A, #08H
    JZ WAIT_RELEASE3
    ACALL DELAY_20MS
    SJMP KEY_SCAN      ; 扫完所有键,回到总扫描

END


矩阵键盘程序:
;====================================================================
; 4x4矩阵键盘扫描与数码管显示程序
; 硬件连接:
;   P1.0-P1.3: 键盘行输出
;   P1.4-P1.7: 键盘列输入
;   P3.2: INT0(前两列按键中断)
;   P3.3: INT1(后两列按键中断)
;   P3.1: TXD(串口输出,驱动数码管)
;====================================================================

        ORG     0000H        ; 设置程序起始地址为0000H(复位向量)
        LJMP    MAIN        ; 无条件跳转到主程序MAIN处执行

        ORG     0003H        ; 设置外部中断0(INT0)向量地址
        LJMP    INT0_HANDLER ; 当发生INT0中断时,跳转到INT0_HANDLER处理

        ORG     0013H        ; 设置外部中断1(INT1)向量地址
        LJMP    INT1_HANDLER ; 当发生INT1中断时,跳转到INT1_HANDLER处理

;====================================================================
; 主程序
;====================================================================
MAIN:   MOV     SP, #70H      ; 设置堆栈指针SP为70H(避免覆盖工作寄存器区)
        MOV     IE, #85H      ; 开中断控制寄存器:
                           ;   EA=1(开总中断),
                           ;   EX0=1(开INT0中断),
                           ;   EX1=1(开INT1中断)
        MOV     TCON, #05H    ; 设置外部中断触发方式:
                           ;   IT0=1(INT0下降沿触发),
                           ;   IT1=1(INT1下降沿触发)
        MOV     SCON, #00H    ; 配置串口为模式0(同步移位寄存器方式)
        MOV     P1, #0F0H     ; 初始化P1口:
                           ;   P1.0-P1.3(低4位)=0(输出行扫描信号)
                           ;   P1.4-P1.7(高4位)=1(输入列检测信号)

MAIN_LOOP:
        SJMP    MAIN_LOOP     ; 无限循环(所有工作在中断处理中完成)

;====================================================================
; 前两列按键中断处理(INT0)
;====================================================================
INT0_HANDLER:
        PUSH    ACC           ; 保护累加器A的值到堆栈
        PUSH    PSW           ; 保护程序状态字PSW到堆栈
        PUSH    DPH           ; 保护数据指针高8位DPH到堆栈
        PUSH    DPL           ; 保护数据指针低8位DPL到堆栈

        LCALL   DELAY_10MS    ; 调用延时子程序消抖(约10ms)

        JB      P3.2, INT0_EXIT ; 检查P3.2是否仍为低电平:
                           ;   若为高电平,说明是按键抖动,跳转到INT0_EXIT返回

        MOV     R0, #0        ; 初始化行计数器R0=0(从第1行开始扫描)
        MOV     R1, #0FEH     ; 初始化行扫描码R1=11111110B(选中第1行)

KEY_SCAN_0:
        MOV     P1, R1        ; 将行扫描码输出到P1口(低4位)
        JNB     P1.4, KEY_00  ; 检测第1列(P1.4):
                           ;   若为低电平,说明第1列有按键按下,跳转到KEY_00
        JNB     P1.5, KEY_01  ; 检测第2列(P1.5):
                           ;   若为低电平,说明第2列有按键按下,跳转到KEY_01

        MOV     A, R1         ; 将当前行扫描码复制到累加器A
        RL      A             ; 将A循环左移一位(生成下一行扫描码)
                           ;   例如: 11111110B → 11111101B → 11111011B → 11110111B
        MOV     R1, A         ; 将新的行扫描码保存回R1
        INC     R0            ; 行计数器R0加1(指向下一行)
        CJNE    R0, #4, KEY_SCAN_0 ; 比较R0与4:
                           ;   若不等于4,说明未扫描完所有行,继续扫描下一行

INT0_EXIT:
        POP     DPL           ; 从堆栈恢复数据指针低8位DPL
        POP     DPH           ; 从堆栈恢复数据指针高8位DPH
        POP     PSW           ; 从堆栈恢复程序状态字PSW
        POP     ACC           ; 从堆栈恢复累加器A
        RETI                  ; 中断返回,恢复PC值并开中断

;====================================================================
; 后两列按键中断处理(INT1)
;====================================================================
INT1_HANDLER:
        PUSH    ACC           ; 保护累加器A
        PUSH    PSW           ; 保护程序状态字PSW
        PUSH    DPH           ; 保护数据指针高8位DPH
        PUSH    DPL           ; 保护数据指针低8位DPL

        LCALL   DELAY_10MS    ; 调用延时子程序消抖

        JB      P3.3, INT1_EXIT ; 检查P3.3是否仍为低电平
                           ;   若为高电平,说明是抖动,跳转到INT1_EXIT返回

        MOV     R0, #0        ; 初始化行计数器R0=0
        MOV     R1, #0FEH     ; 初始化行扫描码R1=11111110B

KEY_SCAN_1:
        MOV     P1, R1        ; 输出行扫描码到P1口
        JNB     P1.6, KEY_10  ; 检测第3列(P1.6):
                           ;   若为低电平,跳转到KEY_10
        JNB     P1.7, KEY_11  ; 检测第4列(P1.7):
                           ;   若为低电平,跳转到KEY_11

        MOV     A, R1         ; 复制当前行扫描码到A
        RL      A             ; 循环左移生成下一行扫描码
        MOV     R1, A         ; 保存新的行扫描码
        INC     R0            ; 行计数器加1
        CJNE    R0, #4, KEY_SCAN_1 ; 未扫描完4行则继续扫描

INT1_EXIT:
        POP     DPL           ; 恢复DPL
        POP     DPH           ; 恢复DPH
        POP     PSW           ; 恢复PSW
        POP     ACC           ; 恢复ACC
        RETI                  ; 中断返回

;====================================================================
; 按键处理与数码管显示
;====================================================================
KEY_00: MOV     A, R0         ; 将行号(R0)复制到累加器A
        MOV     B, #4         ; 将常数4放入寄存器B
        MUL     AB            ; 执行乘法: A = A × B (行号×4)
        MOV     R2, A         ; 将计算结果(行号×4)保存到R2
        SJMP    DISPLAY_KEY   ; 跳转到DISPLAY_KEY显示按键值

KEY_01: MOV     A, R0         ; 复制行号到A
        MOV     B, #4         ; 常数4放入B
        MUL     AB            ; 行号×4
        ADD     A, #1         ; 加1(第2列对应的偏移量)
        MOV     R2, A         ; 保存按键值到R2
        SJMP    DISPLAY_KEY   ; 跳转到显示处理

KEY_10: MOV     A, R0         ; 复制行号到A
        MOV     B, #4         ; 常数4放入B
        MUL     AB            ; 行号×4
        ADD     A, #2         ; 加2(第3列对应的偏移量)
        MOV     R2, A         ; 保存按键值到R2
        SJMP    DISPLAY_KEY   ; 跳转到显示处理

KEY_11: MOV     A, R0         ; 复制行号到A
        MOV     B, #4         ; 常数4放入B
        MUL     AB            ; 行号×4
        ADD     A, #3         ; 加3(第4列对应的偏移量)
        MOV     R2, A         ; 保存按键值到R2

DISPLAY_KEY:
        MOV     A, R2         ; 将按键值从R2复制到累加器A
        MOV     DPTR, #SEG_TAB ; 将段码表的基地址存入数据指针DPTR
        MOVC    A, @A+DPTR    ; 查表获取按键值对应的段码
                           ;   A = 段码表[按键值]
        MOV     SBUF, A       ; 将段码发送到串口数据缓冲区SBUF
                           ;   触发串口发送过程
        JNB     TI, $         ; 等待发送完成标志位TI置位
                           ;   $表示当前地址,形成原地循环
        CLR     TI            ; 手动清除发送完成标志位TI
        SJMP    INT0_EXIT     ; 跳转到INT0_EXIT恢复现场并返回

;====================================================================
; 延时子程序(约10ms @12MHz晶振)
;====================================================================
DELAY_10MS:
        MOV     R7, #20       ; 设置外层循环计数器R7=20
DELAY_LOOP1:
        MOV     R6, #248      ; 设置内层循环计数器R6=248
DELAY_LOOP2:
        DJNZ    R6, DELAY_LOOP2 ; 内层循环: R6减1,若不为0则继续循环
        DJNZ    R7, DELAY_LOOP1 ; 外层循环: R7减1,若不为0则继续循环
        RET                   ; 子程序返回

;====================================================================
; 共阳极数码管段码表(0-F)
;====================================================================
SEG_TAB:
        DB      0C0H, 0F9H, 0A4H, 0B0H, 99H, 92H, 82H, 0F8H ; 0-7的段码
        DB      80H, 90H, 88H, 83H, 0C6H, 0A1H, 86H, 8EH     ; 8-F的段码

        END                     ; 程序结束标记

DA转换程序:
ORG 0000H
    LJMP MAIN
    ORG 100H
;程序初始化
MAIN:   MOV A,#00H               ;赋初值
UP:     MOVX @DPTR, A            ;输出  4机器周期
        INC A                    ;A的值加1 1机器周期
        CJNE A ,0FFH ,UP          ;如果上次计算的结果不是0,就跳转 当A加到255,再变成0之后就会下降。
DOWN:   DEC A                    ;同上
        MOVX @DPTR, A            ;4个机器周期
        CJNE A ,#00H , DOWN      ;条件满足时使用4个机器周期,条件不满足时使用3个机器周期
        INC A                    ;1个机器周期
        SJMP UP                  ;2个机器周期

        END
屏幕截图 2025-06-29 095233.png

矩阵键盘

矩阵键盘

DA转换

DA转换

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

ID:1155590 发表于 2025-6-29 21:40 | 显示全部楼层
嗯,刚好学到这个,很好的参考
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表