RTT学习

固件尺寸优化

使用RTT进行工程构建时,为了实现业务需求,我们常常会增加驱动文件、组件或者软件包等等,并且在调试代码时也可能需要使能调试相关的功能(例如打开ulog功能)或者自行打印一些调试的信息。
因此,我们会编译得到一个稍微冗余的固件。对于MCU的Flash比较紧张时,我们需要考虑代码体积的优化,使其尽量精简,这样的代码在之后的迭代开发中才可以实现小而美的目标。

下面是几个可以去考虑的优化的方向:

  • 裁剪
  • 选择合适的优化等级
  • 开启newlib-nano选项
  • 使用Map File分析工具
  • readelf命令分析ELF文件
  • 更好同类型Flash较大的硬件平台

需要注意的是,并不是所有的优化都是行之有效的,如果收效甚微的优化却造成了系统性能的大幅衰减,这是非常不可取的,所以优化的时候要认真分析,综合考虑,不可能一蹴而就。

在RTT潘多拉开发板上实现电源管理

随着物联网(IoT)的兴起,产品 对功耗的需求越来越强烈。
作为数据采集的传感器节点通常需要在电池供电时长期工作,而作为联网的SOC也需要有快速的响应功能和较低的功耗。

在产品开发的起始阶段,首先考虑是尽快完成产品的功能开发。在产品功能逐步完善之后,就需要加入电源管理功能。为了适应IoT的这种需求,RT-Thread提供了电源管理框架。电源管理框架的理念是尽量透明,使得产品加入低功耗功能更加轻松。

本文的示例都是在潘多拉开发板下运行。潘多拉开发板是 RT-Thread 和正点原子联合推出的硬件平台,该平台上专门为 IoT 领域设计,并提供了丰富的例程和文档。

MCU通常提供了多种时钟源供用户选择。例如潘多拉开发板上板载的STM32L475就可以选择LSI/MSI/HSI等内部时钟,还可以选择HSE/LSE等外部时钟。MCU 内通常也集成了 PLL(Phase-locked loops),基于不同的时钟源,向 MCU 的其他模块提供更高频率的时钟。

为了支持低功耗功能。MCU里面也会提供不同的休眠模式。例如STM32L475里,可以分成SLEEP模式、STOP模式、STANDBY模式。这些模式还可以有进一步的细分,以适应不同的场合。

STM32L系列是STMicroelectronics推出的低功耗微控制器系列之一,相对于其他系列,它在以下几个方面具有明显的低功耗特点:

低静态功耗(Standby Power):STM32L系列微控制器在待机模式下具有极低的功耗,可以实现电流耗用在几个微安以下的水平。这使得它非常适合对电池寿命要求高的应用,例如便携式设备和物联网(IoT)设备。
低动态功耗(Active Power):除了待机模式下的低功耗外,在活动状态下,STM32L系列微控制器也能够通过有效的功耗管理功能实现低功耗运行。它们通常具有灵活的时钟控制和内置的节能模式,可以根据实际需求动态调整工作频率和电压,以最小化功耗。
低电压运行:STM32L系列微控制器通常能够在较低的电压下工作,这有助于降低功耗。它们支持在较低的电源电压范围内工作,而不牺牲性能。
低功耗外设:除了核心处理器的低功耗特性外,STM32L系列微控制器的外设通常也被设计成低功耗。例如,它们的ADC(模数转换器)和DAC(数模转换器)通常具有低功耗模式,以便在需要时降低功耗。
功耗优化的工艺技术:STMicroelectronics在制造STM32L系列微控制器时采用了功耗优化的工艺技术,例如低漏电流工艺,以确保在各种工作条件下都能实现低功耗。
总的来说,STM32L系列微控制器通过采用一系列的硬件和软件优化技术,以及灵活的功耗管理功能,使得其在待机和活动状态下都能够实现极低的功耗,从而非常适用于对功耗要求严格的应用场景。

低功耗模式介绍

STM32具有运行、睡眠、停止和待机四种工作模式。
上电后默认是在运行模式,当内核不需要继续运行时,可以选择后面三种低功耗模式。

在这里插入图片描述

WFI命令和WFE命令

内核指令,使用函数的格式“__WFI()”和“__WFE”来调用。

在这里插入图片描述

配置工程

配置PM组件:勾选Components—>Device Drivers —>Uusing Power Management device drivers

配置内核选项:使用PM组件需要更大的IDLE线程的栈,这里使用2048字节,例程里还使在这里插入图片描述
用Software timer,所以需要开启相应的配置

