【尚硅谷】FreeRTOS学习笔记(更新中更新时间2024.10.12)

在网上看到的一段很形象的描述,放在这里给大家娱乐一下。

裸机开发:n个人拉屎,先进去一个拉完,下一个再来。
看门狗:如果有人拉完屎还占着,茅坑刷视频,把他拖出去
中断系统:n个人拉屎,先进去的拉完,下一个再来。如果有拉肚子的,当前在拉的人无条件让出茅坑,让拉肚子的先拉。
rtos:n个人拉屎,每个人拉10秒钟,憋住。下一个人拉,在轮询。拉肚子的可以先拉完不打断。
双核:两个茅坑
dma:拉屎的时候不用大脑操控阔约肌,由脊髓自动操控,使得大脑可以刷抖音
系统栈:就是每个人的肠胃,保存了每个人拉屎中断时剩余的屎量(当前状态的变量)
中断向量表:记录了每个人是否拉肚子和拉肚子的缓急情况,以此调度茅坑
rtos邮箱:正在拉屎的人告诉厕所管理员的信息。
局部变量:每个人肚子里剩余的屎,故拉完屎后,局部变量不再占用ram
全局变量:每个人往厕所门上贴的纸条,故全局变量地址不变且永远占据内存空间。
ram:公共厕所
阻塞式:如果一个人便秘,就一直占据茅坑,直到拉出来。
非阻塞:便秘的时候在茅坑外等待,想拉了再冲进去
flash:每个人初始的屎量,排队顺序。
ld文件:每个人家的地址和公厕的地址

1. 简介

1.1 RTOS简介

RTOS(实时操作系统)是指一类系统,如 FreeRTOS,uC/OS,RTX,RT-Thread 等,都是 RTOS 类操作系统。

FreeRTOS 由美国的 Richard Barry 于 2003 年发布。

FreeRTOS 于 2017 年被亚马逊收购,改名为 AWS FreeRTOS。

1.2 FreeRTOS优势

FreeRTOS 通过 MIT 开源许可免费分发,包括一个内核和一组不断丰富的 IoT 库,适用于所有行业领域。FreeRTOS 的构建突出可靠性和易用性

  • 开源和免费:FreeRTOS是一款开源的RTOS,采用MIT许可证发布,可以免费使用、修改和分发。

  • 轻量级设计:FreeRTOS注重轻量级设计,适用于资源受限的嵌入式系统,不占用过多内存和处理器资源。

  • 广泛应用:FreeRTOS在嵌入式领域得到广泛应用,包括工业自动化、医疗设备、消费电子产品、汽车电子等。

  • 多平台支持:FreeRTOS的设计注重可移植性,可以轻松地移植到不同的硬件平台,支持多种处理器架构。

  • 丰富的功能:提供了多任务调度、任务通信、同步等功能,适用于复杂的嵌入式应用场景。

简单来说:使用人数多,轻量级,开源免费,兼容性强,功能丰富

2. FreeRTOS基础知识

2.1 多任务处理

使用多任务操作系统可以简化原本复杂的软件应用程序的设计:

  • 操作系统的多任务处理和任务间通信功能允许将复杂的应用程序 划分为一组更小且更易于管理的任务。

  • 这种划分可以简化软件测试,确保团队分工明确,并促进代码复用。

  • 复杂的时序和排序细节将由 RTOS 内核负责,从而减轻了应用程序代码的负担。

简单来说:FreeRTOS可以帮助我们把复杂的项目细分为一小块一小块。

2.2 多任务处理与并发

常规单核处理器一次只能执行一个任务,但多任务操作系统可以快速切换任务, 使所有任务看起来像是同时在执行。下图展示了 三个任务相对于时间的执行模式。任务名称用不同颜色标示,并写在左侧。时间从左向右移动, 彩色线条显示在特定时间执行的任务。上方展示了所感知的并发执行模式, 下方展示了实际的多任务执行模式。

 简单理解:单核就是一个厕所,多核就是多个厕所。如果只有一个厕所,同一个时间只能一个人上厕所。但是想要同时上厕所,通过调度多人快速轮循上厕所看起来像是多人一起上厕所。

2.3 任务调度

由上可知想要看起来像多人上厕所,就需要一个管理人员负责调度,所以任务调度器就出现了。

调度器负责决定在任何特定时间哪个人去上厕所。

FreeRTOS 默认使用固定优先级抢占式调度策略,对同等优先级的任务执行时间片轮询调度:

  • 抢占式调度:FreeRTOS采用抢占式调度方式,允许更高优先级的任务在任何时刻抢占正在执行的低优先级任务。这确保了高优先级任务能够及时响应,并提高了系统的实时性。

  • 时间片轮询:在相同优先级的任务之间,FreeRTOS采用时间片轮转策略。每个任务执行一个时间片,如果有其他同优先级的任务等待执行,则切换到下一个任务。这有助于公平地分配CPU时间。

但是并不是说高优先级的任务会一直执行,导致低优先级的任务无法得到执行。如果高优先级任务等待某个资源(延时或等待信号量等)而无法执行,调度器会选择执行其他就绪的高优先级的任务。

简单理解:坐飞机的话分为vip通道和普通通道,你是vip说明你优先级高,你可以先上飞机。这就是抢占式调度。时间片轮训类似A,B,C三个人同时想上厕所(优先级相同),A先去拉一会(拉一个时间片)然后出出来,B再去拉一会(拉一个时间片)然后出出来,C再去拉一会(拉一个时间片)然后出出来,然后一直循环

2.4 任务状态

