闹钟寄存器中断的实现-库函数版(STC8)
本文介绍了STC8单片机中RTC闹钟中断功能的实现方法。主要内容包括:1) STC8系列单片机RTC闹钟模块的核心寄存器组及其功能,如时间设置寄存器、控制寄存器和标志寄存器;2) 关键寄存器的详细解析,包括闹钟时间寄存器的BCD码存储格式和特殊掩码功能;3) 完整的闹钟中断配置流程,包含RTC初始化、闹钟时间设置和中断服务程序编写。文中还提供了基于STC8H1K的寄存器定义和配置代码示例,展示了如
基于STC8单片机的闹钟寄存器中断
在嵌入式系统中,精准的定时唤醒功能是许多应用的核心需求——从智能家居的定时开关,到工业设备的周期性巡检,再到便携式设备的低功耗设计,都离不开可靠的闹钟中断机制。STC8系列单片机内置的RTC闹钟模块,通过丰富的寄存器配置,可实现年月日级到秒级的精准定时,且功耗低至微安级,是替代传统定时芯片的理想选择。
一、STC8闹钟中断的核心寄存器
STC8的RTC闹钟功能由专用寄存器组控制,这些寄存器负责设置闹钟时间、配置中断触发条件和管理中断状态。以STC8H1K08为例,核心寄存器包括:
| 寄存器名称 | 地址 | 功能描述 |
|---|---|---|
| RTC_ALARM_YEAR | 0xF0 | 闹钟年份寄存器(BCD码,00-99) |
| RTC_ALARM_MONTH | 0xF1 | 闹钟月份寄存器(BCD码,01-12) |
| RTC_ALARM_DAY | 0xF2 | 闹钟日期寄存器(BCD码,01-31) |
| RTC_ALARM_HOUR | 0xF3 | 闹钟小时寄存器(BCD码,00-23) |
| RTC_ALARM_MIN | 0xF4 | 闹钟分钟寄存器(BCD码,00-59) |
| RTC_ALARM_SEC | 0xF5 | 闹钟秒寄存器(BCD码,00-59,部分型号无) |
| RTC_CON | 0xF6 | RTC控制寄存器,包含闹钟中断使能位 |
| RTC_FLAG | 0xF7 | RTC标志寄存器,包含闹钟中断标志位 |
这些寄存器采用BCD码存储时间(如"15时"存储为0x15),与RTC实时时间寄存器的格式保持一致,便于比较匹配。
二、关键寄存器深度解析
1. 闹钟时间寄存器(RTC_ALARM_*)
闹钟时间寄存器用于设置触发时刻,每个字段的含义与RTC实时时间寄存器一一对应:
- 年份:00-99(与RTC_YEAR对应)
- 月份:01-12(与RTC_MONTH对应)
- 日期:01-31(与RTC_DAY对应)
- 小时:00-23(与RTC_HOUR对应)
- 分钟:00-59(与RTC_MIN对应)
特殊值与掩码功能:部分型号支持"不比较"掩码,例如将月份设为0x00表示"不比较月份",仅当其他字段匹配时触发闹钟。这种设计让闹钟可以灵活配置为"每天固定时间"(忽略年月日)或"每月固定日期"(忽略年份)等模式。
2. RTC控制寄存器(RTC_CON)
RTC_CON的关键位定义(不同型号可能有差异):bit7: RTC_EN - RTC使能位(1=开启RTC)
bit6: RTC_RDY - RTC就绪标志(1=RTC稳定运行)
bit5: ALARM_EN - 闹钟中断使能位(1=允许闹钟中断)
bit4: … - 其他功能位(如温度补偿、时钟源选择)
配置示例:使能闹钟中断RTC_CON |= (1 << 5); // ALARM_EN=1
3. RTC标志寄存器(RTC_FLAG)
RTC_FLAG用于记录中断状态,必须在中断服务程序中手动清除:bit0: ALARM_FLAG - 闹钟中断标志(1=闹钟触发)
清除方法:RTC_FLAG &= ~(1 << 0); // 清除闹钟中断标志
三、闹钟中断的完整配置流程
实现闹钟中断需要三个核心步骤:初始化RTC、配置闹钟时间、使能中断并编写服务程序。以下是基于STC8H1K的完整代码示例。
1. 寄存器定义与类型声明// 寄存器定义(根据数据手册调整地址)
// I2C 配置函数定义
/**************** I2C初始化函数 *****************/
static void I2C_config(void)
{
I2C_InitTypeDef I2C_InitStructure;
I2C_InitStructure.I2C_Mode = I2C_Mode_Master; //主从选择 I2C_Mode_Master, I2C_Mode_Slave
I2C_InitStructure.I2C_Enable = ENABLE; //I2C功能使能, ENABLE, DISABLE
I2C_InitStructure.I2C_MS_WDTA = DISABLE; //主机使能自动发送, ENABLE, DISABLE
I2C_InitStructure.I2C_Speed = 13; //总线速度=Fosc/2/(Speed*2+4), 0~63
// 400k, 24M => 13
I2C_Init(&I2C_InitStructure);
NVIC_I2C_Init(I2C_Mode_Master,DISABLE,Priority_0); //主从模式, I2C_Mode_Master, I2C_Mode_Slave; 中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3
I2C_SW(I2C_P33_P32); //I2C_P14_P15,I2C_P24_P25,I2C_P33_P32
}
// 外部中断3配置
/******************** INT配置 ********************/
static void Exti_config(void)
{
EXTI_InitTypeDef Exti_InitStructure; //结构定义
Exti_InitStructure.EXTI_Mode = EXT_MODE_RiseFall;//中断模式, EXT_MODE_RiseFall,EXT_MODE_Fall
Ext_Inilize(EXT_INT3,&Exti_InitStructure); //初始化
NVIC_INT3_Init(ENABLE,Priority_0); //中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3
}
// PCF8563初始化
void PCF8563_init() {
EAXSFR(); /* 扩展寄存器访问使能 */
// P32 P33 开漏
P3_MODE_OUT_OD(GPIO_Pin_2 | GPIO_Pin_3);
// P37 准双向
P3_MODE_IO_PU(GPIO_Pin_7);
I2C_config(); // I2C配置
Exti_config(); // 外部中断3
}
时间结构体(BCD码转十进制)
typedef struct {
u16 year;
u8 month;
u8 day;
u8 weekday;
u8 hour;
u8 minute;
u8 second;
} Clock_t;
2. RTC与闹钟初始化
// 设置时间
void PCF8563_set_clock(Clock_t temp) {
u8 p[7] = {0}; // 写完整 年月日 星期几 时分秒
// 秒的寄存器地址为: 0x02
// 秒: 第0~3位记录个位,第4~6位记录十位
// 十位 个位
p[0] = WRITE_BCD(temp.second);
// 分: 第0~3位,保存个数,第4到6位,保存十位
p[1] = WRITE_BCD(temp.minute);
// 时:第0~3位,保存个数,第4到5位,保存十位
p[2] = WRITE_BCD(temp.hour);
// 日:第0~3位,保存个数,第4到5位,保存十位
p[3] = WRITE_BCD(temp.day);
// 周:第0~2位,保存个数
p[4] = temp.weekday;
// 月_世纪: 第0~3位记录个位,第4位记录十位,第7位为0,世纪数为20xx,为1,世纪数为21xx
p[5] = WRITE_BCD(temp.month);
// 月的第7位
if (temp.year >= 2100) { // 第7位置1
p[5] |= (1 << 7);
} // 第7位置0,不处理就是0
// 年:第0~3位,保存个数,第4到7位,保存十位
// 2025 ===> 25
p[6] = WRITE_BCD(temp.year%100);
I2C_WriteNbyte(PCF8563_DEV_ADDR, PCF8563_REG_SECOND, p, 7);
}
// 获取时间
void PCF8563_get_clock(Clock_t *temp) {
u8 p[7] = {0};
u8 flag;
// 读时间
I2C_ReadNbyte(PCF8563_DEV_ADDR, PCF8563_REG_SECOND, p, 7);
// 10进制 用 16进制表示 低4位放个位 高4位放10位
// 秒: 第0~3位记录个位,第4~6位记录十位
temp->second = READ_BCD(p[0]);
// 分: 第0~3位,保存个数,第4到6位,保存十位
temp->minute = READ_BCD(p[1]);
// 时:第0~3位,保存个数,第4到5位,保存十位
temp->hour = READ_BCD(p[2]);
// 日:第0~3位,保存个数,第4到5位,保存十位
temp->day = READ_BCD(p[3]);
// 周:第0~2位,保存个数
temp->weekday = p[4]; // 如果是星期日,是0
// 月_世纪: 第0~3位记录个位,第4位记录十位,第7位为0,世纪数为20xx,为1,世纪数为21xx
// 处理第7位
// 取出第7位
flag = p[5] >> 7;
// 第7位置0, 月的第7位,是年的标志位,不是月的有效数据
p[5] &= ~(1 << 7);
temp->month = READ_BCD(p[5]);
// 年:第0~3位,保存个数,第4到7位,保存十位
temp->year = READ_BCD(p[6]);
if (flag == 1) temp->year += 2100;
else temp->year += 2000;
}
// 设置闹钟
void PCF8563_set_alarm(Alarm_t alarm) {
u8 p[4] = {0};
//===================2.1 闹钟时间设置 寄存器地址 0x09
// 分: 第0~3位,记录个数, 第4~6位记录十位, 第7位:置0启动, 置1禁用
if (alarm.minute != -1) p[0] = WRITE_BCD(alarm.minute);
else p[0] = 0x80;
// 时: 第0~3位,记录个数, 第4~5位记录十位, 第7位:置0启动, 置1禁用
p[1] = alarm.hour != -1 ? WRITE_BCD(alarm.hour) : 0x80 ;
// 日: 第0~3位,记录个数, 第4~5位记录十位, 第7位:置0启动, 置1禁用
p[2] = alarm.day != -1 ? WRITE_BCD(alarm.day) : 0x80 ;
// 周: 第0~2位,记录个数, 第7位:置0启动, 置1禁用
p[3] = alarm.weekday != -1 ? alarm.weekday : 0x80 ;
I2C_WriteNbyte(PCF8563_DEV_ADDR, 0x09, p, 4);
}
// 启用闹钟
void PCF8563_enable_alarm() {
u8 cfg;
//===================2.2 闹钟开启 寄存器地址 0x01
//a) 读原来的配置(不要乱改配置,只改自己的位,其它维持不变)
I2C_ReadNbyte(PCF8563_DEV_ADDR, 0x01, &cfg, 1);
//b) 在原来配置的基础上,清除标志位 第3位:置0清除标志位,置1维持不变
cfg &= ~(1 << 3);
//c) 在原来配置基础上,启动闹钟,第1位:置0禁用,置1启动
cfg |= (1 << 1);
//d) 重新写入配置
I2C_WriteNbyte(PCF8563_DEV_ADDR, 0x01, &cfg, 1);
}
// 禁用闹钟Alarm
void PCF8563_disable_alarm() {
u8 cfg;
//===================2.2 闹钟 寄存器地址 0x01
//a) 读原来的配置(不要乱改配置,只改自己的位,其它维持不变)
I2C_ReadNbyte(PCF8563_DEV_ADDR, 0x01, &cfg, 1);
//b) 在原来配置的基础上,清除标志位 第3位:置0清除标志位,置1维持不变
cfg &= ~(1 << 3);
//c) 在原来配置基础上,禁用闹钟,第1位:置0禁用,置1启动
cfg &= ~(1 << 1);
//d) 重新写入配置
I2C_WriteNbyte(PCF8563_DEV_ADDR, 0x01, &cfg, 1);
}
// 清理闹钟标记
void PCF8563_alarm_clear_flag() {
u8 cfg;
// 清除闹钟的标志位,才能重复触发闹钟
//a) 读原来的配置(不要乱改配置,只改自己的位,其它维持不变)
I2C_ReadNbyte(PCF8563_DEV_ADDR, 0x01, &cfg, 1);
//b) 在原来配置的基础上,清除标志位 第3位:置0清除标志位,置1维持不变
cfg &= ~(1 << 3);
//c) 重新写入配置
I2C_WriteNbyte(PCF8563_DEV_ADDR, 0x01, &cfg, 1);
}
3. 闹钟中断服务程序// 闹钟中断服务函数(复用T2中断,向量地址0x2B)
Exti.Isr.c代码文件
void int3_callback(); // 声明
/========================================================================
// 函数: INT3_ISR_Handler
// 描述: INT3中断函数.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2020-09-23
//========================================================================
void INT3_ISR_Handler (void) interrupt INT3_VECTOR //进中断时已经清除标志
{
// TODO: 在此处添加用户代码
// P03 = ~P03;
int3_callback(); // 调用
WakeUpSource = 4;
}
4. 主函数调用void main() {
#include "GPIO.h"
#include "Delay.h"
#include "UART.h" // 串口配置 UART_Configuration
#include "NVIC.h" // 中断初始化NVIC_UART1_Init
#include "Switch.h" // 引脚切换 UART1_SW_P30_P31
#include "PCF8563.h"
void GPIO_config() {
GPIO_InitTypeDef info;
// ===== UART1 P30 P31
info.Mode = GPIO_PullUp; // 准双向
info.Pin = GPIO_Pin_0 | GPIO_Pin_1; // 引脚
GPIO_Inilize(GPIO_P3, &info);
}
// 串口配置函数的定义
void UART_config(void) {
// >>> 记得添加 NVIC.c, UART.c, UART_Isr.c <<<
COMx_InitDefine COMx_InitStructure; //结构定义
COMx_InitStructure.UART_Mode = UART_8bit_BRTx; //模式, UART_ShiftRight,UART_8bit_BRTx,UART_9bit,UART_9bit_BRTx
COMx_InitStructure.UART_BRT_Use = BRT_Timer1; //选择波特率发生器, BRT_Timer1, BRT_Timer2 (注意: 串口2固定使用BRT_Timer2)
COMx_InitStructure.UART_BaudRate = 115200ul; //波特率, 一般 110 ~ 115200
COMx_InitStructure.UART_RxEnable = ENABLE; //接收允许, ENABLE或DISABLE
COMx_InitStructure.BaudRateDouble = DISABLE; //波特率加倍, ENABLE或DISABLE
UART_Configuration(UART1, &COMx_InitStructure); //初始化串口1 UART1,UART2,UART3,UART4
NVIC_UART1_Init(ENABLE,Priority_1); //中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3
UART1_SW(UART1_SW_P30_P31); // 引脚选择, UART1_SW_P30_P31,UART1_SW_P36_P37,UART1_SW_P16_P17,UART1_SW_P43_P44
}
void int3_callback() { // 外部中断3的回调
Clock_t temp;
printf("===========alarm===========\n");
// 清理闹钟标记
PCF8563_alarm_clear_flag();
// ============== 重置时间,为了重复验证闹钟
temp.year = 2025, temp.month = 8, temp.day = 11;
temp.weekday = 1; // 如果是星期日,是0
temp.hour = 23, temp.minute = 59, temp.second = 54;
PCF8563_set_clock(temp); // 设置时间
}
void main() {
Clock_t temp; // 结构体变量
Alarm_t alarm;
EA = 1; // 使能中断总开关
GPIO_config(); // GPIO配置
UART_config(); // 串口配置
PCF8563_init(); // PCF8563 初始化
// ==============================写时间
temp.year = 2025, temp.month = 8, temp.day = 11;
temp.weekday = 1; // 如果是星期日,是0
temp.hour = 23, temp.minute = 59, temp.second = 54;
PCF8563_set_clock(temp); // 设置时间
// ============================== 闹钟
// 不匹配 day 和 weekday
alarm.day = -1, alarm.weekday = -1, alarm.hour = 0, alarm.minute = 0;
PCF8563_set_alarm(alarm); // 设置闹钟时间
PCF8563_enable_alarm(); // 启动闹钟
// PCF8563_disable_alarm(); // 禁用闹钟
while (1){
PCF8563_get_clock(&temp); // 获取时间
printf("%02d-%02d-%02d\n", (int)temp.year, (int)temp.month, (int)temp.day);
printf("weekday: %02d\n", (int)temp.weekday);
printf("%02d:%02d:%02d\n", (int)temp.hour, (int)temp.minute, (int)temp.second);
delay_ms(250); // 每隔1s 发送1次
delay_ms(250);
delay_ms(250);
delay_ms(250);
}
}
四、实战技巧与优化方案
1. 闹钟模式灵活配置
通过设置"不比较"掩码,可实现多种定时模式:
- 每日闹钟:年、月、日设为00,仅匹配时、分
- 每月闹钟:年设为00,匹配月、日、时、分
- 单次闹钟:全字段匹配,触发后关闭闹钟使能
2. 低功耗设计要点
- 主程序在等待闹钟时可进入掉电模式(Power-Down)
- 通过RTC闹钟中断唤醒单片机,电流可降至1μA以下// 进入掉电模式等待闹钟
PCON |= 0x02; // 置位PD位进入掉电模式
nop();
3. 时间同步与校准
- 定期通过串口或NTP服务器同步时间到RTC
- 利用RTC的校准寄存器(如RTC_CAL)补偿晶振误差
结语
STC8单片机的闹钟寄存器中断系统,通过精细化的寄存器配置,为嵌入式设备提供了低功耗、高精度的定时解决方案。掌握RTC_ALARM_*系列寄存器的配置逻辑,理解闹钟中断的使能与标志管理机制,是实现可靠定时功能的核心。无论是简单的每日提醒,还是复杂的多模式定时任务,STC8的闹钟中断都能以简洁的硬件设计和高效的软件逻辑满足需求,成为嵌入式系统中的"精准时间管家"。
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐

所有评论(0)