首页 > 其他分享 >rt-thread 系统pm组件在4.1.1版本的无法正常唤醒的问题解决方法

rt-thread 系统pm组件在4.1.1版本的无法正常唤醒的问题解决方法

时间:2024-06-02 14:43:23浏览次数:24  
标签:rt 4.1 thread pm SLEEP MODE tick PM

在老的rt-thread 版本系统pm组件调试ok,后来使用4.1.1版本时发现进入低功耗后无法正常唤醒,问题解决路径如下

硬件信息:cpu  STM32L431CCT6

新建系统打开pm组件后也没有drv_pm.c和drv_lptim.c自动添加,需要到系统目下找到并复制到driver目录下

C:\RT-ThreadStudio\repo\Extract\RT-Thread_Source_Code\RT-Thread\4.1.1\bsp\stm32\libraries\HAL_Drivers

 将drv_lptim.h放到drivers/include下

drv_pm.c需要做如下改动

1.添加头文件 #include <drivers/pm.h>

2.sleep函数 case PM_SLEEP_MODE_DEEP:下时钟设定改为系统初始化时的SystemClock_Config();

3.run函数不同速度下的时钟配置改为系统初始化时的SystemClock_Config();,此处可不修改,因为系统运行时一般不调整系统时钟速度,有需要的调速功能根据速度修改主频即可

代码如下

/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2019-05-06     Zero-Free    first version
 */

#include <board.h>
#include <drv_lptim.h>
#include <drivers/pm.h>

static void uart_console_reconfig(void)
{
    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;

    rt_device_control(rt_console_get_device(), RT_DEVICE_CTRL_CONFIG, &config);
}

/**
 * This function will put STM32L4xx into sleep mode.
 *
 * @param pm pointer to power manage structure
 */
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:
        if (pm->run_mode == PM_RUN_MODE_LOW_SPEED)
        {
            /* Enter LP SLEEP Mode, Enable low-power regulator */
            HAL_PWR_EnterSLEEPMode(PWR_LOWPOWERREGULATOR_ON, PWR_SLEEPENTRY_WFI);
        }
        else
        {
            /* 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 */
        SystemClock_Config();
        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 uint8_t run_speed[PM_RUN_MODE_MAX][2] =
{
    {80, 0},
    {80, 1},
    {24, 2},
    {2,  3},
};

static void run(struct rt_pm *pm, uint8_t mode)
{
    static uint8_t last_mode;
    static char *run_str[] = PM_RUN_MODE_NAMES;

    if (mode == last_mode)
        return;
    last_mode = mode;

    /* 1. 设置 MSI 作为 SYSCLK 时钟源,以修改 PLL */
    SystemClock_MSI_ON();

    /* 2. 根据RUN模式切换时钟频率(HSI) */
    switch (mode)
    {
    case PM_RUN_MODE_HIGH_SPEED:
    case PM_RUN_MODE_NORMAL_SPEED:
        HAL_PWREx_DisableLowPowerRunMode();
        //SystemClock_80M();
        SystemClock_Config();
        /* Configure the main internal regulator output voltage (Range1 by default)*/
        HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
        break;
    case PM_RUN_MODE_MEDIUM_SPEED:
        HAL_PWREx_DisableLowPowerRunMode();
        SystemClock_Config();
        /* Configure the main internal regulator output voltage */
        HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE2);
        break;
    case PM_RUN_MODE_LOW_SPEED:
        //SystemClock_2M();
        SystemClock_Config();
        /* Enter LP RUN mode */
        HAL_PWREx_EnableLowPowerRunMode();
        break;
    default:
        break;
    }

    /* 3. 关闭 MSI 时钟 */
    // SystemClock_MSI_OFF();

    /* 4. 更新外设时钟 */
    uart_console_reconfig();
    /* Re-Configure the Systick time */
    HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / RT_TICK_PER_SECOND);
    /* Re-Configure the Systick */
    HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

    rt_kprintf("switch to %s mode, frequency = %d MHz\n", run_str[mode], run_speed[mode][0]);
}