FreeRTOS中任务共存在4种状态:

  • 运行态:当任务实际执行时,它被称为处于运行状态。如果运行 RTOS 的处理器只有一个内核, 那么在任何给定时间内都只能有一个任务处于运行状态。注意在STM32中,同一时间仅一个任务处于运行态。

  • 就绪态:准备就绪任务指那些能够执行(它们不处于阻塞或挂起状态), 但目前没有执行的任务, 因为同等或更高优先级的不同任务已经处于运行状态。

  • 阻塞态:如果任务当前正在等待延时或外部事件,则该任务被认为处于阻塞状态。

  • 挂起态:类似暂停,调用函数 vTaskSuspend() 进入挂起态,需要调用解挂函数vTaskResume()才可以进入就绪态。

只有就绪态可转变成运行态,其他状态的任务想运行,必须先转变成就绪态。转换关系如下:

这四种状态中,除了运行态,其他三种任务状态的任务都有其对应的任务状态列表:

  • 就绪列表:pxReadyTasksLists[x],其中x代表任务优先级数值。

  • 阻塞列表:pxDelayedTaskList。

  • 挂起列表:xSuspendedTaskList。

  每种状态都维护了一个列表List(其实用的链表):
        就绪态是特殊的:
            ① 维护一个32bit的变量,对应优先级有任务,对应的bit位置1,方便查找;
            ② 每一个优先级,都单独维护了一个列表(链表),相同优先级的任务在同一个列表中

代码实现:如果想使用状态机实现一个音乐播放器

 char status = 'C';switch (status)case 'A' :播放处理;if(暂停按键) status = 'B';if(停止按键) status = 'C';case 'B' : 暂停处理;if(停止按键) status='C';if(播放按键) status='A';case 'C' : 停止处理;if(播放按键) status = 'A';

2.5 上下文切换

当一个任务执行时,它会利用处理器/微控制器寄存器,并像其他程序一样访问 RAM 和 ROM。这些资源(处理器寄存器,堆栈等)一起组成了任务执行上下文

一个任务在即将执行将两个处理器寄存器内包含的数值相加之前被挂起。 当该任务被挂起时,其他任务会执行,还可能会修改处理器寄存器的数值。恢复时, 该任务不会知道处理器寄存器已经被修改过了——如果它使用经修改过的数值, 那么求和会得到一个错误的数值。

为了防止这种类型的错误,任务恢复时必须有一个与挂起之前相同的上下文 。通过在任务挂起时保存任务的上下文,操作系统内核负责确保上下文保持不变。任务恢复时,其保存的上下文在执行之前由操作系统内核恢复。

保存被挂起的任务的上下文和恢复被恢复的任务的上下文的过程被称为 上下文切换

  • 将 TaskA在相应的处理器寄存器中的上下文保存到其任务堆栈中。

  • 将 TaskB 的上下文从其任务堆栈中恢复到相应的处理器寄存器中

在需要切换任务的时候进行上下文切换,真正执行上下文切换是在PendSV的ISR中处理的。

在FreeRTOS中有以下几个情况会触发PendSV异常产生切换:

  • RTOS 滴答中断:会处理就绪列表,判断是否要切换任务(包括抢占式、时间片轮转)。

  • 任务执行完毕:主动调用任务切换函数进行强制切换。

简单来说:切换上下文就是切换任务,任务之间相互转换到CPU的过程中难免会遇到变量名相同的情况,为保证数据的唯一性,采用上下文切换的方式进行更稳定保存现场。

3. FreeRTOS移植

3.1 简单介绍源码结构

3.1.1 源码下载地址

官网地址:FreeRTOS™ - FreeRTOS™

3.1.2 结构介绍    

所用FreeRTOS版本为202212.01版本

名称

描述

FreeRTOS

FreeRTOS内核

FreeRTOS-Plus

FreeRTOS组件,一般我们会选择使用第三方的组件

tools

工具

GitHub-FreeRTOS-Home

FreeRTOS的GitHub仓库链接

Quick_Start_Guide

快速入门指南官方文档链接

Upgrading-to-FreeRTOS-xxx

升级到指定FreeRTOS版本官方文档链接

History.txt

FreeRTOS历史更新记录

其他

其他

FreeRTOS文件夹结构

名称

描述

Demo

FreeRTOS演示例程,支持多种芯片架构、多种型号芯片

License

FreeRTOS相关许可

Source

FreeRTOS源码,最重要的文件夹

Test

公用以及移植层测试代码

Source文件夹结构如下

名称

描述

include

内包含了FreeRTOS的头文件

portable

包含FreeRTOS移植文件:与编译器相关、编译环境

croutine.c

协程相关文件

event_groups.c

事件相关文件

list.c

列表相关文件

queue.c

队列相关文件

stream_buffer.c

流式缓冲区相关文件

tasks.c

任务相关文件

timers.c

软件定时器相关文件

portable文件夹结构

FreeRTOS操作系统归根到底是一个软件层面的东西,需要跟硬件联系在一起,portable文件夹里面的东西就是连接桥梁。

3.2  FreeRTOS在基于HAL库项目中移植步骤

Demo的芯片型号为: STM32F103ZET6

开发环境为Clion+STMcubeMX

 创建案例只添加了以下功能:按钮与led灯

 3.2.1 目录添加源码文件

我们移植需要以下内核文件

接着我们点进portable  根据我们所用的开发环境进行选择

 最后一个就是FreeRTOSConfig.H文件我这里是复制了一个dome里面的栗子

完整就是这样

 另外我们还需要在CMakeLists.txt里把我们加进来的文件配置进去

3.2.2  系统配置文件修改

FreeRTOSConfig.h中添加如下3个配置:

#define xPortPendSVHandler  PendSV_Handler#define vPortSVCHandler     SVC_Handler#define INCLUDE_xTaskGetSchedulerState   1

3.2.3 修改stm32f1xx_it.c

​​​​​​引入头文件

/* Private includes ----------------------------------------------------------*//* USER CODE BEGIN Includes */#include "FreeRTOS.h"#include "task.h"/* USER CODE END Includes */

注释掉2个函数

// void SVC_Handler(void)// {// }// void PendSV_Handler(void)// {// }

添加SysTick时钟中断服务函数

/* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV */extern void xPortSysTickHandler(void);/* USER CODE END PV */void SysTick_Handler(void){/* USER CODE BEGIN SysTick_IRQn 0 *//* USER CODE END SysTick_IRQn 0 *//* USER CODE BEGIN SysTick_IRQn 1 */if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {xPortSysTickHandler();}/* USER CODE END SysTick_IRQn 1 */}/* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV */extern void xPortSysTickHandler(void);/* USER CODE END PV */void SysTick_Handler(void){/* USER CODE BEGIN SysTick_IRQn 0 *//* USER CODE END SysTick_IRQn 0 *//* USER CODE BEGIN SysTick_IRQn 1 */if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {xPortSysTickHandler();}/* USER CODE END SysTick_IRQn 1 */}

注意:HAL本身和FreeRTOS都默认依赖SysTick,可能出现卡死的问题。

为了保险起见,可以考虑在SYS选择HAL时钟源的时候换成其他的,并且中断优先级设为较高,比如1。(详情请看3.2 在cube里的配置)

3.3 系统配置文件说明

FreeRTOSConfig.h 配置文件作用:对FreeRTOS的功能进行配置和裁剪,以及API函数的使能等。

官网中文说明:定制 - FreeRTOS™

整体的配置项可以分为三类:

  • INCLUDE开头:一般是“INCLUDE_函数名”,函数的使能,1表示可用,0表示禁用。

  • config开头:FreeRTOS的一些功能配置,比如基本配置、内存配置、钩子配置、中断配置等。

  • 其他配置:PendSV宏定义、SVC宏定义。

