近期 RT-Thread 工程師完成了基于瑞薩CPK-RA2L1 開(kāi)發(fā)板的BSP適配,支持了GPIO、UART、I2C、SPI、ADC、DAC、PWM、CAN、on-chip Flash、Watchdog、RTC等外設(shè)驅(qū)動(dòng),并在瑞薩工程師支持下完成了電源組件(低功耗LPM)適配,經(jīng)實(shí)際測(cè)量,芯片在Software Standby階段可達(dá)到的最低平均電流約為0.696uA,本篇筆記記錄低功耗的適配和應(yīng)用。
可通過(guò)以下鏈接查看RA MCU BSP:
https://github.com/RT-Thread/rt-thread/tree/master/bsp/renesas
瑞薩 RA 系列 MCU 開(kāi)發(fā)板的 BSP 制作教程:
https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/tutorial/make-bsp/renesas-ra/RA%E7%B3%BB%E5%88%97BSP%E5%88%B6%E4%BD%9C%E6%95%99%E7%A8%8B
在開(kāi)始介紹低功耗前,先了解一下 RA2L1 MCU 產(chǎn)品群關(guān)鍵特性
-
支持1.6V-5.5V寬范圍工作電壓
-
超低功耗,提供64μA/MHz工作電流和250nA軟件待機(jī)電流,快速喚醒時(shí)間小于5μs
-
采用瑞薩110nm低功耗工藝,用于運(yùn)行和睡眠/待機(jī)模式,并且專(zhuān)門(mén)為電池驅(qū)動(dòng)應(yīng)用設(shè)計(jì)了特殊掉電模式
-
靈活的供電模式可實(shí)現(xiàn)更低的平均功耗,以滿(mǎn)足多種應(yīng)用需求
-
后臺(tái)運(yùn)行的數(shù)據(jù)閃存,支持一百萬(wàn)次擦除/編程循環(huán)
-
采用LQFP封裝,產(chǎn)品涵蓋48引腳至100引腳封裝
低功耗基礎(chǔ)
低功耗的本質(zhì)是系統(tǒng)空閑時(shí) CPU 停止工作,中斷或事件喚醒后繼續(xù)工作。在 RTOS 中,通常包含一個(gè) IDLE 任務(wù),該任務(wù)的優(yōu)先級(jí)最低且一直保持就緒狀態(tài),當(dāng)高優(yōu)先級(jí)任務(wù)未就緒時(shí),OS 執(zhí)行 IDLE 任務(wù)。一般地,未進(jìn)行低功耗處理時(shí),CPU 在 IDLE 任務(wù)中循環(huán)執(zhí)行空指令。RT-Thread 的電源管理組件在 IDLE 任務(wù)中,通過(guò)對(duì) CPU 、時(shí)鐘和設(shè)備等進(jìn)行管理,從而有效降低系統(tǒng)的功耗。
在上圖所示,當(dāng)高優(yōu)先級(jí)任務(wù)運(yùn)行結(jié)束或被掛起時(shí),系統(tǒng)將進(jìn)入 IDLE 任務(wù)中。在 IDLE 任務(wù)執(zhí)行后,它將判斷系統(tǒng)是否可以進(jìn)入到休眠狀態(tài)(以節(jié)省功耗)。如果可以進(jìn)入休眠, 將根據(jù)芯片情況關(guān)閉部分硬件模塊,OS Tick 也非常有可能進(jìn)入暫停狀態(tài)。此時(shí)電源管理框架會(huì)根據(jù)系統(tǒng)定時(shí)器情況,計(jì)算出下一個(gè)超時(shí)時(shí)間點(diǎn),并設(shè)置低功耗定時(shí)器,讓設(shè)備能夠在這個(gè)時(shí)刻點(diǎn)喚醒,并進(jìn)行后續(xù)的工作。當(dāng)系統(tǒng)被(低功耗定時(shí)器中斷或其他喚醒中斷源)喚醒后,系統(tǒng)也需要知道睡眠時(shí)間長(zhǎng)度是多少,并對(duì)OS Tick 進(jìn)行補(bǔ)償,讓系統(tǒng)的OS tick值調(diào)整為一個(gè)正確的值。
PM組件
PM組件是RT-Thread系統(tǒng)中針對(duì)電源管理而設(shè)計(jì)的基礎(chǔ)功能組件, 組件采用分層設(shè)計(jì)思想,分離架構(gòu)和芯片相關(guān)的部分,提取公共部分作為核心。支持多種運(yùn)行模式和休眠模式的管理切換,以及低功耗定時(shí)器的管理。
PM 組件有以下特點(diǎn):
-
PM 組件是基于模式來(lái)管理功耗
-
PM 組件可以根據(jù)模式自動(dòng)更新設(shè)備的頻率配置,確保在不同的運(yùn)行模式都可以正常工作
-
PM 組件可以根據(jù)模式自動(dòng)管理設(shè)備的掛起和恢復(fù),確保在不同的休眠模式下可以正確的掛起和恢復(fù)
-
PM 組件支持可選的休眠時(shí)間補(bǔ)償,讓依賴(lài) OS Tick 的應(yīng)用可以透明使用
-
PM 組件向上層提供設(shè)備接口,如果使用了設(shè)備文件系統(tǒng)組件,那么也可以用文件系統(tǒng)接口來(lái)訪問(wèn)
PM組件支持的休眠模式有
RA系列LPM功能
RA2 MCU支持的LPM類(lèi)型有:
-
Sleep mode
-
Software Standby mode
-
Snooze mode
低功耗模式轉(zhuǎn)換和觸發(fā)源如圖所示:
不同模式間的切換如圖所示,從圖中也可以看出三種模式的功耗關(guān)系是Sleep>Snooze>Standby。
RA2芯片的休眠模式對(duì)應(yīng)PM組件的模式關(guān)系:
配置LPM功能
要使用RA2系列芯片的LPM功能,需要進(jìn)入bsp enesas a2l1-cpk目錄。
-
在menuconfig中使能LPM驅(qū)動(dòng),并勾選要開(kāi)啟的休眠模式,然后保存配置,生成MDK5工程。
-
打開(kāi)PM組件和驅(qū)動(dòng)后,需要增加idle的線程棧大小,可改為1024。
-
打開(kāi)生成的MDK5工程project.uvprojx,然后打開(kāi)FSP配置工具添加LPM相關(guān)配置。下圖是需要添加的stack,包括三種LPM模式的配置以及低功耗定時(shí)器AGT1。
-
創(chuàng)建LPM如下圖所示新建r_lpm,需要根據(jù)使用的模式進(jìn)行配置且不同模式要?jiǎng)?chuàng)建不同的r_lpm。下面將分別介紹三種不同模式的配置,創(chuàng)建步驟就不再贅述。
Sleep mode休眠模式
創(chuàng)建出r_lpm后需要修改Name和Low Power Mode這兩個(gè)配置項(xiàng)。Name需要改為g_lpm_sleep,因?yàn)樵隍?qū)動(dòng)文件中已經(jīng)定義了sleep模式對(duì)應(yīng)的stack名稱(chēng)。Low Power Mode選擇Sleep mode即可。
Standby mode軟件待機(jī)模式
Name需要改為g_lpm_sw_standby。Low Power Mode選擇Software Standby mode即可。
另外在此模式下還需要配置喚醒MCU的中斷源,因?yàn)闀?huì)使用到AGT1做為低功耗定時(shí)器所以AGT1的中斷需要勾選。如果在應(yīng)用中還需要其他中斷源在此模式下喚醒MCU,則勾選對(duì)應(yīng)選項(xiàng)即可。
Snooze mode小睡模式
Name需要改為g_lpm_sw_standby_with_snooze。Low Power Mode選擇Snooze mode即可。
另外在此模式下同樣要配置喚醒MCU的中斷源,因?yàn)闀?huì)使用到AGT1做為低功耗定時(shí)器所以AGT1的中斷需要勾選。如果在應(yīng)用中還需要其他中斷源在此模式下喚醒MCU,則勾選對(duì)應(yīng)選項(xiàng)即可。
AGT1低功耗定時(shí)器
在驅(qū)動(dòng)中使用了MCU的AGT1做為PM組件的低功耗定時(shí)器,用于在休眠狀態(tài)下的系統(tǒng)時(shí)鐘補(bǔ)償。
完成上述配置步驟就已經(jīng)把LPM低功耗模式的相關(guān)配置做完了。然后再根據(jù)應(yīng)用要實(shí)現(xiàn)的功能配置其他外設(shè)。
低功耗DEMO
上文介紹了在RT-Thread的RA2L1上怎么配置LPM的不同模式,接下來(lái)就用一個(gè)小DEMO來(lái)驗(yàn)證下MCU在各種模式下的工作情況。
低功耗DEMO要實(shí)現(xiàn)的功能是,在CPK-RA2L1開(kāi)發(fā)板上用S1按鈕切換不同的低功耗模式,并在msh中打印出模式切換的提示信息。要實(shí)現(xiàn)這個(gè)功能需要在剛才的基礎(chǔ)上添加一個(gè)低功耗的喚醒源。
添加配置
-
創(chuàng)建IRQ中斷,IRQ中斷選擇通道3,詳細(xì)配置如下。
-
在剛才的Snooze和Standby模式的配置里添加IRQ3的喚醒源
-
然后保存并生成配置代碼。
添加測(cè)試代碼
#include
#ifdef BSP_USING_LPM
#include
#include
#include
#define WAKEUP_APP_THREAD_STACK_SIZE 512
#define WAKEUP_APP__THREAD_PRIORITY RT_THREAD_PRIORITY_MAX / 3
#define WAKEUP_EVENT_BUTTON (1 << 0)
static rt_event_t wakeup_event;
#define USER_INPUT "P004"
#define LED2_PIN "P501" /* Onboard LED pins */
void rt_lptimer_init(rt_lptimer_t timer,
const char *name,
void (*timeout)(void *parameter),
void *parameter,
rt_tick_t time,
rt_uint8_t flag);
rt_err_t rt_lptimer_detach(rt_lptimer_t timer);
rt_err_t rt_lptimer_start(rt_lptimer_t timer);
rt_err_t rt_lptimer_stop(rt_lptimer_t timer);
rt_err_t rt_lptimer_control(rt_lptimer_t timer, int cmd, void *arg);
static struct rt_lptimer lptimer;
static void timeout_cb(void *parameter)
{
rt_interrupt_enter();
rt_kprintf(" lptimer callback ");
rt_interrupt_leave();
}
static void lptimer_init(void)
{
rt_lptimer_init(&lptimer,
"lpm",
timeout_cb,
(void*)&wakeup_event,
1000,
RT_TIMER_FLAG_PERIODIC);
}
static void lptimer_stop(void)
{
rt_lptimer_stop(&lptimer);
}
static void lptimer_start(void)
{
rt_lptimer_start(&lptimer);
}
static void led_app(void)
{
static uint8_t key_status = 0x00;
rt_uint32_t led2_pin = rt_pin_get(LED2_PIN);
rt_pin_write(led2_pin, PIN_HIGH);
switch(key_status%4)
{
case 0:/* IDLE */
lptimer_stop();
rt_pm_release(PM_SLEEP_MODE_NONE);
rt_kprintf(" request:IDLE ");
rt_pm_request(PM_SLEEP_MODE_IDLE);
break;
case 1:/* DEEP */
lptimer_stop();
lptimer_start();
rt_pm_release(PM_SLEEP_MODE_IDLE);
rt_kprintf(" request:DEEP ");
rt_pm_request(PM_SLEEP_MODE_DEEP);
break;
case 2:/* STANDBY */
lptimer_stop();
lptimer_start();
rt_pm_release(PM_SLEEP_MODE_DEEP);
rt_kprintf(" request:STANDBY ");
rt_pm_request(PM_SLEEP_MODE_STANDBY);
break;
case 3:/* NONE */
lptimer_stop();
rt_pm_release(PM_SLEEP_MODE_STANDBY);
rt_kprintf(" request:NONE ");
rt_pm_request(PM_SLEEP_MODE_NONE);
break;
default:
break;
}
key_status++;
rt_pin_write(led2_pin, PIN_LOW);
}
static void wakeup_callback(void* p)
{
rt_event_send(wakeup_event, WAKEUP_EVENT_BUTTON);
}
void wakeup_sample(void)
{
/* init */
rt_uint32_t pin = rt_pin_get(USER_INPUT);
rt_kprintf(" pin number : 0x%04X ", pin);
rt_err_t err = rt_pin_attach_irq(pin, PIN_IRQ_MODE_RISING, wakeup_callback, RT_NULL);
if (RT_EOK != err)
{
rt_kprintf(" attach irq failed. ");
}
err = rt_pin_irq_enable(pin, PIN_IRQ_ENABLE);
if (RT_EOK != err)
{
rt_kprintf(" enable irq failed. ");
}
}
static void wakeup_init(void)
{
wakeup_event = rt_event_create("wakup", RT_IPC_FLAG_FIFO);
RT_ASSERT(wakeup_event != RT_NULL);
wakeup_sample();
}
static void pm_mode_init(void)
{
rt_pm_release_all(RT_PM_DEFAULT_SLEEP_MODE);
rt_pm_request(PM_SLEEP_MODE_NONE);
}
void pm_test_entry(void* para)
{
/* 喚醒回調(diào)函數(shù)初始化 */
wakeup_init();
/* 電源管理初始化 */
pm_mode_init();
lptimer_init();
while (1)
{
/* 等待喚醒事件 */
if (rt_event_recv(wakeup_event,
WAKEUP_EVENT_BUTTON,
RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR,
RT_WAITING_FOREVER, RT_NULL) == RT_EOK)
{
led_app();
}
}
}
int pm_test(void)
{
rt_thread_t tid = rt_thread_create(
"pmtest",pm_test_entry,RT_NULL,512,10,10);
if(tid)
rt_thread_startup(tid);
return 0;
}
MSH_CMD_EXPORT(pm_test, pm_test);
// INIT_APP_EXPORT(pm_test);
#endif
將DEMO代碼加入到工程中,可以直接添加到hal_entry.c或新建一個(gè)源文件。
測(cè)試驗(yàn)證
然后編譯下載。開(kāi)發(fā)板連接串口工具,輸入pm_test
命令啟動(dòng)測(cè)試DEMO。
按下S1按鈕切換工作模式,在DEEP、STANDBY模式下會(huì)啟動(dòng)低功耗定時(shí)器,當(dāng)定時(shí)喚醒后會(huì)打印出回調(diào)接口的提示信息。
經(jīng)測(cè)試:
(1)串口通中輸入“pm_test”,觀測(cè)到電流在8.6mA和5.8mA之間變化。
(2)按下S1后,串口通中打印信息為“requestIDLE”,此時(shí)電流約為2.2mA。
(3)再次按下S1后,串口通中打印信息為“requestDEEP”,此時(shí)電流約為1593uA,并間隔產(chǎn)生lptimer中斷。
(4)再次按下S1后,串口通中打印信息為“requestSTANDBY”,此時(shí)電流約為2.4uA,并間隔產(chǎn)生lptimer中斷。
(5)再次按下S1后,串口通中打印信息為“requestNONE”,恢復(fù)為(1)的電流值,然后可循環(huán)執(zhí)行此流程。
-
mcu
+關(guān)注
關(guān)注
146文章
16796瀏覽量
349335 -
瑞薩
+關(guān)注
關(guān)注
33文章
22274瀏覽量
85810 -
RT-Thread
+關(guān)注
關(guān)注
31文章
1249瀏覽量
39721
原文標(biāo)題:基于瑞薩 RA2L1 MCU 的RT-Thread 低功耗應(yīng)用筆記
文章出處:【微信號(hào):RTThread,微信公眾號(hào):RTThread物聯(lián)網(wǎng)操作系統(tǒng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論