/**
 * This function caculate the PM tick from OS tick
 *
 * @param tick OS tick
 *
 * @return the PM tick
 */
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 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 pm_timer_stop(struct rt_pm *pm)
{
    RT_ASSERT(pm != RT_NULL);

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

/**
 * This function calculate how many OS Ticks that MCU have suspended
 *
 * @param pm Pointer to power manage structure
 *
 * @return OS Ticks
 */
static rt_tick_t pm_timer_get_tick(struct rt_pm *pm)
{
    rt_uint32_t timer_tick;

    RT_ASSERT(pm != RT_NULL);

    timer_tick = stm32l4_lptim_get_current_tick();

    return stm32l4_os_tick_from_pm_tick(timer_tick);
}

/**
 * This function initialize the power manager
 */
int drv_pm_hw_init(void)
{
    static const struct rt_pm_ops _ops =
    {
        sleep,
        run,
        pm_timer_start,
        pm_timer_stop,
        pm_timer_get_tick
    };

    rt_uint8_t timer_mask = 0;

    /* Enable Power Clock */
    __HAL_RCC_PWR_CLK_ENABLE();

    /* 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);

 

drv_lptim.c

int stm32l4_hw_lptim_init(void)

选用的时钟可改为LSE,如果没有外部32.768的晶振就不用改,代码如下

int stm32l4_hw_lptim_init(void)
{
    //RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_PeriphCLKInitTypeDef RCC_PeriphCLKInitStruct = {0};

//    /* Enable LSI clock */
//    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI;
//    RCC_OscInitStruct.LSIState = RCC_LSI_ON;
//    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
//    HAL_RCC_OscConfig(&RCC_OscInitStruct);

    /* Select the LSI clock as LPTIM peripheral clock */
    RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LPTIM1;
    RCC_PeriphCLKInitStruct.Lptim1ClockSelection = RCC_LPTIM1CLKSOURCE_LSE;//选择LSE,默认是LSI
    HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct);

    LptimHandle.Instance = LPTIM1;
    LptimHandle.Init.Clock.Source = LPTIM_CLOCKSOURCE_APBCLOCK_LPOSC;
    LptimHandle.Init.Clock.Prescaler = LPTIM_PRESCALER_DIV32;
    LptimHandle.Init.Trigger.Source = LPTIM_TRIGSOURCE_SOFTWARE;
    LptimHandle.Init.OutputPolarity = LPTIM_OUTPUTPOLARITY_HIGH;
    LptimHandle.Init.UpdateMode = LPTIM_UPDATE_IMMEDIATE;
    LptimHandle.Init.CounterSource = LPTIM_COUNTERSOURCE_INTERNAL;
    if (HAL_LPTIM_Init(&LptimHandle) != HAL_OK)
    {
        return -1;
    }

    NVIC_ClearPendingIRQ(LPTIM1_IRQn);
    NVIC_SetPriority(LPTIM1_IRQn, 0);
    NVIC_EnableIRQ(LPTIM1_IRQn);

    return 0;
}

board.c中添加void HAL_LPTIM_MspInit(LPTIM_HandleTypeDef *hlptim)

void HAL_LPTIM_MspInit(LPTIM_HandleTypeDef *hlptim)
{
    if (hlptim->Instance == LPTIM1)
    {
        /* Peripheral clock enable */
        __HAL_RCC_LPTIM1_CLK_ENABLE();
        /* LPTIM1 interrupt Init */
    }
}

board.h中添加头文件#include <rtdevice.h>

#include <rtdevice.h>

pm.c中的函数RT_WEAK rt_tick_t pm_timer_next_timeout_tick(rt_uint8_t mode)中的

return rt_lptimer_next_timeout_tick();暂时没有研究用来干啥的,但肯定不对,可能是调试使用的,暂时不研究,需要在其他地方重构这个函数,此函数带有WEAK头,因此pm.c中的函数自动失效,修改为如下即可

rt_tick_t pm_timer_next_timeout_tick(rt_uint8_t mode)
{
    switch (mode)
    {
        case PM_SLEEP_MODE_LIGHT:
        case PM_SLEEP_MODE_DEEP:
        case PM_SLEEP_MODE_STANDBY:
            return rt_timer_next_timeout_tick();
    }

    return RT_TICK_MAX;
}

添加进入和退出低功耗时的配置外设的函数

void rt_pm_notify(uint8_t event, uint8_t mode, void *data)
{
 if(event == RT_PM_ENTER_SLEEP)
 {
   default_psw_pin_config();
 }
 else
 {
   default_psw_pin_deconfig();
 }
}

添加函数用法

rt_pm_notify_set(rt_pm_notify, RT_NULL);//设置进出低功耗时调用的函数
rt_pm_default_set(PM_SLEEP_MODE_DEEP);//设置默认低功耗模式

低功耗功能使用

1.默认PM_SLEEP_MODE_NONE模式已被申请,不用重新申请

2.rt_pm_request(PM_SLEEP_MODE_DEEP);//请求低功耗模式,请求的模式中只剩下低功耗时,再进入idle任务时会切换到此低功耗模式

rt_pm_release(PM_SLEEP_MODE_NONE);//释放正常模式,此时只剩下低功耗模式
delay(1000);//执行延迟或任务挂起,进入idle任务时自动进入低功耗模式
//唤醒
rt_pm_request(PM_SLEEP_MODE_NONE);//请求正常模式,此时不会进入低功耗
//正常工作