#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H/* 头文件 */
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include <stdint.h>extern uint32_t SystemCoreClock;/* 基础配置项 */
#define configUSE_PREEMPTION                            1                       /* 1: 抢占式调度器, 0: 协程式调度器, 无默认需定义 */
#define configUSE_PORT_OPTIMISED_TASK_SELECTION         1                       /* 1: 使用硬件计算下一个要运行的任务, 0: 使用软件算法计算下一个要运行的任务, 默认: 0 */
#define configUSE_TICKLESS_IDLE                         0                       /* 1: 使能tickless低功耗模式, 默认: 0 */
#define configCPU_CLOCK_HZ                              SystemCoreClock         /* 定义CPU主频, 单位: Hz, 无默认需定义 */
//#define configSYSTICK_CLOCK_HZ                          (configCPU_CLOCK_HZ / 8)/* 定义SysTick时钟频率,当SysTick时钟频率与内核时钟频率不同时才可以定义, 单位: Hz, 默认: 不定义 */
#define configTICK_RATE_HZ                              1000                    /* 定义系统时钟节拍频率, 单位: Hz, 无默认需定义 */
#define configMAX_PRIORITIES                            32                      /* 定义最大优先级数, 最大优先级=configMAX_PRIORITIES-1, 无默认需定义 */
#define configMINIMAL_STACK_SIZE                        128                     /* 定义空闲任务的栈空间大小, 单位: Word, 无默认需定义 */
#define configMAX_TASK_NAME_LEN                         16                      /* 定义任务名最大字符数, 默认: 16 */
#define configUSE_16_BIT_TICKS                          0                       /* 1: 定义系统时钟节拍计数器的数据类型为16位无符号数, 无默认需定义 */
#define configIDLE_SHOULD_YIELD                         1                       /* 1: 使能在抢占式调度下,同优先级的任务能抢占空闲任务, 默认: 1 */
#define configUSE_TASK_NOTIFICATIONS                    1                       /* 1: 使能任务间直接的消息传递,包括信号量、事件标志组和消息邮箱, 默认: 1 */
#define configTASK_NOTIFICATION_ARRAY_ENTRIES           1                       /* 定义任务通知数组的大小, 默认: 1 */
#define configUSE_MUTEXES                               1                       /* 1: 使能互斥信号量, 默认: 0 */
#define configUSE_RECURSIVE_MUTEXES                     1                       /* 1: 使能递归互斥信号量, 默认: 0 */
#define configUSE_COUNTING_SEMAPHORES                   1                       /* 1: 使能计数信号量, 默认: 0 */
#define configUSE_ALTERNATIVE_API                       0                       /* 已弃用!!! */
#define configQUEUE_REGISTRY_SIZE                       8                       /* 定义可以注册的信号量和消息队列的个数, 默认: 0 */
#define configUSE_QUEUE_SETS                            1                       /* 1: 使能队列集, 默认: 0 */
#define configUSE_TIME_SLICING                          1                       /* 1: 使能时间片调度, 默认: 1 */
#define configUSE_NEWLIB_REENTRANT                      0                       /* 1: 任务创建时分配Newlib的重入结构体, 默认: 0 */
#define configENABLE_BACKWARD_COMPATIBILITY             0                       /* 1: 使能兼容老版本, 默认: 1 */
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS         0                       /* 定义线程本地存储指针的个数, 默认: 0 */
#define configSTACK_DEPTH_TYPE                          uint16_t                /* 定义任务堆栈深度的数据类型, 默认: uint16_t */
#define configMESSAGE_BUFFER_LENGTH_TYPE                size_t                  /* 定义消息缓冲区中消息长度的数据类型, 默认: size_t *//* 内存分配相关定义 */
#define configSUPPORT_STATIC_ALLOCATION                 0                       /* 1: 支持静态申请内存, 默认: 0 */
#define configSUPPORT_DYNAMIC_ALLOCATION                1                       /* 1: 支持动态申请内存, 默认: 1 */
#define configTOTAL_HEAP_SIZE                           ((size_t)(10 * 1024))   /* FreeRTOS堆中可用的RAM总量, 单位: Byte, 无默认需定义 */
#define configAPPLICATION_ALLOCATED_HEAP                0                       /* 1: 用户手动分配FreeRTOS内存堆(ucHeap), 默认: 0 */
#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP       0                       /* 1: 用户自行实现任务创建时使用的内存申请与释放函数, 默认: 0 *//* 钩子函数相关定义 */
#define configUSE_IDLE_HOOK                             0                       /* 1: 使能空闲任务钩子函数, 无默认需定义  */
#define configUSE_TICK_HOOK                             0                       /* 1: 使能系统时钟节拍中断钩子函数, 无默认需定义 */
#define configCHECK_FOR_STACK_OVERFLOW                  0                       /* 1: 使能栈溢出检测方法1, 2: 使能栈溢出检测方法2, 默认: 0 */
#define configUSE_MALLOC_FAILED_HOOK                    0                       /* 1: 使能动态内存申请失败钩子函数, 默认: 0 */
#define configUSE_DAEMON_TASK_STARTUP_HOOK              0                       /* 1: 使能定时器服务任务首次执行前的钩子函数, 默认: 0 *//* 运行时间和任务状态统计相关定义 */
#define configGENERATE_RUN_TIME_STATS                   0                       /* 1: 使能任务运行时间统计功能, 默认: 0 */
#if configGENERATE_RUN_TIME_STATS
#include "./BSP/TIMER/btim.h"
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()        ConfigureTimeForRunTimeStats()
extern uint32_t FreeRTOSRunTimeTicks;
#define portGET_RUN_TIME_COUNTER_VALUE()                FreeRTOSRunTimeTicks
#endif
#define configUSE_TRACE_FACILITY                        1                       /* 1: 使能可视化跟踪调试, 默认: 0 */
#define configUSE_STATS_FORMATTING_FUNCTIONS            1                       /* 1: configUSE_TRACE_FACILITY为1时,会编译vTaskList()和vTaskGetRunTimeStats()函数, 默认: 0 *//* 协程相关定义 */
#define configUSE_CO_ROUTINES                           0                       /* 1: 启用协程, 默认: 0 */
#define configMAX_CO_ROUTINE_PRIORITIES                 2                       /* 定义协程的最大优先级, 最大优先级=configMAX_CO_ROUTINE_PRIORITIES-1, 无默认configUSE_CO_ROUTINES为1时需定义 *//* 软件定时器相关定义 */
#define configUSE_TIMERS                                1                               /* 1: 使能软件定时器, 默认: 0 */
#define configTIMER_TASK_PRIORITY                       ( configMAX_PRIORITIES - 1 )    /* 定义软件定时器任务的优先级, 无默认configUSE_TIMERS为1时需定义 */
#define configTIMER_QUEUE_LENGTH                        5                               /* 定义软件定时器命令队列的长度, 无默认configUSE_TIMERS为1时需定义 */
#define configTIMER_TASK_STACK_DEPTH                    ( configMINIMAL_STACK_SIZE * 2) /* 定义软件定时器任务的栈空间大小, 无默认configUSE_TIMERS为1时需定义 *//* 可选函数, 1: 使能 */
#define INCLUDE_vTaskPrioritySet                        1                       /* 设置任务优先级 */
#define INCLUDE_uxTaskPriorityGet                       1                       /* 获取任务优先级 */
#define INCLUDE_vTaskDelete                             1                       /* 删除任务 */
#define INCLUDE_vTaskSuspend                            1                       /* 挂起任务 */
#define INCLUDE_xResumeFromISR                          1                       /* 恢复在中断中挂起的任务 */
#define INCLUDE_vTaskDelayUntil                         1                       /* 任务绝对延时 */
#define INCLUDE_vTaskDelay                              1                       /* 任务延时 */
#define INCLUDE_xTaskGetSchedulerState                  1                       /* 获取任务调度器状态 */
#define INCLUDE_xTaskGetCurrentTaskHandle               1                       /* 获取当前任务的任务句柄 */
#define INCLUDE_uxTaskGetStackHighWaterMark             1                       /* 获取任务堆栈历史剩余最小值 */
#define INCLUDE_xTaskGetIdleTaskHandle                  1                       /* 获取空闲任务的任务句柄 */
#define INCLUDE_eTaskGetState                           1                       /* 获取任务状态 */
#define INCLUDE_xEventGroupSetBitFromISR                1                       /* 在中断中设置事件标志位 */
#define INCLUDE_xTimerPendFunctionCall                  1                       /* 将函数的执行挂到定时器服务任务 */
#define INCLUDE_xTaskAbortDelay                         1                       /* 中断任务延时 */
#define INCLUDE_xTaskGetHandle                          1                       /* 通过任务名获取任务句柄 */
#define INCLUDE_xTaskResumeFromISR                      1                       /* 恢复在中断中挂起的任务 *//* 中断嵌套行为配置 */
#ifdef __NVIC_PRIO_BITS#define configPRIO_BITS __NVIC_PRIO_BITS
#else#define configPRIO_BITS 4
#endif#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY         15                  /* 中断最低优先级 */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    5                   /* FreeRTOS可管理的最高中断优先级 */
#define configKERNEL_INTERRUPT_PRIORITY                 ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY            ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_API_CALL_INTERRUPT_PRIORITY           configMAX_SYSCALL_INTERRUPT_PRIORITY/* FreeRTOS中断服务函数相关定义 */
#define xPortPendSVHandler                              PendSV_Handler
#define vPortSVCHandler                                 SVC_Handler/* 断言 */
#define vAssertCalled(char, int) printf("Error: %s, %d\r\n", char, int)
#define configASSERT( x ) if( ( x ) == 0 ) vAssertCalled( __FILE__, __LINE__ )/* FreeRTOS MPU 特殊定义 */
//#define configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS 0
//#define configTOTAL_MPU_REGIONS                                8
//#define configTEX_S_C_B_FLASH                                  0x07UL
//#define configTEX_S_C_B_SRAM                                   0x07UL
//#define configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY            1
//#define configALLOW_UNPRIVILEGED_CRITICAL_SECTIONS             1/* ARMv8-M 安全侧端口相关定义。 */
//#define secureconfigMAX_SECURE_CONTEXTS         5#endif /* FREERTOS_CONFIG_H */

