FreeRTOS教程10 低功耗
1、准备材料
STM32CubeMX软件(Version 6.10.0)
Keil µVision5 IDE(MDK-Arm)
2、学习目标
本文主要学习 FreeRTOS 低功耗的相关知识,包括HAL 库基础时钟、FreeRTOS 基础时钟、低功耗处理和 Tickless 模式等知识
3、前提知识
3.1、HAL 库基础时钟
当我们使用 STM32CubeMX 软件配置一个基本的工程时,往往需要首先在 Pinout & Configuration 页面 RCC 中配置 HSE 和 LSE ,然后在 SYS 中配置 Debug 和 Timebase Source,这些都是必不可少的配置步骤,其中 Timebase Source 可以选择默认的 SysTick ,也可以选择任何一个定时器外设
3.1.1、使用 SysTick 定时器
学习 STM32 HAL 库开发,在 SYS 中配置 Timebase Source 时,一般将时基源保持默认的 SysTick 即可,那么这个默认的 SysTick 是如何被初始化以及使用呢?
3.1.1.1、工作原理
打开 “STM32CubeMX教程1 工程建立” 文章配置的 STM32 空工程,找到 main.c 文件中的 main() 主函数,SysTick 在主函数第一个被执行的函数HAL_Init() 中得到初始化,具体如下图所示
其中滴答定时器频率 uwTickFreq 参数默认为 HAL_TICK_FREQ_DEFAULT(1KHZ) ,当然也可以根据需要修改为 10HZ 和 100HZ,如下述枚举类型定义
typedef enum
{
HAL_TICK_FREQ_10HZ = 100U,
HAL_TICK_FREQ_100HZ = 10U,
HAL_TICK_FREQ_1KHZ = 1U,
HAL_TICK_FREQ_DEFAULT = HAL_TICK_FREQ_1KHZ
} HAL_TickFreqTypeDef;
当初始化完毕之后,滴答定时器就会以固定频率发生中断,然后进入中断回调函数 SysTick_Handler() 中,滴答定时器中断默认就会开启
3.1.1.2、中断处理
在 STM32CubeMX 软件的 NVIC 管理页面,可以发现默认开启的滴答定时器中断 Time base: System tick timer ,在软件上该中断不可关闭,但是可以设置中断优先级,具体如下图所示
在 stm32f4xx_it.c 文件中可以找到滴答定时器的回调函数 SysTick_Handler() ,其只调用了 HAL_IncTick() 函数,该函数只做了一件事情,就是每次发生滴答定时器中断的时候,将一个名为 uwTick 的全局变量加 1 ( uwTickFreq 参数默认为1),具体如下所示
根据这个全局变量的值我们就可以做一些延时的工作,比如常用到的 HAL_Delay() 延时函数就是通过滴答定时器中断来实现的,具体如下所述
/**
* @brief HAL 库延时函数
* @param Delay:延时时间,单位为ms
* @retval None
*/
__weak void HAL_Delay(uint32_t Delay)
{
uint32_t tickstart = HAL_GetTick();
uint32_t wait = Delay;
/* 最少等待一个频率时间 */
if (wait < HAL_MAX_DELAY)
{
wait += (uint32_t)(uwTickFreq);
}
/* 空循环延时等待 */
while((HAL_GetTick() - tickstart) < wait)
{
}
}
另外还有 HAL_SuspendTick() 和 HAL_ResumeTick() 两个控制滴答定时器中断停止和启动的函数,具体如下所述
/**
* @brief 挂起滴答定时器中断
* @retval None
*/
__weak void HAL_SuspendTick(void)
{
/* 禁用 SysTick 中断 */
SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk;
}
/**
* @brief 恢复挂起的滴答定时器中断
* @retval None
*/
__weak void HAL_ResumeTick(void)
{
/* 使能 SysTick 中断 */
SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk;
}
3.1.2、使用其他定时器
当 SysTick 被其他软件使用时(比如本系列教程的 FreeRTOS),STM32 还可以选择任何一个定时器外设作为其 HAL 库的时基源,比如选择基础定时器 TIM6
3.1.2.1、工作原理
当在 STM32CubeMX 软件中配置 SYS 中的 Timebase Source 为 TIM6 然后生成工程之后,与 “3.1.1、使用 SysTick 定时器” 小节不同的是,其首先会在 Core 文件夹下多出一个名为 stm32f4xx_hal_timebase_tim.c 的文件,该文件中涉及了所有关于 TIM6 作为 HAL 库系统嘀嗒定时器的配置程序,使用 TIM6 作为 HAL 库系统嘀嗒定时器的初始化步骤如下图所示
上图内容其实就是将基础定时器 TIM6 初始化为一个周期为 1ms 的定时器,并且启动其周期中断回调,如果对上述代码不了解可以阅读 “STM32CubeMX教程5 TIM 定时器概述及基本定时器” 实验
3.1.2.2、中断处理
当选择基础定时器 TIM6 作为 SysTick 时,在STM32CubeMX软件的 NVIC 管理中 TIM6 的中断就会被强制打开并且软件内不可关闭,但是同样可以修改优先级,如下图所示
同样可以在 stm32f4xx_it.c 文件中可以找到 TIM6 的中断回调函数 TIM6_DAC_IRQHandler() ,该函数调用了定时器的统一中断处理函数 HAL_TIM_IRQHandler() ,该函数根据使用的不同定时器功能最终调用不同的中断回调函数,这里读者只需要知道其调用了定时器周期回调函数 HAL_TIM_PeriodElapsedCallback() 即可,该函数由 STM32CubeMX 软件在 main.c 文件中自动生成,具体如下所示
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* USER CODE BEGIN Callback 0 */
/* USER CODE END Callback 0 */
if (htim->Instance == TIM6) {
HAL_IncTick();
}
/* USER CODE BEGIN Callback 1 */
/* USER CODE END Callback 1 */
}
从函数体内内容可以看出其原理与 “3.1.1.2、中断处理” 小节所叙述的一致,故此处不再赘述
3.2、FreeRTOS 基础时钟
STM32CubeMX 软件配置使用 FreeRTOS 时,默认将 SysTick 滴答定时器分配给 FreeRTOS 使用,因此如果 HAL 库的时基源也为 SysTick 时,在生成工程代码时软件就会警告用户:“当使用 RTOS 时,强烈建议使用除 Systick 之外的 HAL 时基源,可以从 SYS 下的 Pinout 选项卡更改 HAL 时基源”,具体如下图所示
因此在 STM32 需要使用 FreeRTOS 时,一般将 SysTick 分配给 FreeRTOS 使用,而 HAL 库的时基源一般选择除 SysTick 之外的定时器外设,同时如果用户明确自己不需要使用 HAL 库的 HAL_Delay() 延时函数,则可以关闭 HAL 库的时基源
3.2.1、工作原理
FreeRTOS 的 SysTick 系统时基源是在 vPortSetupTimerInterrupt() 函数中被初始化的,在该函数中有一个名为 configUSE_TICKLESS_IDLE 参数用于设置是否使用 Tickless 模式,这是 FreeRTOS 中提供的一种低功耗模式,将在后面小节介绍,其对 SysTick 的初始化是直接对 SysTick 的寄存器进行操作的,其调用流程如下图所示
SysTick 的寄存器可以阅读 Arm Cortex-M4 Processor Technical Reference Manual手册 “4.1 System control registers” 小节,如下图所示
3.2.2、中断处理
当将 SysTick 分配给 FreeRTOS 初始化并开启对应中断之后,SysTick 的中断会被定义在 cmsis_os2.c 文件中(从 FreeRTOS_v10.3.1 之后),该函数清除了中断标志然后调用了 FreeRTOS 定义的硬件接口文件中的 xPortSysTickHandler() 函数,在 xPortSysTickHandler() 函数中增加了 RTOS 滴答定时器计数量,然后挂起 PednSV 中断,请求上下文切换,具体如下所述
根据上面的分析我们知道了一件关于 FreeRTOS 很重要的事情,也就是:FreeRTOS 的任务调度发起是在系统滴答定时器中断中发起的,然后真正进行上下文切换处理是在 PendSV 中断中执行的
3.3、低功耗处理
3.3.1、睡眠、停止和待机模式
在 “STM32CubeMX教程25 PWR 电源管理 - 睡眠、停止和待机模式” 文章中曾经介绍了关于 STM32 电源管理的睡眠、停止和待机三种低功耗模式,在一个由 FreeRTOS 管理的系统中,一般只使用其中的睡眠模式即可,因为停止和待机模式的唤醒条件相对较为苛刻,感兴趣的读者请自行阅读上述文章
3.3.2、低功耗思路
在一个 FreeRTOS 管理的多任务系统中,当所有任务处理完毕进入阻塞状态等待下次处理时机时,空闲任务会一直执行,如果同时使能了 configUSE_IDLE_HOOK 参数,则每当处理器将要进入空闲任务时,就会先进入空闲任务钩子函数中
因此我们可以在空闲任务钩子函数中设置处理器进入睡眠模式,但是同时也会存在一个问题,就是每次滴答定时器中断都会将处理器唤醒,这样其运行时序图应该如下图所示
3.3.3、Tickless 模式
上述低功耗思路中存在的一个问题:“每次滴答定时器中断都会将处理器唤醒” ,FreeRTOS 提供了一个 Tickless 模式,当处理器空闲时会一直处于睡眠状态,然后在任务即将退出阻塞状态之前处理器提前被唤醒,理想的低功耗模式应该如下图所示
要使用 Tickless 模式只需要启用 configUSE_TICKLESS_IDLE 参数即可,该参数可以通过 STM32CubeMX 软件设置,有三个可以配置的选项,选择 Built in functionality enabled 对应的参数值为 1 ,表示使用 FreeRTOS 内建的函数实现 Tickless 低功耗功能,选择 User defined functionality enabled 则对应的参数值为 2 ,表示使用用户自定义的函数实现 Tickless 低功耗功能,一般选择使用 FreeRTOS 现成的函数来实现 Tickless 低功耗功能,如下图所示
3.3.3.1、工作原理
当启用 Tickless 之后,系统满足以下两点时就会自动进入睡眠模式
- 空闲任务正在运行
- 可运行低功耗的时间大于参数 configEXPECTED_IDLE_TIME_BEFORE_SLEEP 设定值时(默认为2)
用户需要注意的是进入睡眠的时间有最大值 xMaximumPossibleSuppressedTicks 限制,该变量在设置滴答定时器中断 vPortSetupTimerInterrupt() 函数中被计算,当 MCU 频率为168MHz,FreeRTOS 频率为 1000Hz 时,该值为 99 ,也即单次最长进入睡眠时间的最大值为 99 个节拍,具体如下所示
3.3.3.2、vPortSuppressTicksAndSleep() 函数详解
vPortSuppressTicksAndSleep() 是 Tickless 模式实现的具体函数,该函数会在启用 Tickless 模式后在空闲任务中被调用,具体可以参考 “freeRTOS 低功耗模式 和 空闲任务” 文章
4、实验一:Tickless 模式的使用
4.1、实验目标
- 创建任务 Task_Main,在任务中实现 GREEN_LED 和 RED_LED 的闪烁程序
- 启用/关闭 Tickless 模式,对比两种不同情况下开发板的工作电流
4.2、CubeMX相关配置
首先读者应按照 "FreeRTOS教程1 基础知识" 章节配置一个可以正常编译通过的 FreeRTOS 空工程,然后在此空工程的基础上增加本实验所提出的要求
本实验需要初始化开发板上 GREEN_LED 和 RED_LED 两个 LED 灯作为显示,具体配置步骤请阅读“STM32CubeMX教程2 GPIO输出 - 点亮LED灯”,注意虽开发板不同但配置原理一致,如下图所示
单击 Middleware and Software Packs/FREERTOS ,在 Configuration 中单击 Tasks and Queues 选项卡,双击默认任务修改其参数,如下所示
然后在 Configuration 中单击 Config parameters 选项卡,在 Kernel settings 中找到 USE_TICKLESS_IDLE 参数,将其设置为 Disabled 或者 Built in functionality enabled,进行对比实验
最后配置 Clock Configuration 和 Project Manager 两个页面,接下来直接单击 GENERATE CODE 按钮生成工程代码即可
4.3、添加其他必要代码
首先实现任务 Task_Main 使其每隔 500ms 改变一次 GREEN_LED 和 RED_LED 的状态,具体如下所述
void AppTask_Main(void *argument)
{
/* USER CODE BEGIN AppTask_Main */
/* Infinite loop */
for(;;)
{
HAL_GPIO_TogglePin(GREEN_LED_GPIO_Port, GREEN_LED_Pin);
HAL_GPIO_TogglePin(RED_LED_GPIO_Port, RED_LED_Pin);
vTaskDelay(pdMS_TO_TICKS(500));
}
/* USER CODE END AppTask_Main */
}
然后再进入睡眠模式之前关闭系统滴答定时器,在退出睡眠模式之后开启系统滴答定时器,具体如下所述
__weak void PreSleepProcessing(uint32_t ulExpectedIdleTime)
{
/* place for user code */
HAL_SuspendTick();
}
__weak void PostSleepProcessing(uint32_t ulExpectedIdleTime)
{
/* place for user code */
HAL_ResumeTick();
}
4.4、烧录验证
在开启 Tickless 和关闭 Tickless 两种模式下读者可以自行测试开发板工作电流,对比开启和关闭两种模式下工作电流的变化
参考资料
Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf
FreeRTOS教程10 低功耗的更多相关文章
- node-webkit教程(10)Platform Service之File dialogs
node-webkit教程(10)Platform Service之File dialogs 文/玄魂 目录 node-webkit教程(10)Platform Service之File dialog ...
- 【译】ASP.NET MVC 5 教程 - 10:添加验证
原文:[译]ASP.NET MVC 5 教程 - 10:添加验证 在本节中,我们将为Movie模型添加验证逻辑,并确认验证规则在用户试图使用程序创建和编辑电影时有效. DRY 原则 ASP.NET M ...
- Linux pwn入门教程(10)——针对函数重定位流程的几种攻击
作者:Tangerine@SAINTSEC 本系列的最后一篇 感谢各位看客的支持 感谢原作者的付出一直以来都有读者向笔者咨询教程系列问题,奈何该系列并非笔者所写[笔者仅为代发]且笔者功底薄弱,故无法解 ...
- [译]Vulkan教程(10)交换链
[译]Vulkan教程(10)交换链 Vulkan does not have the concept of a "default framebuffer", hence it r ...
- 黑马lavarel教程---10、lavarel模型关联
黑马lavarel教程---10.lavarel模型关联 一.总结 一句话总结: 1.模型关联比较方便,一次定义,后面都可以使用 2.关联关系 使用动态属性进行调用 1.一对多,多对多实例? 一对多: ...
- Directx11教程(10) 画一个简易坐标轴
原文:Directx11教程(10) 画一个简易坐标轴 本篇教程中,我们将在三维场景中,画一个简易的坐标轴,分别用红.绿.蓝三种颜色表示x,y,z轴的正向坐标轴. 为此,我们要先建立一个A ...
- 深度学习与CV教程(10) | 轻量化CNN架构 (SqueezeNet,ShuffleNet,MobileNet等)
作者:韩信子@ShowMeAI 教程地址:http://www.showmeai.tech/tutorials/37 本文地址:http://www.showmeai.tech/article-det ...
- 【MCU】移植AT32库&FreeRTOS教程
目录 前言 1. 移植AT库 1.1 移植内核相关文件 1.2 移植芯片型号相关文件 1.3 移植芯片外设驱动库 1.4 移植配置文件及中断回调函数文件 2. 移植FreeRTOS源码 2.1 获取 ...
- ASP.NET Core 入门教程 10、ASP.NET Core 日志记录(NLog)入门
一.前言 1.本教程主要内容 ASP.NET Core + 内置日志组件记录控制台日志 ASP.NET Core + NLog 按天记录本地日志 ASP.NET Core + NLog 将日志按自定义 ...
- 微信小程序详细图文教程-10分钟完成微信小程序开发部署发布
很多朋友都认为微信小程序申请.部署.发布很难,需要很长时间. 实际上,微信和腾讯云同是腾讯产品,已经提供了10分钟(根据准备资源情况,已完成小程序申请认证)完成小程序开发.部署.发布的方式.当然,实现 ...
随机推荐
- Java集合篇之深入解析ArrayList,这六问你答的上来吗?
写在开头 开年第一篇,先祝各位新的一年身体健康,学业有成,事业有成哈,春节期间就是咔咔乱吃,咔咔乱玩,把学习都抛一边子去了,已经9天没有学习了,深深的懊悔,从今天开始,2024年的学习正式开启,一起给 ...
- NC200179 Colorful Tree
题目链接 题目 题目描述 A tree structure with some colors associated with its vertices and a sequence of comman ...
- NC15162 小H的询问
题目链接 题目 题目描述 小H给你一个数组 \(a\) ,要求支持以下两种操作: 0 l r \((1 \leq l \leq r \leq n)\),询问区间 \([l,r]\) 中权值和最大的有效 ...
- XTW100编程器在Win10下的安装
XTW100 这是一个淘宝上卖得很多的经典编程器, 用于写入24和25系列的存储芯片. 最初使用的是stm32f103c8t6, 因为f103价格飞涨, 市面上大都换成国产的兼容mcu了, 软件和使用 ...
- 【OpenGL ES】基于ValueAnimator的旋转、平移、缩放动效
1 前言 ValueAnimator 基于 Choreographer 的 frame callback 机制,周期性(约16.7ms,与屏幕帧率相关)执行其 doAnimationFrame() ...
- CDN缓存的理解
CDN缓存的理解 CDN即内容分发网络Content Delivery Network,CDN的基本原理是广泛采用各种缓存服务器,将这些缓存服务器分布到用户访问相对集中的地区或网络中,在用户访问网站时 ...
- 使用docker stack方式部署web集群
如何部署swarm集群,请参考: https://blog.csdn.net/IndexMan/article/details/102713777 创建文件夹 mkdir -p /opt/docker ...
- 我又踩坑了!如何为HttpClient请求设置Content-Type?
1. 坑位 最近在重构认证代码,认证过程相当常规: POST /open-api/v1/user-info?client_id&timstamp&rd=12345&sign=* ...
- C++ 多线程的错误和如何避免(6)
加锁的临界区要尽可能的紧凑和小型 问题分析: 当一个线程在临界区内执行时,所有其他试图进入临界区的线程都会被阻止,所以我们应该保证临界区尽可能的小.比如, void CallHome(string m ...
- 在python中发送自定义消息
.py import win32api, win32con, win32gui import win32gui_struct import ctypes from ctypes import * GU ...