定时应用

在定时应用里,创建一个周期性的软件定时器,定时器任务里周期性输出当前的OS Tick。如果创建软件定时器成功之后,使用rt_pm_request(PM_SLEEP_MODE_DEEP)请求深度睡眠模式。

#include <board.h>
#include <rtthread.h>
#include <rtdevice.h>

#ifndef RT_USING_TIMER_SOFT
	#error "Please enable soft timer feature!"
#endif 

#define TIMER_APP_DEFAULT_TICK (RT_TICK_PER_SECOND * 2)

#ifdef RT_USING_PM
static rt_timer_t timer1;

static void _timeout_entry(void *parameter)
{
	rt_kprintf("current tick:%ld\n", rt_tick_get());
}

static int timer_app_init(void)
{
	rt_pm_request(PM_SLEEP_MODE_IDLE);
	rt_pm_request(PM_SLEEP_MODE_LIGHT);

	timer1 = rt_timer_create("timer1_app", _timeout_enntry, RT_NULL, TIMER_APP_DEFAULT_TICK, RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_SOFT_TIMER);
	
	if(timer != RT_NULL)
	{
		rt_timer_start(timer1);

		rt_pm_request(RM_SLEEP_MODE_DEEP);
		return 0;
	}
	else
	{
		return -1;
	}
}
INIT_APP_EXPORT(timer_app_init);
#endif

低功耗状态和模式

RTT PM组件将系统划分为两种状态:运行状态(RUN)和休眠状态(SLEEP)。
运行状态控制CPU的频率,适用于变频场景;
休眠状态根据SOC特性实现休眠CPU,以降低功耗。
两种状态分别使用不同的API接口,独立控制。

休眠状态
休眠状态也就是通常意义上的低功耗状态,通过关闭外设,执行SOC电源管理接口,降低系统功耗。
休眠状态又分为六个模式,呈现为金字塔的形式。
随着模式增加,功耗逐级递减的特点。

  • PM_SLEEP_MODE_NONE:0——系统处于活跃状态,未采取任何的降低功耗状态。
  • PM_SLEEP_MODE_IDLE:1——空闲模式,该模式在系统空闲时停止CPU和部分时钟,任意事件或中断均可以唤醒。
  • PM_SLEEP_MODE_LIGHT:2——轻度睡眠模式,CPU停止,多数时钟和外设停止,唤醒后需要进行时间补偿。
    -PM_SLEEP_MODE_DEEP 3 深度睡眠模式,CPU 停止,仅少数低功耗外设工作,可被特殊中断唤醒
    PM_SLEEP_MODE_STANDBY 4 待机模式,CPU 停止,设备上下文丢失(可保存至特殊外设),唤醒后通常复位PM_SLEEP_MODE_SHUTDOWN 5 关断模式,比 Standby 模式功耗更低, 上下文通常不可恢复, 唤醒后复位

我们可以在msh里输入pm_dump命令观察PM组件的模式状态:
在这里插入图片描述
以上的输出说明,PM组件里Idle、Light Sleep、Deep Sleep都被请求了一次,现在正处于空闲模式(Idle Mode)。

我们依次输入命令pm_release 1和pm_release 2手动释放Idle和Light Sleep模式后,进入Deep Sleep Mode。
进入Deep Sleep Mode之后会定时唤醒,Shell还是一直在输出:

msh />pm_release 1
msh />
msh />current tick: 8023
current tick: 10024
current tick: 12025

msh />pm_release 2
msh />
msh />current tick: 14026
current tick: 16027
current tick: 18028
current tick: 20029
current tick: 22030
current tick: 24031

在这里插入图片描述

按键唤醒应用

在按键唤醒应用里,我们使用wakeup按键来唤醒处于休眠模式的MCU。
一般情况下,在MCU处于比较深度的休眠模式,只能通过特定的方式唤醒。MCU被唤醒之后,会触发相应的中断。

以下例程是从Deep Sleep模式唤醒MCU并闪烁LED之后,再次进入休眠的例程。

#include <board.h>
#include <rtthread.h>
#include <rtdevice.h>

#ifdef RT_USING_PM

#define WAKEUP_EVENT_BUTTON (1<<0)
#define PIN_LED_R GET_PIN(E, 7)
#define WAKEUP_PIN                          GET_PIN(C, 13)
#define WAKEUP_APP_THREAD_STACK_SIZE        1024

static rt_event_t wakeup_event;