3.4 FreeRTOS数据类型

不多说直接看源码

3.4.1 TickType_t

3.4.2 BaseType_t

3.4.3 UBaseType_t

3.4.4 StackType_t

4. FreeRTOS的任务如何创建和删除

4.1 任务创建和删除的函数API

任务的创建和删除本质就是调用FreeRTOS的API函数,主要如下:

API函数

描述

xTaskCreate()

动态方式创建任务

xTaskCreateStatic()

静态方式创建任务

vTaskDelete()

删除任务

 由于静态方式创建极其麻烦而且开发不经常用到这里不做演示。

4.1.1 动态创建任务

步骤:

  1. 将宏configSUPPORT_DYNAMIC_ALLOCATION 配置为  1

  2. 直接调用入口参数并填写参数

  3. 编写任务函数

 4.1.2任务创建函数参数介绍

源码图如上

TaskHandle_t xTaskCreateStatic

(

    TaskFunction_t pxTaskCode,          /* 指向任务函数的指针 */

    const char * const pcName,          /* 任务函数名 可直接传入字符串 */

    const uint32_t ulStackDepth,        /* 任务堆栈大小,单位是4字节 */

    void * const pvParameters,          /* 传递的任务函数参数   */

    UBaseType_t uxPriority,             /* 任务优先级 */

    StackType_t * const puxStackBuffer, /* 任务堆栈,一般为数组,由用户分配 */

    StaticTask_t * const pxTaskBuffer   /* 任务控制块指针,由用户分配 */

)

4.1.3 任务删除函数

 直接调用传参为任务函数指针

简单理解:所谓创建任务,可以简单理解为你买了一个基金并把钱交给基金管理,所谓任务删除就是你不想继续在把钱放到这个基金里想取出来就要提交申请,但是取出来是需要流程的,但是你的收益会立刻结束,删除任务也是一样,你会立刻结束任务函数里的功能,但是不会立刻删除,会先假删,知道空闲任务时再真删。

5. FreeRTOS的任务挂起与恢复

5.1任务的挂起与恢复的API函数

  • vTaskSuspend():挂起任务, 类似暂停,可恢复

  • vTaskResume():恢复被挂起的任务

  • xTaskResumeFromISR():在中断中恢复被挂起的任务

以上图片节选自官方文档任务控制 - FreeRTOS™

5.2 挂起与恢复调度器

  • vTaskSuspendAll():挂起任务调度器,调度器不会进行任务切换,当前任务一直运行。

  • xTaskResumeAll():恢复任务调度器,调度器继续任务切换。

以上节选自内核控制 - FreeRTOS™

5.3 小实验:

任务清单

start_task:用来创建其他的三个任务。
task1:实现LED1每500ms闪烁一次。
task2:实现LED2每500ms闪烁一次。
task3:判断按键按下逻辑,KEY1按下,挂起task1,按下KEY2在任务中恢复task1,KEY3按下,挂起调度器,KEY4按下,恢复调度器,并打印任务的状态。

实现代码

//
// Created by seven on 2024/10/9.
//#include "FreeRTOS_Dome1.h"
#include "FreeRTOS.h"
#include "task.h"#include "stdio.h"#include "LED.h"
#include "Key.h"#define TASK1_PRIORITY 2
#define TASK2_PRIORITY 3
#define TASK3_PRIORITY 4char buffer[30];
TaskHandle_t start_task_handle = NULL;void vStartTask(void *pvParameters);TaskHandle_t task1_handle = NULL;void vDomeTask1(void *pvParameters);TaskHandle_t task2_handle = NULL;void vDomeTask2(void *pvParameters);TaskHandle_t task3_handle = NULL;void vDomeTask3(void *pvParameters);//start_task:用来创建其他的三个任务。
//task1:实现LED1每500ms闪烁一次。
//task2:实现LED2每500ms闪烁一次。
//task3:判断按键按下逻辑,KEY1按下,挂起task1,按下KEY2在任务中恢复task1,KEY3按下,挂起调度器,KEY4按下,恢复调度器,并打印任务的状态。/*** 实现LED1每500ms闪烁一次。* @param pvParameters*/
void vDomeTask1(void *pvParameters) {while (1) {printf("我要翻转LED1了!!!\r\n");LED_Toggle(LED1_Pin);vTaskDelay(500);}
}/*** 实现LED2每500ms闪烁一次* @param pvParameters*/
void vDomeTask2(void *pvParameters) {while (1) {printf("我要翻转LED2了!!!\r\n");LED_Toggle(LED2_Pin);vTaskDelay(500);}
}/*** 判断按键按下逻辑,KEY1按下,挂起task1,按下KEY2在任务中恢复task1,* KEY3按下,挂起调度器,KEY4按下,恢复调度器,并打印任务的状态。* @param pvParameters*/
void vDomeTask3(void *pvParameters) {uint8_t key;while (1) {key = Key_Detect();if (key == KEY1_PRESS) {if (task1_handle != NULL) {printf("我要挂起黄色灯光闪烁任务!!\r\n");vTaskSuspend(task1_handle);}} else if (key == KEY2_PRESS) {if (task1_handle != NULL) {printf("我要恢复黄色灯光闪烁任务!!\r\n");vTaskResume(task1_handle);}} else if (key == KEY3_PRESS) {if (task1_handle != NULL) {printf("我要挂起调度器!!\r\n");vTaskSuspendAll();}} else if (key == KEY4_PRESS) {if (task1_handle != NULL) {printf("我要恢复调度器!!\r\n");xTaskResumeAll();}}vTaskDelay(500);}
}void vStartTask(void *pvParameters) {printf("我已成功进入start_task!!!!\r\n");taskENTER_CRITICAL();printf("进入临界区:保护代码不被打断!!\r\n");xTaskCreate(vDomeTask1,"task1",200,NULL,TASK1_PRIORITY,&task1_handle);xTaskCreate(vDomeTask2,"task2",200,NULL,TASK2_PRIORITY,&task2_handle);xTaskCreate(vDomeTask3,"task3",200,NULL,TASK3_PRIORITY,&task3_handle);printf("任务创建全部创建完成!!!\r\n");vTaskDelete(start_task_handle);printf("删除当前任务完成!!!\r\n");taskEXIT_CRITICAL();printf("启动完毕!!!\r\n");
}void vDomeInit(void) {xTaskCreate(vStartTask,"start_task",200,NULL,1,&start_task_handle);//启用调度器printf("我要启动调度器!!!!\r\n");vTaskStartScheduler();printf("调度器启动成功!!!!\r\n");
}

