基于51单片机的八位数码管测温时钟实例(汇编)

本例提供了一个基于汇编程序实现的51单片机实例,通过八个七段数码管显示时、分、秒的走时,能设置闹铃,能显示当前温度。以下是本例所依赖的电路原理图,系统使用11.0592MHz晶振。

以上是八个数码管的电路,段码和位码共用了P0端口,使用两片锁存器芯片来分时实现传送数据。

以上是十六个按键的矩阵键盘,使用了P3口来扫描并获取键值。

以上是蜂鸣器和温度传感器电路。蜂鸣器通过PNP三极管驱动,P2.3为低电平时响。温度传感器使用的是单总线DS18B20芯片,数据线接在P2.2口。

本例一共使用了六个按键(S1~S6),其功能如下:

S1:增加

S2:减小

S3:调校(包括时钟和闹钟,调校时相应的位会闪烁)

S4:时钟/闹铃(显示切换)

S5:时钟/温度(显示切换,在闹铃显示状态不响应)

S6:闹铃开/关(闹铃生效时第三位数码管会显示小数点)

当检测到DB18B20时点亮P1.7的LED,未检测到点亮P1.0,测温时点亮P1.6,每次温度转换完成取反P1.5引脚电平。

以下是本例的全部汇编代码,为了方便大家学习参考,每行代码都进行了详细注释。

KEY            EQU        7FH            ;键值
H_S            EQU        7EH            ;时的十位
H_G            EQU        7DH            ;时的个位
M_S            EQU        7BH            ;分的十位
M_G            EQU        7AH            ;分的个位
S_S            EQU        78H            ;秒的十位
S_G            EQU        77H            ;秒的个位
CNT            EQU        76H            ;调校闪烁速度控制
SCT            EQU        75H            ;调校状态(时、分、秒、不调校)
_H             EQU        74H            ;
_M             EQU        73H            ;
_S             EQU        72H            ;
A_H_S          EQU        71H            ;闹铃时的十位
A_H_G          EQU        70H            ;闹铃时的个位
A_M_S          EQU        6EH            ;闹铃分的十位
A_M_G          EQU        6DH            ;闹铃分的个位
A_S_S          EQU        6BH            ;闹铃秒的十位
A_S_G          EQU        6AH            ;闹铃秒的个位
A_SCT          EQU        69H            ;闹铃调校状态(时、分、秒、不调校)
A_H            EQU        68H            ;闹铃时
A_M            EQU        67H            ;闹铃分
A_S            EQU        66H            ;闹铃秒
T_S            EQU        65H            ;温度的十位
T_G            EQU        64H            ;温度的个位
T_L            EQU        23H            ;温度低位数据(位寻址区)
T_H            EQU        22H            ;温度高位数据(位寻址区)
FLG_H          BIT        00H            ;时闪烁标志
FLG_M          BIT        01H            ;分闪烁标志
FLG_S          BIT        02H            ;秒闪烁标志
AFLG_H         BIT        03H            ;闹铃时闪烁标志
AFLG_M         BIT        04H            ;闹铃分闪烁标志
AFLG_S         BIT        05H            ;闹铃秒闪烁标志
A_SHOW_FLAG    BIT        06H            ;显示时间还是闹铃标志
DS18B20        BIT        07H            ;发现DS18B20标志
TMP            BIT        08H            ;温度转换延时标志
T_SHOW_FLAG    BIT        09H            ;温度显示标志
A_SET          BIT        0AH            ;闹铃控制开关
;*****************************程序开始*********************************
    ORG        0000H
    AJMP       START
    ORG        000BH
    AJMP       T0_SRV              ;T0中断产生时钟
    ORG        001BH
    AJMP       T1_SVR              ;T1中断实现数码管扫描
    ORG        0030H
START:    
    MOV        SP, #30H            ;初始化堆栈指针
    MOV        7CH, #16            ;显示“-”
    MOV        79H, #16            ;显示“-”
    MOV        6FH, #17            ;显示黑屏
    MOV        6CH, #17            ;显示黑屏
    MOV        SCT, #00H           ;调校状态初始为不调校
    MOV        A_SCT, #00H         ;闹铃调校状态初始为不调校
    MOV        R0, #7EH            ;显示内容首地址
    MOV        R1, #71H            ;闹铃内容首地址
    MOV        R2, #0FEH           ;动态扫描控制
    MOV        R3, #00H            ;软件计数器初始值
    MOV        R7, #09H            ;按键响应时间控制
    MOV        _S, #00H            ;秒初始值
    MOV        _M, #00H            ;分初始值
    MOV        _H, #00H            ;时初始值    
    CLR        FLG_H               ;时初始状态不闪烁
    CLR        FLG_M               ;分初始状态不闪烁
    CLR        FLG_S               ;秒初始状态不闪烁
    CLR        AFLG_H              ;闹铃时初始状态不闪烁
    CLR        AFLG_M              ;闹铃分初始状态不闪烁
    CLR        AFLG_S              ;闹铃秒初始状态不闪烁
    CLR        A_SHOW_FLAG         ;初始状态显示时钟(不显示闹铃)
    CLR        TMP                 ;初始状态为未读取温度
    CLR        T_SHOW_FLAG         ;初始状态为不显示温度
    CLR        A_SET               ;初始状态为闹铃关闭
    MOV        TMOD, #11H          ;T0、T1工作在16位模式下
    MOV        TH0, #4CH
    MOV        TL0, #00H           ;50ms的初始值
    MOV        TH1, HIGH(65536-2000)
    MOV        TL1, LOW(65536-2000);2ms初始值
    MOV        IE, #8AH            ;使能T0、T1及总中断
    SETB       TR0                 ;启动T0
    SETB       TR1                 ;启动T1
