基于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
代码如下: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