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中断唤醒。

 

posted @ 2022-06-07 14:44  SweetTea_lllpc  阅读(3868)  评论(0编辑  收藏  举报