6. FreeRTOS中断管理

在STM32中,中断优先级是通过中断优先级配置寄存器的高4位 [7:4] 来配置的。因此STM32支持最多16级中断优先级,其中数值越小表示优先级越高,即更紧急的中断。(FreeRTOS任务调度的任务优先级相反,是数值越大越优先)

在中断服务函数中调用FreeRTOS的API函数需注意:

  • 中断服务函数的优先级需在FreeRTOS所管理的范围内,阈值由configMAX_SYSCALL_INTERRUPT_PRIORITY指定。

  • 建议将所有优先级位指定为抢占优先级位,方便FreeRTOS管理。

  • 在中断服务函数里边需调用FreeRTOS的API函数,必须使用带“FromISR”后缀的函数。

6.1 FreeRTOS的开关中断

FreeRTOS 开关中断函数在 portmacro.h 中有定义,如下:

调用portENABLE_INTERRUPTS() 它, FreeRTOS会打开管理的所有中断

调用portDISABLE_INTERRUPTS() 它, FreeRTOS会关闭管理的所有中断

6.2 FreeRTOS的临界区代码

  • taskENTER_CRITICAL() :进入临界段。

  • taskEXIT_CRITICAL() :退出临界段。

  • taskENTER_CRITICAL_FROM_ISR() :进入临界段(中断级)。

  • taskEXIT_CRITICAL_FROM_ISR():退出临界段(中断级)。

简而言之:被临界区包括在内的代码就会优先执行不会因为执行中间被打断

6.3 挂起和恢复任务调度器

  • vTaskSuspendAll():挂起任务调度器。

  • xTaskResumeAll():恢复任务调度器。

与临界区不同的是,挂起任务调度器时未关闭中断;这种方式仅仅防止了任务之间的资源争夺,中断仍然可以直接响应;挂起调度器的方法适用于临界区位于任务与任务之间的情况;这样既不需要延迟中断,同时又能确保临界区的安全性。

7. FreeRTOS任务相关API函数

7.1 FreeRTOS任务相关API函数介绍

函数

描述

uxTaskPriorityGet()

获取任务优先级

vTaskPrioritySet()

设置任务优先级

uxTaskGetNumberOfTasks()

获取系统中任务的数量

uxTaskGetSystemState()

获取所有任务状态信息

vTaskGetInfo()

获取指定单个的任务信息

xTaskGetCurrentTaskHandle()

获取当前任务的任务句柄

xTaskGetHandle()

根据任务名获取该任务的任务句柄

uxTaskGetStackHighWaterMark()

获取任务的任务栈历史剩余最小值

eTaskGetState()

获取任务状态

vTaskList()

以“表格”形式获取所有任务的信息

vTaskGetRunTimeStats()

获取任务的运行时间

 详情了解官网:任务实用程序 - FreeRTOS™

8. FreeRTOS时间管理

8.1 延时函数介绍

  • vTaskDelay():相对延时。从执行vTaskDelay()函数开始,直到指定延时的时间结束。

  • xTaskDelayUntil():绝对延时。将整个任务的运行周期视为一个整体,适用于需要以固定频率定期执行的任务。

假设有一个定时器,每隔1秒触发一次,希望在每次触发时执行某个任务。如果使用 vTaskDelay 来实现,那么你只能实现任务每秒执行一次,而不能确保任务在每秒的开始时刻执行。但如果你使用 xTaskDelayUntil,你可以指定任务在每秒的开始时刻执行,即使任务执行的时间不同。

简单理解:相对延时就是大概延迟那么久,绝对延时就是一定延时那么久。一般情况下不会那么严谨,安全除外。一般用vTaskDelay() 

9. FreeRTOS消息队列

9.1 队列简介

队列是任务间通信的主要形式。 它们可以用于在任务之间以及中断和任务之间发送消息。

队列是线程安全的数据结构,任务可以通过队列在彼此之间传递数据。有以下关键特点:

  • FIFO顺序:队列采用先进先出 (FIFO) 的顺序,即先发送的消息会被先接收。

  • 线程安全:队列操作是原子的,确保在多任务环境中的数据完整性。

  • 阻塞和非阻塞操作:任务可以通过阻塞或非阻塞的方式发送和接收消息。如果队列满了或者为空,任务可以选择等待直到有空间或者数据可用,或者立即返回。

  • 优先级继承:FreeRTOS 支持基于优先级的消息传递,确保高优先级任务在队列操作期间不会被低优先级任务阻塞。

  • 可变长度项:队列中的项可以是不同长度的数据块,而不是固定大小。

使用队列,任务可以通过发送消息来共享信息,从而更好地协调和同步系统中的不同部分。

9.2 队列相关API函数介绍

9.2.1 ​​​​​​创建队列

函数

描述

xQueueCreate()

动态方式创建队列

xQueueCreateStatic()

静态方式创建队列

动态创建队列时,FreeRTOS会在运行时从其内置的堆中为队列分配所需的内存空间。这种方式更加灵活,允许系统根据需要动态调整内存。

