倒计时汇编语言RTC实时时钟,使用汇编语言

基于51单片机,及其RTC实时时钟,设计一个可以实现倒计时功能的电子倒计时牌系统。
具体要求如下:
(1)可以用数码管显示倒计时的时分秒信息
(2)可以通过按键对倒计时设置初值;倒计时结束时可发出提示音或者用二极灯管指示。


; 定义端口和控制信号
DIO_PORT   equ P1   ; 数码管段选控制端口
DIO_CTRL   equ P2   ; 数码管位选控制端口
DS1302_RST equ P3.7 ; DS1302复位信号
DS1302_CLK equ P3.6 ; DS1302时钟信号
DS1302_DAT equ P3.5 ; DS1302数据信号
BUZZER     equ P0.0 ; 蜂鸣器信号
LED        equ P0.1 ; 二极灯管信号
BTN_SET    equ P0.2 ; 设置按键信号

; 定义数码管显示字符表
SEG_TABLE  db 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f

; 定义计时变量
hours      dw 0     ; 小时
minutes    dw 0     ; 分钟
seconds    dw 0     ; 秒钟

; 主程序入口
MAIN:
    mov A, #0FFh  ; 设置A寄存器为全1,用于数码管清零
    mov DIO_CTRL, A  ; 数码管位选控制端口置1,关闭所有数码管
    mov DIO_PORT, A  ; 数码管段选控制端口置1,清空显示内容
    mov BUZZER, #0   ; 关闭蜂鸣器
    mov LED, #1      ; 关闭二极灯管
    mov BTN_SET, #1  ; 设置按键信号口为输入
    call DS1302_Init  ; 初始化DS1302时钟芯片
    call SetInitialTime  ; 设置初始时间

COUNTDOWN_LOOP:
    call DS1302_ReadTime  ; 读取当前时间
    call DisplayTime  ; 显示当前时间
    call CheckButton  ; 检查设置按键是否按下
    call Countdown  ; 倒计时处理
    sjmp COUNTDOWN_LOOP

; DS1302时钟芯片初始化
DS1302_Init:
    mov DS1302_RST, #0  ; 拉低DS1302复位信号
    acall DelayShort  ; 延时一段时间
    mov DS1302_RST, #1  ; 拉高DS1302复位信号
    acall DelayShort  ; 延时一段时间
    ret

; 设置初始时间
SetInitialTime:
    acall DisplayTime  ; 显示当前时间
    acall WaitForButtonPress  ; 等待设置按键按下
    acall DelayShort  ; 延时一段时间,防止按键抖动
    acall ReadButton  ; 读取设置按键状态
    cjne A, #0, SetHours  ; 如果按键按下,则设置小时
    acall WaitForButtonRelease  ; 等待设置按键释放
    acall DelayShort  ; 延时一段时间,防止按键抖动
    acall ReadButton  ; 读取设置按键状态
    cjne A, #0, SetMinutes  ; 如果按键按下,则设置分钟
    acall WaitForButtonRelease  ; 等待设置按键释放
    acall DelayShort  ; 延时一段时间,防止按键抖动
    acall ReadButton  ; 读取设置按键状态
    cjne A, #0, SetSeconds  ; 如果按键按下,则设置秒钟
    ret

SetHours:
    acall WaitForButtonRelease  ; 等待设置按键释放
    acall DelayShort  ; 延时一段时间,防止按键抖动
    acall ReadButton  ; 读取设置按键状态
    inc hours  ; 小时加1
    cjne A, #0, SetHours  ; 如果按键按下,则继续增加小时
    ret

SetMinutes:
    acall WaitForButtonRelease  ; 等待设置按键释放
    acall DelayShort  ; 延时一段时间,防止按键抖动
    acall ReadButton  ; 读取设置按键状态
    inc minutes  ; 分钟加1
    cjne A, #0, SetMinutes  ; 如果按键按下,则继续增加分钟
    ret

SetSeconds:
    acall WaitForButtonRelease  ; 等待设置按键释放
    acall DelayShort  ; 延时一段时间,防止按键抖动
    acall ReadButton  ; 读取设置按键状态
    inc seconds  ; 秒钟加1
    cjne A, #0, SetSeconds  ; 如果按键按下,则继续增加秒钟
    ret

; 显示当前时间
DisplayTime:
    mov A, DIO_CTRL  ; 保存数码管位选控制端口状态
    push A  ; 将数码管位选控制端口状态保存到栈中
    mov DIO_CTRL, #0FFh  ; 数码管位选控制端口置1,关闭所有数码管
    mov A, DIO_PORT  ; 保存数码管段选控制端口状态
    push A  ; 将数码管段选控制端口状态保存到栈中

    ; 显示小时
    mov A, hours
    mov R7, #2  ; 循环计数器
    mov R6, #10  ; 循环次数
DISPLAY_HOURS:
    mov R0, #0
    mov R1, A
    div AB, R6
    mov A, B
    add A, #48  ; 转换为ASCII码
    mov DIO_PORT, A
    acall DelayShort  ; 延时一段时间
    mov A, R1
    mov R1, B
    cjne R7, #0, DISPLAY_HOURS  ; 循环显示小时的十位和个位
    mov A, #0  ; 显示冒号
    mov DIO_PORT, A
    acall DelayShort  ; 延时一段时间

    ; 显示分钟
    mov A, minutes
    mov R7, #2  ; 循环计数器
    mov R6, #10  ; 循环次数
