CH573芯片Sleep说明(RTC程序说明)
一、低功耗技术概念
1、它的广播频段和广播时射频开启时间的减少:传统蓝牙使用16~32个频段进行广播,而BLE仅使用3个广播频段;每次广播时的射频开启时间由传统蓝牙的22ms减少为0.6~1.2ms。一旦连接成功后,蓝牙低能耗技术就会切换到37个数据信道之一。在短暂的数据传送期间,无线信号将使用标准蓝牙技术倡导的自适应跳频(AFH)技术以伪随机的方式在信道间切换(虽然标准蓝牙技术使用79个数据信道);
2、它每次只传输少量数据,传输速率是比较低的。因此适用于从微型无线传感器(每半秒交换一次数据)或使用完全异步通信的遥控器等其它外设传送数据;
3、标准蓝牙技术使用的数据包长度较长。在发送这些较长的数据包时,无线设备必须在相对较高的功耗状态保持更长的时间,从而容易使硅片发热。这种发热将改变材料的物理特性,进而改变传送频率(中断链路),除非频繁地对无线设备进行再次校准。再次校准将消耗更多的功率(并且要求闭环架构,使得无线设备更加复杂,从而推高设备价格)。相反,蓝牙低功耗技术使用非常短的数据包——这能使硅片保持在低温状态。因此,蓝牙低功耗收发器不需要较耗能的再次校准和闭环架构,这也一定程度降低了功耗。
综合上面三个特点就决定了低功耗蓝牙相对传统蓝牙功耗更低,更节能。
举个例子,视频传输、高质量音频传输或者传输大量数据的应用就不适合使用低功耗蓝牙;如传输小体量的数据,比如传输传感器的数据到我们的手机可以使用低功耗蓝牙。
二、BLE模式下开启Sleep
1、开启睡眠
在Sleep模式时,睡眠函数相关库已经封装好,只需要调用相关函数即可,宏定义添加HAL_SLEEP,如图:
2、原理讲解
开启Sleep成功后,用户只需要管理唤醒的功能就可以,唤醒的方式分为GPIO中断唤醒和TMOS任务管理唤醒。
在唤醒后执行完对应的任务,将会由协议栈管理自行进入睡眠,不需要用户再自己去调用睡眠函数。注意,不可以使任务执行事件过长,会导致蓝牙数据收发出问题,从而出现断连的现象。
因此,用户只需要管理好唤醒的事件即可。
除了上面两种方式方式唤醒,在使用蓝牙情况下,未连接时,从机会在一定的广播间隔发送广播数据;在连接后,主从机进行收发包的时候也会有数据的交互。此两种情况下都会被唤醒,均由协议栈管理,不需要用户自行管理。
如图可见,开启Sleep后,尖峰时为工作模式,此时被唤醒,在往外发包;空白时为睡眠模式。
总计功耗则为统计时间内所得到的面积大小。
睡眠唤醒的任务是通过TMOS任务调度处理的,具体可参见这个博客:
WCH TMOS用法详解 - debugdabiaoge - 博客园 (cnblogs.com)
3、降低功耗
上图已经显示出当前状态下芯片的功耗大小,如果需要继续调低,可以通过拉大连接间隔。拉大连接间隔的目的便是为了让蓝牙往外发包的频率降低。蓝牙主从连接后,连接间隔小可以实现透传速度快但功耗高,连接间隔大透传速度慢但功耗较低。所以我们在进行蓝牙功耗降低时,一定要根据实际需求出发,而非功耗越低越好。
4、注意点
一般使用longdelay唤醒,唤醒的速度会比较慢。如果使用shortdelay唤醒,则唤醒的速度比较快,但是需要确保中断的函数都是RAM中运行。如果在FLASH中运行,则运行的速度比较慢,使用shortdelay可能导致无法唤醒。
三、PM睡眠模式
1、PM睡眠模式概念
PM睡眠主要是提供用户测试芯片开启睡眠情况下最低功耗的程序测试,这里简单进行讲解并提供相应的唤醒方式。
CH573芯片在不使用蓝牙功能时,其最低功耗可以为多少呢?这里我们测量一下下电模式(Shutdown),相应的数据如图所示:
这里我们的芯片是贴在开发板上的,相应的功耗元器件还没有去除,这里测试出的3.76uA已满足大多数低功耗工作需求。
系统的睡眠模式总共有四种等级,分别为IDLE mode、Halt mode、sleep mode和Shutdown mode,Shutdown模式的功耗是最低的,但是开启Shutdown模式后进行唤醒,芯片会复位。我们的蓝牙睡眠模式默认为sleep模式,可根据实际需求作相应的修改。
2、唤醒方式
芯片可以睡眠,那么也可以被唤醒。蓝牙可以通过TMOS任务的调度或者外部中断的方式进行唤醒。这里PM睡眠唤醒也有两种方式,分别为RTC唤醒和外部中断唤醒(RTC唤醒便是蓝牙中的TMOS任务唤醒)。
① RTC唤醒代码(包含基于RTC的万年历功能)
RTC_TMRFunCfg(Period_1_S); //两种唤醒方式:定时功能、触发功能。这里是使用的定时唤醒,相应参数库已经封装好,可根据需求调用。注意清除对应的标志。
//RTC_TRIGFunCfg(983040); //两种唤醒方式:定时功能、触发功能。这里是使用的触发唤醒,单位是32768,代表1S。注意清除对应的标志。注意:使用触发需要在中断里面再触发一次。
PFIC_EnableIRQ(RTC_IRQn); //开启RTC使能
PWR_PeriphWakeUpCfg( ENABLE, RB_SLP_RTC_WAKE, Edge_LongDelay );
__attribute__((interrupt("WCH-Interrupt-fast"))) //中断服务函数
__attribute__((section(".highcode")))
void RTC_IRQHandler(void)
{
UINT16 py; UINT16 pmon; UINT16 pd; UINT16 ph; UINT16 pm; UINT16 ps;
RTC_GetTime(&py,&pmon,&pd,&ph,&pm,&ps); //设置万年历
printf("%d年%d月%d日%d时%d分%d秒\r\n",py,pmon,pd,ph,pm,ps); //打印万年历计时时间,1S打印一次
RTC_ClearITFlag(RTC_TMR_EVENT); //使用的是定时唤醒,清除对应标志
//RTC_ClearITFlag(RTC_TRIG_EVENT); //使用触发唤醒,注意清除标志修改。注意:使用触发需要在中断里面再触发一次。
}
② 外部中断唤醒代码(PB22引脚唤醒)
/* 配置唤醒源为 GPIO - PB22 */
GPIOB_ModeCfg(GPIO_Pin_22, GPIO_ModeIN_PU);
GPIOB_ITModeCfg(GPIO_Pin_22, GPIO_ITMode_FallEdge); // 下降沿唤醒
PFIC_EnableIRQ(GPIO_B_IRQn);
PWR_PeriphWakeUpCfg(ENABLE, RB_SLP_GPIO_WAKE, Edge_LongDelay);
__attribute__((interrupt("WCH-Interrupt-fast")))
__attribute__((section(".highcode")))
void GPIOB_IRQHandler(void)
{
GPIOB_ClearITFlagBit(GPIO_Pin_8); //清除PB22中断标志,函数会左移14位,所以这里是清除PB8的中断标志
}
一旦系统进入下电模式(详见手册电源管理章节)后,在唤醒信号的作用下,系统将有序执行唤醒操作,唤醒之后系统将执行全局复位,此复位效果类似于上电复位。
RTC 寄存器与PMU 一样常供电,在系统复位或从低功耗模式唤醒后,RTC 的设置和时间维持不变。如果需要下电复位结合万年历使用,可以对复位状态寄存器就行判断,即在下电复位状态后不对RTC初始化。具体代码如下:
status = R8_RESET_STATUS;
if(((status & 0x07) == 0x01 )|| ((status & 0x07) == 0x03 )){
RTC_InitTime(2022,3,31,23,59,40);
printf("Init RTC OK\r\n");
}
if((status & 0x07) == 0x05 ){
printf("RB_RESET_FLAG\n");
}
四、PM完整代码
int main()
{
uint16_t status = 0;
SetSysClock(CLK_SOURCE_PLL_60MHz);
DelayMs(50);
GPIOA_ModeCfg(GPIO_Pin_All, GPIO_ModeIN_PU);
GPIOB_ModeCfg(GPIO_Pin_All, GPIO_ModeIN_PU);
GPIOB_ModeCfg(GPIO_Pin_10, GPIO_ModeOut_PP_5mA); //设置模式 输出
/* 配置串口调试 */
DebugInit();
PRINT("Start @ChipID=%02x\n", R8_CHIP_ID);
DelayMs(200);
#if 1
/* 配置唤醒源为 GPIO - PB22 */
GPIOB_ModeCfg(GPIO_Pin_22, GPIO_ModeIN_PU);
GPIOB_ITModeCfg(GPIO_Pin_22, GPIO_ITMode_FallEdge); // 下降沿唤醒
PFIC_EnableIRQ(GPIO_B_IRQn);
PWR_PeriphWakeUpCfg(ENABLE, RB_SLP_GPIO_WAKE, Edge_LongDelay);
#endif
#if 1
LClk32K_Select(Clk32K_LSE);//启用外部32K
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;
status = R8_RESET_STATUS;
if(((status & 0x07) == 0x01 )|| ((status & 0x07) == 0x03 )){
RTC_InitTime(2022,3,31,23,59,40);
printf("Init RTC OK\r\n");
}
if((status & 0x07) == 0x05 ){
printf("RB_RESET_FLAG\n");
}
RTC_TMRFunCfg(Period_1_S);
// RTC_TRIGFunCfg(983040);
PFIC_EnableIRQ(RTC_IRQn);
PWR_PeriphWakeUpCfg( ENABLE, RB_SLP_RTC_WAKE, Edge_LongDelay );
#endif
#if 1
while(1){
// PRINT("Shut Down\n");
DelayMs(2);
LowPower_Shutdown(0); //全部断电,唤醒后复位
/*
此模式唤醒后会执行复位,所以下面代码不会运行,
注意要确保系统睡下去再唤醒才是唤醒复位,否则有可能变成IDLE等级唤醒
*/
HSECFG_Current(HSE_RCur_100); // 降为额定电流(低功耗函数中提升了HSE偏置电流)
DelayMs(100);
}
#endif
}
void DebugInit(void)
{
GPIOA_SetBits(GPIO_Pin_9);
GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA);
UART1_DefInit();
}
__attribute__((interrupt("WCH-Interrupt-fast")))
__attribute__((section(".highcode")))
void GPIOB_IRQHandler(void)
{
GPIOB_ClearITFlagBit(GPIO_Pin_8);
}
__attribute__((interrupt("WCH-Interrupt-fast")))
__attribute__((section(".highcode")))
void RTC_IRQHandler(void)
{
UINT16 py; UINT16 pmon; UINT16 pd; UINT16 ph; UINT16 pm; UINT16 ps;
RTC_GetTime(&py,&pmon,&pd,&ph,&pm,&ps);
if (RTC_GetITFlag(RTC_TMR_EVENT)) {
printf("%d年%d月%d日%d时%d分%d秒\r\n",py,pmon,pd,ph,pm,ps);
RTC_ClearITFlag(RTC_TMR_EVENT);
}
}
附录:
提供CH582芯片的PM睡眠唤醒,唤醒包括RTC唤醒和外部GPIO中断唤醒。