;*****************************主循环开始*******************************
LOOP:
;==============================判断按键6===============================
    MOV        A, KEY              ;读取键值
    CJNE       A, #06H, T_N        ;判断键值是否为6,为6顺序执行,否则跳转
    MOV        KEY, #00H           ;键值清零
    DJNZ       R7, T_N             ;连续读取9次键值,若都为6证明该按键被按下
    MOV        R7, #09H            ;恢复按键响应时间控制初始值
    JB         A_SHOW_FLAG, T_NS   ;若在闹铃显示状态下则闹铃设置不生效
    JB         T_SHOW_FLAG, T_NS   ;若在温度显示状态下则闹铃设置不生效
    MOV        A, SCT
    CJNE       A, #00, T_NS        ;若在时钟调校状态下则闹铃设置不生效
    CPL        A_SET               ;按键按下开/关闹铃交替
    JB         A_SET, T_N          ;判断是否设置为闹铃响,若不是则不显示“.”
    MOV        7CH, #16            ;恢复显示“-”
;==============================判断按键5===============================
T_N:
    MOV        A, KEY              ;读取键值
    CJNE       A, #05H, T_NS       ;判断键值是否为5,为5顺序执行,否则跳转
    MOV        KEY, #00H           ;键值清零
    DJNZ       R7, ALM_SHOW        ;连续读取9次键值,若都为5证明该按键被按下
    MOV        R7, #09H            ;恢复按键响应时间控制初始值
    JB         A_SHOW_FLAG, T_NS   ;若在闹铃显示状态下则温度显示不生效
    MOV        SCT, #00H           ;按下键则时间调校结束
    CPL        T_SHOW_FLAG         ;按键按下时间/温度功能交替    
;===========================时间显示状态===============================
;---------------------------判断按键3----------------------------------
T_NS:
    JB        A_SHOW_FLAG, N_A     ;判断闹铃显示标志,时钟则顺序执行,闹铃则跳转
    MOV       A, KEY               ;读取键值
    CJNE      A, #03H, ALM_SHOW    ;判断键值是否为3,为3顺序执行,否则跳转
    MOV       KEY, #00H            ;键值清零
    DJNZ      R7, ALM_SHOW         ;连续读取9次键值,若都为3证明该按键被按下
    MOV       R7, #09H             ;恢复按键响应时间控制初始值
    JB        T_SHOW_FLAG, LOOP    ;若在温度显示状态下则按键3不生效
    INC       SCT                  ;调校状态加1
    MOV       A, SCT
    CJNE      A, #4,ALM_SHOW       ;判断调校状态是否已到4
    MOV       SCT, #00H            ;调校状态恢复0
    AJMP      ALM_SHOW             ;跳转到下一个按键状态判别
;===========================闹铃显示状态===============================
;---------------------------判断按键3----------------------------------                 
N_A:                               ;在闹铃状态调校
    MOV       A, KEY               ;读取键值
    CJNE      A, #03H, ALM_SHOW    ;判断键值是否为3,为3顺序执行,否则跳转
    MOV       KEY, #00H            ;键值清零
    DJNZ      R7, ALM_SHOW         ;连续读取9次键值,若都为3证明该按键被按下
    MOV       R7, #09H             ;恢复按键响应时间控制初始值
    INC       A_SCT                ;闹铃调校状态加1
    MOV       A, A_SCT
    CJNE      A, #4, ALM_SHOW      ;判断闹铃调校状态是否已到4
    MOV       A_SCT, #00H          ;闹铃调校状态恢复0
;==============================判断按键4================================
ALM_SHOW:                         
    MOV       A, KEY               ;读取键值
    CJNE      A, #04H, H_N1        ;判断键值是否为4,为4顺序执行,否则跳转
    MOV       KEY, #00H            ;键值清零
    DJNZ      R7, H_N1             ;连续读取9次键值,若都为4证明该按键被按下
    MOV       R7, #09H             ;恢复按键响应时间控制初始值
    JB        T_SHOW_FLAG, L_GO    ;若在温度显示状态下则按键4不生效
    CPL       A_SHOW_FLAG          ;按键按下时间/闹铃功能交替
    JB        A_SHOW_FLAG, A_OK    ;判断闹铃显示标志,时钟则顺序执行,闹铃则跳转
    MOV       A_SCT, #00H          ;按下时闹铃调校结束
    AJMP      H_N1                 ;跳转到下一个按键状态判别
A_OK:
    MOV       SCT, #00H            ;按下时间调校结束
    AJMP      H_N1                 ;跳转到下一个按键状态判别
L_GO:
    AJMP      LOOP                 ;跳回主循环开始
;==========================以下为“时”调校状态===========================
;-----------------------------判断按键1---------------------------------        
H_N1:
    JB        A_SHOW_FLAG, GO_AH_N1;判断闹铃显示标志,时钟则顺序执行,闹铃则跳转(接力跳)    
    MOV       A, SCT               ;获取当前调校状态
    CJNE      A, #01H, M_N1        ;当前调校“时”
    MOV       A, KEY               ;读取键值
    CJNE      A, #01H, H_N2        ;判断键值是否为1,为1顺序执行,否则跳转
    MOV       KEY, #00H            ;键值清零
    DJNZ      R7, H_N2             ;连续读取9次键值,若都为1证明该按键被按下
    INC       _H                   ;“时”加一
    MOV       A, _H
    CJNE      A, #24,H_INC         ;判断“时”是否加到24
    MOV       _H, #00H             ;到24则恢复0
H_INC:
    MOV       R7, #09H             ;恢复按键响应时间控制初始值
