衡阳派盒市场营销有限公司

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

PM2.0組件爬坑記錄

冬至子 ? 來源:zx595 ? 作者:zx595 ? 2023-07-22 15:10 ? 次閱讀

PM2.0組件采用了空閑任務中進行低功耗管理策略,使用時要把空閑任務的堆棧擴大到2048。建議剛使用低功耗組件的先去閱讀該部分源碼部分,這部分代碼并不長,半天時間足夠分析清楚組件的運行邏輯了。

組件默認在無請求時進入低功耗深睡眠模式,被特殊事件喚醒才能繼續工作。我在這里面踩得最大的坑就是從深睡眠中喚醒。

關于這部分首先要去看STM32L4的官方RM手冊,了解如何進入/退出深睡眠,各個電源模式如何切換,再寫裸機代碼驗證之后再去看PM2.0組件會容易很多。

在記錄問題之前著重提醒:在刷完固件驗證之前一定要重新給板子上電再看結果!!!重要的事情說三遍!!!

Q1:無法進入低功耗模式

A1:系統默認是有個電源ID默認請求不睡眠,需要釋放后才可以進入通過電源管理策略進入深睡眠。可以在main.c添加如下代碼即可。

rt_pm_module_delay_sleep(PM_POWER_ID, 5000); // 注意延時進入,防止再次更新程序時找不到深睡眠中的芯片
rt_pm_module_release(PM_POWER_ID, PM_SLEEP_MODE_NONE);

Q2:板子能進入深睡眠了,也測量到功耗下降了,但是為什么程序里的心跳燈不閃了?

A2:其實是因為進入什么睡眠之后芯片需要一些外部事件才能把芯片喚醒繼續工作。這個事件可以是外部中斷、LPTIMER中斷、LPUART中斷。有一些需要額外的配置才能使用。PM2.0一個很優秀的機制就是tickless,原理可以去看大佬的帖子。如果想讓心跳燈繼續按照程序閃爍,需要配置lptimer才能繼續讓芯片定時起來看下有沒有什么任務處理。這里有兩個地方容易出問題:一個是lptimer沒有正確配置,導致芯片無法周期性喚醒執行任務;另一個是醒來之后的時鐘有問題,導致無法正常恢復工作。我更改了以下代碼。

main()中重新初始化lptim。

// __HAL_RCC_LPTIM1_CLK_ENABLE(); // 使能LPTIM1時鐘
extern int stm32l4_hw_lptim_init(void);
stm32l4_hw_lptim_init(); // 初始化LPTIM1
board.c中檢查時鐘重新配置

void SystemClock_ReConfig(uint8_t mode)
{
// SystemClock_MSI_ON(); // 把這個注釋掉
switch (mode)
{
case PM_RUN_MODE_HIGH_SPEED:
case PM_RUN_MODE_NORMAL_SPEED:
SystemClock_80M();
break;
case PM_RUN_MODE_MEDIUM_SPEED:
SystemClock_24M();
break;
case PM_RUN_MODE_LOW_SPEED:
SystemClock_2M();
break;
default:
break;
}
// SystemClock_MSI_OFF();
}

在main.c中(可以添加到其他參加編譯的文件中)重構tickless需要的超時時間源

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;
}

至此設備就能在深睡眠時主動醒來控制LED燈了。

Q3:如何通過按鍵喚醒芯片起來進入正常工作模式?

A3:解決這個問題需要了解兩類重要的API:電源模式的請求和釋放。如果看過電源管理的源碼應該就了解了芯片時如何進入睡眠的,所以在觸發外部中斷之后我們需要請求持有更高等級的電源模式,以防止芯片再次進入睡眠。

scons中打開用戶配置PM模式。

1.jpg

在..boardpm_cfg.h中添加要管理的KEYID

enum pm_module_id
{
PM_NONE_ID = 0,
PM_POWER_ID,
PM_BOARD_ID,
PM_LCD_ID,
PM_KEY_ID,
PM_TP_ID,
PM_OTA_ID,
PM_SPI_ID,
PM_I2C_ID,
PM_ADC_ID,
PM_RTC_ID,
PM_GPIO_ID,
PM_UART_ID,
PM_SENSOR_ID,
PM_ALARM_ID,
PM_BLE_ID,
PM_KEY0_ID,
PM_KEY1_ID,
PM_KEY2_ID,
PM_MODULE_MAX_ID, /* enum must! */
};

