【原创】STM32低功耗模式及中断唤醒(基于BMI160及RTC)的研究
预研目标
六轴静止时,终端进入低功耗模式;六轴震动时,终端正常工作模式,从而极大减少非工作时的电流消耗。
解决方案
机器静止时,依据六轴算法,CPU进入休眠(停止)模式;机器工作时,触发六轴中断唤醒CPU,再配合系统空闲时进入CPU睡眠模式,从而极大降低机器非工作时的电流消耗和降低工作时底电流消耗。
关键技术
STM32功耗模式
按功耗由高到低排列,STM32具有运行、睡眠、停止和待机四种工作模式。上电复位后STM32处于运行状态时,当内核不需要继续运行,就可以选择进入后面的三种低功耗模式降低功耗,这三种模式中,电源消耗不同、唤醒时间不同、唤醒源不同,用户需要根据应用需求,选择最佳的低功耗模式。三种低功耗的模式说明见表1。
表 1 STM32的低功耗模式说明
模式 |
说明 |
进入方式 |
唤醒方式 |
对1.2V区域时钟的影响 |
对VDD区域时钟的影响 |
调压器 |
睡眠 |
内核停止,所有外设包括M4核心的外设,如NVIC、系统时钟(SysTick)等仍在运行 |
调用WFI命令 |
任一中断 |
内核时钟关,对其他时钟和ADC时钟无影响 |
无 |
开 |
调用WFE命令 |
唤醒事件 |
|||||
停止 |
所有的时钟都已停止 |
配置PWR_CR寄存器的PDDS +LPDS 位+SLEEPDEEP位 +WFI或WFE命令 |
任一外部中断( 在外部中断寄存器中设置) |
关闭所有1.2V区域的时钟 |
HSI和HSE的振荡器关闭 |
开启或处于低功耗模式( 依据电源控制寄存器的设定) |
待机 |
1.2V 电源关闭 |
配置PWR_CR寄存器的PDDS +SLEEPDEEP位 +WFI或WFE命令 |
WKUP 引脚的上升沿、RTC闹钟事件、NRST 引脚上的外部复位、IWDG 复位 |
关 |
从表中可以看到,这三种低功耗模式层层递进,运行的时钟或芯片功能越来越少,因而功耗越来越低。
睡眠模式
在睡眠模式中,仅关闭了内核时钟,内核停止运行,但其片上外设,CM4核心的外设全都还照常运行。有两种方式进入睡眠模式,它的进入方式决定了从睡眠唤醒的方式,分别是WFI(wait for interrupt)和WFE(wait for event),即由等待"中断"唤醒和由"事件"唤醒。睡眠模式的各种特性见表 2。
表 2 睡眠模式的各种特性
特性 |
说明 |
立即睡眠 |
在执行WFI 或WFE 指令时立即进入睡眠模式。 |
退出时睡眠 |
在退出优先级最低的中断服务程序后才进入睡眠模式。 |
进入方式 |
内核寄存器的SLEEPDEEP = 0 ,然后调用WFI或WFE指令即可进入睡眠模式; 另外若内核寄存器的SLEEPONEXIT=0时,进入"立即睡眠"模式,SLEEPONEXIT=1时,进入"退出时睡眠"模式。 |
唤醒方式 |
如果是使用WFI指令睡眠的,则可使用任意中断唤醒; 如果是使用WFE指令睡眠的,则由事件唤醒。 |
睡眠时 |
关闭内核时钟,内核停止,而外设正常运行,在软件上表现为不再执行新的代码。这个状态会保留睡眠前的内核寄存器、内存的数据。 |
唤醒延迟 |
无延迟。 |
唤醒后 |
若由中断唤醒,先进入中断,退出中断服务程序后,接着执行WFI指令后的程序;若由事件唤醒,直接接着执行WFE后的程序。 |
停止模式
在停止模式中,进一步关闭了其它所有的时钟,于是所有的外设都停止了工作,但由于其1.2V区域的部分电源没有关闭,还保留了内核的寄存器、内存的信息,所以从停止模式唤醒,并重新开启时钟后,还可以从上次停止处继续执行代码。停止模式可以由任意一个外部中断(EXTI)唤醒。在停止模式中可以选择电压调节器为开模式或低功耗模式,可选择内部FLASH工作在正常模式或掉电模式。停止模式的各种特性见表3。
表 3 停止模式的各种特性
特性 |
说明 |
调压器低功耗模式 |
在停止模式下调压器可工作在正常模式或低功耗模式,可进一步降低功耗 |
FLASH掉电模式 |
在停止模式下FLASH可工作在正常模式或掉电模式,可进一步降低功耗 |
进入方式 |
内核寄存器的SLEEPDEEP =1,PWR_CR寄存器中的PDDS=0,然后调用WFI或WFE指令即可进入停止模式; PWR_CR 寄存器的LPDS=0时,调压器工作在正常模式,LPDS=1时工作在低功耗模式; PWR_CR 寄存器的FPDS=0时,FLASH工作在正常模式,FPDS=1时进入掉电模式。 |
唤醒方式 |
如果是使用WFI指令睡眠的,可使用任意EXTI线的中断唤醒; 如果是使用WFE指令睡眠的,可使用任意配置为事件模式的EXTI线事件唤醒。 |
停止时 |
内核停止,片上外设也停止。这个状态会保留停止前的内核寄存器、内存的数据。 |
唤醒延迟 |
基础延迟为HSI振荡器的启动时间,若调压器工作在低功耗模式,还需要加上调压器从低功耗切换至正常模式下的时间,若FLASH工作在掉电模式,还需要加上FLASH从掉电模式唤醒的时间。 |
唤醒后 |
若由中断唤醒,先进入中断,退出中断服务程序后,接着执行WFI指令后的程序;若由事件唤醒,直接接着执行WFE后的程序。唤醒后,STM32会使用HIS作为系统时钟。 |
待机模式
待机模式,它除了关闭所有的时钟,还把1.2V区域的电源也完全关闭了,也就是说,从待机模式唤醒后,由于没有之前代码的运行记录,只能对芯片复位,重新检测boot条件,从头开始执行程序。它有四种唤醒方式,分别是WKUP(PA0)引脚的上升沿,RTC闹钟事件,NRST引脚的复位和IWDG(独立看门狗)复位。
表 4 待机模式的各种特性
特性 |
说明 |
进入方式 |
内核寄存器的SLEEPDEEP =1,PWR_CR寄存器中的PDDS=1,PWR_CR寄存器中的唤醒状态位WUF=0,然后调用WFI或WFE指令即可进入待机模式; |
唤醒方式 |
通过WKUP引脚的上升沿,RTC闹钟、唤醒、入侵、时间戳事件或NRST引脚外部复位及IWDG复位唤醒。 |
待机时 |
内核停止,片上外设也停止;内核寄存器、内存的数据会丢失;除复位引脚、RTC_AF1引脚及WKUP引脚,其它I/O口均工作在高阻态。 |
唤醒延迟 |
芯片复位的时间 |
唤醒后 |
相当于芯片复位,在程序表现为从头开始执行代码。 |
在以上讲解的睡眠模式、停止模式及待机模式中,若备份域电源正常供电,备份域内的RTC都可以正常运行、备份域内的寄存器及备份域内的SRAM数据会被保存,不受功耗模式影响。
功耗模式选择及配置
功耗模式选择
睡眠模式:RTOS空闲任务进入休眠,sysTick中断唤醒;
停止模式:机器不工作(六轴静止)时,进入停止模式;六轴动时中断/RTC定时中断触发唤醒。
功耗模式配置
注:STOP模式下,唤醒后系统使用HSI作为系统时钟,用户可能需要未进入STOP模式前的系统时钟配置。
RTC定时唤醒喂狗
系统进入STOP模式后,需要RTC中断定时唤醒喂狗防止系统复位。
RTC定时唤醒中断使能:
HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 0xA017, RTC_WAKEUPCLOCK_RTCCLK_DIV16);
RTC定时唤醒中断失能:
HAL_RTCEx_DeactivateWakeUpTimer(&hrtc);
RTC定时唤醒喂狗:
void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{
feed_dog();
}
六轴BMI160
电源模式
在我们的产品场景中,Gyroscope(陀螺仪)未使用,默认配置为Suspend mode,Accelermoter(加速度)使用Normal mode(退出CPU休眠后)或Low power mode(进入休眠前配置);
中断模式
模式选择及配置
这里推荐使用BMI160官方驱动,源码获取参见参考资料第3项,关于驱动具体使用参照源码README.md文件,下面
struct bmi160_dev sensor;
BMI160初始化
struct bmi160_dev sensor; /* BMI160初始化 */
void AccelInit(void)
{ /* You may assign a chip select identifier to be handled later */
sensor.id = ;
sensor.interface = BMI160_SPI_INTF;
sensor.read = user_spi_read;
sensor.write = user_spi_write;
sensor.delay_ms = user_delay_ms; int8_t rslt = BMI160_OK;
rslt = bmi160_init(&sensor);
printf("bmi160 chip id: %02x, accel power:%d, gyro power:%d\r\n", sensor.chip_id, sensor.accel_cfg.power, sensor.gyro_cfg.power); /* After the above function call, accel_cfg and gyro_cfg parameters in the device
structure are set with default values, found in the datasheet of the sensor */
} /* BMI160电源模式配置 */
void AccelConfig(void)
{
int8_t rslt = BMI160_OK;
struct bmi160_pmu_status pmuStatus; /* Select the Output data rate, range of accelerometer sensor */
sensor.accel_cfg.odr = BMI160_ACCEL_ODR_400HZ;
sensor.accel_cfg.range = BMI160_ACCEL_RANGE_4G;
sensor.accel_cfg.bw = BMI160_ACCEL_BW_NORMAL_AVG4; /* Select the power mode of accelerometer sensor */
sensor.accel_cfg.power = BMI160_ACCEL_NORMAL_MODE; /* Set the sensor configuration */
rslt = bmi160_set_sens_conf(&sensor); bmi160_get_power_mode( &pmuStatus, &sensor);
//printf("accel power:%d, gyro power:%d\r\n", pmuStatus.accel_pmu_status, pmuStatus.gyro_pmu_status);
} /* BMI160 Any-motion中断配置 */
void AnyMotionIntCfg(void)
{
struct bmi160_int_settg int_config; /* Select the Interrupt channel/pin */
int_config.int_channel = BMI160_INT_CHANNEL_BOTH;// Interrupt channel/pin 1 /* Select the Interrupt type */
int_config.int_type = BMI160_ACC_ANY_MOTION_INT;// Choosing Any motion interrupt /* Select the interrupt channel/pin settings */
int_config.int_pin_settg.output_en = BMI160_ENABLE;// Enabling interrupt pins to act as output pin
int_config.int_pin_settg.output_mode = BMI160_DISABLE;// Choosing push-pull mode for interrupt pin
int_config.int_pin_settg.output_type = BMI160_DISABLE;// Choosing active low output
int_config.int_pin_settg.edge_ctrl = BMI160_ENABLE;// Choosing edge triggered output
int_config.int_pin_settg.input_en = BMI160_DISABLE;// Disabling interrupt pin to act as input
int_config.int_pin_settg.latch_dur = BMI160_LATCH_DUR_NONE;// non-latched output /* Select the Any-motion interrupt parameters */
int_config.int_type_cfg.acc_any_motion_int.anymotion_en = BMI160_ENABLE;// 1- Enable the any-motion, 0- disable any-motion
int_config.int_type_cfg.acc_any_motion_int.anymotion_x = BMI160_ENABLE;// Enabling x-axis for any motion interrupt
int_config.int_type_cfg.acc_any_motion_int.anymotion_y = BMI160_ENABLE;// Enabling y-axis for any motion interrupt
int_config.int_type_cfg.acc_any_motion_int.anymotion_z = BMI160_ENABLE;// Enabling z-axis for any motion interrupt
int_config.int_type_cfg.acc_any_motion_int.anymotion_dur = ;// any-motion duration
int_config.int_type_cfg.acc_any_motion_int.anymotion_thr = ;// (2-g range) -> (slope_thr) * 3.91 mg, (4-g range) -> (slope_thr) * 7.81 mg, (8-g range) ->(slope_thr) * 15.63 mg, (16-g range) -> (slope_thr) * 31.25 mg /* Set the Any-motion interrupt */
bmi160_set_int_config(&int_config, &sensor); /* sensor is an instance of the structure bmi160_dev */
}
方案验证
测试用例
typedef enum
{
STOP_MODE_WAKE_FROM_NULL,
STOP_MODE_WAKE_FROM_RTC,
STOP_MODE_WAKE_FROM_ACCEL,
}StopModeWakeEnum;
StopModeWakeEnum wakeReason = STOP_MODE_WAKE_FROM_NULL; void StopModeTest(void *pdata)
{
RCC_ClkInitTypeDef clkCfgPre;
uint32_t flatencyPre = ;
uint32_t freqPre = ;
GPIO_InitTypeDef GPIO_InitStruct; AccelInit();
AccelConfig();
AnyMotionIntCfg();
for(;;)
{
printf("\r\nSTM32 runing, system led off\r\n");
PrintSysClkInfo();
SysLedOff();
printf("\r\nSTM32 enter stop mode\r\n");
/*## Configure the Wake up timer ###########################################*/
/* RTC Wake-up Interrupt Generation:
Wake-up Time Base = (RTC_WAKEUPCLOCK_RTCCLK_DIV /(LSI))
Wake-up Time = Wake-up Time Base * WakeUpCounter
= (RTC_WAKEUPCLOCK_RTCCLK_DIV /(LSI)) * WakeUpCounter
==> WakeUpCounter = Wake-up Time / Wake-up Time Base
To configure the wake up timer to 20s the WakeUpCounter is set to 0xA017:
RTC_WAKEUPCLOCK_RTCCLK_DIV = RTCCLK_Div16 = 16
Wake-up Time Base = 16 /(~32.768KHz) = ~0,488 ms
Wake-up Time = ~20s = 0,488ms * WakeUpCounter
==> WakeUpCounter = ~20s/0,488ms = 40983 = 0xA017 */
HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 0xA017, RTC_WAKEUPCLOCK_RTCCLK_DIV16); /* FLASH Deep Power Down Mode enabled */
HAL_PWREx_EnableFlashPowerDown(); /* Enter Stop Mode */
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); HAL_PWREx_DisableFlashPowerDown();
HAL_RCC_GetClockConfig( &clkCfgPre, &flatencyPre);
freqPre = HAL_RCC_GetSysClockFreq ();
SYSCLKConfig_STOP();
/* Disable Wake-up timer */
if(HAL_RTCEx_DeactivateWakeUpTimer(&hrtc) != HAL_OK)
{
/* Initialization Error */
Error_Handler();
} switch(wakeReason)
{ case STOP_MODE_WAKE_FROM_RTC:
printf("\r\nSTM32 wake from stop mode from RTC, system led on\r\n");
break; case STOP_MODE_WAKE_FROM_ACCEL:
printf("\r\nSTM32 wake from stop mode from ACCEL, system led on\r\n");
break; default:
printf("\r\nSTM32 wake from stop mode from %d\r\n", wakeReason);
break;
}
printf("SysFreq:%u, ClkSrc:%d(0:HSI, 1:HSE, 2:PLL, 3:PLLR)\r\n", freqPre, clkCfgPre.SYSCLKSource);
printf("\r\nSTM32 recover system clk config\r\n");
PrintSysClkInfo();
OSTimeDlyHMSM( , , , );
}
} void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{
SysLedOn();
feed_dog();
wakeReason = STOP_MODE_WAKE_FROM_RTC;
} void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
switch(GPIO_Pin)
{
case WAKED_FROM_BT_Pin:
if(GPIO_PIN_SET == HAL_GPIO_ReadPin(WAKED_FROM_BT_GPIO_Port, WAKED_FROM_BT_Pin))
{/*wake start*/
BleSpiRxRestart();
}break; case INT_ALARM_Pin:
break; case GYRO_INT1_Pin:
if(GPIO_PIN_SET == HAL_GPIO_ReadPin(GYRO_INT1_GPIO_Port, GYRO_INT1_Pin))
{ }
break; case GYRO_INT2_Pin:
if(GPIO_PIN_SET == HAL_GPIO_ReadPin(GYRO_INT2_GPIO_Port, GYRO_INT2_Pin))
{
SysLedOn();
wakeReason = STOP_MODE_WAKE_FROM_ACCEL;
}
break;
default:
break;
}
}
实验步骤
- 测试用例烧录终端,连接打印串口、打开串口调试助手;
- 18:15:49前保持终端静止、待进入CPU休眠后再次震动终端1次,重复操作2次;
测试log输出:
实验结论
CPU进入停止模式后,RTC定时中断/六轴中断可唤醒CPU,唤醒后CPU使用HSI作为系统时钟源,CPU进入停止模式后功耗极大降低,可实现机器非工作时电流消耗的极大降低。
参考资料
1. 《STM32F401xB/C and STM32F401xD/E advanced Arm®-based 32-bit MCUs》
2.《Description of STM32F4 HAL and LL drivers》
3.https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BMI160-DS000.pdf
4. https://github.com/BoschSensortec/BMI160_driver
作者:大毛孩 出处: https://www.cnblogs.com/damaohai/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。如果觉得还有帮助的话,可以点一下右下角的【推荐】。
【原创】STM32低功耗模式及中断唤醒(基于BMI160及RTC)的研究的更多相关文章
- STM32低功耗模式与烟雾报警器触发信号电路设计
1.STM32的3种低功耗模式 STM32有3种低功耗模式,分别是睡眠模式.停机模式和待机模式. 2.STM32在不同模式下的电流消耗 a.工作模式 消耗电流在27mA至36mA之间. b.睡眠模式 ...
- [原创]STM32 BOOT模式配置以及作用
一.三种BOOT模式介绍 所谓启动,一般来说就是指我们下好程序后,重启芯片时,SYSCLK的第4个上升沿,BOOT引脚的值将被锁存.用户可以通过设置BOOT1和BOOT0引脚的状态,来选择在复位后的启 ...
- Stm32使用串口空闲中断,基于队列来接收不定长、不定时数据
串口持续地接收不定长.不定时的数据,把每一帧数据缓存下来且灵活地利用内存空间,下面提供一种方式供参考.原理是利用串口空闲中断和DMA,每当对方发来一帧完整的数据后,串口接收开始空闲,触发中断,在中断处 ...
- STM32学习笔记(九) 外部中断,待机模式和事件唤醒
学会知识只需要不段的积累和提高,但是如何将知识系统的讲解出来就需要深入的认知和系统的了解.外部中断和事件学习难度并不高,不过涉及到STM32的电源控制部分,还是值得认真了解的,在本文中我将以实际代码为 ...
- STM32中用 stop 模式 配合低功耗模式下的自动唤醒(AWU) 能否实现FreeRTOS tickless 模式
已经实现 ,2018年11月17日11:56:42,具体 如下: 第一步 : 修改 void vPortSetupTimerInterrupt( void ) 函数 ,修改原来的 systick 定 ...
- 亲测实验,stm32待机模式和停机模式唤醒程序的区别,以及唤醒后程序入口
这两天研究了STM32的低功耗知识,低功耗里主要研究的是STM32的待机模式和停机模式.让单片机进入的待机模式和停机模式比较容易,实验中通过设置中断口PA1来响应待机和停机模式. void EXTI1 ...
- stm32的低功耗模式:
一.待机模式.待机模式是低功耗中最低功耗的,内部电压调节电路被关闭, HSE.HIS.PLL被关闭:进入待机模式后,SRAM和寄存器的内容将丢失. (CPU停止,外设停止,RAM的数据寄存器的内容 ...
- STM32的低功耗模式
一 待机模式standby和STOP模式的区别: 进入低功耗模式:都一样,都是先关闭相应时钟,关闭相应外设,配置相应所有IO口(浮动输入),然后配置相应的唤醒中断源,中断影响的O口,然后调用相应函数进 ...
- stm8 停机模式与外部中断唤醒中一个小问题
做了一个简单的项目,电路板使用电池供电,需要系统在待机时低功耗.而对外接口只有4个按键,也就是唤醒必须要通过这四个按键. 系统功能就不介绍了,只给出进入低功耗的代码和退出低功耗的代码. 使用芯片为st ...
随机推荐
- Spring MVC -- 下载文件
像图片或者HTML文件这样的静态资源,在浏览器中打开正确的URL即可下载,只要该资源是放在应用程序的目录下,或者放在应用程序目录的子目录下,而不是放在WEB-INF下,tomcat服务器就会将该资源发 ...
- 不同版本的ArcMap在Oracle中创建镶嵌数据集的不同行为
如果不同版本的ArcMap连接到同一个Oracle数据库上,分别执行"创建镶嵌数据集",它们的行为是一样的吗? 答案是:不一样,会有细微的差别 在本例中,ArcMap的版本分别是1 ...
- activiti学习5:开启流程和流程前进
目录 activiti学习5:开启流程和流程前进 一.流程和任务的关系 二.开启流程 2.1根据流程定义key开启流程 三.查询用户任务 3.1 TaskQuery 四.完成任务 activiti学习 ...
- jquery如何生成图片验证码
jQuery(function($){ /**生成一个随机数**/ function randomNum(min, max) { return Math.floor(Math.random() * ( ...
- 【jquery】【ztree】节点添加自定义按钮、编辑和删除事件改成自己定义事件
setting添加 edit: { drag: { isCopy: false, isMove: true }, enable: true,//设置是否处于编辑状态 showRemoveBtn: sh ...
- RSA非对称式加解密笔记
1.服务器生成[公钥]和[私钥],成对生成: 2.客户端生成证书信息,使用[公钥]进行加密,前提是有公钥,并生成证书信息: 3.客户端发送自身的计算机名.MAC.用户名.证书内容给服务器: 4.服务器 ...
- redis连接池(JedisPool)资源归还及timeout详解
转载. https://blog.csdn.net/yaomingyang/article/details/79043019 一.连接池资源类详解都在注释上 package redis.v1.clie ...
- Delphi RSA加解密【 (RSA公钥加密,私钥解密)、(RSA私钥加密,公钥解密)、MD5加密、SHA加密】
作者QQ:(648437169) 点击下载➨delphi RSA加解密 [Delphi RSA加解密]支持 (RSA公钥加密,私钥解密).(RSA私钥加密,公钥解密).MD5加密.SHA1加密.SHA ...
- day48——css样式
day48 通过调试窗口还可以玩一个神奇的东西 document.body.contentEditable=true css样式 高度宽度 width宽度 height高度 块级标签能设置高度宽度,内 ...
- Python之路【第二十四篇】:数据库索引
数据库索引 一.索引简介 索引在mysql中也叫做"键",是存储引擎用于快速找到记录的一种数据结构.索引对于良好的性能非常关键,尤其是当表中的数据量越来越大时,索引对于性能的影响愈 ...