DISPLAY_MINUTES:
    mov R0, #0
    mov R1, A
    div AB, R6
    mov A, B
    add A, #48  ; 转换为ASCII码
    mov DIO_PORT, A
    acall DelayShort  ; 延时一段时间
    mov A, R1
    mov R1, B
    cjne R7, #0, DISPLAY_MINUTES  ; 循环显示分钟的十位和个位
    mov A, #0  ; 显示冒号
    mov DIO_PORT, A
    acall DelayShort  ; 延时一段时间

    ; 显示秒钟
    mov A, seconds
    mov R7, #2  ; 循环计数器
    mov R6, #10  ; 循环次数
DISPLAY_SECONDS:
    mov R0, #0
    mov R1, A
    div AB, R6
    mov A, B
    add A, #48  ; 转换为ASCII码
    mov DIO_PORT, A
    acall DelayShort  ; 延时一段时间
    mov A, R1
    mov R1, B
    cjne R7, #0, DISPLAY_SECONDS  ; 循环显示秒钟的十位和个位

    pop A  ; 恢复数码管段选控制端口状态
    mov DIO_PORT, A  ; 恢复数码管段选控制端口状态
    acall DelayShort  ; 延时一段时间
    pop A  ; 恢复数码管位选控制端口状态
    mov DIO_CTRL, A  ; 恢复数码管位选控制端口状态
    acall DelayShort  ; 延时一段时间
    ret

; 检查设置按键是否按下
CheckButton:
    acall ReadButton  ; 读取设置按键状态
    cjne A, #0, Exit  ; 如果按键按下,则跳出循环
    ret

Exit:
    ret

; 倒计时处理
Countdown:
    mov A, seconds
    orl A, minutes
    orl A, hours
    jz CountdownEnd  ; 如果时间为0,则倒计时结束

    ; 减少一秒钟
    dec seconds
    cjne seconds, #0FFh, CountdownCheckMin  ; 如果秒钟不为0xFF,则跳过减少分钟的步骤
    dec minutes  ; 秒钟减少到0后,减少一分钟
CountdownCheckMin:
    cjne minutes, #0FFh, CountdownCheckHour  ; 如果分钟不为0xFF,则跳过减少小时的步骤
    dec hours  ; 分钟减少到0后,减少一小时
CountdownCheckHour:
    ret

CountdownEnd:
    acall Beep  ; 发出提示音
    acall BlinkLED  ; 闪烁二极灯管
    ret

; 延时一段时间
DelayShort:
    ; 在此处填入延时代码
    ret

; 读取设置按键状态
ReadButton:
    ; 在此处填入读取按键状态代码
    ret

; 等待设置按键按下
WaitForButtonPress:
    ; 在此处填入等待按键按下代码
    ret

; 等待设置按键释放
WaitForButtonRelease:
    ; 在此处填入等待按键释放代码
    ret

; 发出提示音
Beep:
    ; 在此处填入发出提示音代码
    ret

; 闪烁二极灯管
BlinkLED:
    ; 在此处填入闪烁二极灯管代码
    ret

; DS1302时钟芯片读取当前时间
DS1302_ReadTime:
    ; 在此处填入读取当前时间代码
    ret

下面是一个使用汇编语言编写的基于51单片机的倒计时电子牌系统的示例代码。这个代码使用了DS1302实时时钟模块来实现倒计时功能,并通过数码管显示剩余的时分秒信息。同时,按键可以用于设置倒计时的初始值,倒计时结束时会通过蜂鸣器发出提示音。

; 倒计时电子牌系统
; 使用51单片机和DS1302实时时钟模块
; 数码管显示倒计时的时分秒信息
; 按键设置倒计时初值,倒计时结束时发出提示音或使用二极管指示

; 定义I/O端口
DSEG    AT  0
LEDDATA DB  0x0F ; 用于控制数码管显示的数据端口
KEYDATA DB  0xFF ; 用于读取按键输入的数据端口
BUZZER  DB  0xFE ; 用于控制蜂鸣器的数据端口
LED     DB  0xFD ; 用于控制指示二极管的数据端口

; 定义常量
TM_SECOND   EQU 0x00 ; DS1302的秒寄存器地址
TM_MINUTE   EQU 0x01 ; DS1302的分寄存器地址
TM_HOUR     EQU 0x02 ; DS1302的时寄存器地址

; 定义变量
COUNT_DOWN  DS  3 ; 倒计时剩余的时分秒信息
SET_FLAG    DS  1 ; 按键设置倒计时初值的标志
BEEP_FLAG   DS  1 ; 发出提示音的标志

; 初始化DS1302
INIT_DS1302:
    MOV     P1, #0x00 ; 将P1端口用于DS1302的数据传输
    MOV     P2, #0x01 ; 将P2.0端口用于DS1302的时钟信号
    MOV     P3, #0x02 ; 将P3.1端口用于DS1302的复位信号
    MOV     A, #0x8E  ; 写入DS1302的控制命令,打开写保护
    ACALL   DS1302_WRITE
    MOV     A, #0x90  ; 写入DS1302的控制命令,关闭写保护并启用时钟
    ACALL   DS1302_WRITE
    RET