偷個懶,直接copy大佬的代碼,保存為key.c

/*

Copyright (c) 2006-2020, RT-Thread Development Team

SPDX-License-Identifier: Apache-2.0

Change Logs:
Date Author Notes
2020-09-08 zhangsz init first
*/
#include
#include
#define DBG_ENABLE
#define DBG_SECTION_NAME "key"
#define DBG_LEVEL DBG_LOG
#include
#define PIN_KEY0 GET_PIN(D, 10)
#define PIN_KEY1 GET_PIN(D, 9)
#define PIN_KEY2 GET_PIN(D, 8)
void key0_irq_callback(void *parameter)
{
static uint8_t key0_status = 0x00;
key0_status ^= 0x01;
if (key0_status == 0x00)
rt_pm_sleep_idle_release(PM_KEY0_ID);
else
rt_pm_sleep_idle_request(PM_KEY0_ID);
LOG_D("[key0_irq]n");
}
void key1_irq_callback(void *parameter)
{
static uint8_t key1_status = 0x00;
key1_status ^= 0x01;
if (key1_status == 0x00)
rt_pm_sleep_light_release(PM_KEY1_ID);
else
rt_pm_sleep_light_request(PM_KEY1_ID);
LOG_D("[key1_irq]n");
}
void key2_irq_callback(void parameter)
{
static uint8_t key2_status = 0x00;
key2_status ^= 0x01;
if (key2_status == 0x00)
rt_pm_sleep_none_release(PM_KEY2_ID);
else
rt_pm_sleep_none_request(PM_KEY2_ID);
LOG_D("[key2_irq]n");
}
int key_gpio_init(void)
{
LOG_D("key_gpio_init.n");
/
set key pin mode to input /
LOG_D("PIN_KEY0=%d,PIN_KEY1=%d,PIN_KEY2=%dn",
PIN_KEY0, PIN_KEY1, PIN_KEY2);
rt_pin_mode(PIN_KEY0, PIN_MODE_INPUT_PULLUP);
rt_pin_mode(PIN_KEY1, PIN_MODE_INPUT_PULLUP);
rt_pin_mode(PIN_KEY2, PIN_MODE_INPUT_PULLUP);
/
set interrupt mode and attach interrupt callback function /
rt_pin_attach_irq(PIN_KEY0, PIN_IRQ_MODE_FALLING, key0_irq_callback, NULL);
rt_pin_attach_irq(PIN_KEY1, PIN_IRQ_MODE_FALLING, key1_irq_callback, NULL);
rt_pin_attach_irq(PIN_KEY2, PIN_IRQ_MODE_FALLING, key2_irq_callback, NULL);
/
enable interrupt */
rt_pin_irq_enable(PIN_KEY0, PIN_IRQ_ENABLE);
rt_pin_irq_enable(PIN_KEY1, PIN_IRQ_ENABLE);
rt_pin_irq_enable(PIN_KEY2, PIN_IRQ_ENABLE);
return 0;
}
INIT_APP_EXPORT(key_gpio_init);
修改..applicationsSConscript腳本,把key.c加入工程

src = ['main.c','key.c']

最后用scons重新構建工程即可

這里實現了通過三個不同的按鍵進入不同的電源模式。可以發現離開深睡眠模式之后系統的控制臺又可以正常工作了!

Q4:如何通過低功耗串口喚醒芯片起來進入正常工作模式?

A4:原理與Q3的按鍵喚醒類似,只不過通過串口喚醒需要去做一些硬件配置。
配置方式放在..librariesHAL_Driversdrv_usart.c中

