- 打卡等级:常住居民II
- 打卡总天数:93
- 最近打卡:2025-06-14 13:12:15
超级版主
- 积分
- 12717
|
你不用担心这些问题,一个MCU的中断是不会跑飞的,基本肯定是你的程序的问题,你肯定是关闭RTC后依旧查询RTC相关的信息,导致一直查询卡死。
专门改个例子给你测试,直接下载HEX文件测试,主频11.0592MHz。
主程序开启RTC 5秒,RTC秒中断返回日期、时间。
然后关闭RTC 3秒,暂停返回3秒钟。
之后又开启RTC,继续返回时间5秒。
重复上述操作,并未出现你说的卡死情况,程序运行正常,我的程序还不是一个很简单的程序。
==========================================
请先别修改程序, 直接下载"02A-RTC(内部32K)基本操作-串口1设置返回"里的"rtc.hex"测试, 主频选择11.0592MHZ. 测试正常后再修改移植.
MCU通过串口1返回: 年 月 日 时 分 秒: 21-6-19 12:34:56
通过串口发送设置值: 21-6-19 12:34:56
通过串口发送闹钟值: A12:34:56
默认参数:
串口1设置均为 1位起始位, 8位数据位, 1位停止位, 无校验.
串口1(P3.0 P3.1): 115200bps.
==========================================
测试结果截图:
工程源码压缩包:
02A-RTC(内部32K)基本操作-串口1设置返回.rar
(136.9 KB, 下载次数: 0)
为了表明我的程序不是简单程序。源码拷贝如下:
/*********************************************************/
#define FOSC 11059200L //定义主时钟
#include "..\STC8Hxxx.h"
/************* 功能说明 **************
请先别修改程序, 直接下载"02A-RTC(内部32K)基本操作-串口1设置返回"里的"rtc.hex"测试, 主频选择11.0592MHZ. 测试正常后再修改移植.
MCU通过串口1返回: 年 月 日 时 分 秒: 21-6-19 12:34:56
通过串口发送设置值: 21-6-19 12:34:56
通过串口发送闹钟值: A12:34:56
默认参数:
串口1设置均为 1位起始位, 8位数据位, 1位停止位, 无校验.
串口1(P3.0 P3.1): 115200bps.
串口要发送数据2次(间隔4秒内), 第一次唤醒(唤醒需要时间, 第一次接收数据将会错误), 第二次数据才会正确接收.
******************************************/
/************* 本地常量声明 **************/
#define RX1_Length 32 /* 接收缓冲长度 */
/************* 本地变量声明 **************/
u8 xdata RX1_Buffer[RX1_Length]; //接收缓冲
u8 RX1_Cnt; //串口接收计数.
u8 RX1_TimeOut; //串口接收超时
bit B_TX1_Busy; // 发送忙标志
u8 year, month, day, hour, minute, second; //RTC实时时间
bit B_1S; //秒中断, 秒有变化
bit B_10ms;
u8 OpTime; // 此变量非0时, 不睡眠, 连续运行程序(本例串口唤醒后连续运行5秒, 以便正确接收串口数据)
u8 GetDataIndex; //获取数据索引仅仅用于读取计数
u8 GetDataState; //获取数据状态, 非0则错误
u8 ALARM_hour; // RTC闹钟的小时值
u8 ALARM_minute; // RTC闹钟的分钟值
u8 ALARM_second; // RTC闹钟的秒值
bit B_ALARM;
u16 Nx10ms;
/************* 本地函数声明 **************/
void UART1_config(u32 brt, u8 timer, u8 io); // brt: 通信波特率, timer=2: 波特率使用定时器2, 其它值: 使用Timer1做波特率. io=0: 串口1切换到P3.0 P3.1, =1: 切换到P3.6 P3.7, =2: 切换到P1.6 P1.7.
void UART1_TxByte(u8 dat);
void UART1_PrintString(u8 *puts);
u8 SetRTC(void); //设置RTC时间函数
void RTC_config(void); //RTC初始化函数
void RTC_read(void); //读RTC时间函数
void ReturnRTC(void); //返回时间信息
u8 GetData(void); //获取数据, 设置时间命令: 21-6-19 12:34:56
void delay_ms(u8 ms);
u8 Timer0_Config(u8 t, u32 reload); //t=0: reload值是主时钟周期数, t=1: reload值是时间(单位us), 返回0正确, 返回1装载值过大错误.
//========================================================================
// 函数: void main(void)
// 描述: 主函数
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注:
//========================================================================
void main(void)
{
P_SW2 |= 0x80; //SFR enable
P0M1 = 0; P0M0 = 0;
P1M1 = 0; P1M0 = 0;
P2M1 = 0; P2M0 = 0;
P3M1 = 0; P3M0 = 0;
P4M1 = 0; P4M0 = 0;
P5M1 = 0; P5M0 = 0;
P1n_pure_input(Pin7+Pin6);
EA = 1;
OpTime = 0;
RTC_config();
UART1_config(115200UL, 1, 0); // brt: 通信波特率, timer=2: 波特率使用定时器2, 其它值: 使用Timer1做波特率. io=0: 串口1切换到P3.0 P3.1, =1: 切换到P3.6 P3.7, =2: 切换到P1.6 P1.7.
UART1_PrintString("STC8H RTC Test Prgramme!\r\n");
Timer0_Config(0, FOSC / 100); //t=0: reload值是主时钟周期数, (中断频率, 100次/秒)
Nx10ms = 0;
while (1)
{
if(B_10ms)
{
B_10ms = 0;
if(++Nx10ms >= 800) //8秒一个周期,RTC开启5秒,关闭3秒
{
Nx10ms = 0;
RTCCR = 0x01; //使能RTC, 并开始RTC计数
UART1_PrintString("允许RTC\r\n");
}
else if(Nx10ms == 500) //关闭
{
RTCCR = 0x00; //关闭RTC, 并开始RTC计数
UART1_PrintString("关闭RTC\r\n");
}
if(RX1_TimeOut != 0)
{
if(--RX1_TimeOut == 0) //设置时间命令: 21-6-19 12:34:56
{
if(RX1_Buffer[0] == 'A') //设置闹铃
{
GetDataIndex = 1; //获取数据索引仅仅用于读取计数
GetDataState = 0; //获取数据状态, 非0则错误
ALARM_hour = GetData(); //获取数据 时
ALARM_minute = GetData(); //获取数据 分
ALARM_second = GetData(); //获取数据 秒
if(GetDataState == 0) //获取数据状态, 为0则正确, 非0则错误
{
ALAHOUR = ALARM_hour; // RTC闹钟的小时值
ALAMIN = ALARM_minute; // RTC闹钟的分钟值
ALASEC = ALARM_second; // RTC闹钟的秒值
ALASSEC = 0; // RTC闹钟的1/128秒值
RTCIEN |= 0x80; //中断使能, 0x80:闹钟中断, 0x40:日中断, 0x20:小时中断, 0x10:分钟中断, 0x08:秒中断, 0x04:1/2秒中断, 0x02:1/8秒中断, 0x01:1/32秒中断
UART1_PrintString("已设置闹铃!\r\n");
}
else
{
UART1_PrintString("设置闹铃数据错误!\r\n");
UART1_PrintString("错误代码: ");
UART1_TxByte(GetDataState+'0');
UART1_PrintString("\r\n");
}
}
else //设置时间
{
GetDataIndex = 0; //获取数据索引仅仅用于读取计数
GetDataState = 0; //获取数据状态, 非0则错误
year = GetData(); //获取数据 年
month = GetData(); //获取数据 月
day = GetData(); //获取数据 日
hour = GetData(); //获取数据 时
minute = GetData(); //获取数据 分
second = GetData(); //获取数据 秒
if(GetDataState == 0) //获取数据状态, 为0则正确, 非0则错误
{
UART1_PrintString("获取设置时间: ");
ReturnRTC();
if(SetRTC() == 0) UART1_PrintString("已设置时间!\r\n");
else UART1_PrintString("设置时间数据错误!\r\n");
}
else
{
UART1_PrintString("设置时间数据错误!\r\n");
UART1_PrintString("错误代码: ");
UART1_TxByte(GetDataState+'0');
UART1_PrintString("\r\n");
}
}
RX1_Cnt = 0;
}
}
}
if(B_ALARM)
{
B_ALARM = 0;
UART1_PrintString("滴滴滴! 闹铃! \r\n");
}
if(B_1S) //秒有变化
{
B_1S = 0;
RTC_read();
if(OpTime != 0) OpTime--; //连续操作时间
ReturnRTC(); //从串口返回时间信息
}
/* if(OpTime == 0) // OpTime==0才进入掉电模式
{
PCON |= 0x02;
NOP(5);
} */
PCON |= 0x01; //空闲模式
NOP(5);
}
}
/********************** 获取串口数据 *****************************/
u8 GetData(void)
{
u8 i,j,k;
if(GetDataIndex >= RX1_Cnt) //
{
GetDataState = 1;
return 0;
}
if(RX1_Cnt < 6) //数据长度错误 设置时间 设置闹铃
{
GetDataState = 2;
return 0;
}
i = 0;
k = 0;
while(GetDataIndex <= RX1_Cnt) //设置时间命令: 21-6-19 12:34:56, 设置闹钟命令: A12:34:56
{
j = RX1_Buffer[GetDataIndex];
if((j == '-') || (j == ' ') || (j == ':') || (GetDataIndex == RX1_Cnt)) //数据段结束
{
GetDataIndex++;
if(i == 0) //数据错误
{
GetDataState = 3;
return 0;
}
else return k; //数据正确
}
if((j < '0') || (j > '9')) //数据合法性检测
{
GetDataIndex++;
GetDataState = 4;
return 0;
}
k = k * 10 + j - '0';
i++;
GetDataIndex++;
}
GetDataState = 9;
return 10; //数据错误
}
/********************** 串口返回时间信息 *****************************/
void ReturnRTC(void)
{
UART1_TxByte(year/10+'0'); //年
UART1_TxByte(year%10+'0');
UART1_TxByte('-');
UART1_TxByte(month/10+'0'); //月
UART1_TxByte(month%10+'0');
UART1_TxByte('-');
UART1_TxByte(day/10+'0'); //日
UART1_TxByte(day%10+'0');
UART1_TxByte(' ');
UART1_TxByte(hour/10+'0'); //时
UART1_TxByte(hour%10+'0');
UART1_TxByte(':');
UART1_TxByte(minute/10+'0'); //分
UART1_TxByte(minute%10+'0');
UART1_TxByte(':');
UART1_TxByte(second/10+'0'); //秒
UART1_TxByte(second%10+'0');
UART1_TxByte(0x0d);
UART1_TxByte(0x0a);
}
//========================================================================
// 函数: void delay_N_10us(unsigned int j), STC8xxx、AI8xxx系列
// 描述: 延时函数。
// 参数: j: 要延时的10us数, 1~65535*10us. 自动适应主时钟.
// 返回: none.
//========================================================================
void delay_N_10us(unsigned int j)
{
unsigned char i;
do
{
i = (FOSC + 150000UL) / 300000UL -4;
_nop_();
_nop_();
while(--i) ;
}while(--j);
}
/********************** 设置RTC时间 *****************************/
u8 SetRTC(void)
{
P_SW2 |= 0x80; //SFR enable
if(year > 99) return 1;
if((month == 0) || (month > 12)) return 2;
if((day == 0) || (day > 31)) return 3;
if(hour > 23) return 4;
if(minute > 59) return 5;
if(second > 59) return 6;
INIYEAR = year;
INIMONTH = month;
INIDAY = day;
INIHOUR = hour;
INIMIN = minute;
INISEC = second;
INISSEC = 0;
RTCCFG = 0x01 | 0x02; //设置RTC时间, |0x00:选择外部32K时钟, |0x02:选择内部32K时钟.
delay_N_10us(50); //等待初始化完成. 设置RTC时间需要32768Hz的1个周期时间,大约30.5us. 由于同步, 所以实际等待时间是0~30.5us.
//如果不等待设置完成就睡眠, 则RTC会由于设置没完成, 停止计数, 唤醒后才继续完成设置并继续计数.
return 0;
}
/********************** RTC配置函数 *****************************/
void RTC_config(void) //RTC初始化函数
{
P_SW2 |= 0x80; //SFR enable
P1n_pure_input(0xc0); //P1.6 P1.7设置为高阻输入
P1IE = ~0xc0; //P1.6 P1.7关闭数字输入功能
IRC32KCR = 0x80; //启动内部32K IRC
while (!(IRC32KCR & 1)) ; //等待时钟稳定
year = 25;
month = 6;
day = 11;
hour = 12;
minute = 0;
second = 0;
RTCCR = 0x01; //使能RTC, 并开始RTC计数
SetRTC(); //设置RTC时间
RTCIEN = 0x08; //中断使能, 0x80:闹钟中断, 0x40:日中断, 0x20:小时中断, 0x10:分钟中断, 0x08:秒中断, 0x04:1/2秒中断, 0x02:1/8秒中断, 0x01:1/32秒中断
RTCIF = 0; //中断标志, 0x80:闹钟中断, 0x40:日中断, 0x20:小时中断, 0x10:分钟中断, 0x08:秒中断, 0x04:1/2秒中断, 0x02:1/8秒中断, 0x01:1/32秒中断
ALAHOUR = 8; //闹钟小时
ALAMIN = 30; //闹钟分钟
ALASEC = 0; //闹钟秒
ALASSEC = 0; //闹钟1/128秒
}
/********************** 读取RTC时间信息 *****************************/
void RTC_read(void)
{
P_SW2 |= 0x80; //SFR enable
year = RTC_YEAR; // RTC的年计数值
month = RTC_MONTH; // RTC的月计数值
day = RTC_DAY; // RTC的日计数值
hour = RTC_HOUR; // RTC的时计数值
minute = RTC_MIN; // RTC的分计数值
second = RTC_SEC; // RTC的秒计数值
// ssecond = RTC_SSEC; // RTC的1/128秒计数值
}
//========================================================================
// 函数: SetTimer2Baudraye(u16 dat)
// 描述: 设置Timer2做波特率发生器。
// 参数: dat: Timer2的重装值.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注:
//========================================================================
void SetTimer2Baudraye(u16 dat) // 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.
{
AUXR &= ~(1<<4); //Timer stop
AUXR &= ~(1<<3); //Timer2 set As Timer
AUXR |= (1<<2); //Timer2 set as 1T mode
TH2 = (u8)(dat >> 8);
TL2 = (u8)dat;
IE2 &= ~(1<<2); //禁止中断
AUXR |= (1<<4); //Timer run enable
}
//========================================================================
// 函数: void UART1_config(u32 brt, u8 timer, u8 io)
// 描述: UART1初始化函数。
// 参数: brt: 通信波特率.
// timer: 波特率使用的定时器, timer=2: 波特率使用定时器2, 其它值: 使用Timer1做波特率.
// io: 串口1切换到的IO, io=1: 串口1切换到P3.0 P3.1, =1: 切换到P3.6 P3.7, =2: 切换到P1.6 P1.7.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注:
//========================================================================
void UART1_config(u32 brt, u8 timer, u8 io) // brt: 通信波特率, timer=2: 波特率使用定时器2, 其它值: 使用Timer1做波特率. io=0: 串口1切换到P3.0 P3.1, =1: 切换到P3.6 P3.7.
{
brt = 65536UL - (FOSC / 4) / brt;
if(timer == 2) //波特率使用定时器2
{
AUXR |= 0x01; //S1 BRT Use Timer2;
SetTimer2Baudraye((u16)brt);
}
else //波特率使用定时器1
{
TR1 = 0;
AUXR &= ~0x01; //S1 BRT Use Timer1;
AUXR |= (1<<6); //Timer1 set as 1T mode
TMOD &= ~(1<<6); //Timer1 set As Timer
TMOD &= ~0x30; //Timer1_16bitAutoReload;
TH1 = (u8)(brt >> 8);
TL1 = (u8)brt;
ET1 = 0; // 禁止Timer1中断
INT_CLKO &= ~0x02; // Timer1不输出高速时钟
TR1 = 1; // 运行Timer1
}
if(io == 1) {S1_USE_P36P37(); P3n_standard(0xc0);} //切换到 P3.6 P3.7
else if(io == 2) {S1_USE_P16P17(); P1n_standard(0xc0);} //切换到 P1.6 P1.7
else {S1_USE_P30P31(); P3n_standard(0x03);} //切换到 P3.0 P3.1
SCON = (SCON & 0x3f) | (1<<6); // 8位数据, 1位起始位, 1位停止位, 无校验
// PS = 1; //高优先级中断
ES = 1; //允许中断
REN = 1; //允许接收
}
//========================================================================
// 函数: void UART1_TxByte(u8 dat)
// 描述: 串口1发送一个字节数据函数
// 参数: dat: 要发送的一个字节数据.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注:
//========================================================================
void UART1_TxByte(u8 dat)
{
B_TX1_Busy = 1; //标志发送忙
SBUF = dat; //发一个字节
while(B_TX1_Busy); //等待发送完成
}
//========================================================================
// 函数: void UART1_PrintString(u8 *puts)
// 描述: 串口1字符串打印函数
// 参数: puts: 字符串指针.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注:
//========================================================================
void UART1_PrintString(u8 *puts)
{
for (; *puts != 0; puts++) UART1_TxByte(*puts);
}
//========================================================================
// 函数: void UART1_int (void) interrupt UART1_VECTOR
// 描述: 串口1中断函数
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注:
//========================================================================
void UART1_ISR (void) interrupt UART1_VECTOR
{
if(RI)
{
RI = 0;
if(RX1_Cnt >= RX1_Length) RX1_Cnt = 0;
RX1_Buffer[RX1_Cnt++] = SBUF;
RX1_TimeOut = 5;
OpTime = 5; //串口一旦唤醒, 接着5秒不睡眠, 以便正确接收数据. 唤醒帧将不能正确接收(因为唤醒需要计时us时间), 除非用很慢的速度(比如2400).
}
if(TI)
{
TI = 0;
B_TX1_Busy = 0;
}
}
//========================================================================
// 函数:u8 Timer0_Config(u8 t, u32 reload)
// 描述: timer0初始化函数.
// 参数: t: 重装值类型, 0表示重装的是系统时钟数, 其余值表示重装的是时间(us).
// reload: 重装值.
// 返回: 0: 初始化正确, 1: 重装值过大, 初始化错误.
// 版本: V1.0, 2018-3-5
//========================================================================
u8 Timer0_Config(u8 t, u32 reload) //t=0: reload值是主时钟周期数, t=1: reload值是时间(单位us)
{
TR0 = 0; //停止计数
if(t != 0) reload = (u32)(((float)FOSC * (float)reload)/1000000UL); //重装的是时间(us), 计算所需要的系统时钟数.
if(reload >= (65536UL * 12)) return 1; //值过大, 返回错误
if(reload < 65536UL) AUXR |= 0x80; //1T mode
else
{
AUXR &= ~0x80; //12T mode
reload = reload / 12;
}
reload = 65536UL - reload;
TH0 = (u8)(reload >> 8);
TL0 = (u8)(reload);
ET0 = 1; //允许中断
// PT0 = 1; //高优先级中断
TMOD = (TMOD & ~0x03) | 0; //工作模式, 0: 16位自动重装, 1: 16位定时/计数, 2: 8位自动重装, 3: 16位自动重装, 不可屏蔽中断
// TMOD |= 0x04; //对外计数或分频
INT_CLKO |= 0x01; //输出时钟
P3n_standard(Pin5); //P3.5设置为准双向口
TR0 = 1; //开始运行
return 0;
}
//========================================================================
// 函数: void timer0_int (void) interrupt TIMER0_VECTOR
// 描述: timer0中断函数.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2016-5-12
//========================================================================
void timer0_ISR (void) interrupt TIMER0_VECTOR
{
B_10ms = 1;
}
//========================================================================
// 函数: void RTC_ISR(void) interrupt RTC_VECTOR
// 描述: 扩展中断函数(中断号>=32的中断)
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-2
// 备注:
//========================================================================
void RTC_ISR(void) interrupt RTC_VECTOR
{
u8 i;
i = RTCIF;
RTCIF = 0; //中断标志, 0x80:闹钟中断, 0x40:日中断, 0x20:小时中断, 0x10:分钟中断, 0x08:秒中断, 0x04:1/2秒中断, 0x02:1/8秒中断, 0x01:1/32秒中断
if(i & 0x08) //秒中断
{
B_1S = 1; //秒标志
P33 = ~P33; //输出2秒周期方波, 方便测试精度
}
if(i & 0x80) //闹铃中断
{
B_ALARM = 1; //闹铃标志
}
}
|
|