CH5xx 系列低功耗蓝牙芯片 RTC 模块使用指南

一、RTC 基本特性

CH5xx 系列芯片内置 RTC(实时时钟)模块,在低功耗模式下,只要低频时钟保持运行,RTC 即可持续走时。

时钟源选择(影响精度):

内部 RC 振荡器(LSI) :成本低,无需外接元件。校准后精度约 1000 ppm,对应日误差约 1~2 分钟

外部 LSE 晶振(推荐 32768 Hz) :精度高,建议选用 12.5 pF 负载电容、20 ppm 频率偏差的型号,日误差约 2 秒

使用外部晶振时,芯片内部自带匹配电容,无需外接电容,通过配置寄存器选择负载档位即可:

LSECFG_Capacitance(LSECap_21p);

负载电容计算
公式为 (C1×C2)/(C1+C2) + C3,通常 C1 = C2,C3 为 PCB 寄生电容(约 2 pF),则等效电容 = C/2 + 2。若晶振负载要求 12.5 pF,则 C/2 + 2 = 12.5 ⇒ C = 21 pF,故配置 21 pF 挡位。


二、时钟源配置关键点

RTC 计时基准固定为 32768 Hz,时钟源配置错误将导致系统时间偏慢约 2%

1. 蓝牙工程(含 TMOS 任务调度)

在蓝牙例程中,宏 CLK_OSC32K 的取值决定时钟源和时钟频率:

配置值 时钟源 说明
0 外部 LSE 晶振(32768 Hz) 精度最高,蓝牙产品推荐
2 内部 32K RC 振荡器(需校准) 精度稍低,但可满足一般需求
1(默认) 内部 RC 输出 32000 Hz 与 RTC 基准不匹配,禁止使用,否则时间慢约 2%

蓝牙工程中需在预编译中配置 CLK_OSC32K = 0 或 2,避免使用默认值 1。

2. 普通非蓝牙工程(无 TMOS)

若普通工程需要 RTC 实时计时,必须定义宏 CLK_OSC32K = 2(选择内部 RC)。
如果未定义,Calibration_LSI() 校准函数会默认以 32000 Hz 为基准进行校准,与 RTC 所需的 32768 Hz 不匹配,同样会导致系统时间偏慢约 2%。


三、内部 RC 校准与误差管理

内部 32K RC 振荡器受温度影响较大,误差高于外部晶振。
建议:应用层定期调用 Calibration_LSI() 进行频率校准,以补偿温漂。
在蓝牙从机例程中,系统已默认每 2 分钟自动执行一次校准,无需额外处理。


四、RTC 使用注意事项

要点 说明
初始化时机 RTC_InitTime() 仅允许在系统初始化阶段调用一次。蓝牙 TMOS 工程中重复调用会破坏任务调度,运行时修改时间请使用“基准时间 + 偏移量”的方式,勿再次调用初始化函数。
中断标志清除 RTC 中断触发后,必须手动调用 RTC_ClearITFlag() 清除对应标志,否则会反复进入中断。
低功耗唤醒 通过 PWR_PeriphWakeUpCfg() 配置 RTC 为唤醒源后,芯片在睡眠/停机模式下可由 RTC 中断唤醒。
CH579 特殊处理 该芯片需额外调用 RTC_ModeFunDisable(RTC_TRIG_MODE); 关闭硬件默认开启的 TRIG 中断,否则会异常进入中断导致死机。其他型号调用此语句无害,建议统一加入。

五、RTC 测试例程(以 CH592 为例)

以下代码演示 RTC 初始化、中断配置及停机模式下的走时保持。

#include "CH59x_common.h"

/*********************************************************************
 * @fn      RTC_Test_Init
 * @brief   RTC 初始化总入口
 * @return  none
 *********************************************************************/
