rtthread和stm32低功耗pm组件学习

pm组件是适配于rtthread的电源功耗管理组件,移植需要进行一定适配,并且有些问题需要注意,现在做一个记录

1.PM组件移植

1.PM组件初始化

需要在rtthread的设置里面开启相关功能,并且在hal_config里面开启相应宏定义(lptim),之后添加初始化代码

static int drv_pm_hw_init(void)
{
    static const struct rt_pm_ops _ops =
    {
        sleep,
        RT_NULL,
        _drv_pm_timer_start,
        _drv_pm_timer_stop,
        _drv_pm_timer_get_tick
    };

    rt_uint8_t timer_mask;

    /* initialize timer mask */
    timer_mask = 1UL << PM_SLEEP_MODE_DEEP;

    /* initialize system pm module */
    rt_system_pm_init(&_ops, timer_mask, RT_NULL);

    return 0;
}
INIT_BOARD_EXPORT(drv_pm_hw_init);

创建了一个结构体,第一个参数是不同功耗模式下的具体操作,pm组件只是会调用该函数并不会自动降低功耗。之后是pm组件的时钟(这里用stm32l4系列的低功耗时钟lptim)的配置。还有就是在低功耗模式下计算节拍数来补偿系统节拍。然后启动tick_less,这可以降低操作系统的定时中断造成的唤醒数。

关于 Tickless 功能的解释可以参考论坛的文章 RT-Thread PM 组件 TICKLESS 原理分析 和RT-Thread精通PM功耗调优 - Tickless篇

之后是注册该结构体

关于上述填充的函数的实现

static void sleep(struct rt_pm *pm, uint8_t mode)
{
    switch (mode)
    {
    case PM_SLEEP_MODE_NONE:
        break;

    case PM_SLEEP_MODE_IDLE:
        // __WFI();
        break;

    case PM_SLEEP_MODE_LIGHT:
        /* Enter SLEEP Mode, Main regulator is ON */
        HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
        break;

    case PM_SLEEP_MODE_DEEP:
        /* Enter STOP 2 mode  */
        HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);
        /* Re-configure the system clock */
        rt_update_system_clock();
        break;

    case PM_SLEEP_MODE_STANDBY:
        /* Enter STANDBY mode */
        HAL_PWR_EnterSTANDBYMode();
        break;

    case PM_SLEEP_MODE_SHUTDOWN:
        /* Enter SHUTDOWNN mode */
        HAL_PWREx_EnterSHUTDOWNMode();
        break;

    default:
        RT_ASSERT(0);
        break;
    }
}
static rt_tick_t stm32l4_pm_tick_from_os_tick(rt_tick_t tick)
{
    rt_uint32_t freq = stm32l4_lptim_get_countfreq();

    return (freq * tick / RT_TICK_PER_SECOND);
}

/**
 * This function caculate the OS tick from PM tick
 *
 * @param tick PM tick
 *
 * @return the OS tick
 */
static rt_tick_t stm32l4_os_tick_from_pm_tick(rt_uint32_t tick)
{
    static rt_uint32_t os_tick_remain = 0;
    rt_uint32_t ret, freq;

    freq = stm32l4_lptim_get_countfreq();
    ret = (tick * RT_TICK_PER_SECOND + os_tick_remain) / freq;

    os_tick_remain += (tick * RT_TICK_PER_SECOND);
    os_tick_remain %= freq;

    return ret;
}

/**
 * This function start the timer of pm
 *
 * @param pm Pointer to power manage structure
 * @param timeout How many OS Ticks that MCU can sleep
 */
static void _drv_pm_timer_start(struct rt_pm *pm, rt_uint32_t timeout)
{
    RT_ASSERT(pm != RT_NULL);
    RT_ASSERT(timeout > 0);

    if (timeout != RT_TICK_MAX)
    {
        /* Convert OS Tick to pmtimer timeout value */
        timeout = stm32l4_pm_tick_from_os_tick(timeout);
        if (timeout > stm32l4_lptim_get_tick_max())
        {
            timeout = stm32l4_lptim_get_tick_max();
        }

        /* Enter PM_TIMER_MODE */
        stm32l4_lptim_start(timeout);
    }
}

/**
 * This function stop the timer of pm
 *
 * @param pm Pointer to power manage structure
 */
static void _drv_pm_timer_stop(struct rt_pm *pm)
{
    RT_ASSERT(pm != RT_NULL);

    /* Reset pmtimer status */
    stm32l4_lptim_stop();
}

到这里pm初始化基本完成