; 从DS1302读取时间
READ_TIME:
    MOV     A, #TM_SECOND ; 要读取的寄存器地址
    ACALL   DS1302_READ
    MOV     COUNT_DOWN, A ; 保存秒数
    MOV     A, #TM_MINUTE ; 要读取的寄存器地址
    ACALL   DS1302_READ
    MOV     COUNT_DOWN+1, A ; 保存分钟
    MOV     A, #TM_HOUR ; 要读取的寄存器地址
    ACALL   DS1302_READ
    MOV

     COUNT_DOWN+2, A ; 保存小时
    RET

; 向DS1302写入时间
WRITE_TIME:
    MOV     A, #TM_SECOND ; 要写入的寄存器地址
    MOV     A, COUNT_DOWN ; 秒数
    ACALL   DS1302_WRITE
    MOV     A, #TM_MINUTE ; 要写入的寄存器地址
    MOV     A, COUNT_DOWN+1 ; 分钟
    ACALL   DS1302_WRITE
    MOV     A, #TM_HOUR ; 要写入的寄存器地址
    MOV     A, COUNT_DOWN+2 ; 小时
    ACALL   DS1302_WRITE
    RET

; DS1302读取操作
DS1302_READ:
    MOV     P1, A ; 将要读取的寄存器地址写入P1
    MOV     P3.1, #0 ; 复位信号置低
    NOP
    NOP
    MOV     P3.1, #1 ; 复位信号置高
    MOV     R0, #0 ; 初始化计数器
    MOV     A, P1 ; 读取数据
    ACALL   DS1302_DELAY ; 延时
    NOP
    MOV     C, P1.7 ; 读取最高位标志位
    RRC     A ; 右移一位
    MOV     C, P1.7 ; 读取最高位标志位
    RLC     A ; 左移一位,还原最高位
    INC     R0 ; 计数器加1
    CJNE    R0, #8, DS1302_READ ; 循环读取8位数据
    RET

; DS1302写入操作
DS1302_WRITE:
    MOV     P1, A ; 将要写入的数据写入P1
    MOV     P3.1, #0 ; 复位信号置低
    NOP
    NOP
    MOV     P3.1, #1 ; 复位信号置高
    MOV     R0, #0 ; 初始化计数器
    MOV     A, P1 ; 写入数据
    ACALL   DS1302_DELAY ; 延时
    NOP
    SETB    P1.7 ; 设置最高位标志位
    RLC     A ; 左移一位,最高位置0
    MOV     C, P1.7 ; 读取最高位标志位
    RRC     A ; 右移一位,还原最高位
    INC     R0 ; 计数器加1
    CJNE    R0, #8, DS1302_WRITE ; 循环写入8位数据
    RET

; DS1302读写延时
DS1302_DELAY:
    MOV     R1, #10 ; 设置延时计数
    DJNZ    R1, $ ; 延时
    RET

; 7段数码管显示
DISPLAY:
    MOV     A, COUNT_DOWN ; 获取秒数
    ACALL   DISPLAY_DIGIT ; 显示个位数
    MOV     A, COUNT_DOWN+1 ; 获取分钟
    ACALL   DISPLAY_DIGIT ; 显示十位数
    MOV     A, COUNT_DOWN+2 ; 获取小时
    ACALL   DISPLAY_DIGIT ; 显示

百位数
    RET

; 数码管显示单个数位
DISPLAY_DIGIT:
    MOV     LEDDATA, A ; 将数值写入数码管显示端口
    ACALL   DELAY ; 延时
    MOV     LEDDATA, #0x00 ; 关闭数码管
    ACALL   DELAY ; 延时
    RET

; 延时函数
DELAY:
    MOV     R2, #0xFF ; 设置延时计数
DELAY_LOOP:
    DJNZ    R2, DELAY_LOOP ; 延时
    RET

; 按键检测
CHECK_KEY:
    MOV     A, KEYDATA ; 读取按键输入
    CJNE    A, #0xFF, KEY_PRESSED ; 判断是否有按键按下
    RET

; 按键按下处理
KEY_PRESSED:
    MOV     SET_FLAG, #1 ; 设置倒计时初值的标志置位
    RET

; 发出提示音
BEEP:
    MOV     BUZZER, #0x01 ; 发出蜂鸣器信号
    ACALL   DELAY ; 延时
    MOV     BUZZER, #0x00 ; 停止蜂鸣器
    ACALL   DELAY ; 延时
    RET

; 倒计时功能
COUNT_DOWN_FUNC:
    MOV     SET_FLAG, #0 ; 清除设置倒计时初值的标志
    MOV     BEEP_FLAG, #0 ; 清除发出提示音的标志
COUNT_DOWN_LOOP:
    CALL    READ_TIME ; 读取当前时间
    CALL    DISPLAY ; 显示剩余时间
    CALL    CHECK_KEY ; 检测按键
    JZ      COUNT_DOWN_LOOP ; 如果没有按键按下,继续倒计时
    CJNE    SET_FLAG, #1, COUNT_DOWN_LOOP ; 如果设置倒计时初值的标志不为1,继续倒计时
    CALL    SET_COUNT_DOWN ; 设置倒计时初值
    JMP     COUNT_DOWN_LOOP ; 继续倒计时

; 设置倒计时初值
SET_COUNT_DOWN:
    MOV     A, #0x00 ; 清零
    MOV     COUNT_DOWN, A ; 秒数
    MOV     COUNT_DOWN+1, A ; 分钟
    MOV     COUNT_DOWN+2, A ; 小时
