上文说了STM32L4的几种低功耗模式,将其应用起来作为一个低功耗的延时方案。

为什么使用低功耗定时器,在追求长时间续航时,单片机有时需要切换到低功耗模式或者停止模式下,在这种模式下,系统主时钟关闭,有一些依赖于系统主时钟的应用程序,可能会发生出现某些奇怪的情况。因此在休眠唤醒后对主时钟进行校准,防止出现莫名其妙的BUG。

LPTIM全称:Low Power TIM ,我们将其运行在计数模式下,其时钟源可以选择低速时钟(单片机在STOP模式下,低速时钟依然运行),在cubemx中打开定时器,预分频器选择1,这样的话计数时间最长,也就是从0到65535需要计数2秒,触发源选择软件。

图片3.png

开启LPTIM的中断,我们将由溢出中断来唤醒系统。生成代码:
使用HAL_LPTIM_TimeOut_Start_IT(&hlptim1, 0, 32767);代码开启定时器,预分频系数选择0,最大计数值为65535,在一分频的情况下最高两秒产生中断。
时钟配置界面,给LPTIM配置为内部低速定时器,其他部分按照自己需求配置:

图片4.png

任何中断可以把单片机从STOP模式下唤醒,切记打开定时器中断:

图片5.png

如果涉及到休眠模式,可以将所有没用到的引脚设置为浮空模式,可以降低上下拉电阻带来的功耗消耗:

图片6.png

其他的设置按照需求或者个人的习惯来就好。生成代码。将C文件与头文件添加到工程中:

/*
 * LP_Delay.c
 *
 *  Created on: 2022年5月19日
 *      Author: 田帅康
 */
#include "LP_Delay.h"
#include "lptim.h"
#include "stdio.h"
/*
 * 函数说明:在休眠状态下延时,旨在降低空显时间的功耗
 * 传入参数:毫秒  范围0~2000
 * 返回值:传入值
 * */
uint16_t Sleep_Delayms(uint16_t ms)
{
    uint16_t cnt=ms*32767/1000;
    __HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_MSI);  // 设定唤醒后的时钟源
    HAL_LPTIM_MspInit(&hlptim1);//定时器初始化
    HAL_LPTIM_TimeOut_Start_IT(&hlptim1, 0, cnt);//设置中断计数值
    __HAL_RCC_PWR_CLK_ENABLE();  // 打开电源控制时钟
    SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |0| SysTick_CTRL_ENABLE_Msk;//这里很重要,不然休眠会被提前中断
    HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);
    return ms;
}
/*
 * 函数说明:LPTIM1回调函数
 * */
void HAL_LPTIM_CompareMatchCallback(LPTIM_HandleTypeDef *hlptim)
{
    HAL_LPTIM_MspDeInit(&hlptim1);  // 关闭LP定时器
    SystemClock_Config(); // 配置系统时钟
    SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk; // 打开Systick的中断
    SCB->SCR &= ~SCB_SCR_SLEEPONEXIT_Msk; // 退出中断时不再自动进入低功耗模式
    //printf("In the EXTI to wake up!\r\n");
}




LP_Delay.h
/*
 * LP_Dealy.h
 *
 *  Created on: 2022年5月19日
 *      Author: shumei
 */

#ifndef LP_DELAY_LP_DEALY_H_
#define LP_DELAY_LP_DEALY_H_

#include "main.h"

uint16_t Sleep_Delayms(uint16_t ms);
void HAL_LPTIM_CompareMatchCallback(LPTIM_HandleTypeDef *hlptim);
#endif /* LP_DELAY_LP_DEALY_H_ */

引用示例:

While(1)
{
    Sleep_Delayms(200);
      //HAL_Delay(1000);
      HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
      printf("%d\r\n",HAL_GetTick());
}

可以将系统心跳打印出来,并判断与HAL_Delay延时函数的区别。

注意:并不是简单的引用HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);函数就达到低功耗了,实际上的低功耗都是一点一点抠出来的,比如,使用的外设Deinit掉,没有用到的引脚都改为浮空模式。

次日更新:
由于我们已经知道了在STOP2模式下,可以被任何中断唤醒,因此在实际使用中可能会产生提前退出延时的情况,因此进行一个判断,如果是LPTIM产生的溢出中断,那么定时器计数值为0,如果不为零,算出实际延时时间,并返回数值。
更新后的代码:

/*
 * 函数说明:在休眠状态下延时,旨在降低空显时间的功耗
 * 传入参数:毫秒  范围0~2000
 * 返回值:传入值
 * */
uint16_t Sleep_Delayms(uint16_t ms)
{
    HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,1);
    uint16_t cnt=(ms-1)*32767/1000;
    __HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_MSI);  // 设定唤醒后的时钟源
    HAL_LPTIM_MspInit(&hlptim1);//定时器初始化
    __HAL_RCC_PWR_CLK_ENABLE();  // 打开电源控制时钟
    SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |0| SysTick_CTRL_ENABLE_Msk;//这里很重要,不然休眠会被提前中断
    HAL_LPTIM_TimeOut_Start_IT(&hlptim1, 0, cnt);//设置中断计数值

    HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);//进入到休眠模式

    if(!HAL_LPTIM_ReadCounter(&hlptim1))
    {
        HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,0);
        return ms;
    }
    else
    {
        HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,0);
        ms=HAL_LPTIM_ReadCounter(&hlptim1)*1000/32768;
        return ms;
    }
    return ms;
}