void RTC_Test_Init(void)
{
    RTC_ClearITFlag(RTC_TRIG_EVENT);            // 清中断标志

    //==================== 1. 选择 32K 时钟源 ====================
#if 0   // 选项 A:内部 LSI 32K RC(需校准)
    LClk32K_Select(Clk32K_LSI);
    Calibration_LSI(Level_64);   //校准内部32K RC;TMOS蓝牙工程会2min自动校准
#else   // 选项 B:外部 LSE 32.768K 晶振(高精度,推荐)
    R8_SAFE_ACCESS_SIG = SAFE_ACCESS_SIG1;      // 解锁安全寄存器
    R8_SAFE_ACCESS_SIG = SAFE_ACCESS_SIG2;
    R8_CK32K_CONFIG |= RB_CLK_XT32K_PON;        // 开启外部晶振供电
    R8_SAFE_ACCESS_SIG = 0;                     // 锁定安全寄存器
    LClk32K_Select(Clk32K_LSE);
#endif

    //==================== 2. 设置初始时间 ====================
    RTC_InitTime(2026, 8, 7, 12, 0, 0);         // TMOS 工程仅上电调用一次

    //==================== 3. 配置中断模式(二选一) ====================
#if 1   // TRIG 自由触发模式(可自定义周期)
    RTC_TRIGFunCfg(32768 * 5);                  // 5 秒中断
#else   // TMR 固定周期模式(预定义周期)
    RTC_TMRFunCfg(Period_1_S);                  // 1 秒中断
#endif

    PFIC_EnableIRQ(RTC_IRQn);                   // 使能 RTC 中断

    //==================== 4. 配置为低功耗唤醒源 ====================
    PWR_PeriphWakeUpCfg(ENABLE, RB_SLP_RTC_WAKE, Long_Delay);
}

/*********************************************************************
 * @fn      DebugInit
 * @brief   调试串口(UART1)初始化
 *********************************************************************/
void DebugInit(void)
{
    GPIOA_SetBits(GPIO_Pin_9);
    GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA);
    UART1_DefInit();
}

/*********************************************************************
 * @fn      main
 * @brief   主函数
 *********************************************************************/
int main(void)
{
    SetSysClock(CLK_SOURCE_PLL_60MHz);          // 系统时钟 60 MHz

    // 所有 IO 上拉输入,降低功耗
    GPIOA_ModeCfg(GPIO_Pin_All, GPIO_ModeIN_PU);
    GPIOB_ModeCfg(GPIO_Pin_All, GPIO_ModeIN_PU);

    DebugInit();

    // 读取初始时间并打印
    uint16_t year, mon, day, hour, min, sec;
    RTC_GetTime(&year, &mon, &day, &hour, &min, &sec);
    PRINT("start: %04d-%02d-%02d %02d:%02d:%02d \r\n", year, mon, day, hour, min, sec);
    PRINT("RTC Demo Start\r\n");

    RTC_Test_Init();

    while (1)
    {
        PRINT("shut down mode sleep \n");
        DelayMs(2);
        LowPower_Shutdown(0);   // 进入停机模式(RTC 继续走时,唤醒后芯片复位重跑 main)
    }
}

/*********************************************************************
 * @fn      RTC_IRQHandler
 * @brief   RTC 中断服务函数
 *********************************************************************/
__attribute__((interrupt("WCH-Interrupt-fast")))
__attribute__((section(".highcode")))
void RTC_IRQHandler(void)
{
    uint16_t year, mon, day, hour, min, sec;
    RTC_GetTime(&year, &mon, &day, &hour, &min, &sec);

    // ----- TRIG 中断 -----
    if (RTC_GetITFlag(RTC_TRIG_EVENT))
    {
        PRINT("TRIG: %04d-%02d-%02d %02d:%02d:%02d \r\n", year, mon, day, hour, min, sec);
        RTC_TRIGFunCfg(32768 * 1);              // 重装载为 1 秒周期
        RTC_ClearITFlag(RTC_TRIG_EVENT);
    }

    // ----- TMR 中断 -----
    if (RTC_GetITFlag(RTC_TMR_EVENT))
    {
        PRINT("TMR: %04d-%02d-%02d %02d:%02d:%02d \r\n", year, mon, day, hour, min, sec);
        RTC_ClearITFlag(RTC_TMR_EVENT);
    }
}

 

posted on 2023-09-20 14:24  WCH蓝牙应用分享  阅读(2751)  评论(0)    收藏  举报