電源管理組件
嵌入式系統(tǒng)低功耗管理的目的在于滿足用戶對(duì)性能需求的前提下,盡可能降低系統(tǒng)能耗以延長(zhǎng)設(shè)備待機(jī)時(shí)間。高性能與有限的電池能量在嵌入式系統(tǒng)中矛盾最為突出,硬件低功耗設(shè)計(jì)與軟件低功耗管理的聯(lián)合應(yīng)用成為解決矛盾的有效手段。現(xiàn)在的各種 MCU 都或多或少的在低功耗方面提供了管理接口。比如對(duì)主控時(shí)鐘頻率的調(diào)整、工作電壓的改變、總線頻率的調(diào)整甚至關(guān)閉、外圍設(shè)備工作時(shí)鐘的關(guān)閉等。有了硬件上的支持,合理的軟件設(shè)計(jì)就成為節(jié)能的關(guān)鍵,一般可以把低功耗管理分為三個(gè)類別:
處理器電源管理主要實(shí)現(xiàn)方式:對(duì) CPU 頻率的動(dòng)態(tài)管理,以及系統(tǒng)空閑時(shí)對(duì)工作模式的調(diào)整。
設(shè)備電源管理主要實(shí)現(xiàn)方式:關(guān)閉個(gè)別閑置設(shè)備
系統(tǒng)平臺(tái)電源管理主要實(shí)現(xiàn)方式:針對(duì)特定系統(tǒng)平臺(tái)的非常見(jiàn)設(shè)備具體定制。
隨著物聯(lián)網(wǎng) (IoT) 的興起,產(chǎn)品對(duì)功耗的需求越來(lái)越強(qiáng)烈。作為數(shù)據(jù)采集的傳感器節(jié)點(diǎn)通常需要在電池供電時(shí)長(zhǎng)期工作,而作為聯(lián)網(wǎng)的 SOC 也需要有快速的響應(yīng)功能和較低的功耗。
在產(chǎn)品開(kāi)發(fā)的起始階段,首先考慮是盡快完成產(chǎn)品的功能開(kāi)發(fā)。在產(chǎn)品功能逐步完善之后,就需要加入電源管理 (Power Management,以下簡(jiǎn)稱 PM) 功能。為了適應(yīng) IoT 的這種需求,RT-Thread 提供了電源管理組件。電源管理組件的理念是盡量透明,使得產(chǎn)品加入低功耗功能更加輕松。
PM 組件介紹
RT-Thread 的 PM 組件采用分層設(shè)計(jì)思想,分離架構(gòu)和芯片相關(guān)的部分,提取公共部分作為核心。在對(duì)上層提供通用的接口同時(shí),也讓底層驅(qū)動(dòng)對(duì)組件的適配變得更加簡(jiǎn)單。
主要特點(diǎn)
RT-Thread PM 組件主要特點(diǎn)如下所示:
基于模式來(lái)管理功耗,空閑時(shí)動(dòng)態(tài)調(diào)整工作模式,支持多個(gè)等級(jí)的休眠。
對(duì)應(yīng)用透明,組件在底層自動(dòng)完成電源管理。
支持運(yùn)行模式下動(dòng)態(tài)變頻,根據(jù)模式自動(dòng)更新設(shè)備的頻率配置,確保在不同的運(yùn)行模式都可以正常工作。
支持設(shè)備電源管理,根據(jù)模式自動(dòng)管理設(shè)備的掛起和恢復(fù),確保在不同的休眠模式下可以正確的掛起和恢復(fù)。
支持可選的休眠時(shí)間補(bǔ)償,讓依賴 OS Tick 的應(yīng)用可以透明使用。
向上層提供設(shè)備接口,如果打開(kāi)了 devfs 組件,那么也可以通過(guò)文件系統(tǒng)接口訪問(wèn)。
低功耗的本質(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è)正確的值。
低功耗狀態(tài)和模式
RT-Thread PM 組件將系統(tǒng)劃分為兩種狀態(tài):運(yùn)行狀態(tài)(RUN)和休眠狀態(tài)(Sleep)。運(yùn)行狀態(tài)控制 CPU 的頻率,適用于變頻場(chǎng)景;休眠狀態(tài)根據(jù) SOC 特性實(shí)現(xiàn)休眠 CPU,以降低功耗。兩種狀態(tài)分別使用不同的 API 接口,獨(dú)立控制。
休眠狀態(tài)休眠狀態(tài)也就是通常意義上的低功耗狀態(tài),通過(guò)關(guān)閉外設(shè)、執(zhí)行 SOC 電源管理接口,降低系統(tǒng)功耗。休眠狀態(tài)又分為六個(gè)模式,呈現(xiàn)為金字塔的形式。隨著模式增加,功耗逐級(jí)遞減的特點(diǎn)。下面是休眠狀態(tài)下模式的定義,開(kāi)發(fā)者可根據(jù)具體的 SOC 實(shí)現(xiàn)相應(yīng)的模式,但需要遵循功耗逐級(jí)降低的特點(diǎn)。
運(yùn)行狀態(tài)運(yùn)行狀態(tài)通常用于改變 CPU 的運(yùn)行頻率,獨(dú)立于休眠模式。當(dāng)前運(yùn)行狀態(tài)劃分了四個(gè)等級(jí):高速、正常、中速、低速,如下:
PM組件的實(shí)現(xiàn)接口
在 RT-Thrad PM 組件中,外設(shè)或應(yīng)用通過(guò)投票機(jī)制對(duì)所需的功耗模式進(jìn)行投票,當(dāng)系統(tǒng)空閑時(shí),根據(jù)投票數(shù)決策出合適的功耗模式,調(diào)用抽象接口,控制芯片進(jìn)入低功耗狀態(tài),從而降低系統(tǒng)功耗。當(dāng)未進(jìn)行進(jìn)行任何投票時(shí),會(huì)以默認(rèn)模式進(jìn)入(通常為空閑模式)。
pm組件的控制塊:
static struct rt_pm _pm;
API接口:
請(qǐng)求休眠模式
void rt_pm_request(uint8_t sleep_mode);
sleep_mode 取以下枚舉值:
enum { /* sleep modes */ PM_SLEEP_MODE_NONE = 0, /* 活躍狀態(tài) */ PM_SLEEP_MODE_IDLE, /* 空閑模式(默認(rèn)) */ PM_SLEEP_MODE_LIGHT, /* 輕度睡眠模式 */ PM_SLEEP_MODE_DEEP, /* 深度睡眠模式 */ PM_SLEEP_MODE_STANDBY, /* 待機(jī)模式 */ PM_SLEEP_MODE_SHUTDOWN, /* 關(guān)斷模式 */ PM_SLEEP_MODE_MAX, };
調(diào)用該函數(shù)會(huì)將對(duì)應(yīng)的模式計(jì)數(shù)加1,并鎖住該模式。此時(shí)如果請(qǐng)求更低級(jí)別的功耗模式,將無(wú)法進(jìn)入,只有釋放(解鎖)先前請(qǐng)求的模式后,系統(tǒng)才能進(jìn)入更低的模式;向更高的功耗模式請(qǐng)求則不受此影響。該函數(shù)需要和 rt_pm_release 配合使用,用于對(duì)某一階段或過(guò)程進(jìn)行保護(hù)。下面是具體代碼實(shí)現(xiàn):
void rt_pm_request(rt_uint8_t mode) { rt_base_t level; struct rt_pm *pm; if (_pm_init_flag == 0) return; if (mode 》 (PM_SLEEP_MODE_MAX - 1)) return; level = rt_hw_interrupt_disable(); pm = &_pm; if (pm-》modes[mode] 《 255) pm-》modes[mode] ++;//將對(duì)應(yīng)的模式計(jì)數(shù)加1 rt_hw_interrupt_enable(level); }
釋放休眠模式
void rt_pm_release(uint8_t sleep_mode);
調(diào)用該函數(shù)會(huì)將對(duì)應(yīng)的模式計(jì)數(shù)減1,配合 rt_pm_request 使用,釋放先前請(qǐng)求的模式。下面是具體代碼實(shí)現(xiàn):
void rt_pm_release(rt_uint8_t mode) { rt_ubase_t level; struct rt_pm *pm; if (_pm_init_flag == 0) return; if (mode 》 (PM_SLEEP_MODE_MAX - 1)) return; level = rt_hw_interrupt_disable(); pm = &_pm; if (pm-》modes[mode] 》 0) pm-》modes[mode] --;//將對(duì)應(yīng)的模式計(jì)數(shù)減1 rt_hw_interrupt_enable(level); }
特殊情況下,比如某個(gè)階段并不允許系統(tǒng)進(jìn)入更低的功耗模式,此時(shí)可以通過(guò) rt_pm_request 和 rt_pm_release 對(duì)該過(guò)程進(jìn)行保護(hù)。如 I2C 讀取數(shù)據(jù)期間,不允許進(jìn)入深度睡眠模式(可能會(huì)導(dǎo)致外設(shè)停止工作),因此可以做如下處理:
/* 請(qǐng)求輕度睡眠模式(I2C外設(shè)該模式下正常工作) */ rt_pm_request(PM_SLEEP_MODE_LIGHT); /* 讀取數(shù)據(jù)過(guò)程 */ /* 釋放該模式 */ rt_pm_release(PM_SLEEP_MODE_LIGHT);
設(shè)置運(yùn)行模式
int rt_pm_run_enter(uint8_t run_mode);
run_mode 可以取以下枚舉值:
enum { /* run modes*/ PM_RUN_MODE_HIGH_SPEED = 0, /* 高速 */ PM_RUN_MODE_NORMAL_SPEED, /* 正常(默認(rèn)) */ PM_RUN_MODE_MEDIUM_SPEED, /* 中速 */ PM_RUN_MODE_LOW_SPEED, /* 低速 */ PM_RUN_MODE_MAX, };
調(diào)用該函數(shù)改變 CPU 的運(yùn)行頻率,從而降低運(yùn)行時(shí)的功耗。此函數(shù)只提供級(jí)別,具體的 CPU 頻率應(yīng)在移植階段視實(shí)際情況而定。
下面是具體代碼實(shí)現(xiàn):
int rt_pm_run_enter(rt_uint8_t mode) { rt_base_t level; struct rt_pm *pm; if (_pm_init_flag == 0) return -RT_EIO; if (mode 》 PM_RUN_MODE_MAX) return -RT_EINVAL; level = rt_hw_interrupt_disable(); pm = &_pm; if (mode 《 pm-》run_mode) { /* change system runing mode */ pm-》ops-》run(pm, mode); /* changer device frequency */ _pm_device_frequency_change(mode); } else { pm-》flags |= RT_PM_FREQUENCY_PENDING; } pm-》run_mode = mode; rt_hw_interrupt_enable(level); return RT_EOK; }
設(shè)置進(jìn)入/退出休眠模式的回調(diào)通知
void rt_pm_notify_set(void (*notify)(uint8_t event, uint8_t mode, void *data), void *data);
event 為以下兩個(gè)枚舉值,分別標(biāo)識(shí)進(jìn)入/退出休眠模式。
enum { RT_PM_ENTER_SLEEP = 0, /* 進(jìn)入休眠模式 */ RT_PM_EXIT_SLEEP, /* 退出休眠模式 */ };
在應(yīng)用進(jìn)入/退出休眠模式會(huì)觸發(fā)回調(diào)通知。下面是具體代碼實(shí)現(xiàn):
void rt_pm_notify_set(void (*notify)(rt_uint8_t event, rt_uint8_t mode, void *data), void *data) { _pm_notify.notify = notify; _pm_notify.data = data; }
注冊(cè)PM設(shè)備
void rt_pm_device_register(struct rt_device *device, const struct rt_device_pm_ops *ops)
與應(yīng)用不同,某些外設(shè)可能在進(jìn)入低功耗狀態(tài)時(shí)執(zhí)行特定操作,退出低功耗時(shí)采取措施恢復(fù),此時(shí)可以通過(guò)注冊(cè)PM設(shè)備來(lái)實(shí)現(xiàn)。通過(guò)注冊(cè) PM 設(shè)備,在進(jìn)入低功耗狀態(tài)之前,會(huì)觸發(fā)注冊(cè)設(shè)備的 suspend 回調(diào),開(kāi)發(fā)者可在回調(diào)里執(zhí)行自己的操作;類似地,從低功耗狀態(tài)退出時(shí),也會(huì)觸發(fā) resume 回調(diào)。運(yùn)行模式下的頻率改變同樣會(huì)觸發(fā)設(shè)備的 frequency_change 回調(diào)。下面是具體代碼實(shí)現(xiàn):
void rt_pm_device_register(struct rt_device *device, const struct rt_device_pm_ops *ops) { rt_base_t level; struct rt_device_pm *device_pm; RT_DEBUG_NOT_IN_INTERRUPT; level = rt_hw_interrupt_disable(); device_pm = (struct rt_device_pm *)RT_KERNEL_REALLOC(_pm.device_pm, (_pm.device_pm_number + 1) * sizeof(struct rt_device_pm)); if (device_pm != RT_NULL) { _pm.device_pm = device_pm; _pm.device_pm[_pm.device_pm_number].device = device; _pm.device_pm[_pm.device_pm_number].ops = ops; _pm.device_pm_number += 1; } rt_hw_interrupt_enable(level); }
設(shè)置進(jìn)入/退出休眠模式的回調(diào)通知和注冊(cè)為設(shè)備的回調(diào)通知流程:
首先應(yīng)用設(shè)置進(jìn)出休眠狀態(tài)的回調(diào)函數(shù),然后調(diào)用 rt_pm_request 請(qǐng)求休眠模式,觸發(fā)休眠操作;PM 組件在系統(tǒng)空閑時(shí)檢查休眠模式計(jì)數(shù),根據(jù)投票數(shù)給出推薦的模式;接著 PM 組件調(diào)用 notfiy 通知應(yīng)用,告知即將進(jìn)入休眠模式;然后對(duì)注冊(cè)的 PM 設(shè)備執(zhí)行掛起操作,返回 OK 后執(zhí)行 SOC 實(shí)現(xiàn)的的休眠模式,系統(tǒng)進(jìn)入休眠狀態(tài)(如果使能時(shí)間補(bǔ)償,休眠之前會(huì)先啟動(dòng)低功耗定時(shí)器)。此時(shí) CPU 停止工作,等待事件或者中斷喚醒。當(dāng)系統(tǒng)被喚醒后,由于全局中斷為關(guān)閉狀態(tài),系統(tǒng)繼續(xù)從該處執(zhí)行,獲取睡眠時(shí)間補(bǔ)償系統(tǒng)的心跳,依次喚醒設(shè)備,通知應(yīng)用從休眠模式退出。如此一個(gè)周期執(zhí)行完畢,退出,等待系統(tǒng)下次空閑。模式的切換代碼實(shí)現(xiàn):當(dāng)任務(wù)進(jìn)入到空閑線程,最終是調(diào)用此函數(shù)進(jìn)入低功耗和喚醒的
static void _pm_change_sleep_mode(struct rt_pm *pm, rt_uint8_t mode) { rt_tick_t timeout_tick, delta_tick; rt_base_t level; int ret = RT_EOK; if (mode == PM_SLEEP_MODE_NONE) { pm-》sleep_mode = mode; pm-》ops-》sleep(pm, PM_SLEEP_MODE_NONE); } else { level = rt_pm_enter_critical(mode); /* Notify app will enter sleep mode */ if (_pm_notify.notify) _pm_notify.notify(RT_PM_ENTER_SLEEP, mode, _pm_notify.data); /* Suspend all peripheral device */ ret = _pm_device_suspend(mode); if (ret != RT_EOK) { _pm_device_resume(mode); if (_pm_notify.notify) _pm_notify.notify(RT_PM_EXIT_SLEEP, mode, _pm_notify.data); rt_pm_exit_critical(level, mode); return; } /* Tickless*/ if (pm-》timer_mask & (0x01 《《 mode)) { timeout_tick = rt_timer_next_timeout_tick(); if (timeout_tick == RT_TICK_MAX) { if (pm-》ops-》timer_start) { pm-》ops-》timer_start(pm, RT_TICK_MAX); } } else { timeout_tick = timeout_tick - rt_tick_get(); if (timeout_tick 《 RT_PM_TICKLESS_THRESH) { mode = PM_SLEEP_MODE_IDLE; } else { pm-》ops-》timer_start(pm, timeout_tick); } } } /* enter lower power state */ pm-》ops-》sleep(pm, mode); /* wake up from lower power state*/ if (pm-》timer_mask & (0x01 《《 mode)) { delta_tick = pm-》ops-》timer_get_tick(pm); pm-》ops-》timer_stop(pm); if (delta_tick) { rt_tick_set(rt_tick_get() + delta_tick); rt_timer_check(); } } /* resume all device */ _pm_device_resume(pm-》sleep_mode); if (_pm_notify.notify) _pm_notify.notify(RT_PM_EXIT_SLEEP, mode, _pm_notify.data); rt_pm_exit_critical(level, mode); } }
移植的實(shí)現(xiàn)原理
RT-Thread 低功耗管理系統(tǒng)從設(shè)計(jì)上分離運(yùn)行模式和休眠模式,獨(dú)立管理,運(yùn)行模式用于變頻和變電壓,休眠調(diào)用芯片的休眠特性。對(duì)于多數(shù)芯片和開(kāi)發(fā)來(lái)說(shuō),可能并不需要考慮變頻和變電壓,僅需關(guān)注休眠模式。底層功能的實(shí)現(xiàn)已經(jīng)有Sunwancn大神對(duì)STM32做了全系列的適配,以下是底層實(shí)現(xiàn)原理,用戶也可以自行根據(jù)自身情況對(duì)底層進(jìn)行裁剪或增強(qiáng)。(注意: 驅(qū)動(dòng)可能有更新,移植請(qǐng)到gitee下載最新pm驅(qū)動(dòng)。地址在pm-ports-stm32-new 分支:https://gitee.com/sunwancn/rt-thread/tree/pm-ports-stm32-new)
PM 組件的底層功能都是通過(guò)struct rt_pm_ops結(jié)構(gòu)體里的函數(shù)完成:
/** * low power mode operations */ struct rt_pm_ops { void (*sleep)(struct rt_pm *pm, uint8_t mode); void (*run)(struct rt_pm *pm, uint8_t mode); void (*timer_start)(struct rt_pm *pm, rt_uint32_t timeout); void (*timer_stop)(struct rt_pm *pm); rt_tick_t (*timer_get_tick)(struct rt_pm *pm); };
移植休眠模式移植休眠模式僅需關(guān)注 sleep 接口,下面是具體的實(shí)現(xiàn):
void stm32_sleep(struct rt_pm *pm, rt_uint8_t mode) { switch (mode) { case PM_SLEEP_MODE_NONE: break; case PM_SLEEP_MODE_IDLE: if (pm-》run_mode == PM_RUN_MODE_LOW_SPEED) { /* Enter LP SLEEP Mode, Enable low-power regulator */ HAL_PWR_EnterSLEEPMode(PWR_LOWPOWERREGULATOR_ON, PWR_SLEEPENTRY_WFI); } else { /* Enter SLEEP Mode, Main regulator is ON */ HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI); } break; case PM_SLEEP_MODE_LIGHT: if (pm-》run_mode == PM_RUN_MODE_LOW_SPEED) { __HAL_FLASH_SLEEP_POWERDOWN_ENABLE(); /* Enter LP SLEEP Mode, Enable low-power regulator */ HAL_PWR_EnterSLEEPMode(PWR_LOWPOWERREGULATOR_ON, PWR_SLEEPENTRY_WFI); __HAL_FLASH_SLEEP_POWERDOWN_DISABLE(); } else { /* Enter SLEEP Mode, Main regulator is ON */ HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI); } break; case PM_SLEEP_MODE_DEEP: /* Disable SysTick interrupt */ CLEAR_BIT(SysTick-》CTRL, (rt_uint32_t)SysTick_CTRL_TICKINT_Msk); if (pm-》run_mode == PM_RUN_MODE_LOW_SPEED) { /* Clear LPR bit to back the normal run mode */ CLEAR_BIT(PWR-》CR1, PWR_CR1_LPR); /* Enter STOP 2 mode */ HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI); /* Set Regulator parameter to lowpower run mode */ SET_BIT(PWR-》CR1, PWR_CR1_LPR); } else { /* Enter STOP 2 mode */ HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI); } /* Enable SysTick interrupt */ SET_BIT(SysTick-》CTRL, (rt_uint32_t)SysTick_CTRL_TICKINT_Msk); /* Re-configure the system clock */ systemclock_reconfig(pm-》run_mode); break; case PM_SLEEP_MODE_STANDBY: __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU); /* Enter STANDBY mode */ HAL_PWR_EnterSTANDBYMode(); break; case PM_SLEEP_MODE_SHUTDOWN: __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU); /* Enter SHUTDOWNN mode */ HAL_PWREx_EnterSHUTDOWNMode(); break; default: RT_ASSERT(0); break; } }
移植時(shí)間補(bǔ)償接口某些情況下,我們可能需要系統(tǒng)在空閑時(shí)進(jìn)入 Stop 模式,以達(dá)到更低的降功耗效果。根據(jù)手冊(cè)可知,Stop 2 模式會(huì)關(guān)閉系統(tǒng)時(shí)鐘,當(dāng)前的 OS Tick 基于內(nèi)核的 Systick 定時(shí)器。那么在系統(tǒng)時(shí)鐘停止后,OS Tick 也會(huì)停止,對(duì)于某些依賴 OS Tick 的應(yīng)用,在進(jìn)入 Stop 2 模式,又被中斷喚醒后,就會(huì)出現(xiàn)問(wèn)題,因此需要在系統(tǒng)喚醒后,對(duì) OS Tick 進(jìn)行補(bǔ)償。Stop 2 模式下,絕大多數(shù)外設(shè)都停止工作,僅低功耗定時(shí)器 1(LP_TIM1)和RTC,選擇 LSI 作為時(shí)鐘源后,仍然能正常運(yùn)行,所以可以選擇 LP_TIM1 或者RTC 作為 Stop 2 模式的時(shí)間補(bǔ)償定時(shí)器。
休眠的時(shí)間補(bǔ)償需要實(shí)現(xiàn)三個(gè)接口,分別用于啟動(dòng)低功耗定時(shí)器、停止定時(shí)器、喚醒后獲取休眠的 Tick,下面是具體的實(shí)現(xiàn):
static void stm32_pm_timer_start(struct rt_pm *pm, rt_uint32_t timeout) { RT_ASSERT(pm != RT_NULL); RT_ASSERT(timeout 》 0); if (timeout != RT_TICK_MAX) { /* Convert OS Tick to PM timer timeout value */ timeout = stm32_pm_tick_from_os_tick(timeout); if (timeout 》 stm32_pmtim_get_tick_max()) { timeout = stm32_pmtim_get_tick_max(); } /* Enter PM_TIMER_MODE */ stm32_pmtim_start(timeout); } }
static void stm32_pm_timer_stop(struct rt_pm *pm) { RT_ASSERT(pm != RT_NULL); /* Reset PM timer status */ stm32_pmtim_stop(); }
static rt_tick_t stm32_pm_timer_get_tick(struct rt_pm *pm) { rt_uint32_t timer_tick; RT_ASSERT(pm != RT_NULL); timer_tick = stm32_pmtim_get_current_tick(); return stm32_os_tick_from_pm_tick(timer_tick); }
休眠時(shí)間補(bǔ)償?shù)囊浦蚕鄬?duì)并不復(fù)雜,根據(jù) Tick 配置低功耗定時(shí)器超時(shí),喚醒后獲取實(shí)際休眠時(shí)間并轉(zhuǎn)換為OS Tick,告知 PM 組件即可。
移植運(yùn)行模式移植休眠模式僅需關(guān)注 run接口,下面是具體的實(shí)現(xiàn):
void stm32_run(struct rt_pm *pm, rt_uint8_t mode) { static rt_uint32_t last_mode; static char *run_str[] = PM_RUN_MODE_NAMES; struct rcc_conf_struct sconf = _rcc_conf[mode]; if (mode == last_mode) return; if (stm32_run_freq[mode][0] != stm32_run_freq[last_mode][0]) { if (_rcc_conf[last_mode].low_pow_run_en && !sconf.low_pow_run_en) { /* Disable the Low-power Run mode */ HAL_PWREx_DisableLowPowerRunMode(); } systemclock_msi_on(last_mode); if (mode 《 last_mode) { /* Frequency increase */ HAL_PWREx_ControlVoltageScaling(sconf.volt_scale); _set_sysclock[mode](); } else { /* Frequency reduce */ _set_sysclock[mode](); HAL_PWREx_ControlVoltageScaling(sconf.volt_scale); } if (sconf.volt_scale == PWR_REGULATOR_VOLTAGE_SCALE2 || _osc_conf.osc_type == RCC_OSCILLATORTYPE_MSI) { /* Configure the wake up from stop clock to MSI */ __HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_MSI); } else { /* Configure the wake up from stop clock to HSI */ __HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_HSI); } if (sconf.low_pow_run_en) { /* Enable the Low-power Run mode */ HAL_PWREx_EnableLowPowerRunMode(); } systemclock_msi_off(mode); #if defined(RT_USING_SERIAL) /* Re-Configure the UARTs */ uart_console_reconfig(); #endif /* Re-Configure the Systick time */ HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / RT_TICK_PER_SECOND); /* Re-Configure the Systick */ HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK); } last_mode = mode; rt_kprintf(“switch to %s mode, frequency = %d %sHz ”, run_str[mode], stm32_run_freq[mode][0], (stm32_run_freq[mode][1] == 1) ? “M” : “K”); if ((stm32_run_freq[mode][0] / stm32_run_freq[mode][1]) 》 OSC_CONF_SYS_FREQ_MAX) rt_kprintf(“warning: The frequency has over than %d MHz ”, OSC_CONF_SYS_FREQ_MAX); }
自定義運(yùn)行級(jí)別時(shí)鐘樹(shù)配置函數(shù)PM 組件驅(qū)動(dòng)在給定運(yùn)行頻率時(shí),已經(jīng)盡量自動(dòng)最優(yōu)化配置時(shí)鐘樹(shù),但有時(shí)外設(shè)時(shí)鐘還是沒(méi)有達(dá)到自己想要的頻率,這時(shí)可以自己配置時(shí)鐘樹(shù),在 board.c 添加以下單個(gè)或所有函數(shù),代碼可參考 SystemClock_Config() 函數(shù):
rt_uint16_t stm32_run_freq[PM_RUN_MODE_MAX][2] = { /* The actual frequency is 1/divisor MHz, divisor = {1, 1000} */ /* {sysclk frequency, divisor} */ {/* 配置高頻運(yùn)行時(shí)的時(shí)鐘 */, /* 分頻系數(shù) */}, /* High speed */ {/* 配置普通運(yùn)行時(shí)的時(shí)鐘 */, /* 分頻系數(shù) */}, /* Normal speed */ {/* 配置中低運(yùn)行時(shí)的時(shí)鐘 */, /* 分頻系數(shù) */}, /* Medium speed */ {/* 配置低頻運(yùn)行時(shí)的時(shí)鐘 */, /* 分頻系數(shù) */}, /* Low speed, MSI clock 2.0 MHz */ }; void stm32_systemclock_high(void) { /* 添加代碼,配置高頻運(yùn)行時(shí)的時(shí)鐘樹(shù) */ } void stm32_systemclock_normal(void) { /* 添加代碼,配置普通速度運(yùn)行時(shí)的時(shí)鐘樹(shù) */ } void stm32_systemclock_medium(void) { /* 添加代碼,配置中低頻運(yùn)行時(shí)的時(shí)鐘樹(shù) */ } void stm32_systemclock_low(void) { /* 添加代碼,配置低頻運(yùn)行時(shí)的時(shí)鐘樹(shù) */ }
當(dāng)?shù)退俚念l率小于2MHz時(shí),要注意以下2點(diǎn):
串口波特率如果設(shè)置過(guò)高,將不能正常通信
在時(shí)鐘頻率很低時(shí),要適當(dāng)減小 RT_TICK_PER_SECOND 值,不然由于 OS_tick 過(guò)短,某些線程將不能完成任務(wù),從而不能進(jìn)入低功耗模式
初始化PM組件注意:休眠模式的時(shí)間補(bǔ)償需要在初始化階段通過(guò)設(shè)置 timer_mask 的對(duì)應(yīng)模式的 bit 控制開(kāi)啟。例如需要開(kāi)啟 Deep Sleep 模式下的時(shí)間補(bǔ)償,在實(shí)現(xiàn) timer 相關(guān)的 ops 接口后,初始化時(shí)設(shè)置相應(yīng)的bit:
int drv_pm_hw_init(void) { static const struct rt_pm_ops _ops = { stm32_sleep, stm32_run, stm32_pm_timer_start, stm32_pm_timer_stop, stm32_pm_timer_get_tick }; rt_uint8_t timer_mask = 0; /* Enable Power Clock */ __HAL_RCC_PWR_CLK_ENABLE(); /* initialize timer mask */ timer_mask = 1UL 《《 PM_SLEEP_MODE_DEEP; /* initialize system pm module */ rt_system_pm_init(&_ops, timer_mask, RT_NULL); return 0; } INIT_BOARD_EXPORT(drv_pm_hw_init);
void rt_system_pm_init(const struct rt_pm_ops *ops, rt_uint8_t timer_mask, void *user_data) { struct rt_device *device; struct rt_pm *pm; pm = &_pm; device = &(_pm.parent); device-》type = RT_Device_Class_PM; device-》rx_indicate = RT_NULL; device-》tx_complete = RT_NULL; #ifdef RT_USING_DEVICE_OPS device-》ops = &pm_ops; #else device-》init = RT_NULL; device-》open = RT_NULL; device-》close = RT_NULL; device-》read = _rt_pm_device_read; device-》write = _rt_pm_device_write; device-》control = _rt_pm_device_control; #endif device-》user_data = user_data; /* register PM device to the system */ rt_device_register(device, “pm”, RT_DEVICE_FLAG_RDWR); rt_memset(pm-》modes, 0, sizeof(pm-》modes)); pm-》sleep_mode = _pm_default_sleep; pm-》run_mode = RT_PM_DEFAULT_RUN_MODE; pm-》timer_mask = timer_mask; pm-》ops = ops; pm-》device_pm = RT_NULL; pm-》device_pm_number = 0; _pm_init_flag = 1; }
STM32L4 移植 PM
STM32L4 的低功耗模式簡(jiǎn)介:
STM32L4系列 是 ST 公司推出的一款超低功耗的 Crotex-M4 內(nèi)核的 MCU,支持多個(gè)電源管理模式,其中最低功耗 Shutdown 模式下,待機(jī)電流僅 30 nA。ST 公司 把 L4系列 的電管管理分為很多種,但各個(gè)模式的并非功耗逐級(jí)遞減的特點(diǎn),下面是各個(gè)模式之間的狀態(tài)轉(zhuǎn)換圖:
盡管 STM32L4系列 的低功耗模式很多,但本質(zhì)上并不復(fù)雜,理解它的原理有助于我們移植驅(qū)動(dòng),同時(shí)更好的在產(chǎn)品中選擇合適的模式。最終決定 STM32L4系列 系統(tǒng)功耗的主要是三個(gè)因素:穩(wěn)壓器(voltage regulator)、CPU 工作頻率、芯片自身低功耗的處理,下面分別對(duì)三個(gè)因素進(jìn)行闡述。
穩(wěn)壓器L4 使用兩個(gè)嵌入式線性穩(wěn)壓器為所有數(shù)字電路、待機(jī)電路以及備份時(shí)鐘域供電,分別是主穩(wěn)壓器(main regulator,下文簡(jiǎn)稱 MR)和低功耗穩(wěn)壓器(low-power regulator,下文簡(jiǎn)稱 LPR)。穩(wěn)壓器在復(fù)位后處于使能狀態(tài),根據(jù)應(yīng)用模式,選擇不同的穩(wěn)壓器對(duì) Vcore 域供電。其中,MR 的輸出電壓可以由軟件配置為不同的范圍(Range 1 和 Rnage 2)。穩(wěn)壓器應(yīng)用場(chǎng)合
穩(wěn)壓器應(yīng)用場(chǎng)合
MR(Range 1)Vcore = 1.2V,用于運(yùn)行模式、睡眠模式和停止模式0,MR 未 Vcore 域提供全功率
MR(Range 2)Vcore = 1.0V,使用的場(chǎng)景同上
LPR用于低功耗運(yùn)行模式、低功耗休眠模式、停止模式 1、停止模式2
OFFStandby 和 Shutdown 模式下,MR 和 LPR 都被關(guān)閉
CPU 工作頻率通過(guò)降低 CPU 的主頻達(dá)到降低功耗的目的:MR 工作在 Range 1 正常模式時(shí),SYSCLK 最高可以工作在 80M;MR 工作在 Range 2 時(shí),SYSCLK 最高不能超過(guò) 26 M;低功耗運(yùn)行模式和低功耗休眠模式,即 Vcore 域由 LPR 供電,SYSCLK 必須小于 2M。
芯片本身的低功耗處理芯片本身定義了一系列的休眠模式,如 Sleeep、Stop、Standby 和 Shutdown,前面的四種模式功耗逐漸降低,實(shí)質(zhì)是芯片內(nèi)部通過(guò)關(guān)閉外設(shè)和時(shí)鐘來(lái)實(shí)現(xiàn)。
配置工程
配置 PM 組件:
配置內(nèi)核選項(xiàng):使用 PM 組件需要更大的 IDLE 線程的棧,這里使用了1024 字節(jié)
在空閑線程中會(huì)調(diào)用rt_system_power_manager接口來(lái)進(jìn)入低功耗模式:
/** * This function will enter corresponding power mode. */ void rt_system_power_manager(void) { rt_uint8_t mode; if (_pm_init_flag == 0) return; /* CPU frequency scaling according to the runing mode settings */ _pm_frequency_scaling(&_pm); /* Low Power Mode Processing */ mode = _pm_select_sleep_mode(&_pm); _pm_change_sleep_mode(&_pm, mode); }
保存后,可以看到pm.c已經(jīng)被添加到了工程:
然后添加PM組件的設(shè)備驅(qū)動(dòng),驅(qū)動(dòng)的最新地址:pm-ports-stm32-new 分支:https://gitee.com/sunwancn/rt-thread/tree/pm-ports-stm32-new注意: 目前所使用的驅(qū)動(dòng)不是最新版本,移植請(qǐng)到gitee下載最新pm驅(qū)動(dòng)。
從 t-threadspstm32librariesHAL_Drivers,拷貝如下四個(gè)文件到工程的drivers文件夾下:
本項(xiàng)目選擇的是使用RTC作為STOP后的時(shí)間補(bǔ)償,所以需要打開(kāi)rtc設(shè)備和所使用的宏:
注: 如果沒(méi)有使用RTT的自身的RTC函數(shù)的話,前面2個(gè)宏可以不要。
責(zé)任編輯:pj
-
電源
+關(guān)注
關(guān)注
185文章
18368瀏覽量
256255 -
處理器
+關(guān)注
關(guān)注
68文章
19893瀏覽量
235164
發(fā)布評(píng)論請(qǐng)先 登錄
TPS650830 用于 Skylake 處理器的可編程中間輸入電壓范圍電源管理IC數(shù)據(jù)手冊(cè)

處理器超頻技巧與注意事項(xiàng)
低功耗處理器的優(yōu)勢(shì)分析
EE-172:使用ADSP-BF535 Blackfin處理器的動(dòng)態(tài)電源管理功能

盛顯科技:拼接處理器如何實(shí)現(xiàn)高效數(shù)據(jù)拼接操作?

對(duì)稱多處理器和非對(duì)稱多處理器的區(qū)別
如何利用PMICs設(shè)計(jì)靈活的處理器電源系統(tǒng)

ARM處理器的尋址方式
電源監(jiān)控電路-電源管理芯片
盛顯科技:投影融合處理器如何實(shí)現(xiàn)圖像的處理和融合?

盛顯科技:投影融合處理器主要的應(yīng)用場(chǎng)景有哪些?

評(píng)論