相反,静态创建队列要求用户在编译时手动为队列分配内存,而不依赖于FreeRTOS的堆管理。这使得内存的分配在编写代码时就能确定,因此在资源受限或对内存使用有严格要求的嵌入式系统中可能更为合适。

总体而言,动态创建提供了更大的灵活性,但可能会增加堆管理的复杂性。静态创建则更为直观,适用于在编译时就能确定内存分配的情况。选择使用哪种方式通常取决于系统的需求和设计考虑。

简而言之:不太熟练就用动态简单好用

9.2.2 往队列写入消息

函数

描述

xQueueSend()

往队列的尾部写入消息

xQueueSendToBack()

同 xQueueSend()

xQueueSendToFront()

往队列的头部写入消息

xQueueOverwrite()

覆写队列消息(只用于队列长度为 1 的情况)

xQueueSendFromISR()

在中断中往队列的尾部写入消息

xQueueSendToBackFromISR()

同 xQueueSendFromISR()

xQueueSendToFrontFromISR()

在中断中往队列的头部写入消息

xQueueOverwriteFromISR()

在中断中覆写队列消息(只用于队列长度为 1 的情况)

9.2.3 从队列读取消息

函数

描述

xQueueReceive()

从队列头部读取消息,并删除消息

xQueuePeek()

从队列头部读取消息

xQueueReceiveFromISR()

在中断中从队列头部读取消息,并删除消息

xQueuePeekFromISR()

在中断中从队列头部读取消息

由于一个一个测试截图工作量过于复杂,过完国庆时间很难挤,所以详细测试截图可能会留在以后发布,得点个关注么么哒!!!

10. 信号量

10.1 信号量的简介

FreeRTOS中的信号量是一种用于任务间同步和资源管理的机制。信号量可以是二进制的(只能取0或1)也可以是计数型的(可以是任意正整数)。信号量的基本操作包括“获取”和“释放”。

比如动车上的卫生间,一个卫生间同时只能容纳一个人,由指示灯来表示是否有人在使用。当我们想使用卫生间的时候,有如下过程:

  • 判断卫生间是否有人使用(判断信号量是否有资源)

  • 卫生间空闲(信号量有资源),那么就可以直接进入卫生间(获取信号量成功)

  • 卫生间使用中(信号量没有资源),那么这个人可以选择不上卫生间(获取信号量失败),也可以在门口等待(任务阻塞)

信号量与队列的区别如下:

信号量

队列

主要用于管理对共享资源的访问,确保在同一时刻只有一个任务可以访问共享资源

用于任务之间的数据通信,通过在任务之间传递消息,实现信息的传递和同步。

可以是二进制信号量(Binary Semaphore)或计数信号量(Counting Semaphore)

存储和传递消息的数据结构,任务可以发送消息到队列,也可以从队列接收消息。

适用于对资源的互斥访问,控制任务的执行顺序,或者限制同时访问某一资源的任务数量。

适用于在任务之间传递数据,实现解耦和通信。

 二值信号量相关函数:

函数

描述

xSemaphoreCreateBinary()

使用动态方式创建二值信号量

xSemaphoreCreateBinaryStatic()

使用静态方式创建二值信号量

xSemaphoreGive()

释放信号量

xSemaphoreGiveFromISR()

在中断中释放信号量

xSemaphoreTake()

获取信号量

xSemaphoreTakeFromISR()

在中断中获取信号量

 简单理解:二值信号量可以理解为是否通电,没通电就是不能用,通电就能。

更新日期:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/447629.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2-2 数据库的介绍

无处不在的数据库 信息化社会&#xff0c;无处不在的就是数据。数据包括数据的存储和数据的计算&#xff0c;编程语言可以归纳为数据计算的一类。 数据库就是存储数据的一个库。 数据库如何存储数据&#xff1f; 可以分为3个层级&#xff0c;分别是库&#xff0c;表&#x…

一、el-table的滚动条加粗并解决遮挡内容问题

近期接到产品提的需求&#xff0c;反馈用户说table里面的滚动条过小&#xff0c;不方便拖动&#xff0c;希望加粗&#xff0c;然后我就研究了下如何加粗&#xff0c;发现加粗后会导致遮挡内容的问题&#xff0c;并予以解决。以下是实现和解决的方法和步骤。 先看看官网的滚动条…

JavaScript 入门

1. HTML、CSS、JavaScript 之间的关系 HTML&#xff1a;网页的结构&#xff08;骨&#xff09; CSS&#xff1a;网页的表现&#xff08;皮&#xff09; JavaScript&#xff1a;网页的行为&#xff08;魂&#xff09; 2. 引入方式 3种引入方式&#xff0c;语法如下&#xff…

安科瑞ARB5弧光保护在船舶中压配电板中的应用-安科瑞黄安南

摘要&#xff1a;船舶中压配电板弧光故障导致的设备损坏和停电事故&#xff0c;不仅会造成较大的经济损失&#xff0c;而且严重影响船舶电站的安全稳定运行&#xff0c;威胁船舶电站操作人员的安全。弧光保护是基于电力系统开关柜发生弧光故障时而设计的一套母线保护系统&#…

AMD新推EPYC与MI325X,挑战英伟达AI市场地位

在人工智能&#xff08;AI&#xff09;加速器领域&#xff0c;AMD近日于美国旧金山举办的“推进人工智能”&#xff08;Advancing AI Event&#xff09;活动中&#xff0c;宣布了一系列新产品的发布&#xff0c;直接对标英伟达&#xff0c;意图在AI芯片市场占据更大份额。 AMD新…

10.14学习日志

一.矩阵 接上篇 11.伴随矩阵 设 A 是一个 nn 的方阵&#xff0c;其元素为 aij。伴随矩阵 adj(A)或A* 是一个 nn的矩阵&#xff0c;其第 i 行第 j 列的元素是 A 的余子式 Mji 的代数余子式 Cji&#xff0c;即&#xff1a; 其中 Mji是 A 的第j 行第i 列元素的余子式&#xff0…