int lpuart1_wakeup_config(void)
{
#ifdef BSP_USING_LPUART1
UART_WakeUpTypeDef WakeUpSelection;
/* make sure that no LPUART transfer is on-going /
while(__HAL_UART_GET_FLAG(&(uart_obj[LPUART1_INDEX].handle), USART_ISR_BUSY) == SET);
/
make sure that LPUART is ready to receive

  • (test carried out again later in HAL_UARTEx_StopModeWakeUpSourceConfig) /
    while(__HAL_UART_GET_FLAG(&(uart_obj[LPUART1_INDEX].handle), USART_ISR_REACK) == RESET);
    /
    set the wake-up event:
  • specify wake-up on RXNE flag /
    //UART_WAKEUP_ON_STARTBIT
    WakeUpSelection.WakeUpEvent = UART_WAKEUP_ON_READDATA_NONEMPTY;
    if (HAL_UARTEx_StopModeWakeUpSourceConfig(&(uart_obj[LPUART1_INDEX].handle), WakeUpSelection)!= HAL_OK)
    {
    rt_kprintf("lpuart1_wakeup_config error!!n");
    Error_Handler();
    }
    /
    Enable the LPUART Wake UP from STOP mode Interrupt /
    __HAL_UART_ENABLE_IT(&(uart_obj[LPUART1_INDEX].handle), UART_IT_WUF);
    #endif
    return 1;
    }
    int lpuart1_wakeup_disable(void)
    {
    #ifdef BSP_USING_LPUART1
    HAL_UARTEx_DisableStopMode(&(uart_obj[LPUART1_INDEX].handle));
    #endif
    return 1;
    }
    int lpuart1_wakeup_enable(void)
    {
    #ifdef BSP_USING_LPUART1
    /
    enable MCU wake-up by LPUART */
    HAL_UARTEx_EnableStopMode(&(uart_obj[LPUART1_INDEX].handle));
    #endif
    return 1;
    }
    還需要自己實現lpuart1的驅動初始,這部分可以用CubeMX生成即可。這里不再詳細介紹。需要額外注意的是:要想讓LPUART在深睡眠下工作來喚醒芯片,需要把時鐘配置為LSE。這里注意要把波特率設置為9600,LSE的頻率下波特率上限就是9600.
    1.jpg
    生成lpuart的HAL驅動之后就是對接到rtt的設備框架。注意SCons中使能宏定義
    BSP_USING_LPUART1
    可能會提示缺少相關定義,在uart_config.h中添加即可

#if defined(BSP_USING_LPUART1)
#ifndef LPUART1_CONFIG
#define LPUART1_CONFIG
{
.name = "lpuart1",
.Instance = LPUART1,
.irq_type = LPUART1_IRQn,
}
#endif /* LPUART1_CONFIG /
#endif /
BSP_USING_LPUART1 */
Kconfig添加LPUART的選項卡

config BSP_USING_LPUART1
bool "Enable LPUART1"
default n

至此驅動部分已經添加完成。

接下來是使用示例,使用PB10和PB11作為低功耗串口的收發腳。代碼copy的大佬的,我做了一些修改方便驗證。代碼的運行邏輯是:上電5秒后深度睡眠,接收到低功耗串口的數據喚醒,并把數據加1后原樣返回;當接收到數據0x0BB時認為通訊完成,再次釋放持有的電源模式,進入深度睡眠。