static void wakeup_callback(void *args)
{
	rt_event_send(wakeup_event, WAKEUP_EVENT_BUTTON);
}

static void wakeup_init(void)
{
	rt_pin_mode(WAKEUP_PIN, PIN_MODE_INPUT_PULLUP);
	rt_pin_attach_irq(WAKEUP_PIN, PIN_IRQ_MODE_FALLING, wakeup_callback, RT_NULL);
	rt_pin_irq_enable(WAKEUP_PIN, 1);
}

static void wakeup_app_entry(void *parameter)
{
	wakeup_init();
	rt_pm_request(PM_SLEEP_MODE_DEEP);

	while(1)
	{
		if(rt_event_recv(wakeup_event, WAKEUP_EVENT_BUTTON, RT_EVENT_FLAG_AND|RT_EVENT_FLAG_CLEAR, RT_NULL) == RT_EOK)
		{
			rt_pm_request(PM_SLEEP_MODE_NONE);
			rt_pin_mode(PIN_LED_R, PIN_MODE_OUTPUT);
			rt_pin_write(PIN_LED_R, 0);
			rt_thread_delay(rt_tick_from_millisecond(500));
            rt_pin_write(PIN_LED_R, 1);

			rt_pm_release(PM_SLEEP_MODE_NONE);
		}
	}
}

static int wakeup_app(void)
{
	rt_thread_t tid;

	wakeup_event = rt_event_cceate("wakeup",RT_IPC_FLAG_PRIO);
	RT_ASSERT(wakeup_event != RT_NULL);

	tid = rt_thread_create("wakeup_app", wakeup_app_entry, RT_NULL, WAKEUP_APP_THREAD_STACK_SIZE, RT_MAIN_THREAD_PRIORITY, 20);
	RT_ASSERT(tid != RT_NULL);
	rt_thread_startup(tid);
	return 0;
}
INIT_APP_EXPORT(wakeup_app);
#endif

上面的代码里,我们创建一个线程,这个线程里注册了按键中断回调函数,接着请求深度睡眠模式,每当唤醒中断之后就会触发回调。回调函数里会发送事件WAKEUP_EVENT_BUTTON。这样我们线程里收到这个事件之后,首先请求None模式,然后完成LED闪烁功能之后,再去释放None。

在这里插入图片描述

STM32L4的低功耗模式简介

STM32L476是ST公司推出的一款超低功耗的Cortex-M4内核的MCU,支持多个电源管理模式,其中最低功耗shutdown模式下,待机电流仅30nA。
ST公司把L476的电源管理分为很多种,但各个模式的并非功耗逐级递减的特点,下面是各个模式之间的状态转换图:
在这里插入图片描述
在这里插入图片描述
仅管STM32L476的低功耗模式很多,但本质上并不复杂,理解它的原理有助于我们移植驱动,同时在更好的产品中选择合适的模式。

最终决定STM32L476系统功耗的主要三个因素:稳压器(voltage regulator)、CPU工作频率、芯片自身低功耗的处理,下面分别对三个因素进行阐述。

稳压器
L4使用两个嵌入式线性稳压器为所有数字电路、待机电路以及备份时钟域供电,分别是主稳压器(main regulator,下文简称MR)和低功耗稳压器(low-power regulator,简称LPR)。
稳压器在复位后处于使能状态,根据应用模式,选择不同的稳压器对Vcore域供电。
其中,MR的输出电压可以由软件配置为不同的范围(Range1和Range2)。

  • MR(Range1):Vcore=1.2V,用于运行模式、睡眠模式和停止模式0,MR为Vcore提供全功率。
  • MR(Range2):Vcore=1.0V,使用的场景同上
  • LPR:用于低功耗运行模式、低功耗睡眠模式、停止模式1、停止模式2
  • OFF:Standby和Shutdown模式下,MR和LPR都被关闭

在这里插入图片描述

CPU工作频率
通过降低CPU的主频达到降低功耗的目的:MR在Range1正常模式时,SYSCLK最高可以工作在80M;MR工作在Range2,SYSCLK最高不能超过26M;低功耗运行模式和低功耗休眠模式,即Vcore域由LPR供电,SYSCLK必须小于2M。

芯片本身的低功耗处理
芯片本身定义了一系列的休眠模式,如Sleep、Stop、Standby和Shutdown,前面的四种模式功耗逐级递减,实质上芯片内部通过关闭外设和时钟来实现。

STM32WLE5JC低功耗模式

该器件支持多种低功耗模式,以实现低功耗、短启动时间、可用外设和可用唤醒源之间的最佳折衷。