;----------------------------判断按键2----------------------------------
H_N2:
    MOV        A, KEY              ;读取键值
    CJNE       A, #02H, M_N1       ;判断键值是否为2,为2顺序执行,否则跳转
    MOV        KEY, #00H           ;键值清零
    DJNZ       R7, M_N1            ;连续读取9次键值,若都为2证明该按键被按下
    DEC        _H                  ;“时”减一
    MOV        A, _H
    CJNE       A, #0FFH,H_DEC      ;判断“时”是否减到负值
    MOV        _H, #23             ;到负值则恢复23
H_DEC:
    MOV        R7, #09H            ;恢复按键响应时间控制初始值
    AJMP       M_N1                ;跳转到下一个按键状态判别
GO_AH_N1:
    AJMP       AH_N1               ;跳转接力
;==========================以下为“分”调校状态===========================
;----------------------------判断按键1----------------------------------
M_N1:
    MOV        A, SCT              ;获取当前调校状态
    CJNE       A, #02H, S_N1       ;当前调校“分”
    MOV        A, KEY              ;读取键值
    CJNE       A, #01H, M_N2       ;判断键值是否为1,为1顺序执行,否则跳转
    MOV        KEY, #00H           ;键值清零
    DJNZ       R7, M_N2            ;连续读取9次键值,若都为1证明该按键被按下
    INC        _M                  ;“分”加一
    MOV        A, _M
    CJNE       A, #60,M_INC        ;判断“分”是否加到60
    MOV        _M, #00H            ;到60则恢复0
M_INC:
    MOV        R7, #09H            ;恢复按键响应时间控制初始值
;----------------------------判断按键2----------------------------------
M_N2:
    MOV        A, KEY              ;读取键值
    CJNE       A, #02H, S_N1       ;判断键值是否为2,为2顺序执行,否则跳转
    MOV        KEY, #00H           ;键值清零
    DJNZ       R7, S_N1            ;连续读取9次键值,若都为2证明该按键被按下
    DEC        _M                  ;“分”减一
    MOV        A, _M
    CJNE       A, #0FFH,M_DEC      ;判断“分”是否减到负值
    MOV        _M, #59             ;到负值则恢复59
M_DEC:
    MOV        R7, #09H            ;恢复按键响应时间控制初始值
;==========================以下为“秒”调校状态===========================
;----------------------------判断按键1----------------------------------
S_N1:
    MOV        A, SCT               ;获取当前调校状态
    CJNE       A, #03H, GO_ON       ;当前调校“秒”
    MOV        A, KEY               ;读取键值
    CJNE       A, #01H, S_N2        ;判断键值是否为1,为1顺序执行,否则跳转
    MOV        KEY, #00H            ;键值清零
    DJNZ       R7, S_N2             ;连续读取9次键值,若都为1证明该按键被按下
    INC        _S                   ;“秒”加一
    MOV        A, _S
    CJNE       A, #60,S_INC         ;判断“秒”是否加到60
    MOV        _S, #00H             ;到60则恢复0
S_INC:
    MOV        R7, #09H             ;恢复按键响应时间控制初始值
;---------------------------判断按键2----------------------------------
S_N2:
    MOV        A, KEY               ;读取键值
    CJNE       A, #02H, GO_ON       ;判断键值是否为2,为2顺序执行,否则跳转
    MOV        KEY, #00H            ;键值清零
    DJNZ       R7, GO_ON            ;连续读取9次键值,若都为2证明该按键被按下
    DEC        _S                   ;“秒”减一
    MOV        A, _S
    CJNE       A, #0FFH,S_DEC       ;判断“秒”是否减到负值
    MOV        _S, #59              ;到负值则恢复59
S_DEC:
    MOV        R7, #09H             ;恢复按键响应时间控制初始值
GO_ON:
    AJMP       CLOCK                ;跳转到时间显示拆分程序
;======================以下为闹铃“时”调校状态===========================
;-----------------------------判断按键1---------------------------------
AH_N1:
    MOV        A, A_SCT            ;获取当前闹铃调校状态
    CJNE       A, #01H, AM_N1      ;当前闹铃调校“时”
    MOV        A, KEY              ;读取键值
    CJNE       A, #01H, AH_N2      ;判断键值是否为1,为1顺序执行,否则跳转
    MOV        KEY, #00H           ;键值清零
    DJNZ       R7, AH_N2           ;连续读取9次键值,若都为1证明该按键被按下
    INC        A_H                 ;闹铃“时”加一
    MOV        A, A_H
    CJNE       A, #24,AH_INC       ;判断闹铃“时”是否加到24
    MOV        A_H, #00H           ;到24则恢复0
AH_INC:
    MOV        R7, #09H            ;恢复按键响应时间控制初始值
;----------------------------判断按键2----------------------------------
AH_N2:
    MOV        A, KEY              ;读取键值
    CJNE       A, #02H, AM_N1      ;判断键值是否为2,为2顺序执行,否则跳转
    MOV        KEY, #00H           ;键值清零
    DJNZ       R7, AM_N1           ;连续读取9次键值,若都为2证明该按键被按下
    DEC        A_H                 ;闹铃“时”减一
    MOV        A, A_H
    CJNE       A, #0FFH, AH_DEC    ;判断闹铃“时”是否减到负值
    MOV        A_H, #23            ;到负值则恢复23
AH_DEC:
    MOV        R7, #09H            ;恢复按键响应时间控制初始值
;======================以下为闹铃“分”调校状态===========================
;-----------------------------判断按键1---------------------------------
AM_N1:
    MOV        A, A_SCT            ;获取当前闹铃调校状态
    CJNE       A, #02H, AS_N1      ;当前闹铃调校“分”
    MOV        A, KEY              ;读取键值
    CJNE       A, #01H, AM_N2      ;判断键值是否为1,为1顺序执行,否则跳转
    MOV        KEY, #00H           ;键值清零
    DJNZ       R7, AM_N2           ;连续读取9次键值,若都为1证明该按键被按下
    INC        A_M                 ;闹铃“分”加一
    MOV        A, A_M
    CJNE       A, #60,AM_INC       ;判断闹铃“分”是否加到60
    MOV        A_M, #00H           ;到60则恢复0