//完成工作时可再次释放正常模式就能再次进入低功耗
rt_pm_release(PM_SLEEP_MODE_NONE);

//进入低功耗的条件就是当前申请的模式中只有低功耗模式,idle函数进入时会切换模式,多个模式时按照最高优先级模式切换,PM_SLEEP_MODE_NONE最高,PM_SLEEP_MODE_SHUTDOWN最低
    /* sleep modes */
    PM_SLEEP_MODE_NONE = 0,
    PM_SLEEP_MODE_IDLE,
    PM_SLEEP_MODE_LIGHT,
    PM_SLEEP_MODE_DEEP,
    PM_SLEEP_MODE_STANDBY,
    PM_SLEEP_MODE_SHUTDOWN,
    PM_SLEEP_MODE_MAX,

 

标签:rt,4.1,thread,pm,SLEEP,MODE,tick,PM
From: https://www.cnblogs.com/arthurly/p/18227123

相关文章

  • Part 3.1 深度优先搜索
    深度优先搜索(DFS),即按照深度优先的顺序搜索的算法。深度优先搜索一般使用栈来实现。[USACO1.5]八皇后CheckerChallenge题目描述一个如下的6×6......
  • 手机版XMind思维导图v24.04.11解锁付费版
    软件信息此版本由678收集整理发布。无需付费,所有功能全开启,欢迎各位机友下载。这是一款帮助你工作学习使用的一款效率软件,如果你有一个plan,那么你需要通过XMind思维导图手机版app去帮你制定一个详细的计划,然后添加各种趣味的信息,用来随时提醒你处理,欢迎下载使用。关于版本by......
  • STMF4立创天空星相关学习笔记 USART
    #本文有参考官方文档,连接就放在最后了,大家可以去看看,写的比较好,同时在此声明,本系列笔记是用来,记录学习过程中产生的问题,以及查询结果逐渐理解的过程。#USART    笔者理解:一种常用的通信方式,分为:同步通信、异步通信,两种二者差别在于,连接双方是不是有着相同的时钟频率,......
  • 立创天空星STMF4学习笔记,USART RS232 RS485相关理解
    为了写,这一篇文章笔者,参考了一些视频,文档,地址放最后,大家可以去看看。#USART为什么只能进行短距离通信,一般只在1M之内进行同行#USART单片机最为常用的一种通信方式之一,一般用于点对点通信,不能进行一主多从,进行相关通信,相关电平取值范围如下。        高电平......
  • 红米AX6000 Openwrt FIP 没权限 Can't open device for writing!
     ......
  • 基于FREERTOS的STM32多功能手表(软件设计)
    目录前言程序现象 项目背景项目介绍目前版本实现的功能设计到的freertos知识使用到的硬件硬件连线图实现思路任务调度流程图​编辑 任务具体操作导图      代码讲解freertos初始化按键中断回调函数显示时间任务显示菜单任务其它任务(ShowCalenda......
  • arturia minilab mk2 midi键盘 连接 flstudio (其他型号也是适用)
    小白学编曲,买了一个二手midi键盘作为入门,刚到的时候一头雾水,摸索了一阵子之后明白如何使用了。这个方法可以根据自己的喜好来随意配置midi键盘与水果的交互软件准备flstudio21(其他版本也类似)midicontrolcenterarturia的官网就可以下到https://www.arturia.com/support/......
  • CF1961E Turtle and Intersected Segments 题解
    题目链接点击打开链接题目解法不是,我这咋不会做/fn边数很多的最小生成树有一个方法是\(boruvka\),但这个处理完全图的比较方便另一个方法是用到一个\(trick\):连出的图中的环,可以删去最长边扫描线是容易想到的,主要是如何把连的边数缩减到合理的范围内考虑扫描线到当前时刻......
  • 基于BERT-BILSTM的中文情感识别
            欢迎来到BERT-BiLSTM中文情感识别项目!我们利用BERT模型提取文本语义特征,结合BiLSTM网络学习时序信息,显著提升中文情感识别性能。为解决训练时间长问题,我们部署在GPU环境,加速模型训练。项目提供可视化中文情感识别系统,欢迎贡献代码、建议或数据,共同优化模型,让中......
  • 儿童节变身小小音乐家,用ModelArts制作一张AIGC音乐专辑
    本文分享自华为云社区《儿童节变身小小音乐家,用ModelArts制作一张AIGC音乐专辑》,作者:华为云社区精选。儿童节,如何给小朋友准备一份特别的礼物?这份AIGC音乐专辑制作攻略一定要收下一段文字灵感就能编织出一曲悠扬悦耳的旋律童话、梦幻、探险……任何关键词都可以成为音乐......