CEC-T20
美国加州 CEC-T20 中照明标准要求在网络环境良好的情况下,待机功耗在 0.2W。这里的 0.2W 是整个产品的功耗要求,也就是模组 + 外围电路的功耗,为了给除模组外的电路留下一定的功耗空间,我们一般要求待机时模组的功耗要小于 15mA。
下面我们将为如何使用 TuyaOS-联网单品开发包达到 T20 标准进行一个介绍。其大致思路为通过使用 FreeRTOS Tickless 和 WIFI 的节能模式(PSM,Power Save Mode)两种方式来降低设备的功耗,这样带来的副作用是会使设备的实时性会降低,WiFi 数据的接收的时延也会增大。那么什么是 Tickless 和 PSM 呢?
Tickless
大家都知道在 FreeRTOS 中当所有任务都挂起或阻塞时,就会进入到 IDLE 任务中。Tickless 就是利用当 RTOS 进入到 IDLE 时使芯片进入到睡眠模式,然后通过计算下个高优先级的任务会在什么时候来临,然后在将要下个优先级任务执行的时候退出睡眠状态,从而实现功耗的降低。关于 FreeRTOS 中的 Tickless 更多的介绍您可以参考这篇文档 FreeRTOS 低功耗之 tickless 模式IT 之道的博客-CSDN博客tickless
但要注意的是在一些芯片平台中,其退出睡眠状态可能是有时间下限的。如:某款芯片要求在从 IDLE 任务中进入睡眠后要求最少要休眠 300ms,经过计算下个高优先级的任务在 100ms 后来到,但由于最少要睡眠 300ms,那么这个任务只能在 300ms 才能得到执行。
通过了解 Tickless 的原理就可以发现,如果想要有效的降低功耗,应对您的程序进行合理的规划,使得任务较长时间处于 IDLE 任务中。在 TuyaOS-联网单品开发包中关于打开 tickless 的接口如下:
Code: Select all
/* include/components/tal_system/include/tal_sleep.h */
/**
* @brief set cpu lowpower mode
*
* @param[in] lp_enable
*
* @return none
*/
VOID_T tal_cpu_set_lp_mode(BOOL_T lp_enable);
/**
* @brief get cpu lowpower mode
*
* @param[in] none
*
* @return TRUE or FALSE
*/
BOOL_T tal_cpu_get_lp_mode(VOID_T);
/**
* @brief cpu lowpower enable
*
* @param[in] none
*
* @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
*/
OPERATE_RET tal_cpu_lp_enable(VOID_T);
/**
* @brief cpu lowpower disable
*
* @param[in] none
*
* @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
*/
OPERATE_RET tal_cpu_lp_disable(VOID_T);
在调用 tal_cpu_lp_enable(); 使能低功耗之前,应先调用 tal_cpu_set_lp_mode(TRUE); 接口打开低功耗。进入到 tickless 模式后会有以下日志打印:
Code: Select all
*******************************tuya_os_adapt_set_cpu_lp_mode,en = 1, mode = 0
PSM
当 Station 进入节能模式(PSM)后,AP 会缓存下行数据,AP 周期性的向外广播缓存区情况,当 Station 休眠结束后通过接收到的广播得知自己是否有数据被缓存了,如果有就进行数据请求,否则继续休眠。您可以通过这篇文档 802.11协议精读10:节能模式(PSM)了解更多。在 TuyaOS 初始化时默认已经打开了节能模式,DTIM 设置为 1。
在 TuyaOS-联网单品开发包中通过改变 Station 的休眠时间(DTIM)来降低其功耗的接口如下:
Code: Select all
/* include/components/tal_wifi/include/tal_wifi.h */
/**
* @brief enable wifi lowpower
*
* @param[in] none
* @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
*/
OPERATE_RET tal_wifi_lp_enable(VOID_T);
/**
* @brief disable wifi lowpower
*
* @param[in] none
* @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
*/
OPERATE_RET tal_wifi_lp_disable(VOID_T);
/**
* @brief set the wifi low power dtim.
*
* @note called before enter wifi low power mode
*
* @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
*/
VOID_T tal_wifi_set_lps_dtim(UINT_T dtim);
您可以通过改变 DTIM 来改变 Station 休眠的时间,dtim 越大越有利于降低 WiFi 设备功耗,相对于的带来数据时延也就越大。
成功调用 tal_wifi_lp_enable(); 并进入到低功耗模式后会有以下打印:
Code: Select all
<tal_wifi_lpen> disable_cnt:0
<tal_cpu_lp> disable_cnt:0
*******************************tuya_os_adapt_set_cpu_lp_mode,en = 1, mode = 0
使用注意
TuyaOS-联网单品开发包中为了低功耗功能可以跨线程、多线程并发使用,为此做出了一些特殊的处理。有时当我们调用了 tal_ wifi/cpu lp_enable(); 时并没有真的进入对应的低功耗功能,这是因为在 TuyaOS 在进入低功耗之后您调用多少次 tal wifi/cpu lp_disable(); 需要再调用对应次数的 tal wifi/cpu lp_enable(); 才能再次进入对应的低功耗模式。因此我们在使用时 disable() 和 _enable() 要成对的使用。
示例:上电后调用一次enable 进入低功耗之后,调用了 3 次 disable 退出了低功耗,这时您调用了 1 次 enable 并没有进入低功耗,当您第 3 次调用 enable 才会进入低功耗。
经过对上面对相关接口原理的介绍,我们已经有了一定的了解,下面将对上述接口的使用做一个示例:
Code: Select all
STATIC VOID __wifi_low_power_task(VOID_T *arg)
{
for (;;) {
/* 当我们需要芯片能够及时接收 WiFi 数据的时候,
就需要先调用 "tal_wifi_lp_disable()" 关闭设备的节能模式(PSM)。
如:设备需要实时接收 wifi 管理包功能。 */
tal_wifi_lp_disable();
...
/* 处理完成后再使能设备的节能模式(PSM) */
tal_wifi_lp_enable();
tal_system_sleep(5000);
}
}
STATIC VOID __cpu_low_power_task(VOID_T *arg)
{
for (;;) {
/* 当我们需要芯片做一些实时性高的任务的时候,
需要先调用 "tal_cpu_lp_disable()" 关闭 tickless。
如:在线程中扫描按键。 */
tal_cpu_lp_disable();
...
/* 处理完成后再使能 tickless */
tal_cpu_lp_enable();
tal_system_sleep(7000);
}
tal_thread_delete(__wifi_lp_hdl2);
}
VOID low_power_init(VOID )
{
/* enable low power */
tal_cpu_set_lp_mode(TRUE);
/* The higher the dtim, the lower the power consumption */
tal_wifi_set_lps_dtim(2);
/* enter tickless&WiFi low-power mode */
tal_wifi_lp_enable();
THREAD_CFG_T thrd_param = {1024, 4, "wifi_lp"};
tal_thread_create_and_start(&__wifi_lp_hdl1, NULL, NULL, __wifi_low_power_task, NULL, &thrd_param);
thrd_param.thrdname = "cpu_lp";
tal_thread_create_and_start(&__wifi_lp_hdl2, NULL, NULL, __cpu_low_power_task, NULL, &thrd_param);
return;
}