AM_INC:
    MOV        R7, #09H            ;恢复按键响应时间控制初始值
;----------------------------判断按键2----------------------------------
AM_N2:
    MOV        A, KEY              ;读取键值
    CJNE       A, #02H, AS_N1      ;判断键值是否为2,为2顺序执行,否则跳转
    MOV        KEY, #00H           ;键值清零
    DJNZ       R7, AS_N1           ;连续读取9次键值,若都为2证明该按键被按下
    DEC        A_M                 ;闹铃“分”减一
    MOV        A, A_M
    CJNE       A, #0FFH, AM_DEC    ;判断闹铃“分”是否减到负值
    MOV        A_M, #59            ;到负值则恢复59
AM_DEC:
    MOV        R7, #09H            ;恢复按键响应时间控制初始值
;======================以下为闹铃“秒”调校状态===========================
;-----------------------------判断按键1---------------------------------
AS_N1:
    MOV        A, A_SCT            ;获取当前闹铃调校状态
    CJNE       A, #03H, CLOCK      ;当前闹铃调校“秒”
    MOV        A, KEY              ;读取键值
    CJNE       A, #01H, AS_N2      ;判断键值是否为1,为1顺序执行,否则跳转
    MOV        KEY, #00H           ;键值清零
    DJNZ       R7, AS_N2           ;连续读取9次键值,若都为1证明该按键被按下
    INC        A_S                 ;闹铃“秒”加一
    MOV        A, A_S
    CJNE       A, #60,AS_INC       ;判断闹铃“秒”是否加到60
    MOV        A_S, #00H           ;到60则恢复0
AS_INC:
    MOV        R7, #09H            ;恢复按键响应时间控制初始值
;----------------------------判断按键2----------------------------------
AS_N2:
    MOV        A, KEY              ;读取键值
    CJNE       A, #02H, CLOCK      ;判断键值是否为2,为2顺序执行,否则跳转
    MOV        KEY, #00H           ;键值清零
    DJNZ       R7, CLOCK           ;连续读取9次键值,若都为2证明该按键被按下
    DEC        A_S                 ;闹铃“秒”减一
    MOV        A, A_S
    CJNE       A, #0FFH, AS_DEC    ;判断闹铃“秒”是否减到负值
    MOV        A_S, #59            ;到负值则恢复59
AS_DEC:
    MOV        R7, #09H            ;恢复按键响应时间控制初始值
;========================以下为时间显示拆分============================
;----------------------------“秒”拆分----------------------------------
CLOCK:
    JB         A_SHOW_FLAG, ALARM  ;判断闹铃显示标志,时钟则顺序执行,闹铃则跳转
    MOV        A, _S               ;读取“秒”
    MOV        B, #10              ;加载被除数10
    DIV        AB                  ;“秒”除以10
    JB         FLG_S, S_OFF        ;判断秒显示标志,若为1则显示黑屏,否则正常显示
    MOV        S_S, A              ;十位数部分
    MOV        S_G, B              ;个位数部分
    AJMP       M_ON                ;跳到“分”拆分部分
S_OFF:
    MOV        S_S, #17            ;显示黑屏
    MOV        S_G, #17            ;显示黑屏
;----------------------------“分”拆分----------------------------------
M_ON:
    MOV        A, _M               ;读取“分”
    MOV        B, #10              ;加载被除数10
    DIV        AB                  ;“分”除以10
    JB         FLG_M, M_OFF        ;判断秒显示标志,若为1则显示黑屏,否则正常显示
    MOV        M_S, A              ;十位数部分
    MOV        M_G, B              ;个位数部分
    AJMP       H_ON                ;跳到“时”拆分部分
M_OFF:
    MOV        M_S, #17            ;显示黑屏
    MOV        M_G, #17            ;显示黑屏
;----------------------------“时”拆分----------------------------------
H_ON:    
    MOV        A, _H               ;读取“时”
    MOV        B, #10              ;加载被除数10
    DIV        AB                  ;“时”除以10
    JB         FLG_H, H_OFF        ;判断秒显示标志,若为1则显示黑屏,否则正常显示
    MOV        H_S, A              ;十位数部分
    MOV        H_G, B              ;个位数部分
    AJMP       LOOP                ;跳回主循环开始
H_OFF:
    MOV        H_S, #17            ;显示黑屏
    MOV        H_G, #17            ;显示黑屏
    AJMP       LOOP                ;跳回主循环开始
;========================以下为闹铃显示拆分============================
;----------------------------闹铃“秒”拆分------------------------------
ALARM:
    MOV        A, A_S              ;读取闹铃“秒”
    MOV        B, #10              ;加载被除数10
    DIV        AB                  ;闹铃“秒”除以10
    JB         AFLG_S, AS_OFF      ;判断秒显示标志,若为1则显示黑屏,否则正常显示
    MOV        A_S_S, A            ;十位数部分
    MOV        A_S_G, B            ;个位数部分
    AJMP       AM_ON               ;跳到闹铃“分”拆分部分
AS_OFF:
    MOV        A_S_S, #17          ;显示黑屏
    MOV        A_S_G, #17          ;显示黑屏
;----------------------------闹铃“分”拆分------------------------------
AM_ON:
    MOV        A, A_M              ;读取闹铃“分”
    MOV        B, #10              ;加载被除数10
    DIV        AB                  ;闹铃“分”除以10
    JB         AFLG_M, AM_OFF      ;判断秒显示标志,若为1则显示黑屏,否则正常显示
    MOV        A_M_S, A            ;十位数部分
    MOV        A_M_G, B            ;个位数部分
    AJMP       AH_ON               ;跳到闹铃“时”拆分部分