SET_COUNT_DOWN_LOOP:
    CALL    DISPLAY ; 显示设置的初值
    CALL    CHECK_KEY ; 检测按键
    JZ      SET_COUNT_DOWN_LOOP ; 如果没有按键按下,继续设置
    CJNE    SET_FLAG, #0, SET_COUNT_DOWN_LOOP ; 如果设置倒计时初值的标志不为0,继续设置
    CALL    WRITE_TIME ; 写入设置的初值
    RET

; 主程序入口
MAIN:
    ACALL   INIT_DS1302 ; 初始化DS1302
    ACALL   COUNT_DOWN_FUNC ; 开始倒计时功能
    JMP     MAIN ; 循环执行

END

请注意,以上代码仅为示例,实际使用时需要根据具体的硬件电路和单片机的引脚分配进行适

当的修改。此外,需要确保正确连接DS1302实时时钟模块、数码管、按键、蜂鸣器和二极管,并在代码中使用正确的I/O端口进行控制。

以下是基于51单片机和RTC实时时钟的倒计时电子钟系统的汇编语言实现,使用了4位共阳数码管进行显示,其中,P0.0-P0.3用于控制数码管的位选,P2.0-P2.7用于控制数码管的段选,P3.2-P3.3用于接收按键输入,P3.4用于发出提示音,P3.5用于控制二极管显示。


; 定义常数
CountDownMin equ 10 ; 初值为10分钟
CountDownSec equ CountDownMin * 60 ; 初值转换为秒数
DelayTime equ 50 ; 延时时间

; 定义端口地址
DataPort equ P2
SelectPort equ P0

; 定义按键和二极管的端口地址
KeyPort equ P3
BuzzerPort equ P3.4
LEDPort equ P3.5

; 定义数码管显示表
DisplayTable: db 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x以下是基于51单片机和RTC实时时钟的倒计时电子钟系统的汇编语言实现,使用了4位共阳数码管进行显示,其中,P0.0-P0.3用于控制数码管的位选,P2.0-P2.7用于控制数码管的段选,P3.2-P3.3用于接收按键输入,P3.4用于发出提示音,P3.5用于控制二极管显示。

```assembly
; 定义常数
CountDownMin equ 10 ; 初值为10分钟
CountDownSec equ CountDownMin * 60 ; 初值转换为秒数
DelayTime equ 50 ; 延时时间

; 定义端口地址
DataPort equ P2
SelectPort equ P0

; 定义按键和二极管的端口地址
KeyPort equ P3
BuzzerPort equ P3.4
LEDPort equ P3.5

; 定义数码管显示表
DisplayTable: db 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x以下是基于51单片机和RTC实时时钟的倒计时电子钟系统的汇编语言实现,使用了4位共阳数码管进行显示,其中,P0.0-P0.3用于控制数码管的位选,P2.0-P2.7用于控制数码管的段选,P3.2-P3.3用于接收按键输入,P3.4用于发出提示音,P3.5用于控制二极管显示。

```assembly
; 定义常数
CountDownMin equ 10 ; 初值为10分钟
CountDownSec equ CountDownMin * 60 ; 初值转换为秒数
DelayTime equ 50 ; 延时时间

; 定义端口地址
DataPort equ P2
SelectPort equ P0

; 定义按键和二极管的端口地址
KeyPort equ P3
BuzzerPort equ P3.4
LEDPort equ P3.5

; 定义数码管显示表
DisplayTable: db 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x以下是基于51单片机和RTC实时时钟的倒计时电子钟系统的汇编语言实现,使用了4位共阳数码管进行显示,其中,P0.0-P0.3用于控制数码管的位选,P2.0-P2.7用于控制数码管的段选,P3.2-P3.3用于接收按键输入,P3.4用于发出提示音,P3.5用于控制二极管显示。

