目录
一、概念及其应用
1.1应用
1.2STM32电源管理系统
2.3STM32低功耗模式
2.3.1睡眠模式
2.3.2停止模式
2.3.3待机模式
三、Tickless低功耗模式
3.1低功耗模式配置
3.2低功耗模式应用
3.3低功耗电路分析
3.4低功耗处理相关接口
四、实现原理
4.1任务等待删除的检查
4.2空闲任务
4.3Tickless流程
4.4休眠处理
4.4.1计算SysTick装载值
4.4.2进入低功耗模式
4.4.3恢复systick
一、概念及其应用
1.1应用
可穿戴的、电池供电、待机时间长的产品:
还有%80的物联网产品
1.2STM32电源管理系统
主要分为:备份域(VBAT备用电池供电)、调压器供电电路(内部1.2V提供供电)、ADC电源电路(模数分离保证ADC测量精度)
实际上,低功耗就是做减法,做关断
2.3STM32低功耗模式
包括睡眠模式、停机模式、待机模式
2.3.1睡眠模式
-
仅关闭内核时钟,内核停止运行,但片上外设包括CortexM4核心外设仍正常运行
-
WFI、WFE两种方式进入,即由等待中断唤醒和等待事件唤醒
一般情况下不用睡眠模式,因为它实现不了大面积的低功耗
2.3.2停止模式
-
进一步关闭所有外设的时钟,但不关闭1.2V区域的部分电源,
-
还保留了内核寄存器、内存的信息,所以从停止模式(任意一个外部中断EXTI)唤醒,重启时钟后还可以从上次停止处继续执行代码。
-
停止模式中可选择电压调节器为开模式或低功耗模式,可选择内部FLASH工作在正常模式或掉电模式
2.3.3待机模式
-
关闭所有时钟,同时关闭1.2V区域的所有电源,唤醒后,只能对芯片复位,从头开始执行程序
-
四种唤醒方式,WKUP引脚上升沿,RTC闹钟事件,NRST引脚的复位、IWDG独立看门狗的复位
三、Tickless低功耗模式
通过空闲任务实现,核心是处理tickless函数
3.1低功耗模式配置
-
configUSE_TICKLESS_IDLE 设置为1采用低功耗tickless模式,为0不采用
-
configEXPECTED_IDLE_TIME_BEFORE_SLEEP默认设置为2,表示休眠时间必须>2个tick值(不然也没意义)
-
configPRE_SLEEP_PROCESSIN()和configPOST_SLEEP_PROCESSIN()是rtos给用户提供的接口,分别是进入、退出低功耗模式要处理的事情
-
函数vPortSuppressTicksAndSleep是实际的低功耗执行代码
3.2低功耗模式应用
必须要掌握的硬件知识包括四部分:主控芯片、电源管理、外设模块、外部接口
-
主控芯片早期以MSP430为主,静态电流165uA,现在以STM8/32 L为主 静态电流87uA
-
电源管理包括LDO超低静态电流、输出可关断,DC/DC 主要性能指标是高转换效率
-
外设模块以无线通信技术包括GPRS/GPS/WIFI/Zigbee/LoRa/NB-IOT,须具备休眠模式,休眠电流、待机电流小,中断可唤醒
-
外部接口分为输入和输出,输入方面如CAN、RS485、232、LCD屏等需要间隙供电,输出方面利用MOS管脉冲控制
3.3低功耗电路分析
可通过搜芯片手册—www.alldatasheet.com 寻找器件特性 如静态电流quiescent current
3.4低功耗处理相关接口
除了停止模式和待机模式,PWR电源控制和GPIO端口还分别提供了相关接口用于低功耗处理
四、实现原理
4.1任务等待删除的检查
-
实际接口static void prvCheckTaskWaitingTermination
-
循环判断等待被删除的任务数量标记是否大于0(此标记在任务删除的接口中会操作这个数)
-
挂起调度器
-
读取删除任务自身列表里任务状态是否为空
-
开启调度器
-
进入临界段
-
获取任务控制块
-
从任务列表项移除任务
-
任务总计数-1
-
等待删除计数-1
-
退出临界段
-
释放任务控制块
-
4.2空闲任务
main.c中 osKernelStart—>vTaskStartScheduler中自动创建空闲任务
-
实际接口static portTASK_FUNCTION(prvIdleTask,pvParameters)
-
先进行任务等待删除的检查
-
判断是否为抢占式调度
-
如果不是,代表为时间片调度,则触发上下文切换,因为空闲任务本身优先级是最低的
-
如果是抢占式调度,则查看是否有和空闲任务优先级相同的其他任务处于就绪态,如果有就进行上下文切换(要是高于空闲任务的优先级,自然由调度器去调度处理)
-
-
接着就是钩子函数的处理,这个由用户自己填充代码
-
最后就是tickless处理,放在下文阐述
4.3Tickless流程
-
获取系统的最小时间片 xExpectedIdleTime = prvGetExpectedIdleTime()
-
判断是否大于休眠空闲处理的最小间隔 2Tick
-
是的话,挂起调度器
-
再次获取系统的最小时间片(相当于更新一下时间,防止由于其他任务调度了,时间产生了误差)
-
再次判断是否大于休眠空闲处理的最小间隔 2Tick
-
进行休眠处理,传入系统的最小时间片portSUPPRESS_TICKS_AND_SLEEP(xExpectedIdleTime )
-
恢复调度器
-
prvGetExpectedIdleTime()——获取系统最小时间片
如果有比空闲任务优先级高或者相同优先级的任务处于就绪态,直接返回0
否则返回值=系统解锁时间-系统tick计数值(其实就是当前系统的最小时间片
4.4休眠处理
4.4.1计算SysTick装载值
_weak void vPortSuppressTicksAndSleep(系统最小时间片)
-
系统最小时间片溢出检查 最大只能是systick的最大装载值
-
关闭systick定时器
-
systick重载值=当前systick计数值+单次系统tick值*(系统最小时间片-1)
-
判断装载值是否大于补偿周期 之后减去补偿时间
-
最终得到systick重载值
-
关闭中断——关闭所有中断 (和进入临界段不一样)
这里的关闭的中断稍微特殊一点,不会进行中断处理但是会唤醒CPU
4.4.2进入低功耗模式
-
判断是否有其他任务进入了就绪态
-
是的话,终止休眠状态,当前systick计数值放到systick装载寄存器中,启动systick,重新赋值装载寄存器值为一个系统的tick周期,开启中断
-
否则装载休眠systick装载值,清除systick当前计数值,启动systick定时器,进入休眠前的工作,然后休眠
-
退出休眠,停止systick定时器,使能中断
-
4.4.3恢复systick
-
判断是否为systick唤醒的
-
是的话,systick恢复值=单个tick周期-(休眠装载值-当前tick计数值)如果恢复值很小或者很大就赋值为1个tick周期
-
把systick恢复值赋值给systick装载值
-
休眠周期的补偿值,单位为tick,也就是1ms
-
-
不是的话,休眠运行装载值=休眠装载值-当前systick计数值
-
休眠运行周期,单位为tick
-
装载恢复systick装载值
-
-
-
清除systick计数值
-
进入临界段
-
使能systick
-
补偿系统的tick周期值,也就是说运行了多少时间,因为调度器恢复的时候,会根据tick值进行遍历,保证实时性
-
恢复systick周期为1个tick