从零开始使用最新版Paddle【PaddleOCR系列】——第一部分:文本检测和识别模型的环境安装与基础使用

目录 一、环境安装配置 1.基本环境配置&#xff1a;torch与paddlepaddle安装 2.专精任务配置&#xff1a;PaddleX与PaddleOCR插件安装 3.测试数据配置&#xff1a;测试数据集下载与验证 二、模型基础使用 1.使用OCR模型预测 ​ 2.使用Detect检测模型 ​ 3.使用…

【AI论文精读5】知识图谱与LLM结合的路线图-P2

【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】 P1 4 知识图谱增强的LLMs 大语言模型&#xff08;LLMs&#xff09;在许多自然语言处理任务中取得了令人期待的结果。然而&#xff0c;LLMs因缺乏实用知识和在推理过程中容易产生事实性错误而受到批评。为了解决这个问题…

JVM系列(八) -运行期的几种优化技术

一、摘要 在之前的文章中我们谈到过&#xff0c;相比 C/C 语言&#xff0c;Java 语言在运行效率方面要稍逊一些&#xff0c;因为 Java 应用程序是在虚拟机上运行&#xff0c;而 C/C 程序是直接编译成平台相应的机器码来运行程序。 从虚拟机对外发布开始&#xff0c;开发团队一…

interwirelessac9560感叹号,电脑无法连接wifi,无法搜索到wifi

interwirelessac9560感叹号 电脑无法连接wifi&#xff0c;无法搜索到wifi 原因 这可能是wifl模块出现了问题。 解决方案 1、winx 打开&#xff0c;选择【设备管理器】 2、选择网络适配器 右键打开wireless-AC&#xff0c;选择【卸载设备】。 3、关机2分钟后&#xff0c…

JavaWeb 15.详解Servlet及其源码

目录 一、Servlet简介 1.动态资源和静态资源 静态资源 动态资源 生活举例 动/静态资源的响应过程 2.Servlet简介 Servlet执行流程 Servlet作用 二、Servlet开发流程 1.目标 2.开发流程 3.问题 ① Servlet-api.jar导入问题 ② Content-Type响应头的问题 4.Servlet_url-pattern的…

ORACLE SELECT INTO 赋值为空,抛出 NO DATA FOUND 异常

例子&#xff1a; DECLARE ORDER_NUM VARCHAR2(20); BEGIN SELECT S.ORDER_NUM INTO ORDER_NUM FROM SALES_ORDER S WHERE S.ID122344; DBMS_OUTPUT.PUT_LINE(单号: || ORDER_NUM); END; 在查询结果为空的情况下&#xff0c;以上代码会报错&#xff1a;未找到任何数据 解决方…

体育直播系统定制怎么做?掌握这些架构功能设计方案!

要开发一个高效、便捷、又充满乐趣的体育直播平台&#xff0c;我们需要依托先进的技术架构和丰富的功能模块&#xff0c;以提供卓越的观赛体验。如下参考“东莞梦幻网络科技”预先开发的体育直播系统源码&#xff0c;他们采用了哪些技术和模块来提升用户体验。 一、系统架构&am…

Redis-02 数据持久化

redis持久化即将数据从内存写入磁盘&#xff0c;Redis提供了两种持久化的方式&#xff1a;RDB和AOF。 1.RDB RDB持久化&#xff1a;Redis可以将内存中的数据定期快照保存到磁盘上的一个二进制文件中。RDB持久化是一种比较紧凑的文件格式&#xff0c;适用于备份和灾难恢复。通过…

【氮化镓】低温对p-GaN HEMT迁移率、阈值电压和亚阈值摆幅的影响

本期分享一篇低温对p-GaN HEMT 迁移率、阈值电压和亚阈值摆幅影响进行表征和建模的研究论文。文章作者Shivendra Kumar Singh、Thien Sao Ngo、Tian-Li Wu(通讯作者)和Yogesh Singh Chauhan,分别来资源中国台湾阳明交通大学国际半导体技术学院、印度理工学院坎普尔分校电气工…

爬虫之数据解析

数据解析 数据解析这篇内容, 很多知识涉及到的都是以前学习过的内容了, 那这篇文章我们主要以实操为主, 来展开来讲解关于数据解析的内容。 360搜索图片 请求的url大家不需要再找了, 相信大家都会找请求了, 寻找请求从我的第一篇爬虫的博客开始到现在一直都在写,这边的话, 我已…

CSS 图标和文本对齐

比如下面一段HTML代码&#xff0c;我们想在图标旁边显示文本或者数字 <body> <div><img src"smile.svg" alt"smile"><span>12</span></div> <div><img src"heartShape.svg" alt"…

光伏仿真系统在光伏项目开发中有哪些应用场景?

光伏仿真系统在光伏项目开发中的应用场景广泛&#xff0c;涵盖了从项目规划、设计优化到运维管理的全过程。 一、项目规划与选址 1、气象模拟与评估 光伏仿真系统能够基于历史气象数据和先进的预测模型&#xff0c;模拟不同地理位置、不同季节和时间段的光照强度、温度、湿度…

Fomality基础知识

formal主要用于等价性检查&#xff0c;检查步骤如下&#xff1a; 1.Read design 2.Set up 3.Matching&#xff1a;Map corresponding signals between pairs of designs 4.Verification&#xff1a;Compare the logic cones that drive the mapped signals 二&#xff1a;…

Hadoop生态圈三大组件:HDFS的读写流程、MapReduce计算流程、Yarn资源调度

文章目录 1. HDFS的读写流程1.1 HDFS读流程1.2 HDFS写流程 2. MapReduce计算流程3. Yarn资源调度一、客户端请求资源二、Resource Manager处理请求三、任务资源计算与申请四、Resource Manager分配资源五、Node Manager执行任务六、任务执行与监控 1. HDFS的读写流程 1.1 HDFS…