AM_OFF:
    MOV        A_M_S, #17          ;显示黑屏
    MOV        A_M_G, #17          ;显示黑屏
;----------------------------闹铃“时”拆分------------------------------
AH_ON:    
    MOV        A, A_H              ;读取闹铃“时”
    MOV        B, #10              ;加载被除数10
    DIV        AB                  ;闹铃“时”除以10
    JB         AFLG_H, AH_OFF      ;判断秒显示标志,若为1则显示黑屏,否则正常显示
    MOV        A_H_S, A            ;十位数部分
    MOV        A_H_G, B            ;个位数部分
    AJMP       LOOP                ;跳回主循环开始
AH_OFF:
    MOV        A_H_S, #17          ;显示黑屏
    MOV        A_H_G, #17          ;显示黑屏                                     
    AJMP       LOOP                ;跳回主循环开始
;*****************************定时器T1********************************    
T1_SVR:
    PUSH       ACC                 ;累加器压栈
    PUSH       PSW                 ;程序状态字压栈
    MOV        TH1, HIGH(65536-2000)
    MOV        TL1, LOW(65536-2000);重载2ms初始值    
    MOV        A, R2               ;读取当前的位扫描值
    MOV        P0, A               ;输出当前位扫描值
    SETB       P2.7                ;设置位锁存器高电平(直通)
    CLR        P2.7                ;设置位锁存器低电平(锁住)
    JB         A_SHOW_FLAG, A_SH   ;判断闹铃显示标志,时钟则顺序执行,闹铃则跳转
    JNB        T_SHOW_FLAG, T_GO   ;判断温度显示模块,显示温度则顺序,否则跳转
    JNB        ACC.7, ACC_7        ;判断数码管扫描位置是否为倒数第1位
    JNB        ACC.6, ACC_6        ;判断数码管扫描位置是否为倒数第2位
    MOV        H_S, #16            ;原时间“时”的十位部分显示“-”
    MOV        H_G, #16            ;原时间“时”的个位部分显示“-”
    MOV        M_S, #16            ;原时间“分”的十位部分显示“-”
    MOV        M_G, #16            ;原时间“分”的个位部分显示“-”
    AJMP       T_GO
ACC_7:
    MOV        R0, #T_G            ;显示温度个位
    AJMP       T_GO
ACC_6:
    MOV        R0, #T_S            ;显示温度十位
T_GO:
    MOV        A, @R0              ;读取“时”
    AJMP       SH                  ;跳转到查表部分
A_SH:
    MOV        A, @R1              ;读取闹铃“时”
SH:
    MOV        DPTR, #TABLE        ;获取字形码存放地址
    MOVC       A, @A+DPTR          ;查表获得字形码
    MOV        P0, A               ;输出字形码
    SETB       P2.6                ;设置段锁存器高电平(直通)
    CLR        P2.6                ;设置段锁存器低电平(锁住)
;========================调用矩阵键盘扫描============================
    MOV        A, R2               ;读取当前的位扫描值
    ORL        A, #0F0H            ;高四位置1(作为输入)
    MOV        P3, A               ;输出到矩阵键盘接口进行扫描
    ACALL      KEY_SCAN            ;调用矩阵键盘子程序
;====================================================================
    MOV        A, R2               ;读取当前的位扫描值
    RL         A                   ;左移
    JB         A_SHOW_FLAG, A_DEC  ;判断闹铃显示标志,时钟则顺序执行,闹铃则跳转
    DEC        R0                  ;时间显示内容的地址减一
    AJMP       OK
A_DEC:
    DEC        R1                  ;闹铃显示内容的地址减一
OK:
    JB         ACC.0, N_1          ;判断是否8位数码管全部扫描完毕
    JB         A_SHOW_FLAG, A_OUT  ;判断闹铃显示标志,时钟则顺序执行,闹铃则跳转
    MOV        R0, #H_S            ;全部扫描完毕则恢复时间显示内容的地址
    AJMP       N_1
A_OUT:
    MOV        R1, #A_H_S          ;全部扫描完毕则恢复闹铃显示内容的地址
N_1:
    MOV        R2, A               ;回存左移后的位扫描值
    POP        PSW                 ;弹出程序状态字
    POP        ACC                 ;弹出累加器
    RETI                           ;中断返回
;***************************矩阵键盘扫描******************************
KEY_SCAN:
    PUSH       ACC                 ;累加器压栈
    PUSH       PSW                 ;程序状态字压栈
    MOV        A, P3               ;读取矩阵键盘接口
    JNB        ACC.4, L_4          ;判断第一列是否有低电平出现
    JNB        ACC.5, L_5          ;判断第二列是否有低电平出现
    JNB        ACC.6, L_6          ;判断第三列是否有低电平出现
    JNB        ACC.7, L_7          ;判断第四列是否有低电平出现
    AJMP       OUT                 ;都没有低电平则退出(无键按下)
;==========================第一列有键按下=============================
L_4:
    MOV        A, R2               ;读取当前的位扫描值
    JNB        ACC.0, K_1          ;判断是否第一行输出了低电平
    JNB        ACC.1, K_5          ;判断是否第二行输出了低电平
    JNB        ACC.2, K_9          ;判断是否第三行输出了低电平
    JNB        ACC.3, K_13         ;判断是否第四行输出了低电平
    AJMP       OUT                 ;都没有低电平则退出(干扰)
;-------------------------第一行输出了低电平--------------------------
K_1:
    MOV        KEY, #01H           ;编码键值为1
    AJMP       OUT                 ;跳转到退出子程序
