GD32F103C8T6 RTC 待机模式唤醒后能够记录时间但是不走时

GD32F103C8T6 RTC 待机模式唤醒后能够记录时间但是不走时

MUC是GD32F103C8T6,初次上电配置RTC并写入初始时间。时钟源选择了内部40k(暂时没有接外部低速时钟)想要实现的是在低功耗情况下的日历更新。即在待机模式下进行RTC走时。

待机模式唤醒以后,没有再次配置RTC的情况下APB与寄存器同步一直等待。待机模式唤醒后,如果重新配置RTC配置项除了不复位备份域其他与初次上电的配置完全相同,此时可以同步APB和寄存器,但是读出的数值是进入待机模式时最后保存的时间。

现在的问题是,为什么不配置RTC的情况下无法同步APB和寄存器,为啥同步了二者之后似乎在待机的状态下RTC并没有走时。

请各位大能指点一二。

不知道你这个问题是否已经解决, 如果还没有解决的话:
  • 以下回答来自chatgpt:

    根据问题描述,主要有两个问题需要解决: 1. 在不重新配置RTC的情况下无法同步APB和寄存器的问题。 2. 重新配置RTC后,在待机状态下RTC仍然没有走时的问题。

    首先,我们来解决第一个问题。从参考资料中可以看到,当后备电源断电之后,RTC相关寄存器和后备寄存器的值会丢失。因此,在待机模式唤醒后,APB和寄存器无法同步。为了解决这个问题,我们需要在待机模式唤醒后,重新配置RTC的值。

    解决方法是,在RTC配置时,将当前时间写入RTC计数器中,并在后备寄存器中写入一个特殊字符作为标志检查位,以便下次进行检查。具体步骤如下:

    1. 初始化后备寄存器,读取是否存在特殊字符标志。
    2. 如果存在特殊字符标志,说明之前已经进行过首次RTC设置,可以直接读取当前时间。
    3. 如果不存在特殊字符标志,说明需要进行首次RTC设置。根据当前年份、月份、日期、小时、分钟和秒钟,计算出与1970年1月1日之间的秒数,并将该秒数写入RTC计数器。
    4. 将特殊字符标志写入后备寄存器,表示已经进行过首次RTC设置。

    下面是按照上述步骤编写的代码示例:

    #include "gd32f10x.h"
    
    // 初始化RTC
    void RTC_Init() {
        // 启用Power Clock时钟
        RCC_APB1PeriphClock_Enable(RCC_APB1PERIPH_PWR, ENABLE);
        // 解锁电源管理单元
        PWR_BackupAccess_Enable(ENABLE);
        // 启用RTC时钟
        RCC_LSI_Enable(ENABLE);
        while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);
        RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
        RCC_RTCCLK_Enable(ENABLE);
    
        // 配置RTC
        RTC_DeInit();
        RTC_SetPrescaler(32767); // RTC 1秒钟计数器周期为1Hz
        RTC_WaitSync();
        RTC_WaitForLastTask();
    }
    
    // 获取当前时间
    void RTC_GetTime(uint32_t *year, uint8_t *month, uint8_t *day, uint8_t *hour, uint8_t *minute, uint8_t *second) {
        while (RTC_GetFlagStatus(RTC_FLAG_RTOFF) == RESET);
        RTC_WaitForLastTask();
    
        *year = RTC_GetCounter() / 31536000 + 1970;
        uint32_t days = RTC_GetCounter() % 31536000 / 86400;
        *month = 1;
        while (days > 0) {
            uint8_t month_days = 30;
            if (*month == 2) {
                month_days = ((RTC_GetCounter() / 31536000 + 1970) % 4 == 0) ? 29 : 28;
            } else if (*month == 1 || *month == 3 || *month == 5 || *month == 7 || *month == 8 || *month == 10 || *month == 12) {
                month_days = 31;
            }
            if (days >= month_days) {
                days -= month_days;
                (*month)++;
            } else {
                break;
            }
        }
        *day = days + 1;
        *hour = (RTC_GetCounter() % 86400) / 3600;
        *minute = (RTC_GetCounter() % 3600) / 60;
        *second = (RTC_GetCounter() % 3600) % 60;
    }
    
    // 设置当前时间
    void RTC_SetTime(uint32_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) {
        while (RTC_GetFlagStatus(RTC_FLAG_RTOFF) == RESET);
        RTC_WaitForLastTask();
    
        uint32_t total_seconds = 0;
        for (uint32_t i = 1970; i < year; i++) {
            if ((i % 4 == 0 && i % 100 != 0) || i % 400 == 0) {
                total_seconds += 31622400; // 闰年的秒钟数
            } else {
                total_seconds += 31536000; // 平年的秒钟数
            }
        }
        for (uint8_t i = 1; i < month; i++) {
            uint8_t month_days = 30;
            if (i == 2) {
                month_days = ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ? 29 : 28;
            } else if (i == 1 || i == 3 || i == 5 || i == 7 || i == 8 || i == 10 || i == 12) {
                month_days = 31;
            }
            total_seconds += month_days * 86400; // 月份秒钟数相加
        }
        total_seconds += (day - 1) * 86400;
        total_seconds += hour * 3600;
        total_seconds += minute * 60;
        total_seconds += second;
    
        RTC_SetCounter(total_seconds);
        RTC_WaitForLastTask();
    }
    
    // 待机模式唤醒时重新同步APB和寄存器
    void RTC_SyncClock() {
        RCC_ResetFlag();
        RCC_PCLK1Config(RCC_HCLK_Div8);
        RCC_HCLKConfig(RCC_SYSCLK_Div1);
        RCC_ClocksTypeDef RCC_Clocks;
        RCC_GetClocksFreq(&RCC_Clocks);
        SysTick_Config(RCC_Clocks.HCLK_Frequency / 1000);
    }
    
    int main() {
        RTC_Init();
    
        if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5) {
            // 第一次上电配置RTC,设置初始时间
            RTC_SetTime(2022, 10, 1, 12, 0, 0);
            BKP_WriteBackupRegister(BKP_DR1, 0xA5A5); // 设置标志位
        }
    
        // 进入待机模式
        PWR_EnterSTANDBYMode();
    
        // 唤醒后重新同步APB和寄存器
        RTC_SyncClock();
    
        // 读取当前时间
        uint32_t year;
        uint8_t month, day, hour, minute, second;
        RTC_GetTime(&year, &month, &day, &hour, &minute, &second);
    
        // 输出当前时间
        printf("Current Time: %04d-%02d-%02d %02d:%02d:%02d\n", year, month, day, hour, minute, second);
    
        return 0;
    }
    

    接下来,我们来解决第二个问题:重新配置RTC后,在待机状态下RTC仍然没有走时的问题。

    根据参考资料,RTC 的走时依赖于 RTC 时钟源。在 GD32F103C8T6 上配置 RTC 的时钟源为内部 40k 时钟源。根据 GD32F10x 参考手册的描述,待机模式下 RTC 的时钟源可以选择内部 40k 时钟源或外部低速时钟。

    为了使 RTC 在待机模式下继续走时,需要满足以下两个条件: 1. 配置 RTC 时钟源为外部低速时钟(如果没有接入外部低速时钟,则无法满足该条件)。 2. 在待机模式唤醒后重新初始化 RTC。

    由于没有接入外部低速时钟,我们无法满足第一个条件,因此 RTC 在待机模式下无法继续走时。如果需要实现该功能,需要将外部低速时钟接入到 GD32F103C8T6,并重新配置 RTC 的时钟源。

    回答问题: 针对问题标题为 "GD32F103C8T6 RTC 待机模式唤醒后能够记录时间但是不走时" 的问题,根据已有的参考资料和知识,我的解决方案如下:

    1. 首先,需要在配置 RTC 时钟源时选择外部低速时钟。如果已经接入了外部低速时钟,可以通过修改代码中的 RCC_RTCCLKConfig() 函数配置 RTC 时钟源为外部低速时钟。否则,无法实现 RTC 在待机模式下继续走时的功能。
    2. 在待机模式唤醒后,需要重新初始化 RTC,通过调用 RTC_Init() 函数重新配置 RTC 的时钟源和计数器,以便使 RTC 在待机模式下继续走时。同时,根据之前的判断逻辑,如果标志位存在表示已经进行过首次 RTC 设置,可以直接读取当前时间。
    3. 如果无法接入外部低速时钟,那么 RTC 在待机模式唤醒后仍然无法继续走时。

    以上是根据已有的参考资料和知识给出的解决方案,如果还有其他问题或需要进一步细化的部分,请提供更多的信息。


如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^