```assembly
; 定义常数
CountDownMin equ 10 ; 初值为10分钟
CountDownSec equ CountDownMin * 60 ; 初值转换为秒数
DelayTime equ 50 ; 延时时间

; 定义端口地址
DataPort equ P2
SelectPort equ P0

; 定义按键和二极管的端口地址
KeyPort equ P3
BuzzerPort equ P3.4
LEDPort equ P3.5

; 定义数码管显示表
DisplayTable: db 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x80 ; 显示0-7的数字

; 定义变量
CountDownValue dw CountDownSec ; 倒计时初值,单位为秒
CurrentCountDownValue dw ? ; 当前倒计时值
IsCountingDown bit ? ; 是否正在倒计时

; 定义子程序
; 数码管显示子程序
DisplayDigit: ; 参数:DL-要显示的数字
    mov DataPort, DL ; 将要显示的数字送入数据口
    mov SelectPort, #0x01 ; 第一位数码管选通
    acall Delay ; 延时
    mov SelectPort, #0x02 ; 第二位数码管选通
    acall Delay ; 延时
    mov SelectPort, #0x04 ; 第三位数码管选通
    acall Delay ; 延时
    mov SelectPort, #0x08 ; 第四位数码管选通
    acall Delay ; 延时
    ret

; 延时子程序
Delay: ; 无参数
    mov R7, #DelayTime ; 将延时时间送入R7
    Delay1: ; 延时循环
        djnz R7, Delay1 ; 循环延时
        ret

; RTC由于没有具体的RTC模块的型号和接口信息,因此无法提供完整的RTC模块的代码实现。以下是基于假设的RTC模块接口实现的部分代码,需要根据实际的RTC模块接口进行修改。

; RTC读取子程序
ReadRTC: ; 无参数
    ; 假设RTC模块的接口为I2C
    ; 具体的I2C读取操作需要根据实际情况进行修改
    ; 假设读取的RTC数据保存在R0中
    ret

; 主程序
Main:
    ; 初始化
    mov IsCountingDown, #0 ; 清零计数标志
    mov CurrentCountDownValue, #0 ; 清零当前倒计时值
    mov BuzzerPort, #0 ; 关闭提示音
    mov LEDPort, #0 ; 关闭二极管显示

    ; 设置倒计时初值
    acall SetCountDownValue

    MainLoop:
        ; 读取RTC数据
        acall ReadRTC

        ; 更新倒计时值
        acall UpdateCountDownValue

        ; 显示倒计时值
        acall DisplayCountDownValue

        ; 判断是否倒计时结束
        cjne CurrentCountDownValue, #0, NotFinished ; 倒计时未结束,跳转到NotFinished标记
        mov IsCountingDown, #0 ; 倒计时结束,清零计数标志
        mov BuzzerPort, #1 ; 发出提示音
        mov LEDPort, #1 ; 显示二极管

        NotFinished:
        ; 处理按键输入
        acall CheckKeyInput

        ; 循环执行
        sjmp MainLoop

    ; 结束程序
    end

; 设置倒计时初值子程序
SetCountDownValue:
    ; 显示当前倒计时初值
    mov A, CountDownMin ; 将初值转换为十进制
    acall DisplayDigit ; 显示分钟数
    mov A, #':'
    acall DisplayDigit ; 显示冒号
    mov A, #0 ; 显示秒数的十位
    acall DisplayDigit ; 显示秒数的十位
mov A, #0 ; 显示秒数的个位
acall DisplayDigit ; 显示秒数的个位

mipsasm
Copy
; 等待按键输入
WaitForKey:
    jb KeyPort.2, WaitForKey ; 等待按键按下
    mov A, KeyPort ; 读取按键值
    anl A, #0x0C ; 只保留第3和第2位
    jz WaitForKey ; 如果按下的不是第3或第2个按键,则继续等待
    cjne A, #0x04, SetMin ; 如果按下的是第3个按键,则跳转到SetMin标记
    cjne A, #0x08, SetSec ; 如果按下的是第2个按键,则跳转到SetSec标记

; 设置分钟数
SetMin:
    mov A, CountDownMin ; 将初值转换为十进制
    acall DisplayDigit ; 显示分钟数
    mov A, #':'
    acall DisplayDigit ; 显示冒号
    mov A, #0 ; 显示秒数的十位
    acall DisplayDigit ; 显示秒数的十位
    mov A, #0 ; 显示秒数的个位
    acall DisplayDigit; 等待按键输入
WaitForKey:
    jb KeyPort.2, WaitForKey ; 等待按键按下
    mov A, KeyPort ; 读取按键值
    anl A, #0x0C ; 只保留第3和第2位
    jz WaitForKey ; 如果按下的不是第3或第2个按键,则继续等待
    cjne A, #0x04, SetMin ; 如果按下的是第3个按键,则跳转到SetMin标记
    cjne A, #0x08, SetSec ; 如果按下的是第2个按键,则跳转到SetSec标记

; 设置分钟数
SetMin:
    mov A, CountDownMin ; 将初值转换为十进制
    acall DisplayDigit ; 显示分钟数
    mov A, #':'
    acall DisplayDigit ; 显示冒号
    mov A, #0 ; 显示秒数的十位
    acall DisplayDigit ; 显示秒数的十位
    mov A, #0 ; 显示秒数的个位
    acall DisplayDigit
    ; 等待按键输入
    WaitForRelease:
        jnb KeyPort.2, WaitForRelease ; 等待按键释放
    acall Delay ; 延时

    ; 设置秒数
    SetSec:
        mov A, CurrentCountDownValue ; 将当前倒计时值转换为十进制
        div AB, #60 ; 将秒数转换为十进制
        mov A, B ; 取出十位数字
        acall DisplayDigit ; 显示秒数的十位
        mov A, #':'
        acall DisplayDigit ; 显示冒号
        mov A, AmodB ; 取出个位数字
        acall DisplayDigit ; 显示秒数的个位

        ; 等待按键输入
        WaitForKey2:
            jb KeyPort.2, WaitForKey2 ; 等待按键按下
            mov A, KeyPort ; 读取按键值
            anl A, #0x0C ; 只保留第3和第2位
            jz WaitForKey2 ; 如果按下的不是第3或第2个按键,则继续等待
            cjne A, #0x04, SetMin ; 如果按下的是第3个按键,则跳转到SetMin标记
            cjne A, #0x08, SetSec ; 如果按下的是第2个按键,则继续设置秒数

        ; 更新倒计时初值
        mov A, B ; 取出十位数字
        mov B, AmodB ; 取出个位数字
        mul AB, #60 ; 将十位数字乘以60
        add A, B ; 加上个位数字
        mov CountDownValue, A ; 将更新后的初值存入CountDownValue变量
        mov CurrentCountDownValue, A ; 将更新后的初值存入CurrentCountDownValue变量
        ret
; 更新倒计时值子程序
UpdateCountDownValue:
; 如果正在倒计时,则减去1秒
jb IsCountingDown, NotCountingDown ; 如果没有在倒计时,则跳转到NotCountingDown标记
dec CurrentCountDownValue ; 减去1秒
NotCountingDown:
ret

; 显示倒计时值子程序
DisplayCountDownValue:
; 将当前倒计时值转换为十进制
mov A, CurrentCountDownValue
div AB, #60 ; 将秒数转换为十进制
mov R7, A ; 保存十位数字
mov A, B ; 取出十位数字
acall DisplayDigit ; 显示秒数的十位
mov A, #':'
acall DisplayDigit ; 显示冒号
mov A, AmodB ; 取出个位数字
acall DisplayDigit ; 显示秒数的个位
ret

; 显示数字子程序
DisplayDigit:
; 将数字转换为7段LED的编码
anl A, #0x0F ; 只保留低4位
mov R6, #0xFF ; 设置默认值为全灭
cjne A, #0, CheckOne ; 如果是0以外的数字,则跳转到CheckOne标记
mov R6, #0xC0 ; 如果是0,则设置为0的编码
sjmp DisplayLEDs ; 跳转到DisplayLEDs标记
CheckOne:
cjne A, #1, CheckTwo ; 如果是1以外的数字,则跳转到CheckTwo标记
mov R6, #0xF9 ; 如果是1,则设置为1的编码
sjmp DisplayLEDs ; 跳转到DisplayLEDs标记
CheckTwo:
cjne A, #2, CheckThree ; 如果是2以外的数字,则跳转到CheckThree标记
mov R6, #0xA4 ; 如果是2,则设置为2的编码
sjmp DisplayLEDs ; 跳转到DisplayLEDs标记
CheckThree:
cjne A, #3, CheckFour ; 如果是3以外的数字,则跳转到CheckFour标记
mov R6, #0xB0 ; 如果是3,则设置为3的编码
sjmp DisplayLEDs ; 跳转到DisplayLEDs标记
CheckFour:
cjne A, #4, CheckFive ; 如果是4以外的数字,则跳转到CheckFive标记
mov R6, #0x99 ; 如果是4,则设置为4的编码
sjmp DisplayLEDs ; 跳转到DisplayLEDs标记
CheckFive:
cjne A, #5, CheckSix ; 如果是5以外的数字,则跳转到CheckSix标记
mov R6, #0x92 ; 如果是5,则设置为5的编码
sjmp DisplayLEDs ; 跳转到DisplayLEDs标记
CheckSix:
cjne A, #6, CheckSeven ; 如果是6以外的数字,则跳转到CheckSeven标记
mov R6, #0x82 ; 如果是6,则设置为6的编码
sjmp DisplayLEDs ; 跳转到DisplayLEDs标记
CheckSeven:
cjne A, #7, CheckEight ; 如果是7以外的数字,则跳转到CheckEight标记
mov R6, #0xF8 ; 如果是7,则设置为7的编码
sjmp DisplayLEDs ; 跳转到DisplayLEDs标记
CheckEight:
cjne A, #8, CheckNine ; 如果是8以外的数字,则跳转到CheckNine标记
mov R6, #0x80 ; 如果是8,则设置为8的编码
sjmp DisplayLEDs ; 跳转到DisplayLEDs标记
CheckNine:
mov R6, #0x90 ; 如果是9,则设置为9的编码
; 显示数字
DisplayLEDs:
mov P1, R6 ; 将编码输出到7段LED
acall Delay ; 延时
mov P1, #0xFF ; 关闭7段LED
acall Delay ; 延时



#define ds1302_sec_add          0x80        
#define ds1302_min_add          0x82        
#define ds1302_hr_add           0x84        
#define ds1302_date_add         0x86        
#define ds1302_month_add        0x88        
#define ds1302_day_add          0x8a        
#define ds1302_year_add         0x8c        
#define ds1302_control_add      0x8e        
#define ds1302_charger_add      0x90                     
#define ds1302_clkburst_add     0xbe

sbit SCK = P2^2;
sbit IO = P2^3;
sbit RST = P2^4;

void ds1302_write_byte(unsigned char addr, unsigned char byte)
{
      unsigned char i;
      addr = addr & 0xfe;
      SCK = 0;
      RST = 0;

      RST = 1;
      for(i = 0; i < 8; i++)
      {
               IO = addr & 0x01;
              SCK = 0;
              SCK = 1;
              addr >>= 1;
       }
      for(i = 0; i < 8; i++)
      {
               IO = byte & 0x01;
              SCK = 0;
              SCK = 1;
              byte >>= 1;
      }
}

unsigned char ds1302_read_byte(unsigned char addr)
{
      unsigned char i;
      unsigned char temp = 0;
      addr = addr & 0xfe;
      SCK = 0;
      RST = 0;

      RST = 1;
      addr =addr + 1;
      for(i = 0; i < 8; i++)
      {
               IO = addr & 0x01;
              SCK = 0;
              SCK = 1;
              addr >>= 1;
       }
       for(i = 0; i < 8; i++)
       {
              SCK = 1;
              SCK = 0;
              temp >>= 1;
              if(IO)
              {
                  temp += 0x80;
              }
        }
        RST = 0;
        return temp;
}
void ds1302_write_time()
{
      unsigned char temp;
      unsigned char temp1;
      unsigned char i;
      for(i = 0; i < 7; i++)
      {
              temp = timebuf[i]/10;
              temp1 = timebuf[i]%10;
              disbuf[i] = (temp << 4) | temp1;
      }
    ds1302_write_byte(ds1302_control_add, 0x00);
    ds1302_write_byte(ds1302_hr_add, disbuf[0]);
    ds1302_write_byte(ds1302_min_add, disbuf[1]);
    ds1302_write_byte(ds1302_sec_add, disbuf[2]);
    ds1302_write_byte(ds1302_year_add, disbuf[3]);
    ds1302_write_byte(ds1302_month_add, disbuf[4]);
    ds1302_write_byte(ds1302_date_add, disbuf[5]);
    ds1302_write_byte(ds1302_day_add, disbuf[6]);
    ds1302_write_byte(ds1302_control_add, 0x80);
}
void ds1302_read_time()
{
    unsigned char temp;
    unsigned char temp1;
    unsigned char i;
    readbuf[0] = ds1302_read_byte(ds1302_hr_add);
    readbuf[1] = ds1302_read_byte(ds1302_min_add);
    readbuf[2] = ds1302_read_byte(ds1302_sec_add);
    readbuf[3] = ds1302_read_byte(ds1302_year_add);
    readbuf[4] = ds1302_read_byte(ds1302_month_add);
    readbuf[5] = ds1302_read_byte(ds1302_date_add);
    readbuf[6] = ds1302_read_byte(ds1302_day_add);

    for(i = 0; i < 7; i++)
    {
        temp = readbuf[i]>>4;
        temp1 = readbuf[i]&0x0f;
        disbuf[i] = temp*10+temp1;
    }
}


下面是示例代码,可以参考一下

;---------------------51单片机及RTC实时时钟倒计时系统---------------------

;---------------------数据定义---------------------
;定时器常数值
TH0 equ 4ch
TL0 equ 4dh
;RTC寄存器地址
RTC_Addr equ 30h
;数码管的控制信号
DIG1 equ P2.0
DIG2 equ P2.1
DIG3 equ P2.2
DIG4 equ P2.3
;数码管的显示数据
digit_num equ 4
number_table:     db 0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f
;初始倒计时时间(20秒)
timebase equ 20
;按键控制
KEY1 equ P3.1

;---------------------程序代码---------------------
;中断服务程序
Timer0_ISR:         ;定时器0中断服务程序
    push    ACC
    push    PSW
    mov     RTC_Addr,#0ch
    inc     @RTC_Addr
    cjne    @RTC_Addr,#0x60,SECOND_EXIT   ;累加秒计数
    mov     @RTC_Addr,#0               ;清零秒计数
    mov     RTC_Addr,#0dh              ;取出分钟寄存器地址
    inc     @RTC_Addr
    cjne    @RTC_Addr,#0x60,MINUTE_EXIT   ;累加分计数
    mov     @RTC_Addr,#0               ;清零分计数
    mov     RTC_Addr,#0eh              ;取出小时寄存器地址
    inc     @RTC_Addr
    cjne    @RTC_Addr,#0x24,HOUR_EXIT   ;累加时计数
    mov     @RTC_Addr,#0               ;清零时计数
HOUR_EXIT:
MINUTE_EXIT:
SECOND_EXIT:
    pop     PSW
    pop     ACC
    reti

;子程序:延时1ms
Delay1ms:           ;在12MHz下,定时器0计数65536次为1秒,所以6553次为1ms
    push    ACC
    push    P0
    push    P1
    push    PSW         
    mov     TMOD,#1h         ;定时器0,模式1
    mov     TH0,#0f9h       ;定时1ms
    mov     TL0,#0h
    setb    TR0             ;定时器0开始计时
DELAY:  
    jnb     TF0,$          ;等待1ms
    clr     TF0             ;清除定时器0中断标志位
    djnz    R6,$           ;再次计数
    clr     TR0             ;时钟停止
    pop     PSW
    pop     P1
    pop     P0
    pop     ACC
    ret

;子程序:LED指示灯亮
LED_ON:             ;LED指示灯亮
    setb    P3.2
    ret
    
;子程序:LED指示灯灭
LED_OFF:            ;LED指示灯灭
    clr     P3.2
    ret

;主程序
MAIN:
    mov     R7,#digit_num-1  ;从右往左扫描数码管

    INIT:
        ;初始化定时器0
        mov     R6,#5           ;延时5ms用于给数码管稳定时间
        mov     TMOD,#1h        ;定时器0,模式1
        mov     TH0,#0f9h      ;定时1ms
        mov     TL0,#0h
        setb    TR0             ;定时器0开始计时
        init_ds1302:
            ;初始化DS1302
            mov     RTC_Addr,#8ch   ;写入控制命令字,禁止写保护
            mov     ACC,#0x80
            movx    @DPTR,ACC
            mov     RTC_Addr,#0cch  ;写入年月日寄存器地址,禁止写保护
            mov     ACC,#20h       ;年
            movx    @DPTR,ACC
            mov     RTC_Addr,#0dch  ;写入星期寄存器地址,禁止写保护
            mov     ACC,#06h       ;星期六
            movx    @DPTR,ACC
            mov     RTC_Addr,#0ach  ;写入时分秒寄存器地址,禁止写保护
            mov     ACC,#0         ;秒
            movx    @DPTR,ACC
            mov     ACC,#0         ;分
            movx    @DPTR,ACC
            mov     ACC,#0         ;小时
            movx    @DPTR,ACC
            ;读取RTC时间
            mov     RTC_Addr,#81h   ;启动DS1302读取时钟寄存器数据
            mov     ACC,#0         ;全选
            movx    @DPTR,ACC
    ;主循环
    LOOP:
        ;读取按键,以重新设置倒计时时间
        mov     A,KEY1
        cjne    A,#0,RELOAD      ;按键按下,则重新设置倒计时时间

        ;读取RTC时间
        mov     RTC_Addr,#81h   ;启动DS1302读取时钟寄存器数据
        mov     ACC,#0         ;全选
        movx    @DPTR,ACC
        mov     RTC_Addr,#0ah   ;读秒寄存器
        movx    A,@DPTR
        push    ACC
        mov     RTC_Addr,#0bh   ;读分寄存器
        movx    A,@DPTR
        push    ACC
        mov     RTC_Addr,#0ch   ;读时寄存器
        movx    A,@DPTR
        push    ACC

        ;显示倒计时时间
        mov     A,R7            ;取出当前扫描的位数
        mov     P2,#0ffh
        cjne    A,#3,SHIFT     ;第一位不需要移位
        mov     A,#0c0h        ;第一位显示冒号
        jmp     DIG_DISPLAY    
SHIFT:
        mov     A,R7            ;取出当前扫描的位数
        subb    A,#4
        mov     C,A
        mov     A,#1
        rlc     A
        add     A,#8
        rlc     A
        addc    A,#0
        rlc     A
        addc    A,#0
        rlc     A
        addc    A,C
        anl     A,#0fh
        mov     R0,A
        mov     A,number_table(R0)  ;读取当前数码
DIG_DISPLAY:
        mov     @R0,A          ;将当前数码送入缓存区    
        mov     A,P2
        anl     A,8-R7
        jz      DIG_END            ;该位是当前显示位
        orl     A,#0fh
        mov     P2,A            ;关闭该位数码管
        sjmp    NEXT_DIG
DIG_END:
        mov     A,@R0
        mov     P2,A
        sjmp    NEXT_DIG

    ;扫描下一位数码管
    NEXT_DIG:
        DEC     R7
        cpl     P2            ;打开下一位数码管
        mov     R6,#4
        djnz    R6,$          ;等待若干微秒
        cpl     P2            ;关闭当前数码管
        mov     R6,#5
        djnz    R6,$          ;等待若干微秒
        jnb     P3.7,NEXT_DIG    ;等待下一次定时时钟中断

    ;重新设置倒计时时间
    RELOAD:
        mov     R6,#10          ;延时10ms用于去抖
        Delay1ms
        mov     R6,#10          ;设置10秒倒计时
        mov     A,#10
        mov     RTC_Addr,#0ch   ;写时寄存器地址
        movx    @DPTR,A
        dec     A
        mov     RTC_Addr,#0bh   ;写分寄存器地址
        movx    @DPTR,A
        mov     RTC_Addr,#0ah   ;写秒寄存器地址
        movx    @DPTR,#0
        LED_OFF             ;LED指示灯熄灭
        clr     P1.0            ;蜂鸣器停止发声
        jnb     KEY1,$          ;等待按键松开
        mov     R7,#digit_num-1  ;从右往左扫描数码管
        sjmp    INIT

    END
  • 这篇博客: 蓝桥杯嵌入式 ---- 十二届官方模拟试题解析中的 5.RTC实时时钟 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
  • 代码如下:rtc.c:

    #include "rtc.h"
    u8 TimeDisplay=0;
    void rtc_init(void)
    {
      NVIC_InitTypeDef NVIC_InitStructure;
    
      /* Configure one bit for preemption priority */
      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
    
      /* Enable the RTC Interrupt */
      NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
      NVIC_Init(&NVIC_InitStructure);
    
      /* Enable PWR and BKP clocks */
      RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
    
      /* Allow access to BKP Domain */
      PWR_BackupAccessCmd(ENABLE);
    
      /* Reset Backup Domain */
      BKP_DeInit();
    
      /* Enable the LSI OSC */
      RCC_LSICmd(ENABLE);
      /* Wait till LSI is ready */
      while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET)
      {}
      /* Select the RTC Clock Source */
      RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
    
      /* Enable RTC Clock */
      RCC_RTCCLKCmd(ENABLE);
    
      /* Wait for RTC registers synchronization */
      RTC_WaitForSynchro();
    
      /* Wait until last write operation on RTC registers has finished */
      RTC_WaitForLastTask();
    
      /* Enable the RTC Second */
      RTC_ITConfig(RTC_IT_SEC, ENABLE);
    
      /* Wait until last write operation on RTC registers has finished */
      RTC_WaitForLastTask();
    
      /* Set RTC prescaler: set RTC period to 1sec */
      RTC_SetPrescaler(40000);
    
      /* Wait until last write operation on RTC registers has finished */
      RTC_WaitForLastTask();
    }
    void RTC_IRQHandler(void)   //每1S进入中断
    {
      if (RTC_GetITStatus(RTC_IT_SEC) != RESET)
      {
        /* Clear the RTC Second interrupt */
        RTC_ClearITPendingBit(RTC_IT_SEC);
    
    
        /* Enable time update */
        TimeDisplay = 1;         //主函数死循环里的标志位,对定时值进行自加,达到计时的效果
    
        /* Wait until last write operation on RTC registers has finished */
        RTC_WaitForLastTask();
        
      }
    }
    
    

    rtc.h:

    #ifndef RTC_H
    #define RTC_H
    
    #include "stm32f10x.h"
    extern u8 TimeDisplay;
    void rtc_init(void);
    
    #endif