;-------------------------第二行输出了低电平--------------------------
K_5:
    MOV        KEY, #05H           ;编码键值为5
    AJMP       OUT                 ;跳转到退出子程序
;-------------------------第三行输出了低电平--------------------------
K_9:
    MOV        KEY, #09H           ;编码键值为9
    AJMP       OUT                 ;跳转到退出子程序
;-------------------------第四行输出了低电平--------------------------
K_13:
    MOV        KEY, #0DH           ;编码键值为13
    AJMP       OUT                 ;跳转到退出子程序
;==========================第二列有键按下=============================
L_5:
    MOV        A, R2               ;读取当前的位扫描值
    JNB        ACC.0, K_2          ;判断是否第一行输出了低电平
    JNB        ACC.1, K_6          ;判断是否第二行输出了低电平
    JNB        ACC.2, K_10         ;判断是否第三行输出了低电平
    JNB        ACC.3, K_14         ;判断是否第四行输出了低电平
    AJMP       OUT                 ;都没有低电平则退出(干扰)
;-------------------------第一行输出了低电平--------------------------
K_2:
    MOV        KEY, #02H           ;编码键值为2
    AJMP       OUT                 ;跳转到退出子程序
;-------------------------第二行输出了低电平--------------------------
K_6:
    MOV        KEY, #06H           ;编码键值为6
    AJMP       OUT                 ;跳转到退出子程序
;-------------------------第三行输出了低电平--------------------------
K_10:
    MOV        KEY, #0AH           ;编码键值为10
    AJMP       OUT                 ;跳转到退出子程序
;-------------------------第四行输出了低电平--------------------------
K_14:
    MOV        KEY, #0EH           ;编码键值为14
    AJMP       OUT                 ;跳转到退出子程序
;==========================第三列有键按下=============================
L_6:
    MOV        A, R2               ;读取当前的位扫描值
    JNB        ACC.0, K_3          ;判断是否第一行输出了低电平
    JNB        ACC.1, K_7          ;判断是否第二行输出了低电平
    JNB        ACC.2, K_11         ;判断是否第三行输出了低电平
    JNB        ACC.3, K_15         ;判断是否第四行输出了低电平
    AJMP       OUT                 ;都没有低电平则退出(干扰)
;-------------------------第一行输出了低电平--------------------------
K_3:
    MOV        KEY, #03H           ;编码键值为3
    AJMP       OUT                 ;跳转到退出子程序
;-------------------------第二行输出了低电平--------------------------
K_7:
    MOV        KEY, #07H           ;编码键值为7
    AJMP       OUT                 ;跳转到退出子程序
;-------------------------第三行输出了低电平--------------------------
K_11:
    MOV        KEY, #0BH           ;编码键值为11
    AJMP       OUT                 ;跳转到退出子程序
;-------------------------第四行输出了低电平--------------------------
K_15:
    MOV        KEY, #0FH           ;编码键值为15
    AJMP       OUT                 ;跳转到退出子程序
;==========================第四列有键按下=============================
L_7:
    MOV        A, R2               ;读取当前的位扫描值
    JNB        ACC.0, K_4          ;判断是否第一行输出了低电平
    JNB        ACC.1, K_8          ;判断是否第二行输出了低电平
    JNB        ACC.2, K_12         ;判断是否第三行输出了低电平
    JNB        ACC.3, K_16         ;判断是否第四行输出了低电平
    AJMP       OUT                 ;都没有低电平则退出(干扰)
;-------------------------第一行输出了低电平--------------------------
K_4:
    MOV        KEY, #04H           ;编码键值为4
    AJMP       OUT                 ;跳转到退出子程序
;-------------------------第二行输出了低电平--------------------------
K_8:
    MOV        KEY, #08H           ;编码键值为8
    AJMP       OUT                 ;跳转到退出子程序
;-------------------------第三行输出了低电平--------------------------
K_12:
    MOV        KEY, #0CH           ;编码键值为12
    AJMP       OUT                 ;跳转到退出子程序
;-------------------------第四行输出了低电平--------------------------
K_16:
    MOV        KEY, #10H           ;编码键值为16
OUT:
    POP        PSW                 ;弹出程序状态字
    POP        ACC                 ;弹出累加器
    RET                            ;子程序返回
;*****************************定时器T0********************************
T0_SRV:
    PUSH       ACC                 ;累加器压栈
    PUSH       PSW                 ;程序状态字压栈
    MOV        TL0, #00H
    MOV        TH0, #4CH           ;重载50ms初始值
    CJNE       R3, #19, N_2        ;软件计数值20次到达1秒
    MOV        R3, #00H            ;软件计数器清零
    ACALL      C_TIME              ;调用闹铃时间比较子程序
    ACALL      GET_TEMPER          ;调用DS18B20获取温度子程序(1秒获取一次数据)
    INC        _S                  ;秒加一
    MOV        A, _S
    CJNE       A, #60, N_3         ;判断秒是否加到60
    MOV        _S, #00H            ;到60则恢复0
    INC        _M                  ;分加一
    MOV        A, _M
    CJNE       A, #60, N_3         ;判断分是否加到60
    MOV        _M, #00H            ;到60则恢复0
    INC        _H                  ;时加一
    MOV        A, _H
    CJNE       A, #24, N_3         ;判断时是否加到24
    MOV        _H, #00H            ;到24则恢复0
    AJMP       N_3
N_2:
    MOV        A, SCT
    CJNE       A, #00H, NN         ;调校状态时不走时
    INC        R3                  ;软件计数器值加一
NN:
    INC        CNT                 ;调校闪烁速度控制值加一