#include
#include
#define DBG_ENABLE
#define DBG_SECTION_NAME "lpuart"
#define DBG_LEVEL DBG_LOG
#include
#define SAMPLE_UART_NAME "lpuart1"
/* Default config for serial_configure structure /
#define LPUART1_BAUD9600_CONFIG
{
BAUD_RATE_9600, /
9600 bits/s /
DATA_BITS_8, /
8 databits /
STOP_BITS_1, /
1 stopbit /
PARITY_NONE, /
No parity /
BIT_ORDER_LSB, /
LSB first sent /
NRZ_NORMAL, /
Normal mode /
RT_SERIAL_RB_BUFSZ, /
Buffer size /
0
}
extern int lpuart1_wakeup_config(void);
/
用于接收消息的信號/
static struct rt_semaphore rx_sem;
static rt_device_t lp_serial;
static void lpuart_set_config(void)
{
struct serial_configure config = LPUART1_BAUD9600_CONFIG;
rt_device_control(lp_serial, RT_DEVICE_CTRL_CONFIG, &config);
}
extern int lpuart1_wakeup_disable(void);
extern int lpuart1_wakeup_enable(void);
/
接收數據回調函數 /
static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{
lpuart1_wakeup_disable();
rt_pm_module_request(PM_BOARD_ID, PM_SLEEP_MODE_NONE);
/
串口接收到數據后產生中斷,調用此回調函數,然后發送接收信號量 */
rt_sem_release(&rx_sem);
return RT_EOK;
}
static void serial_thread_entry(void parameter)
{
char ch;
lpuart1_wakeup_enable();
while (1)
{
/
從串口讀取一個字節的數據,沒有讀取到則等待接收信號量 /
while (rt_device_read(lp_serial, -1, &ch, 1) != 1)
{
/
阻塞等待接收信號量,等到信號量后再次讀取數據 /
rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
}
/
讀取到的數據通過串口錯位輸出 /
ch = ch + 1;
rt_device_write(lp_serial, 0, &ch, 1);
rt_kprintf("wake from lpuart!n");
if (ch == 0xBC)
{
rt_pm_module_release_all(PM_BOARD_ID, PM_SLEEP_MODE_NONE);
lpuart1_wakeup_enable();
}
}
}
int uart_sample(void)
{
rt_err_t ret = RT_EOK;
char str[] = "hello lpuart!rn";
/
查找系統中的串口設備 /
lp_serial = rt_device_find(SAMPLE_UART_NAME);
if (!lp_serial)
{
LOG_D("find %s failed!n", SAMPLE_UART_NAME);
return RT_ERROR;
}
/
初始化信號量 /
rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
/
以中斷接收及輪詢發送模式打開串口設備 /
rt_device_close(lp_serial);
lpuart_set_config();
LOG_D("lpuart_set_config ok!n");
rt_device_open(lp_serial, RT_DEVICE_FLAG_INT_RX);
/
設置接收回調函數 /
rt_device_set_rx_indicate(lp_serial, uart_input);
/
發送字符串 /
rt_device_write(lp_serial, 0, str, (sizeof(str) - 1));
/
創建 serial 線程 /
rt_thread_t thread = rt_thread_create("serial", serial_thread_entry, RT_NULL, 1024, 25, 10);
/
創建成功則啟動線程 */
if (thread != RT_NULL)
{
rt_thread_startup(thread);
}
else
{
ret = RT_ERROR;
}
return ret;
}
INIT_APP_EXPORT(uart_sample);
INIT_APP_EXPORT(lpuart1_wakeup_config);

這樣寫只是方便測試低功耗串口喚醒設備,實際使用中還是把接收到的數據校驗后封裝成事件處理,事件處理完之后再釋放電源模式比較合理。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 電源管理
    +關注

    關注

    115

    文章

    6193

    瀏覽量

    144944
  • 接收機
    +關注

    關注

    8

    文章

    1184

    瀏覽量

    53631
  • 串口中斷
    +關注

    關注

    0

    文章

    67

    瀏覽量

    14007
  • STM32L4
    +關注

    關注

    1

    文章

    42

    瀏覽量

    9435
  • HAL驅動
    +關注

    關注

    0

    文章

    3

    瀏覽量

    1202
