单片机省电模式的设计

在嵌入式软件的设计里面常常会对产品的功耗有一定的要求,所以对于设计一个合理的省电模块是十分有必要的。
对于一个产品而言,可以将他的工作模式分成如下的几种模式

  1. 空闲状态
  2. 唤醒状态
  3. 休眠状态

关于省电比例: 省电比列越大,单片机进入休眠的时间越长


单片机的省电模块

对于省电模式可以用状态机来进行设置。
空闲状态 --> 唤醒状态 --> 休眠状态

void LPMSavePower(void)
{
    if (0 == g_generalSettingsCache.powerSaveRatio)
        return;

    switch (s_lpmSaverState)
    {
    case LPM_SAVER_STATE_IDLE:
        if ((CLOCK_GetSysTickCount() - s_lpmActivedTick) >= CLOCK_GetTickFromMillisecond(LPM_ACTIVE_TIMEOUT_MS))
        {
            s_lpmSaverState = LPM_SAVER_STATE_AWAKE;
            s_lpmAwakeTick = CLOCK_GetSysTickCount();
        }
        break;
        
    case LPM_SAVER_STATE_AWAKE:
        if ((CLOCK_GetSysTickCount() - s_lpmAwakeTick) >= s_lpmSaverAwakeTicks)
        {
            s_lpmSaverState = LPM_SAVER_STATE_SLEEP;
        }
        break;
        
    case LPM_SAVER_STATE_SLEEP:
        LPMSleep();
        break;

    default:
        break;
    }
}

单片机怎么进入省电模式的前提: 当前单片机没有主动要去完成的事情 ,而是等待在等待某件事件,这个时候系统就可以进入休眠的模式,等待的事情可以通过中断来进行唤醒(从掉电模式进行退出)。单片机也不能一直在休眠模式徘徊,要设定时间从省电模式退出(省电的时间由省电比例来进行确定)。

void LPMSleep(void)
{
    bf_uint8_t suspendMillis;

    //直接按给定的时间进行守候
    suspendMillis = LPM_SAVER_TIMESLICE_MS; 
    if (suspendMillis < LPM_MIN_SUSPEND_MILLIS)
        return;
    // }

    // 开始省电
    
    // 关闭其他耗电的外设
    
    // MCU休眠,终端将暂停在这边,等待唤醒源唤醒,唤醒后继续执行
    // 唤醒源有:WKT定时唤醒、按键中断
    if (!DRV_LPM_Suspend(suspendMillis))
    {
        // 挂起不成功,说明被按键中断唤醒了,终端被激活
        s_lpmSaverState = LPM_SAVER_STATE_IDLE;
    }
    else
    {
        // 挂起成功
        s_lpmSaverState = LPM_SAVER_STATE_AWAKE;

        s_lpmAwakeTick = CLOCK_GetSysTickCount();
        
        s_sleepSliceIndex++;
        if (s_sleepSliceIndex >= g_generalSettingsCache.powerSaveRatio)
        {
            // 完成一轮省电操作(唤醒 + 多个休眠时间片)
            s_sleepSliceIndex = 0;
        }
        else
        {
            // 当前一轮休眠操作还未结束,需要继续执行下一个休眠时间片,
            // 每一休眠时间片之间有一个固定的保护时间
            s_lpmAwakeTick -= (s_lpmSaverAwakeTicks - CLOCK_GetTickFromMillisecond(LPM_SLEEP_GUARDTIME_MS));
        }
    }
    
    // 还原之前关闭的外设
}

省电的时候系统进入掉电模式,在掉电的模式下面通过中断唤醒,使 CPU 从之前的环境继续向下执行。在单片机唤醒的时候需要将之前的中断还原成原来的模式,如本来某个引脚的功能是串口,为了能够在外部插入串口的时候唤醒需要将他的功能转换成引脚。当被唤醒的时候需要将当前引脚的功能进行复原。即将引脚的功能还原成串口继续使用。
代码如下:

 bf_bool_t DRV_LPM_Suspend(bf_uint8_t millis)
{
    bf_uint32_t elapsedTick;
    bf_uint8_t count;

    // 设置串口接收引脚为GPIO,开启管脚中断,用于写频时候的唤醒动作
    {
        BIT_TMP = EA;
        EA = 0;

        MFP_P30_GPIO;
        
        P30_INPUT_MODE;
        ENABLE_P30_PULLUP;
        ENABLE_PIT5_P30_FALLINGEDGE;
        
        EA = BIT_TMP;
    }
    
    s_isWKTExpired = 0;

    // 8位的向上计数型的WKT的时钟为38.4K / 256 = 150Hz,
    // 例如,定时LPM_SLEEP_MS(100ms),需要的周期数为15
    count = (millis * 150) / 1000;
    
    SFRS = 0;
    WKCON = 0x04;
    CWK = 256 - count;
    RWK = 256 - count;
    ENABLE_WKT_INTERRUPT;
    set_WKCON_WKTR;

    // Suspend MCU
    set_PCON_PD;

    // Wake up,根据实际休眠的时长,更新当前的Tick值
    if (s_isWKTExpired)
    {
        elapsedTick = CLOCK_GetTickFromMillisecond(millis);
    }
    else
    {
        SFRS = 1;
        elapsedTick = CLOCK_GetTickFromMillisecond(((count - (256 - CWK)) * 1000UL) / 150);
    }

    CLOCK_SetSysTickCount(CLOCK_GetSysTickCount() + elapsedTick);

    // TIP: 还原串口功能的动作,直接在管脚中断ISR里面执行掉了,见void KEYPAD_IRQHandler(void)函数,
    //        这边仅是作为占位演示功能
#if 0
    // 还原串口功能
    {
        BIT_TMP = EA;
        EA = 0;

        SFRS = 1;
        PINEN &= CLR_BIT4;
        
        MFP_P30_UART0_RXD;
        
        EA = BIT_TMP;
    }
#endif

    return s_isWKTExpired;
}

关于掉电模式和空闲模式的说明

空闲模式:

单片机进入空闲模式的时候,除了cpu处于休眠状态外,其余的硬件全部处于活动状态,芯片中未涉及的数据存储器和特殊功能寄存器中的数据在空闲模式期间都将保持原值

单片机在空闲模式下可由任何一个中断或者是硬件复位唤醒,值得注意的是,使用中断唤醒单片机,程序将从原来的停止的地方继续运行当使用硬件复位时,程序将从头开始执行

掉电模式:

当单片机进入掉电模式(也叫休眠模式)外部晶振停止震动,cpu,定时器,串口全部停止工作,只有外部中断继续工作,使单片机进入掉电模式的指令将成为休眠前单片机工作的最后一条指令,值得注意的是,使用中断唤醒单片机,程序将从原来的停止的地方继续运行,当使用硬件复位时,程序将从头开始执行。


huu红山竹
4 声望2 粉丝

study