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);
}
}
浙公网安备 33010602011771号