默认情况下,在系统或上电复位后,微控制器处于运行模式,范围1。
用户可以选择以下低功耗模式之一:

  • 睡眠模式:CPU时钟关闭,包括CPU核心外设在内的所有外设(其中包括NVIC,SysTick)在发生中断或事件时都可以运行并唤醒CPU。
  • 低功耗运行模式:当系统时钟频率降低到2MHz以下时。代码从SRAM或闪存执行。稳压器在低功率模式,以尽量减少工作电流。
  • 低功耗睡眠模式:从低功耗运行模式进入。
  • 停止0和停止1模式:SRAM1、SRAM2和所有寄存器的内容被保留。VCORE域内所有时钟停止。PLL、MSI、HSI16和HSE32被禁用。LSI和LSE可以保持运行。
    RTC可以保持活动状态。sub-GHz的无线电可能独立于CPU保持活动状态。
  • 停止1提供最多数量的活动外设和唤醒源,与停止2相比,唤醒时间更短,但消耗更高。

移植的具体实现

上文简要说明STM32的低功耗模式和工作原理,下面介绍RTT PM的功能和移植接口。

RTT低功耗管理系统从设计上分离运行模式和休眠模式,独立管理,运行模式用于变频和变电压,休眠调用芯片的休眠特性。对于多数芯片和开发来说,可能并不需要考虑变频和变电压,仅需关注休眠模式。

STM32 L4系列的芯片有运行模式和低功耗运行模式的概念,同时MR还有Range2模式,可用于变频场景。

PM组件的底层功能都是通过struct rt_pm_ops结构体里的函数完成:

struct rt_pm_ops
{
	void (*sleep)(struct rt_pm *pm, uint8_t mode);
	void (*run)(struct rt_pm *pm, uint8_t mode);
	void (*timer_start)(struct rt_pm *pm, rt_uint32_t timeout);
	void (*timer_stop)(struct rt_pm *pm);
	rt_tick_t (*timer_get_tick)
};

移植休眠模式
移植休眠模式仅需关注sleep接口,根据PM用户手册相关介绍,根据PM用户手册额相关介绍,首先将RTT的休眠模式和STM32的模式做一个转换:

  • PM_SLEEP_MODE_NONE——Run:正常运行模式,不进行任何降功耗的措施。
  • PM_SLEEP_MODE_IDLE——Run:正常运行模式,可选择WFI(等待中断唤醒)和WFE(等待事件唤醒),此处暂不处理。
  • PM_SLEEP_MODE_LIGHT——Sleep:轻度睡眠模式,执行ST的Sleep模式
  • PM_SLEEP_MODE_DEEP——Stop2:深度睡眠模式,执行ST的Stop2模式
  • PM_SLEEP_MODE_STANDBY——Standby:待机模式,执行ST的Standby模式
  • PM_SLEEP_MODE_SHUTDOWN——Shutdown:停止模式,执行ST的Shutdown模式
#include <board.h>
#include <rtthread.h>
#include <rtdevice.h>

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:
			HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
		case PM_SLEEP_MODE_DEEP:
			HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);
		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;
	}
}

int rt_hw_pm_init(void)
{
	static const struct rt_pm_ops _ops = 
	{
		sleep,
		RT_NULL,
		RT_NULL,
		RT_NULL,
		RT_NULL
	};

	rt_uint8_t timer_mask = 0;
	
	__HAL_RCC_PWR_CLK_ENABLE();
	
	rt_system_pm_init(&_ops, timer_mask, RT_NULL);
	return 0l
}
INIT_BOARD_EXPORT(rt_hw_pm_init);