;=========================以下控制调校闪烁============================
N_3:
    MOV        A, CNT              ;读取调校闪烁速度控制变量
    CJNE       A, #04H, N_4        ;判断调校闪烁速度控制值是否加到4,否则跳到退出
    MOV        CNT, #00H           ;调校闪烁速度控制变量值恢复0
    JB         A_SHOW_FLAG, A_N    ;判断闹铃显示标志,时钟则顺序执行,闹铃则跳转
;----------------------------时间调校闪烁-----------------------------
    MOV        A, SCT              ;获取当前调校状态
    CJNE       A, #01H, N_5        ;判定“时”是否闪烁
    CPL        FLG_H               ;“时”标志取反(闪烁)
    CLR        FLG_M               ;”分“标志清零(不闪)
    CLR        FLG_S               ;”秒“标志清零(不闪)
    AJMP       N_4                 ;跳转到退出
N_5:
    CJNE       A, #02H, N_6        ;判定“分”是否闪烁
    CLR        FLG_H               ;“时”标志清零(不闪)
    CPL        FLG_M               ;“分”标志取反(闪烁)
    CLR        FLG_S               ;“秒”标志清零(不闪)
    AJMP       N_4                 ;跳转到退出
N_6:
    CJNE       A, #03H, N_7        ;判定“秒”是否闪烁
    CLR        FLG_H               ;“时”标志清零(不闪)
    CLR        FLG_M               ;“分”标志清零(不闪)
    CPL        FLG_S               ;“秒”标志取反(闪烁)
    AJMP       N_4                 ;跳转到退出
N_7:                               
    CLR        FLG_H               ;“时”标志清零(不闪)
    CLR        FLG_M               ;“分”标志清零(不闪)
    CLR        FLG_S               ;“秒”标志清零(不闪)
    AJMP       N_4                 ;跳转到退出
;----------------------------闹铃调校闪烁-----------------------------
A_N:
    MOV        A, A_SCT            ;获取当前闹铃调校状态
    CJNE       A, #01H, AN_5       ;判定闹铃“时”是否闪烁
    CPL        AFLG_H              ;闹铃“时”标志取反(闪烁)
    CLR        AFLG_M              ;闹铃”分“标志清零(不闪)
    CLR        AFLG_S              ;闹铃”秒“标志清零(不闪)
    AJMP       N_4                 ;跳转到退出
AN_5:
    CJNE       A, #02H, AN_6       ;判定闹铃“分”是否闪烁
    CLR        AFLG_H              ;闹铃”时“标志清零(不闪)
    CPL        AFLG_M              ;闹铃“分”标志取反(闪烁)
    CLR        AFLG_S              ;闹铃”秒“标志清零(不闪)
    SJMP       N_4                 ;跳转到退出
AN_6:
    CJNE       A, #03H, AN_7       ;判定闹铃“秒”是否闪烁
    CLR        AFLG_H              ;闹铃”时“标志清零(不闪)
    CLR        AFLG_M              ;闹铃”分“标志清零(不闪)
    CPL        AFLG_S              ;闹铃“秒”标志取反(闪烁)
    AJMP       N_4                 ;跳转到退出
AN_7:                           
    CLR        AFLG_H              ;闹铃”时“标志清零(不闪)
    CLR        AFLG_M              ;闹铃”分“标志清零(不闪)
    CLR        AFLG_S              ;闹铃”秒“标志清零(不闪)
N_4:
    POP        PSW                 ;弹出程序状态字
    POP        ACC                 ;弹出累加器
    RETI                           ;中断返回
;*****************************温度获取********************************
;========================DS18B20初始化子程序==========================
INIT_18B20:
    SETB       P2.2                ;拉高数据线
    NOP                            ;空指令
    CLR        P2.2                ;拉低数据线(需要延时至少ms)
;--------------------------以下延时537微秒----------------------------
    MOV        R1, #3
TSR1:
    MOV        R0, #107
    DJNZ       R0, $
    DJNZ       R1, TSR1
;------------------------------延时结束-------------------------------
    SETB       P2.2               ;拉高数据线
    NOP                           ;空指令
    NOP                           ;空指令
    NOP                           ;空指令
    MOV        R0, #25H           ;重复检测次数
TSR2:
    JNB        P2.2, TSR3         ;等待DS18B20回应
    DJNZ       R0, TSR2           ;重复检测25次
    AJMP       TSR4               ;未检测到DB18B20,跳转
TSR3:
    SETB       DS18B20            ;DS1820存在,置标志位
    CLR        P1.7               ;点亮P1.7LED
    AJMP       TSR5               ;跳转到延时
TSR4:
    CLR        DS18B20            ;DS1820不存在,清标志位
    CLR        P1.0               ;点亮P1.0LED
    AJMP       TSR6               ;跳转到返回
TSR5:
    MOV        R0, #117           ;延时初始值
    DJNZ       R0, $              ;时序要求延时一段时间
TSR6:
    SETB       P2.2               ;拉高数据线
    RET                           ;子程序返回
;=======================获取转换温度值=================================
;此段程序在T0中经过1秒调用一次,巧妙利用了秒定时来实现温度转换所需的延时
GET_TEMPER:
    SETB       RS0                ;RS0置1,使用第1组通用寄存器(R0~R7)
    JB         TMP, DO            ;判断温度延时标志是否为1,为1说明已延时了1秒,可以获取温度数据了
    SETB       TMP                ;置温度延时标志
    SETB       P2.2               ;拉高数据线
    ACALL      INIT_18B20         ;调用DS18B20初始化子程序
    JB         DS18B20, TSS2      ;若DS18B20不存在则返回
    CLR        P1.1               ;DS18B20不存在点亮P1.1LED
    CLR        TMP                ;清温度延时标志
    RET                           ;子程序返回
