CodeBrick笔记,一种支持低功耗的嵌入式操作系统
系统流程
- 主程序module_task_process轮询所有任务,包括低功耗任务。
- 进入低功耗逻辑:当低功耗使能,且系统空闲时,进入低功耗
- 空闲判断依据 is_timeout(system_idle_time, 3000),距离最近空闲时间操作3秒
- 如果有事件需要延时进入低功耗,则更新空闲时间system_idle_time = get_tick();
- 例如按键后亮屏,延时键入低功耗。检测到按键时,更新空闲时间。
- 低功耗处理system_goto_sleep
- 计算最近的一次唤醒时间,
- 然后进入低功耗
- 到时间唤醒
sleep_notify()计算所有需要低功耗的设备,最近
低功耗
1. 时序系统,时间间隔到了会执行
void module_task_process(void)
{
const task_item_t *t;
for (t = &task_tbl_start + 1; t < &task_tbl_end; t++) {
if ((get_tick() - *t->timer) >= t->interval) {
*t->timer = get_tick();
t->handle();
}
}
}
2. 低功耗任务是正常任务的一种
static void pm_task(void)
{
pm_process();
}task_register("pm", pm_task, 0);
3. 低功耗使能+系统空闲 => 进入低功耗
void pm_process(void)
{
if (!pm_watch.enable || !system_is_idle())
return;
system_goto_sleep();
}
3.1 空闲判断:轮询所有任务的空闲标志位
static bool system_is_idle(void)
{
const pm_item_t *it;
for (it = &pm_tbl_start + 1; it < &pm_tbl_end; it++) {
if (it->idle != NULL && !it->idle())
return false;
}
return true;
}
3.2 轮询所有低功耗任务的sleep_notify,获得最近下次唤醒时间,进入休眠,到时间唤醒。
sleep_notify计算下次唤醒时间,(关闭xx外设)
wakeup_notify (初始化xx外设,执行相应程序)
static void system_goto_sleep(void)
{
const pm_item_t *it;
const pm_adapter_t *adt;
unsigned int sleep_time;
unsigned int tmp;
adt = pm_watch.adt;
sleep_time = adt->max_sleep_time;
//休眠处理
for (it = &pm_tbl_start + 1; it < &pm_tbl_end; it++) {
if (it->sleep_notify == NULL)
continue;
tmp = it->sleep_notify(); //休眠请求,并得到设备期待下次唤醒时间
if (tmp && tmp < sleep_time) //计算出所有设备中的最小允许休眠时间
sleep_time = tmp;
}
adt->goto_sleep(sleep_time); //获得
//唤醒处理
for (it = &pm_tbl_start + 1; it < &pm_tbl_end; it++) {
if (it->wakeup_notify == NULL)
continue;
it->wakeup_notify();
}
}
adt->goto_sleep(sleep_time) 指向的目标函数
static unsigned int system_sleep(unsigned int ms)
{
unsigned int start_time = get_tick();
/**
一般来说进入低功耗应根据sleep_time指定时长使用rtc_wakeup_config配置下一次唤
醒时间,这种方式可以让系统休眠时间尽量拉长,功耗也最低。但是对于STM32F4系列MCU
来说,低功耗模式下除了RTC其它时钟都是停止的,没有像L4一样拥有低功耗定时器。如果
突然外部中断唤醒,系统滴答时钟并不好做补偿处理。故对于F4系列MCU,这里提供一个
不需要时间补偿的处理方式,系统只在初始化时配置1次[rtc_wakeup_config],即系统以
最小休眠时间为SYS_TICK_INTERVAL(默认10ms)为单位间歇运行.无论系统是否被外部中
断唤醒都不会影响系统滴答时钟精度,缺点由于唤醒频繁,比理论极限功耗稍大
*/
while (get_tick() - start_time < ms) {
is_rtc_wakekup = false;
PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);
if (!is_rtc_wakekup) //外部中断唤醒
break;
}
system_wakup_config(); //启动HSE
//printf("Sleep Time:%d isrtc:%d...\r\n\r\n", get_tick() - start_time, is_rtc_wakekup);
return get_tick() - start_time;
}
进入低功耗逻辑
基于tick
低功耗任务示例
#define pm_dev_register(name, idle, sleep_notify, wakeup_notify)\
__pm_item_register(name, idle, sleep_notify, wakeup_notify)
static unsigned int key_sleep_notify(void)
{
return (key_busy(&key) || readkey()) ? 20 : 0; /* 非空闲时20ms要唤醒1次*/
} pm_dev_register("key", NULL, key_sleep_notify, NULL);
static bool system_is_idle(void)
{
return is_timeout(system_idle_time, 3000) && tty.rx_isempty() && tty.tx_isempty();
}pm_dev_register("sys", system_is_idle, NULL, NULL);
static unsigned int led_sleep_notify(void)
{
int i;
for (i = 0; i < LED_TYPE_MAX; i++)
if (blink_dev_busy(&led[i]))
return 100; //如果有设备活动,最低100ms唤醒1次
return 0;
} pm_dev_register("led", NULL, led_sleep_notify, NULL);
注册低功耗任务
/**
* @brief 功耗管理项注册
* @param[in] name - 项目名称
* @param[in] idle - 指示设备是否空闲,如果填NULL,则表示允许系统休眠
* @param[in] sleep_notify - 休眠通知,不需要则填NULL
* @param[in] wakeup_notify - 唤醒通知,不需要则填NULL
*/
#define pm_dev_register(name, idle, sleep_notify, wakeup_notify)\
__pm_item_register(name, idle, sleep_notify, wakeup_notify)
系统流程图
不解的地方
- 关闭外设,让设备休眠放在sleep_notify里?
应该是的,sleep_notify让xx外设休眠,并计算下次唤醒时间。
wakeup_notify 让xx外设开启,并执行相应程序。 - system_goto_sleep里,休眠处理部分for(it = &pm_tbl_start + 1; it < &pm_tbl_end; it++)找到最近执行的唤醒函数,goto_sleep休眠完,直接执行it->wakeup_notify();就可以了,需要再for一次?
- 所有低功耗外设可以独立进入低功耗?
- 如果按键改成低功耗,task_register(“key”, key_scan_process, 20); =>pm_dev_register()