问题:
该问题由某客户提出,发生在 STM32L15 STM32L151K6U6 器件上。据其工程师讲述:他撰写一段代码,让 MCU 工作在 LSI 主频上,然后通过 WFI 指令进入 WAIT 模式,电流达到 400uA 左右,与数据手册中所描述的55uA 相去甚远,故请求撰写一例程供他参考。
调研:
我们使用 STM8L-DISCOVERY 这块板子来验证这个电流,板载 MCU 为 STM32L152C6T6。首先,我们先接上电流表:
如图所示,在 STM8L-DISCOVERY 的 JP1 中,将连接 IDD 的 OFF 上的这个跳线帽拿下来,接上电流表,便可以开始验证电流了。
我们先来看一下 STM8L152 的数据手册中,对其主频工作在 LSI 时,RUN 模式下工作的电流的参数:
如红圈内所示,为工作在 LSI 主频下 Run 模式下的电流,典型值为 110uA。我们先来验证一下这个电流。
撰写程序,最主要要处理的部分就是对 I/O 口的配置,根据实际情况,将 I/O 口设置成输出口(必须保证在外部电路不产生电流)或者是带上拉/下拉的输入口的状态,另外就是切换系统时钟为 LSI,将 HIS关闭。另外,在程序开头加上 LED 闪烁一次,指明代码正在工作,在主循环中做了 LED 闪烁功能。主函数如下:
- void main(void)
- {
- CLK_SYSCLKDivConfig(CLK_SYSCLKDiv_1);
- /* Initialize LEDs, flash one time for indicating the code running*/
- GPIO_Init(LD3_GPIO_PORT, LD3_GPIO_PIN, GPIO_Mode_Out_PP_Low_Fast);
- GPIO_Init(LD4_GPIO_PORT, LD4_GPIO_PIN, GPIO_Mode_Out_PP_Low_Fast);
- GPIO_SetBits(LD3_GPIO_PORT, LD3_GPIO_PIN);
- GPIO_SetBits(LD4_GPIO_PORT, LD4_GPIO_PIN);
- Delay(0xFFFF);
- GPIO_ResetBits(LD3_GPIO_PORT, LD3_GPIO_PIN);
- GPIO_ResetBits(LD4_GPIO_PORT, LD4_GPIO_PIN);
- /* Start to prepare for low power */
- /* 1. Configure the I/Os */
- GPIO_Init(GPIOA, GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7,
- GPIO_Mode_Out_PP_Low_Fast);
- GPIO_ResetBits(GPIOA, GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 |
- GPIO_Pin_7);
- GPIO_Init(GPIOB, GPIO_Pin_All, GPIO_Mode_Out_PP_Low_Fast);
- GPIO_ResetBits(GPIOB, GPIO_Pin_All);
- GPIO_Init(GPIOC, GPIO_Pin_0 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 |
- GPIO_Pin_7, GPIO_Mode_Out_PP_Low_Fast);
- GPIO_ResetBits(GPIOC, GPIO_Pin_0 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 |
- GPIO_Pin_6 | GPIO_Pin_7);
- GPIO_Init(GPIOC, GPIO_Pin_1, GPIO_Mode_In_FL_No_IT);
- GPIO_Init(GPIOD, GPIO_Pin_All, GPIO_Mode_Out_PP_Low_Fast);
- GPIO_ResetBits(GPIOD, GPIO_Pin_All);
- GPIO_Init(GPIOE, GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 |
- GPIO_Pin_7, GPIO_Mode_Out_PP_Low_Fast);
- GPIO_ResetBits(GPIOE, GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 |
- GPIO_Pin_5 | GPIO_Pin_7);
- GPIO_Init(GPIOE, GPIO_Pin_6, GPIO_Mode_In_FL_No_IT);
- GPIO_Init(GPIOF, GPIO_Pin_0, GPIO_Mode_Out_PP_Low_Fast);
- GPIO_ResetBits(GPIOF, GPIO_Pin_0);
- /* 2. Switch System Clock to LSI */
- CLK_SYSCLKSourceSwitchCmd(ENABLE);
- CLK_LSICmd(ENABLE);
- while(CLK_GetFlagStatus(CLK_FLAG_LSIRDY) == 0)
- {}
- CLK_SYSCLKSourceConfig(CLK_SYSCLKSource_LSI);
- CLK_SYSCLKSourceSwitchCmd(DISABLE);
- /* 3. Disable HSI */
- CLK_HSICmd(DISABLE);
- while (1)
- {
- }
- }
复制代码
编译,下载测试电流,测得 MCU 系统时钟为 LSI 时,Run 模式的电流为 93.6uA,比数据手册里边的110uA 还小。
那好,我们再来看一下数据手册中关于系统时钟为 LSI 时,进入 WAIT 模式的电流:
如红圈内所示,其典型值为 55uA。我们再来验证一下这个电流:使用 WFI 指令让 CPU 进入 WAIT 模式,当然,由于只为了验证电流,并没有处理唤醒的情况。于是,主函数变为:
- void main(void)
- {
- CLK_SYSCLKDivConfig(CLK_SYSCLKDiv_1);
- /* Initialize LEDs, flash one time for indicating the code running*/
- GPIO_Init(LD3_GPIO_PORT, LD3_GPIO_PIN, GPIO_Mode_Out_PP_Low_Fast);
- GPIO_Init(LD4_GPIO_PORT, LD4_GPIO_PIN, GPIO_Mode_Out_PP_Low_Fast);
- GPIO_SetBits(LD3_GPIO_PORT, LD3_GPIO_PIN);
- GPIO_SetBits(LD4_GPIO_PORT, LD4_GPIO_PIN);
- Delay(0xFFFF);
- GPIO_ResetBits(LD3_GPIO_PORT, LD3_GPIO_PIN);
- GPIO_ResetBits(LD4_GPIO_PORT, LD4_GPIO_PIN);
- /* Start to prepare for low power */
- /* 1. Configure the I/Os */
- GPIO_Init(GPIOA, GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7,
- GPIO_Mode_Out_PP_Low_Fast);
- GPIO_ResetBits(GPIOA, GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 |
- GPIO_Pin_7);
- GPIO_Init(GPIOB, GPIO_Pin_All, GPIO_Mode_Out_PP_Low_Fast);
- GPIO_ResetBits(GPIOB, GPIO_Pin_All);
- GPIO_Init(GPIOC, GPIO_Pin_0 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 |
- GPIO_Pin_7, GPIO_Mode_Out_PP_Low_Fast);
- GPIO_ResetBits(GPIOC, GPIO_Pin_0 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 |
- GPIO_Pin_6 | GPIO_Pin_7);
- GPIO_Init(GPIOC, GPIO_Pin_1, GPIO_Mode_In_FL_No_IT);
- GPIO_Init(GPIOD, GPIO_Pin_All, GPIO_Mode_Out_PP_Low_Fast);
- GPIO_ResetBits(GPIOD, GPIO_Pin_All);
- GPIO_Init(GPIOE, GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 |
- GPIO_Pin_7, GPIO_Mode_Out_PP_Low_Fast);
- GPIO_ResetBits(GPIOE, GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 |
- GPIO_Pin_5 | GPIO_Pin_7);
- GPIO_Init(GPIOE, GPIO_Pin_6, GPIO_Mode_In_FL_No_IT);
- GPIO_Init(GPIOF, GPIO_Pin_0, GPIO_Mode_Out_PP_Low_Fast);
- GPIO_ResetBits(GPIOF, GPIO_Pin_0);
- /* 2. Switch System Clock to LSI */
- CLK_SYSCLKSourceSwitchCmd(ENABLE);
- CLK_LSICmd(ENABLE);
- while(CLK_GetFlagStatus(CLK_FLAG_LSIRDY) == 0)
- {}
- CLK_SYSCLKSourceConfig(CLK_SYSCLKSource_LSI);
- CLK_SYSCLKSourceSwitchCmd(DISABLE);
- /* 3. Disable HSI */
- CLK_HSICmd(DISABLE);
- while (1)
- {
- /* Enter Wait for interrupt mode*/
- wfi();
- }
- }
复制代码
编译,下载测试电流,测得 MCU 系统时钟为 LSI 时,WAIT 模式的电流为 84.8uA,却比数据手册里边的 55uA 大很多。这是为什么呢?
在 STM8L 的参考手册中对于 Flash 的描述,可以看到关于 IDDQ模式的描述:
All STM8L05xx/15xx/16xx and STM8AL31xx/3Lxx Flash program memory and data EEPROM have one low consumption mode, IDDQ. In IDDQ mode, the memory is switched off. It is used for any of the device low power modes: Halt, active-halt, low power wait, and low power run.
When the EEPM bit is set in FLASH_CR1 register, the Flash program memory and data EEPROM automatically enter IDDQ mode when the code is executed from RAM or when the device is in Wait mode.
Refer to Section 3.9.1: Flash control register 1 (FLASH_CR1) for details on WAITM and EEPM bits.
也就是说,当我们进入 WAIT 模式的时候,还可以让 Flash 进入 IDDQ模式,以进一步降低电流。所以,我们必须在 WFI 指令前配置 FLASH_CR1 寄存器中的 WAITM 控制位,让 MCU 在进入 WAIT 模式时,Flash 可以进入 IDDQ模式。查看 stm8l15x_flash.c 文件,我们可以知道只需要加上FLASH_PowerWaitModeConfig(FLASH_Power_IDDQ);就可以了。所以,再修改程序,主程序如下:
- void main(void)
- {
- CLK_SYSCLKDivConfig(CLK_SYSCLKDiv_1);
- /* Initialize LEDs, flash one time for indicating the code running*/
- GPIO_Init(LD3_GPIO_PORT, LD3_GPIO_PIN, GPIO_Mode_Out_PP_Low_Fast);
- GPIO_Init(LD4_GPIO_PORT, LD4_GPIO_PIN, GPIO_Mode_Out_PP_Low_Fast);
- GPIO_SetBits(LD3_GPIO_PORT, LD3_GPIO_PIN);
- GPIO_SetBits(LD4_GPIO_PORT, LD4_GPIO_PIN);
- Delay(0xFFFF);
- GPIO_ResetBits(LD3_GPIO_PORT, LD3_GPIO_PIN);
- GPIO_ResetBits(LD4_GPIO_PORT, LD4_GPIO_PIN);
- /* Start to prepare for low power */
- /* 1. Configure the I/Os */
- GPIO_Init(GPIOA, GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7,
- GPIO_Mode_Out_PP_Low_Fast);
- GPIO_ResetBits(GPIOA, GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 |
- GPIO_Pin_7);
- GPIO_Init(GPIOB, GPIO_Pin_All, GPIO_Mode_Out_PP_Low_Fast);
- GPIO_ResetBits(GPIOB, GPIO_Pin_All);
- GPIO_Init(GPIOC, GPIO_Pin_0 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 |
- GPIO_Pin_7, GPIO_Mode_Out_PP_Low_Fast);
- GPIO_ResetBits(GPIOC, GPIO_Pin_0 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 |
- GPIO_Pin_6 | GPIO_Pin_7);
- GPIO_Init(GPIOC, GPIO_Pin_1, GPIO_Mode_In_FL_No_IT);
- GPIO_Init(GPIOD, GPIO_Pin_All, GPIO_Mode_Out_PP_Low_Fast);
- GPIO_ResetBits(GPIOD, GPIO_Pin_All);
- GPIO_Init(GPIOE, GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 |
- GPIO_Pin_7, GPIO_Mode_Out_PP_Low_Fast);
- GPIO_ResetBits(GPIOE, GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 |
- GPIO_Pin_5 | GPIO_Pin_7);
- GPIO_Init(GPIOE, GPIO_Pin_6, GPIO_Mode_In_FL_No_IT);
- GPIO_Init(GPIOF, GPIO_Pin_0, GPIO_Mode_Out_PP_Low_Fast);
- GPIO_ResetBits(GPIOF, GPIO_Pin_0);
- /* 2. Switch System Clock to LSI */
- CLK_SYSCLKSourceSwitchCmd(ENABLE);
- CLK_LSICmd(ENABLE);
- while(CLK_GetFlagStatus(CLK_FLAG_LSIRDY) == 0)
- {}
- CLK_SYSCLKSourceConfig(CLK_SYSCLKSource_LSI);
- CLK_SYSCLKSourceSwitchCmd(DISABLE);
- /* 3. Disable HSI */
- CLK_HSICmd(DISABLE);
- /* 4. Configure Flash & EEPROM in IDDQ mode while WAIT mode */
- FLASH_PowerWaitModeConfig(FLASH_Power_IDDQ);
- while (1)
- {
- /* Enter Wait for interrupt mode*/
- wfi();
- }
- }
复制代码
编译,下载测试电流,测得 MCU 系统时钟为 LSI 时,WAIT 模式的电流为 31.7uA,却比数据手册里边的 55uA 就要小了。
将参考程序发给客户,客户最后还是发现还有 I/O 口没有处理好所导致的电流过大的问题。
结论:
由于对参考手册理解还不够深入,需要做好更多细节的处理。
处理:
参考代码,修改程序达到要求。
建议:
对于低功耗的处理,一定要仔细处理好各个 I/O 口以及相关的外设等等的控制。
|