```c
struct rt_pm_ops
{
    void (*sleep)(struct rt_pm *pm, rt_uint8_t mode);
    void (*run)(struct rt_pm *pm, rt_uint8_t mode);
    void (*timer_start)(struct rt_pm *pm, rt_uint32_t timeout);
    void (*timer_stop)(struct rt_pm *pm);
    rt_tick_t (*timer_get_tick)(struct rt_pm *pm);
};
struct rt_pm
{
	
	struct rt_device parent;
	rt_uint8_t modes[PM_SLEEP_MODE_MAX];
	rt_uint8_t sleep_mode;
	rt_uint8_t run_mode;

	 /* modules request status*/
    struct rt_pm_module module_status[PM_MODULE_MAX_ID];

    /* sleep request table */
    rt_uint32_t sleep_status[PM_SLEEP_MODE_MAX - 1][(PM_MODULE_MAX_ID + 31) / 32];

    /* the list of device, which has PM feature */
    rt_uint8_t device_pm_number;
    struct rt_device_pm *device_pm;

    /* if the mode has timer, the corresponding bit is 1*/
    rt_uint8_t timer_mask;
    rt_uint8_t flags;

    const struct rt_pm_ops *ops;	
};
void rt_system_pm_init(const struct rt_pm_ops *ops, rt_uint8_t timer_mask, void*user_data)
{
	struct rt_device *device;
	struct rt_pm *pm;

	pm = &_pm;
	device = &(_pm.parent);
	device->type = RT_Device_Class_PM;
	device->rx_indicate = RT_NULL;
	device->tx_complete = RT_NULL;

	device->init        = RT_NULL;
    device->open        = RT_NULL;
    device->close       = RT_NULL;

	device->read = _rt_pm_device_read;
	device->write       = _rt_pm_device_write;
    device->control     = _rt_pm_device_control;

	device->user_data = user_data;
	rt_device_register(device, "pm", RT_DEVICE_FLAG_RDWR);

	rt_memset(pm->modes, 0, sizeof(pm->modes));
	pm->sleep_mode = _pm_default_sleep;

	pm->modes[pm->sleep_mode] = 1;
	pm->modes[pm->sleep_mode] = 1;
    pm->module_status[PM_POWER_ID].req_status = 1;
    pm->run_mode   = RT_PM_DEFAULT_RUN_MODE;
    pm->timer_mask = timer_mask;

    pm->ops = ops;

    pm->device_pm = RT_NULL;
    pm->device_pm_number = 0;

#if IDLE_THREAD_STACK_SIZE <= 256
    #error "[pm.c ERR] IDLE Stack Size Too Small!"
#endif

    _pm_init_flag = 1;
}

## 使用HAL库的嵌入式系统中的电源管理功能

```c
HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);

这个函数的作用是将处理器置于睡眠模式(SLEEP Mode)。在这个函数中,它接受两个参数:

  1. 第一个参数“PWR_MAINREGULATOR_ON”表示主电源调节器(Main regulator)是开启的。这意味着在进入睡眠模式时,主电源调节器仍然处于激活状态,以保持系统的供电。
  2. 第二个参数“PWR_SLEEPENTRY_WFI”表示进入睡眠模式时使用的方法是“等待中断”(Wait For Interrupt,WFI)。这是一种低功耗的等待模式,处理器会等待直到收到中断信号,然后唤醒以执行相应的处理。

这行代码的作用是使处理器进入睡眠模式,并保存主电源调节器处于开启状态。在睡眠模式下,处理器的功耗会降低,以节省能量。

switch(mode)
{
	case mode0:
		break;
	default:
		RT_ASSERT(0);
		break;
}

default分支通常在switch语句中用于处理未被任何’case’匹配的情况。
在这里,RT_ASEERT(0)的作用是触发一个断言。断言是一种用于检查程序运行时条件是否为真的方法。通常情况下,如果条件为真,程序会继续执行;但如果条件为假,断言会触发,中断程序的执行,并且通常会输出一条错误消息。

RT_ASSERT(0)的意思是,如果执行到了switch语句的default分支,即意味着程序处于一种未预期的状态,因此触发断言来中断程序的执行。
RT_ASSERT(0),传递了一个条件为假的断言,因此会导致程序停止执行,并且可能会触发一条错误消息,提示开发者程序运行到了一个意料之外的状态。

__HAL_RCC_PWR_CLK_ENABLE();

是HAL库中用于使能电源时钟的宏。在嵌入式系统中,一些功能模块(比如电源模块)的操作可能需要先使能相应的时钟,以确保这些功能模块能够正常工作。

在这个宏中,__HAL_RCC_PWR_CLK_ENABLE();的作用是启用电源模块的时钟。启用电源时钟后,才能够使用HAL库提供的相关功能函数对电源模块进行配置和控制。

目前为止,ST的休眠模式已经初步加入了,能够满足部分的应用场景。
打开命令行,输入请求/释放休眠模式命令,可以观察到功耗显著降低。

移植时间补偿接口

某些情况下,我们可能需要系统在空闲时进入Stop模式,以达到更低的降功耗效果。
在这里插入图片描述
L476 Stop2模式下的电流可以达到1.6uA左右,ST手册上对Stop2模式的描述如下:

