zhp 发表于 2023-2-1 12:27:55

不停电下载保持RTC参数利用用户接口实现---8H系列

8H2K08U, 8H2K12U, 8H2K17U
8H2K32U
===以上这几个型号开始,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下载软件中“用户接口”的设置如下:(注意,首次下载不能使能用户接口)

神农鼎 发表于 2024-5-4 09:45:33

8H2K08U, 8H2K12U, 8H2K17U
8H2K32U
===以上这几个型号开始,CPU 复位, 不同步复位 RTC







关于ISP的“RTC对时”功能详解 - 触摸按键/80mA大电流LED数码管自动刷新显示/段码LCD/RTC实时时钟/低功耗 国芯技术交流网站 - STC全球32位8051爱好者互助交流社区 (stcaimcu.com)

Jszq-tech 发表于 2023-7-12 13:34:56

为什么时钟保存在0XFE00地址,销毁的时候却是0x0000地址?

zhp 发表于 2023-7-12 14:52:00

Jszq-tech 发表于 2023-7-12 13:34
为什么时钟保存在0XFE00地址,销毁的时候却是0x0000地址?

0xfe00是CODE地址,0x0000是EEPROM地址





EEPROM地址和代码地址的关系请参考数据手册中下面章节的描述


Jszq-tech 发表于 2023-7-12 17:29:16

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:32

zhp 发表于 2023-7-12 14:52
0xfe00是CODE地址,0x0000是EEPROM地址




EEPROM大小设置为16K,我在用户接口页面下用户数据写入地址是0XD000,初始化RTC模块后写EEPROM地址0X1000地址为0X00,为什么我保存在EEPROM地址0X0000-0X0800的一些数据都没了?

zhp 发表于 2023-7-12 21:24:53

Jszq-tech 发表于 2023-7-12 17:29
EEPROM大小设置为16K,我在用户接口页面下用户数据写入地址是0XD000,初始化RTC模块后写EEPROM地址0X1000 ...

按理是不会出现你这种情况的
但有一点需要注意,设置EEPROM大小的硬件选项需要下次重新上电才生效

Jszq-tech 发表于 2023-7-13 14:03:18

zhp 发表于 2023-7-12 21:24
按理是不会出现你这种情况的
但有一点需要注意,设置EEPROM大小的硬件选项需要下次重新上电才生效 ...

不是重新上电生效的问题,试验了多次都是这样的,但是把RTC模块初始化参数放在EEPROM的0X0000地址,其他参数放在后面的扇区中就不会有这种情况。

深圳湾 发表于 2024-5-4 10:42:05

希望能在文档中的RTC章节提一下这个问题,避免踩坑,
我在这点上白白浪费了两天才研究出来不是代码写得不对,
是MCU复位引起时钟清零的,时间宝贵啊,
感觉时间越来越不够用了

神农鼎 发表于 2024-5-4 10:53:55

复位 CPU, 也复位 RTC, 是对的 !
复位 CPU, 不复位 RTC, 也是实际需求 !
现在新的设计开始转向将 CPU复位和 RTC复位分开
===数据手册会持续更新



STC8H2K08U, STC8H2K12U, STC8H2K17U
STC8H2K32U
===以上这几个型号开始,CPU 复位, 不同步复位 RTC




页: [1] 2
查看完整版本: 不停电下载保持RTC参数利用用户接口实现---8H系列