2.pm唤醒设置

在大部分低功耗模式中部分外设中断如EXTI仍然可以使用,在EXTI中断中手动唤醒进入正常运行模式。

static void (*_wakeup_hook)(void);

void bsp_register_wakeup(void (*hook)(void))
{
    RT_ASSERT(hook != RT_NULL);

    _wakeup_hook = hook;
}

static void _wakeup_button_update(void)
{
    /* The Following Wakeup sequence is highly recommended prior to each Standby mode entry
       mainly when using more than one wakeup source this is to not miss any wakeup event.
        - Disable all used wakeup sources,
        - Clear all related wakeup flags,
        - Re-enable all used wakeup sources,
        - Enter the Standby mode.
    */

    /* Disable all used wakeup sources: WKUP pin */
    HAL_PWR_DisableWakeUpPin(DRV_WKUP_PIN);
    /* Clear wake up Flag */
    __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
    /* Enable wakeup pin WKUP2 */
    HAL_PWR_EnableWakeUpPin(DRV_WKUP_PIN_MODE);
}

static void _wakeup_callback(void *args)
{
    /* Wait that user release the User push-button */
    //while (rt_pin_read(USER_WAKEUP_PIN) == 0);

    _wakeup_button_update();

    if (_wakeup_hook)
        _wakeup_hook();
}

static void wakeup_callback(void)
{
    rt_kprintf("key irq callback");
    rt_pm_release_all(3);
    rt_pin_write(PIN_LED_R,PIN_LOW);
    rt_pm_request(PM_SLEEP_MODE_NONE);
}

static int rt_hw_wakeup_init(void)
{
    rt_pin_mode(USER_WAKEUP_PIN, PIN_MODE_INPUT);
    rt_pin_attach_irq(USER_WAKEUP_PIN, DRV_WKUP_PIN_IRQ_MODE, _wakeup_callback, RT_NULL);
    rt_pin_irq_enable(USER_WAKEUP_PIN, 1);

    /* Configure Wakeup pin 2 */
    /* Enable Power Clock*/
    __HAL_RCC_PWR_CLK_ENABLE();
    /* Ensure that MSI is wake-up system clock */
    __HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_MSI);
    /* Update Wakeup Button */
    _wakeup_button_update();
    bsp_register_wakeup(wakeup_callback);

    return 0;
}
INIT_BOARD_EXPORT(rt_hw_wakeup_init);

到这为止就实现了可以进入低功耗并且提供EXTI唤醒。

2.自定义设备的低功耗动作

设备可以提供注册在进入低功耗和离开时进行操作。值得注意的是进入低功耗的时候会把设备停止和恢复的函数都调用,因此在具体实现的时候要判断当前的低功耗模式然后做出相应动作,以下为一个led设备的实例

void mled_regisiter(void){
    struct mled_device *device = rt_malloc(sizeof(struct mled_device));

    if (device == RT_NULL)
    {
        rt_kprintf("Failed to allocate memory\n");
        return;
    }
    device->parent.type = RT_Device_Class_Char;
    device->parent.init = mled_init;
    device->parent.open = mled_open;
    device->parent.close = mled_close;

    rt_device_register(&device->parent, "mled", RT_DEVICE_FLAG_RDWR);

static    struct rt_device_pm_ops mled_pm_ops = {
        .suspend = mled_suspend,
        .resume = mled_resume,
        .frequency_change = mled_frequency_change,
    };
    rt_pm_device_register(&device->parent, &mled_pm_ops);


}
INIT_DEVICE_EXPORT(mled_regisiter);

注册了一个自定义的led设备并且将其注册到pm组件中

static int mled_suspend(const struct rt_device *device, uint8_t mode)
{
    HAL_GPIO_WritePin(GPIOE, GPIO_PIN_7, GPIO_PIN_SET);
    return 0;
}

static void mled_resume(const struct rt_device *device, uint8_t mode)
{
    if(mode<3){
    HAL_GPIO_WritePin(GPIOE, GPIO_PIN_7, GPIO_PIN_RESET);
    }
}

这是设备的挂起和恢复回调函数。

3.注意点

1.在rtthread中系统节拍时会产生中断,因此实际上单片机实在经常被短暂唤醒然后再睡眠,切换模式的回调会反复调用。

2.模式的选择实际上是用的投票模式,如果存在高功耗的票不将其释放就无法进入低的功耗模式。因此在进入低功耗模式时需要使用release函数释放高的票数。具体可以参考RTT官方文档
 

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值