1、准备材料

正点原子stm32f407探索者开发板V2.4

STM32CubeMX软件(Version 6.10.0

Keil µVision5 IDE(MDK-Arm

野火DAP仿真器

XCOM V2.6串口助手

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 之后,系统满足以下两点时就会自动进入睡眠模式

  1. 空闲任务正在运行
  2. 可运行低功耗的时间大于参数 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、实验目标

  1. 创建任务 Task_Main,在任务中实现 GREEN_LED 和 RED_LED 的闪烁程序
  2. 启用/关闭 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 两种模式下读者可以自行测试开发板工作电流,对比开启和关闭两种模式下工作电流的变化

参考资料

STM32Cube高效开发教程(基础篇)

Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf

FreeRTOS教程10 低功耗的更多相关文章

  1. node-webkit教程(10)Platform Service之File dialogs

    node-webkit教程(10)Platform Service之File dialogs 文/玄魂 目录 node-webkit教程(10)Platform Service之File dialog ...

  2. 【译】ASP.NET MVC 5 教程 - 10:添加验证

    原文:[译]ASP.NET MVC 5 教程 - 10:添加验证 在本节中,我们将为Movie模型添加验证逻辑,并确认验证规则在用户试图使用程序创建和编辑电影时有效. DRY 原则 ASP.NET M ...

  3. Linux pwn入门教程(10)——针对函数重定位流程的几种攻击

    作者:Tangerine@SAINTSEC 本系列的最后一篇 感谢各位看客的支持 感谢原作者的付出一直以来都有读者向笔者咨询教程系列问题,奈何该系列并非笔者所写[笔者仅为代发]且笔者功底薄弱,故无法解 ...

  4. [译]Vulkan教程(10)交换链

    [译]Vulkan教程(10)交换链 Vulkan does not have the concept of a "default framebuffer", hence it r ...

  5. 黑马lavarel教程---10、lavarel模型关联

    黑马lavarel教程---10.lavarel模型关联 一.总结 一句话总结: 1.模型关联比较方便,一次定义,后面都可以使用 2.关联关系 使用动态属性进行调用 1.一对多,多对多实例? 一对多: ...

  6. Directx11教程(10) 画一个简易坐标轴

    原文:Directx11教程(10) 画一个简易坐标轴       本篇教程中,我们将在三维场景中,画一个简易的坐标轴,分别用红.绿.蓝三种颜色表示x,y,z轴的正向坐标轴. 为此,我们要先建立一个A ...

  7. 深度学习与CV教程(10) | 轻量化CNN架构 (SqueezeNet,ShuffleNet,MobileNet等)

    作者:韩信子@ShowMeAI 教程地址:http://www.showmeai.tech/tutorials/37 本文地址:http://www.showmeai.tech/article-det ...

  8. 【MCU】移植AT32库&FreeRTOS教程

    目录 前言 1. 移植AT库 1.1 移植内核相关文件 1.2 移植芯片型号相关文件 1.3 移植芯片外设驱动库 1.4 移植配置文件及中断回调函数文件 2. 移植FreeRTOS源码 2.1 获取 ...

  9. ASP.NET Core 入门教程 10、ASP.NET Core 日志记录(NLog)入门

    一.前言 1.本教程主要内容 ASP.NET Core + 内置日志组件记录控制台日志 ASP.NET Core + NLog 按天记录本地日志 ASP.NET Core + NLog 将日志按自定义 ...

  10. 微信小程序详细图文教程-10分钟完成微信小程序开发部署发布

    很多朋友都认为微信小程序申请.部署.发布很难,需要很长时间. 实际上,微信和腾讯云同是腾讯产品,已经提供了10分钟(根据准备资源情况,已完成小程序申请认证)完成小程序开发.部署.发布的方式.当然,实现 ...

随机推荐

  1. CF-926(已更新:B)

    CF-926 两点睡,七点起,阎王夸我好身体-- 主要这场实在是难绷,两个小时都在C题上吊死了,也不是没想过跳题,只是后面的题我更是一点思路都没有-^- "就喜欢这种被揭穿的感觉,爽!&qu ...

  2. ASCII编码的诞生:解决字符标准化与跨平台通信的需求

    在计算机的发展过程中,字符的表示和传输一直是一个重要的问题.为了实现字符的标准化和跨平台通信,ASCII(American Standard Code for Information Intercha ...

  3. 精通C语言:打造高效便捷的通讯录管理系统

    欢迎大家来到贝蒂大讲堂 养成好习惯,先赞后看哦~ 所属专栏:C语言项目 贝蒂的主页:Betty's blog 引言: 在我们大致学习完C语言之后,我们就可以利用目前所学的知识去做一些有意思的项目,而今 ...

  4. 跨界协作:借助gRPC实现Python数据分析能力的共享

    gRPC是一个高性能.开源.通用的远程过程调用(RPC)框架,由Google推出.它基于HTTP/2协议标准设计开发,默认采用Protocol Buffers数据序列化协议,支持多种开发语言. 在gR ...

  5. Spring Boot图书管理系统项目实战-4.基础信息管理

    导航: pre:  3.用户登录 next:5.读者管理 只挑重点的讲,具体的请看项目源码. 1.项目源码 需要源码的朋友,请捐赠任意金额后留下邮箱发送:) 2.页面设计 出版社管理.语种管理.书架管 ...

  6. ckeditor使用技巧总结

    介绍 官方开发者文档:CKEditor 4 documentation 技巧总结 1.挑选需要的插件,打包下载 参考:CKEditor 4.4.1 添加代码高亮显示插件功能--使用官方推荐Code S ...

  7. MYSQL TIMESTAMP自动更新问题

    某张表格里有2个TIMESTAMP类型,time1.time2;建表时time1默认NOT NULL ,time2默认NULL; 之后出现了问题:当只修改time2字段,不操作time1时:time1 ...

  8. C++ STL学习

    C++ STL学习 目录 C++ STL学习 容器库概览 对可以保存在容器中的元素的限制 容器支持的操作 所有容器都支持的操作或容器成员 迭代器 迭代器的公共操作 迭代器的类型 迭代器的const属性 ...

  9. virtualenvwrapper管理虚拟环境

    安装 pip install virtualenvwrapper-win 基本使用 1.创建虚拟环境 mkvirtualenv my_env 使用这个命令,就会在你c盘的当前用户下创建一个Env的文件 ...

  10. 问题:django中对datetime类型数据在pycharm中sqlite3进行修改时,修改后datetime日期数据变成了时间戳类型

    这是正在修改的 提交完之后 问题原因 问题原因是sqlite数据库对日期类型不敏感,Pycharm直接插入会变成图中这样的时间戳,用POST请求添加数据或Django自带的后台管理插入不会有这样的问题 ...