收藏 人收藏

    評論

    相關推薦

    Android利用SurfaceView顯示Camera圖像

    前言前一章《Android利用SurfaceView顯示Camera圖像記(一)》我們已經實現了利用SurfaceView將Camera中的實時幀圖像顯示出來了,我們做這個的主要目錄...
    發表于 07-02 08:14

    RT-Thread PM2.0管理使用指南

    前言本篇主要對RT-Thread PM2.0(2021更新版)做個使用指南,介紹PM管理的新API的使用PM管理(電源管理、功耗管理)是如今穿戴類產品比較關心的產品賣點前面的幾篇文章,或多或少
    發表于 08-09 10:34

    基于PM2.0與STM32L4的LoRaWAN Class A低功耗設備設計方案介紹

    1、基于PM2.0與STM32L4的LoRaWAN Class A低功耗設備設計  RT-Thread PM2.0組件的整體設計思想是PM組件
    發表于 09-27 11:27

    大師建議:電子零組件最看好

    大師建議:電子零組件最看好   臺股第2季拉回是長線布局買點,2010年臺股要買什么股?瑞展產業研究董事長陳忠瑞昨日在一場臺
    發表于 03-22 09:04 ?650次閱讀

    Delphi7組件參考大全

    Delphi7組件參考大全Delphi7組件參考大全Delphi7組件參考大全
    發表于 02-19 15:43 ?0次下載

    寬帶聯網和通信應用光學零組件

    寬帶聯網和通信應用中使用的關于光學零組件
    發表于 05-09 11:38 ?0次下載

    如何判斷TL494組件的故障pdf資料下載

    如何判斷TL494組件的故障pdf資料
    發表于 03-21 11:45 ?14次下載

    AN74組件和測量改進確保16位DAC建立時間

    AN74組件和測量改進確保16位DAC建立時間
    發表于 04-20 08:04 ?8次下載
    AN74<b class='flag-5'>組件</b>和測量改進確保16位DAC建立時間

    嵌入式Linux踩記錄

    Linux踩記錄記錄Linux學習過程踩過的與如何解決踩1解決方法:F10進入BIOS使能虛擬化技術
    發表于 11-01 17:21 ?10次下載
    嵌入式Linux踩<b class='flag-5'>坑</b><b class='flag-5'>記錄</b>

    STM32CubeIDE+FREERTOS踩記錄

    STM32CubeIDE+FREERTOS踩記錄
    發表于 12-05 18:06 ?15次下載
    STM32CubeIDE+FREERTOS踩<b class='flag-5'>坑</b><b class='flag-5'>記錄</b>

    STM32L151的ADC時鐘配置

    目錄一、前言二、挖坑過程三、填記錄一、前言首先說明一下,我所指的“”并不是說STM32L151的時鐘有問題哈,STM32L151的時鐘肯定是沒問題的,只是跟STM32F1或F4的
    發表于 12-27 18:55 ?14次下載
    STM32L151的ADC時鐘配置<b class='flag-5'>爬</b><b class='flag-5'>坑</b>記

    壁機器人的吸附方式有哪些 壁機器人有哪些零組成

    壁機器人的零組成可以根據不同的設計和應用而有所差異。以下是一般常見的壁機器人零
    的頭像 發表于 08-01 16:15 ?1724次閱讀

    搜索出生的百川智能大模型RAG之路總結

    今天對百川的RAG方法進行解讀,百川智能具有深厚的搜索背景,來看看他們是怎么RAG的的吧~
    的頭像 發表于 01-05 15:02 ?1580次閱讀
    搜索出生的百川智能大模型RAG<b class='flag-5'>爬</b><b class='flag-5'>坑</b>之路總結

    隆基綠能HPBC 2.0組件效率破世界紀錄

    近日,隆基綠能宣布了一項振奮人心的消息:其自主研發的HPBC 2.0組件效率已經成功達到了25.4%,這一數據來源于國際權威認證機構——德國弗勞霍夫太陽電池研究所的最新認證報告。這一成果不僅標志著隆
    的頭像 發表于 10-27 15:29 ?549次閱讀

    隆基HPBC 2.0組件產品助力斯里蘭卡能源轉型

    。來自當地的200余位客戶、合作伙伴以及行業同仁參加活動,見證了隆基HPBC 2.0組件產品在斯里蘭卡的榮耀亮相。
    的頭像 發表于 12-11 09:45 ?254次閱讀
    澳门百家乐官网真人娱乐场| 大发888娱乐城出纳柜台| 百家乐官网破解仪| 安泽县| 棋牌游戏开发公司| sz新全讯网网址2290| 太原百家乐招聘| 真人百家乐好不好玩| 百家乐官网开闲的几率多大| 求购百家乐官网程序| 天猫国际娱乐城| 网上现金赌场| 赛马会娱乐城| 银河国际娱乐城| 大发888娱乐城下载| 大发888游戏平台 娱乐场下载| 博彩网百家乐中和局| 大发888官方网站下载| 大发888官方网址| 百家乐注码技术打法| 至尊百家乐20130301| 百家乐代理在线游戏可信吗网上哪家平台信誉好安全 | 网络百家乐官网公式打法| 百家乐官网赌博现金网| 真人百家乐官网在线玩| 博王国际娱乐| 防城港市| 百家乐官网梅花图标| 百家乐官网优博娱乐城| 威尼斯人娱乐城真钱赌博| 大发888 casino exe| 杭州百家乐西园| 威尼斯人娱乐城真钱百家乐| 大发888娱乐能借钱| 乐天堂| 百家乐官网大赢家客户端| 项城市| 百家乐官网发牌的介绍| 作弊百家乐官网赌具价格| 岳阳市| 百家乐官网平注7s88|