不停电下载保持RTC参数利用用户接口实现---8H系列
8H2K08U, 8H2K12U, 8H2K17U8H2K32U
===以上这几个型号开始,CPU 复位, 不同步复位 RTC=======================================其他早期单片机的RTC模块,在单片机复位后RTC相关的特殊功能寄存器也会复位本例程主要用于解决这些早期的MCU, ISP下载后用户的RTC参数被复位丢失的问题解决思路:ISP下载前,先将RTC相关参数通过ISP下载软件的用户接口上传到PC保存,等待ISP下载完成后,下载软件再将保存的相关参数写入到FLASH的指定地址(范例中指定的地址为FE0000H)。ISP下载完成后会立即运行用户代码,用户程序在初始化RTC寄存器时,可从FLASH的指定地址中读取之前上传的RTC相关参数对RTC寄存器进行初始化,即可实现不停电下载保持RTC参数的目的。测试代如下:下载完整的项目程序包#include "stc8h.h"
#include "intrins.h"
#define FOSC 11059200UL
#define BAUD (65536 - FOSC/4/115200)
typedef bit BOOL;
typedef unsigned char BYTE;
struct RTC_INIT
{
BYTE bValidTag; //数据有效标志(0x5a)
BYTE bIniYear; //年(RTC初始化值)
BYTE bIniMonth; //月
BYTE bIniDay; //日
BYTE bIniHour; //时
BYTE bIniMinute; //分
BYTE bIniSecond; //秒
BYTE bIniSSecond; //次秒
BYTE bAlaHour; //时(RTC闹钟设置值)
BYTE bAlaMinute; //分
BYTE bAlaSecond; //秒
BYTE bAlaSSecond; //次秒
};
struct RTC_INIT code InitBlock _at_ 0xfe00;
void SysInit();
void UartInit();
void RTCInit();
void SendUart(BYTE dat);
void UnpackCmd(BYTE dat);
void IapProgram(int addr, char dat);
BOOL fUartBusy;
BOOL fFetchRtc;
BOOL fReset2Isp;
BYTE bUartStage;
BYTE bDump;
void main()
{
SysInit(); //系统初始化
UartInit();
RTCInit();
EA = 1;
fUartBusy = 0;
fFetchRtc = 0;
fReset2Isp = 0;
bUartStage = 0;
while (1)
{
if (fFetchRtc) //获取RTC数据请求
{
fFetchRtc = 0;
RTCCR = 0; //上传当前的RTC值时,必须临时停止RTC,以免发生进位错误
bDump = YEAR; //快速将当前的RTC值缓存,以缩短RTC暂停的时间,减小误差
bDump = MONTH;
bDump = DAY;
bDump = HOUR;
bDump = MIN;
bDump = SEC;
bDump = SSEC;
RTCCR = 1;
SendUart(0x5a); //上传12字节RTC参数
SendUart(bDump);
SendUart(bDump);
SendUart(bDump);
SendUart(bDump);
SendUart(bDump);
SendUart(bDump);
SendUart(bDump);
SendUart(ALAHOUR);
SendUart(ALAMIN);
SendUart(ALASEC);
SendUart(ALASSEC);
}
if (fReset2Isp) //重启请求
{
fReset2Isp = 0;
IAP_CONTR = 0x60; //软件触发复位到系统ISP区
}
}
}
void uart_isr() interrupt UART1_VECTOR
{
BYTE dat;
if (TI)
{
TI = 0;
fUartBusy = 0;
}
if (RI)
{
RI = 0;
dat = SBUF;
switch (bUartStage++) //解析串口命令
{
default:
case 0:
L_Check1st:
if (dat == '@') bUartStage = 1;
else bUartStage = 0;
break;
case 1:
if (dat == 'F') bUartStage = 2;
else if (dat == 'R') bUartStage = 7;
else goto L_Check1st;
break;
case 2:
if (dat != 'E') goto L_Check1st;
break;
case 3:
if (dat != 'T') goto L_Check1st;
break;
case 4:
if (dat != 'C') goto L_Check1st;
break;
case 5:
if (dat != 'H') goto L_Check1st;
break;
case 6:
if (dat != '#') goto L_Check1st;
bUartStage = 0;
fFetchRtc = 1; //当前命令序列为获取RTC数据命令:"@FETCH#"
break;
case 7:
if (dat != 'E') goto L_Check1st;
break;
case 8:
if (dat != 'B') goto L_Check1st;
break;
case 9:
case 10:
if (dat != 'O') goto L_Check1st;
break;
case 11:
if (dat != 'T') goto L_Check1st;
break;
case 12:
if (dat != '#') goto L_Check1st;
bUartStage = 0;
fReset2Isp = 1; //当前命令序列为重启命令:"@REBOOT#"
break;
}
}
}
void rtc_isr() interrupt RTC_VECTOR //RTC中断复位程序
{
RTCIF = 0x00; //清RTC中断标志
P20 = !P20; //P2.0口每秒闪烁一次,测试用
}
void SysInit()
{
P_SW2 |= 0x80;
P0M0 = 0x00; P0M1 = 0x00;
P1M0 = 0x00; P1M1 = 0x00;
P2M0 = 0x00; P2M1 = 0x00;
P3M0 = 0x00; P3M1 = 0x00;
P4M0 = 0x00; P4M1 = 0x00;
P5M0 = 0x00; P5M1 = 0x00;
P6M0 = 0x00; P6M1 = 0x00;
P7M0 = 0x00; P7M1 = 0x00;
}
void UartInit() //串口初始化函数
{
SCON = 0x50;
AUXR = 0x40;
TMOD = 0x00;
TL1 = BAUD;
TH1 = BAUD >> 8;
TR1 = 1;
ES = 1;
}
void RTCInit() //RTC初始化函数
{
//IRC32KCR = 0x80;
//while (!(IRC32KCR & 0x01));
//RTCCFG |= 0x02; //选择内部32K为RTC时钟源
X32KCR = 0xc0;
while (!(X32KCR & 0x01));
RTCCFG &= ~0x02; //选择外部部32K为RTC时钟源
if (InitBlock.bValidTag == 0x5a)
{
INIYEAR= InitBlock.bIniYear; //如果初始化数据块有效,则使用数据块初始化RTC
INIMONTH = InitBlock.bIniMonth;
INIDAY = InitBlock.bIniDay;
INIHOUR= InitBlock.bIniHour;
INIMIN = InitBlock.bIniMinute;
INISEC = InitBlock.bIniSecond;
INISSEC= InitBlock.bIniSSecond;
ALAHOUR= InitBlock.bAlaHour;
ALAMIN = InitBlock.bAlaMinute;
ALASEC = InitBlock.bAlaSecond;
ALASSEC= InitBlock.bAlaSSecond;
IapProgram(0x0000, 0x00); //销毁初始化数据块,以免重复初始化
}
else
{
INIYEAR= 23; //否则初始化RTC为默认值
INIMONTH = 1;
INIDAY = 29;
INIHOUR= 12;
INIMIN = 0;
INISEC = 0;
INISSEC= 0;
ALAHOUR= 0;
ALAMIN = 0;
ALASEC = 0;
ALASSEC= 0;
}
RTCCFG |= 0x01; //写入RTC初始值
RTCCR = 0x01; //RTC开始运行
while (RTCCFG & 0x01); //等待RTC初始化完成
RTCIF = 0x00;
RTCIEN = 0x08; //使能RTC秒中断
}
void SendUart(BYTE dat) //串口发送函数
{
while (fUartBusy);
SBUF = dat;
fUartBusy = 1;
}
void IapProgram(int addr, char dat) //EEPROM编程函数
{
IAP_CONTR = 0x80;
IAP_TPS = 12;
IAP_CMD = 2;
IAP_ADDRL = addr;
IAP_ADDRH = addr >> 8;
IAP_DATA = dat;
IAP_TRIG = 0x5a;
IAP_TRIG = 0xa5;
_nop_();
_nop_();
_nop_();
_nop_();
}
ISP下载软件中“用户接口”的设置如下:(注意,首次下载不能使能用户接口) 8H2K08U, 8H2K12U, 8H2K17U
8H2K32U
===以上这几个型号开始,CPU 复位, 不同步复位 RTC
关于ISP的“RTC对时”功能详解 - 触摸按键/80mA大电流LED数码管自动刷新显示/段码LCD/RTC实时时钟/低功耗 国芯技术交流网站 - STC全球32位8051爱好者互助交流社区 (stcaimcu.com)
为什么时钟保存在0XFE00地址,销毁的时候却是0x0000地址? Jszq-tech 发表于 2023-7-12 13:34
为什么时钟保存在0XFE00地址,销毁的时候却是0x0000地址?
0xfe00是CODE地址,0x0000是EEPROM地址
EEPROM地址和代码地址的关系请参考数据手册中下面章节的描述
zhp 发表于 2023-7-12 14:52
0xfe00是CODE地址,0x0000是EEPROM地址
EEPROM大小设置为16K,我在用户接口页面下用户数据写入地址是0XD000,初始化RTC模块后写EEPROM地址0X1000地址为0X00,为什么我保存在EEPROM地址0X0000-0X0800的一些数据都没了? zhp 发表于 2023-7-12 14:52
0xfe00是CODE地址,0x0000是EEPROM地址
EEPROM大小设置为16K,我在用户接口页面下用户数据写入地址是0XD000,初始化RTC模块后写EEPROM地址0X1000地址为0X00,为什么我保存在EEPROM地址0X0000-0X0800的一些数据都没了? Jszq-tech 发表于 2023-7-12 17:29
EEPROM大小设置为16K,我在用户接口页面下用户数据写入地址是0XD000,初始化RTC模块后写EEPROM地址0X1000 ...
按理是不会出现你这种情况的
但有一点需要注意,设置EEPROM大小的硬件选项需要下次重新上电才生效 zhp 发表于 2023-7-12 21:24
按理是不会出现你这种情况的
但有一点需要注意,设置EEPROM大小的硬件选项需要下次重新上电才生效 ...
不是重新上电生效的问题,试验了多次都是这样的,但是把RTC模块初始化参数放在EEPROM的0X0000地址,其他参数放在后面的扇区中就不会有这种情况。 希望能在文档中的RTC章节提一下这个问题,避免踩坑,
我在这点上白白浪费了两天才研究出来不是代码写得不对,
是MCU复位引起时钟清零的,时间宝贵啊,
感觉时间越来越不够用了
复位 CPU, 也复位 RTC, 是对的 !
复位 CPU, 不复位 RTC, 也是实际需求 !
现在新的设计开始转向将 CPU复位和 RTC复位分开
===数据手册会持续更新
STC8H2K08U, STC8H2K12U, STC8H2K17U
STC8H2K32U
===以上这几个型号开始,CPU 复位, 不同步复位 RTC
页:
[1]
2