Stop2模式基于Cortex-M4深度睡眠模式与外设时钟门控。
在Stop2模式下,Vcore域中的所有时钟都会停止,PLL,MSI,HSI16和HSE振荡器也被禁止。
一些带有唤醒功能(I2C3和LPUART)的外设可以开启HSI16以获取帧,如果该帧不是唤醒帧,也可以在接收到帧后关闭HSI16。
SRAM1、SRAM2、SRAM3和寄存器内容将保留,所有I/O引脚的状态与运行模式下相同。

根据手册可知,Stop2模式会关闭系统时钟,当前的OS Tick基于内核的SysTick定时器。
那么在系统时钟停止后,OS Tick也会停止,对于某些依赖OS Tick的应用,在进入Stop2模式,又被唤醒后,就会出现问题,因此需要在系统唤醒后,对OS Tick进行补偿。
Stop2模式下,绝大多数外设都停止工作,仅低功耗定时器1(LP_TIM1)选择LSI作为时钟源后,仍能够正常运行,所以选择LP_TIM1作为Stop 2模式的事件补偿定时器。

休眠的时间补偿需要实现三个接口,分别用于启动低功耗定时器、停止定时器、唤醒后获取休眠的Tick,下面是具体的实现:

static void pm_timer_start(struct rt_pm *pm, rt_uint32_t timeout)
{
	RT_ASSERT(pm != RT_NULL);
	RT_ASSERT(timeout > 0);

	/*
		当超时为RT_TICK_MAX时,表明系统此时没有依赖OS Tick的应用
		因此不启动低功耗定时器,避免超时唤醒增加系统功耗
	*/
	if(timeout != RT_TICK_MAX)
	{
		timeout = stm32l4_pm_tick_from_os_tick(timeout);
		if(timeout > stm32l4_lptim_get_tick_max())
		{
			timeout = stm32l4_lptim_get_tick_max();
		}
		stm32l4_lptim_start(timeout);
	}
}

static void pm_timer_stop(struct rt_pm *pm)
{
	RT_ASSERT(pm != RT_NULL);
	stm32l4_lptim_stop();
}

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

int rt_hw_pm_init(void)
{
    static const struct rt_pm_ops _ops =
    {
        sleep,
        RT_NULL,
        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;
}

休眠时间补偿的移植并不复杂,根据Tick配置低功耗定时器超时,唤醒后获取实际休眠时间并转换为OS Tick,告知PM组件即可。
另外,从 Stop 2 模式唤醒后,默认会切换到内部的 MSI 时钟,通常需要重新配置时钟树。

相关推荐

  1. RTT学习

    2024-04-26 19:36:01       35 阅读
  2. RTT学习

    2024-04-26 19:36:01       31 阅读
  3. <span style='color:red;'>RTT</span><span style='color:red;'>学习</span>

    RTT学习

    2024-04-26 19:36:01      30 阅读
  4. rtt设备io框架面向对象学习-硬件rtc设备

    2024-04-26 19:36:01       58 阅读
  5. rtt自动初始化机制学习

    2024-04-26 19:36:01       44 阅读
  6. rtt设备io框架面向对象学习-软件模拟rtc设备

    2024-04-26 19:36:01       61 阅读
  7. RTT设备驱动框架学习(GPIO)

    2024-04-26 19:36:01       39 阅读
  8. RTT设备驱动框架学习(UART)

    2024-04-26 19:36:01       39 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-26 19:36:01       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-26 19:36:01       100 阅读
  3. 在Django里面运行非项目文件

    2024-04-26 19:36:01       82 阅读
  4. Python语言-面向对象

    2024-04-26 19:36:01       91 阅读

热门阅读

  1. Docker备忘清单(三)

    2024-04-26 19:36:01       28 阅读
  2. vue3前端请求后端接口动态渲染菜单

    2024-04-26 19:36:01       28 阅读
  3. leetcode144--二叉树的前序遍历

    2024-04-26 19:36:01       29 阅读
  4. 使用Dokcer中的Mysql导入sql文件

    2024-04-26 19:36:01       29 阅读
  5. 银行卡四要素API接口的验证流程

    2024-04-26 19:36:01       39 阅读
  6. make命令

    2024-04-26 19:36:01       35 阅读
  7. 大华相机C#学习之IStream类

    2024-04-26 19:36:01       28 阅读
  8. mybatis - 取值符号:# 和 $的区别

    2024-04-26 19:36:01       39 阅读
  9. 【动态规划】Leetcode 322. 零钱兑换【中等】

    2024-04-26 19:36:01       25 阅读