TSS2:
    CLR        P1.6               ;DS18B20存在点亮P1.6LED
    MOV        A, #0CCH           ;跳过ROM匹配
    ACALL      WRITE_18B20        ;调用写DS18B20子程序
    MOV        A, #44H            ;发出温度转换命令
    ACALL      WRITE_18B20        ;调用写DS18B20子程序
    RET                           ;子程序返回
;--下面的程序是上面程序经过延时1秒之后才执行,等待温度转换结束需要约750ms
DO:    
    ACALL     INIT_18B20          ;准备读温度前先调用初始化    
    MOV       A, #0CCH            ;跳过ROM匹配
    LCALL     WRITE_18B20         ;调用写DS18B20子程序
    MOV       A, #0BEH            ;发出读温度命令
    ACALL     WRITE_18B20         ;调用写DS18B20子程序
    ACALL     READ_18B20          ;调用获取DS18B20温度数据子程序
    ACALL     CONVERT             ;调用数据转换子程序
    CPL       P1.5                ;温度读取完毕P1.5LED取反
    CLR       TMP                 ;清温度延时标志
    CLR       RS0                 ;RS0清0,恢复使用第0组通用寄存器(R0~R7)
    RET                           ;子程序返回
;--------------------------写DS18B20子程序---------------------------------
WRITE_18B20:
    PUSH      ACC                 ;累加器压栈
    PUSH      PSW                 ;程序状态字压栈
    MOV       R2, #8              ;一共写8位数据
    CLR       C                   ;清进位位
WR1:
    CLR       P2.2                ;拉低数据线
    MOV       R3, #5
    DJNZ      R3, $               ;延时
    RRC       A                   ;带进位位右移
    MOV       P2.2, C             ;进位位送数据线
    MOV       R3, #21
    DJNZ      R3, $               ;延时
    SETB      P2.2                ;拉高数据线
    NOP                           ;空指令
    DJNZ      R2, WR1             ;判断是否写完8个位
    SETB      P2.2                ;拉高数据线
    POP       PSW                 ;弹出程序状态字
    POP       ACC                 ;弹出累加器
    RET                           ;子程序返回
;-----------------------获取DS18B20温度数据子程序---------------------------
READ_18B20:
    PUSH      ACC                 ;累加器压栈
    PUSH      PSW                 ;程序状态字压栈
    MOV       R4, #2              ;温度数据将分两次写入两个地址空间
    MOV       R1, #T_L            ;高位存入22H(T_H),低位存入23H(T_L)
RE0:
    MOV       R2, #8              ;一共读8位数据
RE1:
    CLR       C                   ;清进位位
    SETB      P2.2                ;拉高数据线
    NOP                           ;空指令
    NOP                           ;空指令
    CLR       P2.2                ;拉低数据线
    NOP                           ;空指令
    NOP                           ;空指令
    NOP                           ;空指令
    SETB      P2.2                ;拉高数据线    
    MOV       R3, #8
    DJNZ      R3, $               ;延时    
    MOV       C, P2.2             ;读数据线    
    MOV       R3, #21
    DJNZ      R3, $               ;延时    
    RRC       A                   ;带进位位右移
    DJNZ      R2, RE1             ;判断是否读完8个位
    MOV       @R1, A              ;数据写入R1所存地址的空间
    DEC       R1                  ;写入地址减一
    DJNZ      R4, RE0             ;判断是否写完两个地址空间
    MOV       R1, #T_L            ;恢复初始值
    MOV       R4, #2              ;恢复初始值
    POP       PSW                 ;弹出程序状态字
    POP       ACC                 ;弹出累加器
    RET                           ;子程序返回
;------------------------温度数据转换子程序----------------------------
CONVERT:
    PUSH      ACC                 ;累加器压栈
    PUSH      PSW                 ;程序状态字压栈
    MOV       A, T_L              ;温度低位送入累加器
    MOV       C, 10H              ;将T_H中的第0位移入C
    RRC       A                   ;带进位位右移
    MOV       C, 11H              ;将T_H中的第1位移入C
    RRC       A                   ;带进位位右移
    MOV       C, 12H              ;将T_H中的第2位移入C
    RRC       A                   ;带进位位右移
    MOV       C, 13H              ;将T_H中的第3位移入C
    RRC       A                   ;带进位位右移
    MOV       B, #10              ;加载被除数10
    DIV       AB                  ;温度除以10
    MOV       T_S, A              ;温度十位送T_S
    MOV       T_G, B              ;温度个位送T_G
    POP       PSW                 ;弹出程序状态字
    POP       ACC                 ;弹出累加器
    RET                           ;子程序返回
;*****************************闹铃比较********************************
C_TIME:
    PUSH      ACC                 ;累加器压栈
    PUSH      PSW                 ;程序状态字压栈
    JNB       A_SET, C_N1         ;判断闹铃开关是否打开,未打开则跳转
    MOV       7CH, #18            ;显示“-”加“.” ,表示闹铃打开
    MOV       A, _H
    CJNE      A, A_H, C_N1        ;比较“时”是否相等
    MOV       A, _M
    CJNE      A, A_M, C_N1        ;比较“分”是否相等
    CPL       P2.3                ;闹铃取反
    AJMP      C_OUT
C_N1:
    SETB      P2.3                ;关闭闹铃
C_OUT:
    POP       PSW                 ;弹出程序状态字
    POP       ACC                 ;弹出累加器
    RET
;*****************************字形码表********************************
TABLE:
    DB 3FH,06H,5BH,4FH,66H,6DH,7DH,07H      ;0~7字形
    DB 7FH,6FH,77H,7CH,39H,5EH,79H,71H      ;8~F字形
    DB 40H,00H,0C0H                         ;“-”、黑屏字形、“-”加“.” 字形
    END

 

posted @ 2024-12-15 20:06  fxzq  阅读(170)  评论(1编辑  收藏  举报