diff --git "a/\345\265\214\345\205\245\345\274\217-\346\216\247\345\210\266\347\273\204/1.1 STM32F407-\345\212\237\350\203\275\345\274\200\345\217\221/STM32F407-\350\265\204\346\226\231/FreeRTOS \344\273\273\345\212\241.md" "b/\345\265\214\345\205\245\345\274\217-\346\216\247\345\210\266\347\273\204/1.1 STM32F407-\345\212\237\350\203\275\345\274\200\345\217\221/STM32F407-\350\265\204\346\226\231/FreeRTOS \344\273\273\345\212\241.md" deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git "a/\345\265\214\345\205\245\345\274\217-\346\216\247\345\210\266\347\273\204/1.1 STM32F407-\345\212\237\350\203\275\345\274\200\345\217\221/STM32F407-\350\265\204\346\226\231/FreeRTOS \345\237\272\347\241\200.md" "b/\345\265\214\345\205\245\345\274\217-\346\216\247\345\210\266\347\273\204/1.1 STM32F407-\345\212\237\350\203\275\345\274\200\345\217\221/STM32F407-\350\265\204\346\226\231/FreeRTOS \345\237\272\347\241\200.md" deleted file mode 100644 index 61fc2c7536b0e9d99ed01db4d2946d5e12317955..0000000000000000000000000000000000000000 --- "a/\345\265\214\345\205\245\345\274\217-\346\216\247\345\210\266\347\273\204/1.1 STM32F407-\345\212\237\350\203\275\345\274\200\345\217\221/STM32F407-\350\265\204\346\226\231/FreeRTOS \345\237\272\347\241\200.md" +++ /dev/null @@ -1,278 +0,0 @@ -``` - 学习历史记录 2024.11.12 -【正点原子】P9 -基于FreeRTOS v10.4.6版本 - -``` - -# FreeRTOS 源码 - -```c -1.FreeRTOS 官网:https://www.freertos.org/ - -2.源码文件解析 -FreeRTOS : FreeRTOS内核 -FreeRTOS-Plus :FreeRTOS组件 -tool :工具 - -3.FreeRTOS 内核 -Demo : FreeRTOS 演示历程 -License :FreeRTOS 相关许可文件 -Source : FreeRTOS源码 -Test : 公用以及移植测试代码 - -4.Demo 文件 -支持多种芯片架构 -支持多种不同芯片型号 - -5.Source 文件 -include : 内包含了FeeRTOS的头文件 4 -portable : 内包含了FreeRTOS的移植文件 5 -croutine.c : 协程相关文件 -even_groups.c : 事件相关文件 -list.c : 列表相关文件 1 -queue.c : 队列相关文件 2 -stream_buffer.c : 流式缓冲区相关文件 -tasks.c : 任务相关文件 3 -timers.c : 软件定时器相关文件 - -6.portable 文件 -由于是使用MDK开发,是负责软硬件协调的连接桥梁 -keil : 指向RVDS文件 -MemMang :内存管理文件 -RVDS : 不同内核芯片的移植文件 - -``` - -# FreeRTOS 移植 - -实现FreeRTOS的移植 - -``` -1.添加FreeRTOS源码 :将FreeRTOS 源码添加到基础工程、头文件路径中 - -2.FreeRTOSConfig.h :添加FreeRTOSConfig.h配置文件 - -3.修改SYSTEM文件 :sys.c 、delay.c 、usart.c - -4.修改中断相关文件 :Systick中断 、SVC 中断 、PendSV中断 - -5.添加应用程序 :验证是否成功 - -``` - -# FreeRTOS 系统配置文件 - -## FreeRTOSConfig.h - -FreeRTOSConfig.h 配置文件作用:对FreeRTOS进行功能配置和裁剪,以及API函数的使能与失能 - -FreeRTOSConfig.h 文件中可以定义这些API函数的使能或失能,使能就宏定义为1、失能就为0 -列: -#define INCLUDE_vTaskDelete 1 -#define configUSE_PREEMPTION 1 - -``` -#if ( configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H == 1 ) - - #include "freertos_tasks_c_additions.h" - - #ifdef FREERTOS_TASKS_C_ADDITIONS_INIT - static void freertos_tasks_c_additions_init( void ) - { - FREERTOS_TASKS_C_ADDITIONS_INIT(); - } - #endif - -#endif /* if ( configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H == 1 ) */ - -代码介绍:如果在FreeRTOS.h文件中通过宏定义将configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H == 1,就会编译这个函数也就是使能这个函数进行调用。 -``` - -## ''INLUDE_''开始宏 - -``` -#if ( INCLUDE_vTaskDelete == 1 ) - - static void prvDeleteTCB( TCB_t * pxTCB ) PRIVILEGED_FUNCTION; - -#endif - -代码介绍:如果在FreeRTOS.h文件中通过宏定义将INCLUDE_vTaskDelete==1,就会编译这个函数也就是使能这个函数进行调用。 - -``` - -## 宏定义 - -``` -#include <> :包含一个源代码文件 -#define :定义宏 -#undef :取消已定义的宏 -#if :如果给定条件为真,则编译下面的代码 -#ifdef :如果宏已经定义,则编译下面代码 -#ifndef :如果宏没有定义,则编译下面代码 -#elif :如果前面的#if给定条件不为真,当前条件为真,则编译下面代码 -#endif :结束一个#if.....#else条件编译块 - -``` - -## 栈 - -``` - 栈(Stack)是内存中的一个数据结构,它的核心特点是“后进先出”(Last In First Out, LIFO)。栈通常分为两个部分:栈顶(Top)和栈底(Bottom)。每当有新的数据需要存储时,会把它推入栈顶,称为“压栈”;而从堆栈中取出数据时,则是从栈顶开始弹出,称为“出栈”。 -``` - -# FreeRTOS 任务基础知识 - - - -# FreeRTOS 中断配置 - -## Cortex-M 中断 - -``` -1.Cortex-M内核的MCU提供了一个用于中断管理的嵌套向量中断控制器(NVIC) - -2.Cortex-M3和M4的NVIC最多支持240个IRQ(中断请求)、1个不可以屏蔽中断(NMI)、1个Systick(滴答定时器)定时器中断和多个系统异常。 - -3.中断嵌套 - 高优先级的中断可以抢占低优先级的中断 - -4.优先级分组 - Cortex-M3中每个中断源都有两级优先级: -抢占式优先级(pre-emption priority) -子优先级(subpriority),子优先级也叫响应式优先级。 - - 当两个相同的抢占式优先级同时来时,先处理响应式优先级高的(谁优先级高先响应谁) - - 当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断,这个后到来的中断就要等到前一个中断处理完之后才能被处理。 - - 如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断表中的排位顺序决定先处理哪一个 - - 每个中断源都需要被指定这两种优先级,如果用一个字节(8位)表示中断源,将这8位划分成两部分,用前几位表示抢占式优先级,后几位表示响应式优先级,可以有有8种分配方式,如下: - -1. 所有8位用于指定响应优先级 -2. 最高1位用于指定抢占式优先级,最低7位用于指定响应优先级 -3. 最高2位用于指定抢占式优先级,最低6位用于指定响应优先级 -4. 最高3位用于指定抢占式优先级,最低5位用于指定响应优先级 -5. 最高4位用于指定抢占式优先级,最低4位用于指定响应优先级 -6. 最高5位用于指定抢占式优先级,最低3位用于指定响应优先级 -7. 最高6位用于指定抢占式优先级,最低2位用于指定响应优先级 -8. 最高7位用于指定抢占式优先级,最低1位用于指定响应优先级 - -第0组:所有4位用于指定响应优先级 -第1组:最高1位用于指定抢占式优先级,最低3位用于指定响应优先级 -第2组:最高2位用于指定抢占式优先级,最低2位用于指定响应优先级 -第3组:最高3位用于指定抢占式优先级,最低1位用于指定响应优先级 -第4组:所有4位用于指定抢占式优先级 - - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); - * @arg NVIC_PriorityGroup_0: 0 bits for pre-emption priority抢占 - * 4 bits for subpriority响应 - * @arg NVIC_PriorityGroup_1: 1 bits for pre-emption priority抢占 - * 3 bits for subpriority响应 - * @arg NVIC_PriorityGroup_2: 2 bits for pre-emption priority抢占 - * 2 bits for subpriority响应 - * @arg NVIC_PriorityGroup_3: 3 bits for pre-emption priority抢占 - * 1 bits for subpriority响应 - * @arg NVIC_PriorityGroup_4: 4 bits for pre-emption priority抢占 - * 0 bits for subpriority响应 - -5.固定优先级 - Cortex-M 处理器中有些中断是固定优先级的,比如复位、NMI等这些中断优先级都是负数的,优先级也是最高的 - - Cortex-M 处理器有三个固定优先级和 256 个可编程的优先级,最多有 128 个抢占等级,但是实际的优先级数量是由芯片厂商来决定的。 - -6.********* 重要 ********* - 4个相临的字节寄存器可以拼成一个32位的寄存器,因此地址0xE000 ED20~0xE000 ED23 这四个寄存器就可以拼接成一个地址为 0xE000 ED20 的 32 位寄存器。这一点很重要!因为 FreeRTOS 在设置 PendSV 和 SysTick 的中断优先级的时候都是直接操作的地址 0xE000 ED20。 - -7.中断屏蔽特殊寄存器PRIMASK、FAULTMASK、BASEPRI -PRIMASK : - 在许多应用中,需要暂时屏蔽所有的中断一执行一些对时序要求严格的任务,这个时候就可以使用 PRIMASK 寄存器,PRIMASK 用于禁止除 NMI和 HardFalut 外的所有异常和中断,汇编编程的时候可以使用 CPS(修改处理器状态)指令修改 PRIMASK 寄存器的数值: -CPSIE I //清除 PRIMASK(使能中断) -CPSID I //设置 PRIMASK(禁止中断) - -BASEPRI: - 注意!FreeRTOS 的开关中断就是操作 BASEPRI寄存器来实现的!它可以关闭低于某个阈值的中断,高于这个阀值的中断就不会被关闭! -``` - -## FreeRTOS 中断配置宏 - -``` -1.configPRIO_BITS - 此宏用来设置MCU使用几位优先级,STM32使用的是4位,因此此宏为4 - -2.configLIBRARY_LOWEST_INTERRUPT_PRIORITY - 此宏是用来设置最低优先级,前面说了,STM32优先级使用了4位,而且STM32 配置的使用组 4,也就是4位都是抢占优先级。因此优先级数就是16个,最低优先级那就是15。所以此宏就是 15,注意!不同的 MCU 此值不同,具体是多少要看所使用的 MCU 的架构,本教程只针对 STM32 讲解! - -3.ConfgKERNEL_INTERRUPT_PRIORITY - 此宏用于设置内核中断优先级,设置PendSV和滴答定时器的中断优先级,Port.c文件中有 -PendSV 和 SysTick 优先级是在哪里设置的呢?在函数 xPortStartScheduler()中设置,此函数在文件 port.c中,函数如下: -BaseType_t xPortStartScheduler( void ) - -4.ConfgLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY - 此宏用来设置 FreeRTOS 系统可管理的最大优先级,也就是我们在 4.1.5 小节中讲解BASEPRI寄存器说的那个值优先级,这个大家可以自由设置,这里我设置为了5。也就是高于5的优先级(优先级数小于 5)不归 FreeRTOS 管理! - -5.ConfgMAX_SYSCALL_INTERRUPT_PRIORITY ==5 - 低于此优先级的中断可以安全的调用 FreeRTOS 的 API函数,高于此优先级的中断 FreeRTOS 是不能禁止的,中断服务函数也不能调用 FreeRTOS 的 API 函数! -``` - -## FreeRTOS 开关中断 - -``` -1.在portmacro.h查找这几个函数 - 函数 vPortSetBASEPRI()用于设置BASEPRI寄存器的值,开中断时传递0,即vPortSetBASEPRI(0),结果就是开中断; - vPortRaiseBASEPRI()设置为configMAX_SYSCALL_INTERRUPT_PRIORITY,屏蔽低于此优先级的中断。 -``` - -# FreeRTOS 临界段代码 - -``` - 临界段代码也叫做临界区,是指那些必须完整运行,不能被打断的代码段,比如有的外设的初始化需要严格的时序,初始化过程中不能被打断。FreeRTOS 在进入临界段代码的时候需要关闭中断,当处理完临界段代码以后再打开中断。FreeRTOs 系统本身就有很多的临界段代码,这些代码都加了临界段代码保护,我们在写自己的用户程序的时候有些地方也需要添加临界段代码保护。 - -1.freertos有关临界代码保护的函数有4个在task.h中查找宏定义 -taskENTER_CRITICAL() -taskEXIT_CRITICAL() -taskENTER_CRITICAL_FROM_ISR() -taskEXIT_CRITIXAL_FROM_ISR() - -2.任务级临界段代码保护 - takENTER_CRITICAL()和 tasKEXIT_CRITICAL()是任务级的临界代码保护(在task.h中),一个是进入临界段,一个是退出临界段,这两个函数是成对使用的。vPortEnterCritical()和vPortExitCritical()在port.c文件中。 - 可以看到在进入函数 vPortEnterCritical()以后会首先关闭中断,然后给变量 uxCriticalNesting加一,uxCriticalNesting 是个全局变量,用来记录临界段嵌套次数的。函数 vPortExitCritical()是退出临界段调用的,函数每次将 uxCriticalNesting 减一,只有当 uxCriticalNesting为0的时候才会调用函数 pOrENABLE INTERRUPTS()使能中断。这样保证了在有多个临界段代码的时候不会因为某一个临界段代码的退出而打乱其他临界段的保护,只有所有的临界段代码都退出以后才会使能中断! - -3.创建任务级临界代码保护函数 -void taskcritical_test(void) -{ - while(1) - { - taskENTER_CRITICAL();//(1) - total_num+=0.01f; - printf("total_num的值为:%4f\r\n",total_num); - taskEXIT_CRITICAL();//(2) - vTaskDelay(1000); - } - -} - -(1)进入临界区 -(2)退出临界区 -(1)和(2)中间的代码就是临界区代码,注意临界区代码一定要精简!因为进入临界区会关闭中断,这样会导致优先级低于 confgMAX_SYSCALL_INTERRUPT_PRIORITY 的中断得不到及时的响应! - - -4.中断级临界段代码保护 -//定时器3中断服务函数 -void TIM3_IROHandler(void) -{ - if(TIM_GetITStatus(TIM3,TIM_IT_Update)--SET) //溢出中断 - { - status_value=taskENTER_CRITICAL_FROM_ISRO;//(1)进入临界区 - total_num+=l: - printf("float num 的值为: %drn",total num); - taskEXIT_CRITICAL_FROM_ISR(status value);//(2)退出临界区 - } - TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除中断标志位 -} - - -``` - diff --git "a/\345\265\214\345\205\245\345\274\217-\346\216\247\345\210\266\347\273\204/1.1 STM32F407-\345\212\237\350\203\275\345\274\200\345\217\221/STM32F407-\350\265\204\346\226\231/FreeRTOS \351\230\237\345\210\227.md" "b/\345\265\214\345\205\245\345\274\217-\346\216\247\345\210\266\347\273\204/1.1 STM32F407-\345\212\237\350\203\275\345\274\200\345\217\221/STM32F407-\350\265\204\346\226\231/FreeRTOS \351\230\237\345\210\227.md" deleted file mode 100644 index b6c8471bd517f50f28f0d37dd2ad69eba10e45f0..0000000000000000000000000000000000000000 --- "a/\345\265\214\345\205\245\345\274\217-\346\216\247\345\210\266\347\273\204/1.1 STM32F407-\345\212\237\350\203\275\345\274\200\345\217\221/STM32F407-\350\265\204\346\226\231/FreeRTOS \351\230\237\345\210\227.md" +++ /dev/null @@ -1,906 +0,0 @@ -# FreeRTOS Queues - -官网文档链接 [xQueueOverwriteFromISR - FreeRTOS™](https://www.freertos.org/zh-cn-cmn-s/Documentation/02-Kernel/04-API-references/06-Queues/12-xQueueOverwriteFromISR) - -队列是任务间通信的主要形式。它们可以用于在任务之间 以及中断和任务之间发送消息。在大多数情况下,队列用作线程安全的 FIFO(先进先出)缓冲区, 新数据被发送到队列的后面,但也可以发送到前面。 - -## 入队演示 - -队列为空 - -![1742449513039](C:\Users\Pavan\AppData\Roaming\Typora\typora-user-images\1742449513039.png) - -TaskA向队列发送数据1(第一个发送到头部) - -![1742449623258](C:\Users\Pavan\AppData\Roaming\Typora\typora-user-images\1742449623258.png) - -TaskA向队列尾部发送数据2 - -![1742449750484](C:\Users\Pavan\AppData\Roaming\Typora\typora-user-images\1742449750484.png) - -![1742449809181](C:\Users\Pavan\AppData\Roaming\Typora\typora-user-images\1742449809181.png) - -TaskA向队列尾部发送数据3 - -![1742449854520](C:\Users\Pavan\AppData\Roaming\Typora\typora-user-images\1742449854520.png) - -![1742449910948](C:\Users\Pavan\AppData\Roaming\Typora\typora-user-images\1742449910948.png) - - - -## 出队演示 - -TaskB向队列头部接收数据1,xQueueReceive()接收结束后清徐数据1 - -![1742450001450](C:\Users\Pavan\AppData\Roaming\Typora\typora-user-images\1742450001450.png) - -TaskB向队列头部接收数据2,xQueueReceive()接收结束后清徐数据2 - -![1742450025592](C:\Users\Pavan\AppData\Roaming\Typora\typora-user-images\1742450025592.png) - -TaskB向队列头部接收数据3,xQueueReceive()接收结束后清徐数据3 - -![1742450223072](C:\Users\Pavan\AppData\Roaming\Typora\typora-user-images\1742450223072.png) - -最后队列为空 - -![1742449513039](C:\Users\Pavan\AppData\Roaming\Typora\typora-user-images\1742449513039.png) - - - -## 阻塞队列 - -队列API函数允许指定阻塞时间。 - -- 当一个任务试图从一个空队列中读取时,该队列将 进入阻塞状态(因此它不会消耗任何 CPU 时间,且其他任务可以运行) 直到队列中的数据变得可用,或者阻塞时间过期。 -- 当一个任务试图写入到一个满队列时,该队列将 进入阻塞状态(因此它不会消耗任何 CPU 时间,且其他任务可以运行) 直到队列中出现可用空间,或者阻塞时间过期。 -- 如果同一个队列上有多个处于阻塞状态的任务, 那么具有最高优先级的任务将最先解除阻塞。 - -# 队列API函数 - -## 队列存储消息数 - -**uxQueueMessagesWaiting** - -```c -//queue.h -UBaseType_t uxQueueMessagesWaiting( QueueHandle_t xQueue ); -``` - -返回队列中存储的消息数 - -参数: - -- xQueue - - 正在查询队列句柄 - -返回: - -- 队列中可用的消息数 - - - -## 队列存储消息数(中断) - -**uxQueueMessagesWaitingFromISR** - -```c -//queue.h -UBaseType_t uxQueueMessagesWaiting( QueueHandle_t xQueue ); -``` - -返回队列中存储的消息数 - -参数: - -- xQueue - - 正在查询队列句柄 - -返回: - -- 队列中可用的消息数 - - - -## 队列可用空间数 - -**uxQueueSpacesAvailable** - -```c -//queue.h -UBaseType_t uxQueueSpacesAvailable( QueueHandle_t xQueue ); -``` - -返回队列中的可用空间数。 - -**参数:** - -- xQueue - - 正在查询的队列的句柄。 - -**返回:** - -- 队列中可用的可用空间数 - - - -## 删除队列 - -**vQueueDelete** - -```c -void vQueueDelete( QueueHandle_t xQueue ); -``` - -删除队列 — 释放分配用于存储放置在队列中的项目的所有内存。 - -**参数:** - -- *xQueue* - - 要删除的队列的句柄。 - - - -## 队列重置 - -**xQueueReset** - -```c -BaseType_t xQueueReset( QueueHandle_t xQueue ); -``` - -将队列重置为其原始的空状态。 - -**参数:** - -- xQueue - - 正在重置的队列的句柄。 - -**返回:** - -- 因为 FreeRTOS V7.2.0,`xQueueReset()`总是返回 pdPASS。 - - - -## 查询队列是否已空(中断) - -**xQueueIsQueueEmptyFromISR** - -```c -BaseType_t xQueueIsQueueEmptyFromISR( const QueueHandle_t pxQueue ); -``` - -查询队列以确定队列是否为空。此函数只能用于 ISR。 - -**参数:** - -- *xQueue* - - 正在查询的队列的句柄 - -**返回:** - -- 如果队列不为空,则返回 pdFALSE; -- 如果队列为空,则返回 pdTRUE。 - - - -## 查询队列是否已满(中断) - -**xQueueIsQueueFullFromISR** - -```c -BaseType_t xQueueIsQueueFullFromISR( const QueueHandle_t pxQueue ); -``` - -查询队列以确定队列是否已满。此函数只能用于 ISR。 - -**参数:** - -- *xQueue* - - 正在查询的队列的句柄。 - -**返回:** - -- 如果队列未满,则返回 pdFALSE; -- 如果队列已满,则返回 pdTRUE。 - - - -## 从队列的句柄中查找队列名称。 - -**pcQueueGetName** - -```c -const char *pcQueueGetName( QueueHandle_t xQueue ) -``` - -从队列的句柄中查找队列名称。 - -队列只有添加到[队列注册表](https://www.freertos.org/Documentation/02-Kernel/04-API-references/06-Queues/15-vQueueAddToRegistry)时才有名称。 - -**参数:** - -- *xQueue* - - 正在查询的队列的句柄。 - -**返回:** - -如果 xQueue 引用的队列在队列注册表中, 则返回队列的文本名称,否则返回 NULL。 - - - -## 为队列指定名称并将其添加到注册表 - -**vQueueAddToRegistry** - -```c -void vQueueAddToRegistry( - QueueHandle_t xQueue, - char *pcQueueName, - ); - -``` - -为队列指定名称,并将队列添加到注册表。 - -**参数:** - -- *xQueue* - - 添加到注册表的队列的句柄。 - -- *pcQueueName* - - 为队列指定的名称。此为文本字符串,仅为便于调试之用。队列注册表 仅存储指向该字符串的指针,因此该字符串必须具有持久性(全局变量, 或最好是在 ROM/Flash 中),而不是在堆栈上定义。 - -队列注册表有两项用途,都与 RTOS 内核感知调试相关: - -1. 可将文本名称和队列关联,以在调试 GUI 中轻松识别队列。 -2. 包含调试器定位每个已注册队列和信号量所需的信息。 - -队列注册表仅在使用 RTOS 内核感知调试器时才有作用。 - -`configQUEUE_REGISTRY_SIZE`定义可以注册的队列和信号量的最大数量。 仅需注册那些要使用 RTOS 内核感知调试器查看的队列和信号量。 - - - -## 从队列注册表中移除队列 - -**vQueueUnregisterQueue** - -```c - void vQueueUnregisterQueue( QueueHandle_t xQueue ); -``` - -从队列注册表中移除队列。 - -**参数:** - -- xQueue - - 要从注册表中移除的队列的句柄。 - -队列注册表有两个用途,都与 RTOS 内核感知调试相关: - -1. 可将文本名称和队列关联,以在调试 GUI 中轻松识别队列。 -2. 提供调试器定位每个已注册队列和信号量所需的信息。 - -队列注册表仅在使用 RTOS 内核感知调试器时才有作用。 - -`configQUEUE_REGISTRY_SIZE` 定义可以注册的队列和信号量的最大数量。 仅需注册那些要使用 RTOS 内核感知调试器查看的队列和信号量。 - -# 队列入队方式 - -队列是任务间通信的主要形式。它们可以用于在任务之间 以及中断和任务之间发送消息。在大多数情况下,队列用作线程安全的 FIFO(先进先出)缓冲区, 新数据被发送到队列的后面,但也可以发送到前面。 - -```c -static void prvCopyDataFromQueue( Queue_t * const pxQueue, - void * const pvBuffer ) -``` - -## FIFO(先进先出) - - 当队列入队采用后向入队时(往队列发送数据时,发送到队列尾部,从队列读取数据时,从队列头部读取) ,与排队相似。 - -排队的特点: - -排在前面的先服务,后面来的人都要排在后面,等待前面的人服务完后才轮到自己。 - -## LIFO(后进先出) - -当队列入队采用前向入队时(往队列发送数据时,发送到队列头部,从队列读取数据时,从队列头部读取) ,与栈相似。 - -栈的特点: - -后进先出(LIFO):最后进栈的元素将首先出栈,类似于将子弹放压进弹匣的顶部,取子弹时总是从顶部开始。 - -# 队列初始化与复位 - -## prvInitialiseNewQueue() - -```c -static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength,//队列长度 - const UBaseType_t uxItemSize,//队列项目长度 - uint8_t * pucQueueStorage,//队列项目存储区 - const uint8_t ucQueueType,//队列类型 - Queue_t * pxNewQueue )//队列结构体 -``` - - - -## xQueueGenericReset() - -```c -BaseType_t xQueueGenericReset( - QueueHandle_t xQueue,//队列句柄 - BaseType_t xNewQueue // - ) -``` - - 该函数是用于重置队列,那么既然重置队列了,必然会将队列中的队列项都清空,将队列中的一些指针也都恢复成最初状态 。 - -**参数:** - -- xQueue - - 需要复位的队列句柄 - -- xNewQueue - - 为pdFALSE :表明队列不是第一次初始化,只复位队列即可。 - 为pdTRUE :表明队列是第一次初始化是新队列。 - -# 队列的通用函数 - -在队列中的创建队列以及入队或者出队的API函数本质上都是这些通用函数在起作用。 - -## xQueueGenericCreate() - -动态 - -```c -QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, - const UBaseType_t uxItemSize, - const uint8_t ucQueueType ) -``` - -静态 - -```c -QueueHandle_t xQueueGenericCreate - ( - const UBaseType_t uxQueueLength, - const UBaseType_t uxItemSize, - uint8_t *pucQueueStorage, - StaticQueue_t *pxStaticQueue, - const uint8_t ucQueueType - ) -``` - -- uxQueueLength:队列项数目 -- uxItemSize:每个队列项的大小 -- pucQueueStorage:使用静态分配队列时才使用,指向定义队列存储空间,如果使用动态分配队列空间(默认),向这个参数传递NULL。 -- pxStaticQueue:使用静态分配队列时才使用,指向队列控制结构体,如果使用动态分配队列空间(默认),向这个参数传递NULL。 - - ucQueueType:类型。可能的值为: - - queueQUEUE_TYPE_BASE:表示队列 - - queueQUEUE_TYPE_SET:表示队列集合 - - queueQUEUE_TYPE_MUTEX:表示互斥量 - - queueQUEUE_TYPE_COUNTING_SEMAPHORE:表示计数信号量 - - queueQUEUE_TYPE_BINARY_SEMAPHORE:表示二进制信号量 - - queueQUEUE_TYPE_RECURSIVE_MUTEX :表示递归互斥量 - -​ 初始化后的队列项内存 - -​ ![img](https://i-blog.csdnimg.cn/blog_migrate/ac1e7606e7d3e1f3a133b70d5e95147f.png) - - - -## xQueueGenericSend() - -```c -BaseType_t xQueueGenericSend - ( - QueueHandle_t xQueue, - const void * const pvItemToQueue, - TickType_t xTicksToWait, - const BaseType_t xCopyPosition - ) -``` - -- xQueue:队列句柄 -- pvItemToQueue:指针,指向要入队的项目 -- xTicksToWait:如果队列满,等待队列空闲的最大时间,如果队列满并且xTicksToWait被设置成0,函数立刻返回。时间单位为系统节拍时钟周期,宏portTICK_PERIOD_MS可以用来辅助计算真实延时值。如果INCLUDE_vTaskSuspend设置成1,并且指定延时为portMAX_DELAY将引起任务无限阻塞(没有超时)。 -- xCopyPosition:入队位置,可以选择从队列尾入队、从队列首入队和覆盖式入队。 - -​ 通用入队操作流程图 - -​ ![img](https://i-blog.csdnimg.cn/blog_migrate/365b0c658f33161a64830964d29616ae.png) - - - -## xQueueGenericSendFromISR () - - 这个函数用于入队,用于中断服务程序中。根据参数的不同,可以从队列尾入队、从队列首入队也可以覆盖式入队。覆盖式入队用于只有一个队列项的场合,入队时如果队列已满,则将之前的队列项覆盖掉。 - -```c -BaseType_t xQueueGenericSendFromISR - ( - QueueHandle_t xQueue, - const void * const pvItemToQueue, - BaseType_t * const pxHigherPriorityTaskWoken, - const BaseType_t xCopyPosition - ) -``` - -- xQueue:队列句柄。 - -- pvItemToQueue:指针,指向要入队的项目。 - -- pxHigherPriorityTaskWoken:如果入队导致一个任务解锁,并且解锁的任务优先级高于当前运行的任务,则该函数将*pxHigherPriorityTaskWoken设置成pdTRUE。如果xQueueSendFromISR()设置这个值为pdTRUE,则中断退出前需要一次上下文切换。从FreeRTOS V7.3.0起,pxHigherPriorityTaskWoken称为一个可选参数,并可以设置为NULL。 - - - -- xCopyPosition:入队位置,可以选择从队列尾入队、从队列首入队和覆盖式入队。 - - - - - -## xQueueGenericReceive() - -```c -BaseType_t xQueueGenericReceive( - QueueHandle_t xQueue, - void * pvBuffer, - TickType_t xTicksToWait, - BaseType_t xJustPeek - ) -``` - -**参数:** - -- xQueue - - 队列句柄 - -- pvBuffer - - 保存数据的缓存区,将拷贝的数据保存在这个缓存区中。 - -- xTicksToWait - - 阻塞时间,此参数指示当队列满的时候任务进入阻塞态等待队列空闲的最大时间。如果为0的话当队列满的时候就立即返回;当为portMAX DELAY 的话就会一直等待,直到队列有空闲的队列项,也就是死等,但是宏INCLUDE vTaskSuspend 必须为1。 - -- xJustPeek - - 标记当读取成功以后是否删除掉队队列项,当为pdTRUE 的时候就不用删除,也就是说你后面再调用函数xQueueReceive()获取的队列项是一样的。当为pdFALSE 的时候就会删除掉这个队列项。 - -**返回值:** - -- pdTRUE :从队列中读取数据成功。 -- pdFASLE 从队列中读取数据失败。 - - - - - - - -# 创建队列 - - - -## **xQueueCreate** - -```c - QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, - UBaseType_t uxItemSize ); - -``` - -创建新[队列](https://www.freertos.org/Documentation/02-Kernel/02-Kernel-features/02-Queues-mutexes-and-semaphores/01-Queues/)并返回一个可以引用该队列的 句柄。[configSUPPORT_DYNAMIC_ALLOCATION]() 必须在 FreeRTOSConfig.h 中设置为 1,或保留为未定义状态(默认为 1), 才可使用此 RTOS API 函数。 - -每个队列都需要 RAM 来保存队列状态 以及队列中包含的项目(队列存储区)。 - -如果使用 xQueueCreate()创建队列,则所需的 RAM 会自动 从FreeRTOS 堆中分配; - -如果使用xQueueCreateStatic()创建队列,则RAM由程序编写者提供,这会产生更多的参数,但能够在编译时静态分配RAM。 - - - -**参数:** - -- *uxQueueLength* - - 队列一次可存储的最大项目数。 - -- *uxItemSize* - - 存储队列中每个项目所需的大小(以字节为单位)。 - - 项目通过复制而非引用的方式入队,因此该参数值是每个入队项目将复制的 字节数。队列中的每个项目必须具有相同的大小。 - -**返回:** - -- 如果队列创建成功,则返回所创建队列的句柄。如果创建队列所需的内存无法分配,则返回 NULL。 - - - -## **xQueueCreateStatic** - -```c - QueueHandle_t xQueueCreateStatic( - UBaseType_t uxQueueLength, - UBaseType_t uxItemSize, - uint8_t *pucQueueStorageBuffer, - StaticQueue_t *pxQueueBuffer ); -``` - - 如果使用 xQueueCreateStatic() 创建队列, 则 RAM 由应用程序编写者提供,这会产生更多的参数, 但这样能够在编译时静态分配 RAM 。 - -**参数:** - -- *uxQueueLength* - - 队列一次可存储的最大项目数。 - -- *uxItemSize* - - 存储队列中每个项目所需的大小(以字节为单位)。 - - 项目通过复制而非引用的方式入队,因此该参数值是每个入队项目将复制的 字节数。队列中的每个项目必须具有相同的大小。 - -- *pucQueueStorageBuffer* - - 如果 uxItemSize 不为零,则 pucQueueStorageBuffer 必须指向一个 uint8_t 数组,该数组的大小 至少要能容纳队列中最多可能存在的项目的总字节数, 即 ( uxQueueLength * uxItemSize ) 字节。如果 uxItemSize 为零,则 pucQueueStorageBuffer 可以为 NULL。 - -- *pxQueueBuffer* - - 必须指向 StaticQueue_t 类型的变量,该变量将用于保存队列的数据结构体。 - -**返回:** - -如果队列创建成功, 则返回所创建队列的句柄。如果 pxQueueBuffer 为 NULL,则返回 NULL。 - - - -# 任务级入队函数 - -![1742464306226](C:\Users\Pavan\AppData\Roaming\Typora\typora-user-images\1742464306226.png) - -### xQueueSend() - -```c - BaseType_t xQueueSend( - QueueHandle_t xQueue, - const void * pvItemToQueue, - TickType_t xTicksToWait - ); -``` - -### xQueueSendToBack - -```c - BaseType_t xQueueSendToBack( - QueueHandle_t xQueue, - const void * pvItemToQueue, - TickType_t xTicksToWait - ); - -``` - -### xQueueSendToFront - -```c - BaseType_t xQueueSendToFront( - QueueHandle_t xQueue, - const void * pvItemToQueue, - TickType_t xTicksToWait - ); -``` - -​ 这三个函数都是用于向队列中发送消息的,这三个函数本质都是宏,其中函数`xQueueSend()`和 `xQueueSendToBack()`是一样的,都是后向入队,即将新的消息插入到队列的后面。函数`xQueueSendToToFront()`是前向入队,即将新消息插入到队列的前面。然而!这三个函数最后都是调用的同一个函数:`xQueueGenericSend()`。这三个函数只能用于任务函数中,不能用于中断服务函数,中断服务函数有专用的函数,它们以“FromISR”结尾。 - -**参数:** - -​ xQueue:队列句柄 - -​ pvItemToQueue:指向要发送的信息。 - -​ xTicksToWait : 阻塞时间,此参数指示当队列满的时候任务进入阻塞态等待队列空闲的最大时间。如果为0的话当队列满的时 候就立即返回;当为portMAX DELAY 的话就会一直等待,直到队列有空闲的队列项,也就是死等,但是宏 INCLUDE vTaskSuspend 必须为1. - -**返回值:** - - pdPASS : 向队列发送成功。 - -errQUEUE_FULLL :队列已满,消息发送失败。 - -### xQueueOverwrite - -``` - BaseType_t xQueueOverwrite( - QueueHandle_t xQueue, - const void * pvItemToQueue - ); - -``` - -此函数也是用于向队列发送数据的,当队列满了以后会覆写掉旧的数据,不管这个旧数据有没有被其他任务或中断取走。这个函数常用于向那些长度为1的队列发送消息,此函数也是一个宏,最终调用的也是函数 xQueueGenericSend()。 - -**参数:** - -xQueue:队列句柄。 - -pvItemToQueue:指向发送的消息。 - -**返回值:** - -pdPASS: 向队列发送消息成功,此函数也只会返回 pdPASS!因为此函数执行过程中不在乎队列满不满,满了的话我就覆写掉旧的数据,总之肯定能成功。 - - - -# 中断级入队函数 - -![1742464343699](C:\Users\Pavan\AppData\Roaming\Typora\typora-user-images\1742464343699.png) - -### xQueueSendFromISR - -```c - BaseType_t xQueueSendFromISR - ( - QueueHandle_t xQueue, - const void *pvItemToQueue, - BaseType_t *pxHigherPriorityTaskWoken - ); -``` - -### xQueueSendToBackFromISR - -````c - BaseType_t xQueueSendToBackFromISR - ( - QueueHandle_t xQueue, - const void *pvItemToQueue, - BaseType_t *pxHigherPriorityTaskWoken - ); -```` - -### xQueueSendToFrontFromISR - -```c - BaseType_t xQueueSendToFrontFromISR - ( - QueueHandle_t xQueue, - const void *pvItemToQueue, - BaseType_t *pxHigherPriorityTaskWoken - ); - -``` - -​ 这三个函数也是向队列中发送消息,都是只能用于中断服务函数中。这三个函数本质上是宏,其中函数xQueueSendFromISR()和 xQueueSendToBackFromISR()是一样的,都是后向入队,即将新的消息插入队列的后面。函数xQueueSendToFrontFromISR()是前向入队(压栈),即将新的消息插入队列的前面。 - - - -**参数:** - -- xQueue : 队列句柄 - -- pvItemToQueue :指向要发送的消息 - -- pxHigherPriorityTaskWoken :标记退出此函数以后是否进行任务切换,这个变量的值由这三个函数来设置的,用户不用 - - ​ 进行设置,用户只需要提供一个变量来保存这个值就可以了。当此值为pdTRUE的时候 退出中断服务函数之一定进程一次任务切换。 - -**返回值:** - -pdTRUE : 向消息队列发送成功。 - -errQUEUE_FULL : 队列已满,消息发送失败。 - - - -**注意:他们都没设置阻塞的时间值,原因是其他是在中断服务函数中调到的不是在任务中,没有这个说法。** - - - -### xQueueOverwriteFromISR - -```c -BaseType_t xQueueOverwrite -( - QueueHandle_t xQueue, - const void * pvItemToQueue - BaseType_t *pxHigherPriorityTaskWoken -); - -``` - -这个函数是xQueueOverwrite()的中断级版本,用在中断服务函数中,在队列满的时候自动覆写掉旧的数据的。 - -**参数:** - -- xQueue : 队列句柄 - -- pvItemToQueue :指向要发送的消息 - -- pxHigherPriorityTaskWoken :标记退出此函数以后是否进行任务切换,这个变量的值由这三个函数来设置的,用户不用 - - ​ 进行设置,用户只需要提供一个变量来保存这个值就可以了。当此值为pdTRUE的时候 退出中断服务函数之一定进程一次任务切换。 - -**返回值:** - -pdTRUE : 向消息队列发送成功。 - -errQUEUE_FULL : 队列已满,消息发送失败。 - - - - - - - - - - - -# 任务级出队函数 - -![1742464501017](C:\Users\Pavan\AppData\Roaming\Typora\typora-user-images\1742464501017.png) - -## xQueueReceive - -```c -BaseType_t xQueueReceive( - QueueHandle_t xQueue, - void *pvBuffer, - TickType_t xTicksToWait - ); - -``` - - 这是用于调用 `xQueueGenericReceive()` 函数的宏。 从队列中接收项目。该项目通过复制接收,因此必须提供足够大小的缓冲区。 创建队列时定义了复制到缓冲区中的字节数。 - -**参数:** - -- xQueue - - 要从中接收项目的队列的句柄。 - -- pvBuffer - - 指向要将所接收项目复制到缓冲区的指针。 - -- xTicksToWait - - 阻塞时间,此参数指示当队列满的时候任务进入阻塞态等待队列空闲的最大时间。如果为0的话当队列满的时候就立即返回;当为portMAX DELAY 的话就会一直等待,直到队列有空闲的队列项,也就是死等,但是宏INCLUDE vTaskSuspend 必须为1. - -**返回值:** - -- pdTRUE : 表示接收成功。 -- pdFALSE : 表示接收失败。 -- errQUEUE_EMPTY :表示队列为空时接收失败。 - -## xQueuePeek - -```c - BaseType_t xQueuePeek( - QueueHandle_t xQueue, - void *pvBuffer, - TickType_t xTicksToWait - ); - -``` - - 这是一个调用 xQueueGenericReceive() 函数的宏。 - - 从队列中接收项目,而无须从队列中删除该项目。 项目由副本接收,因此必须提供适当大小的缓冲区 。队列创建时,复制到缓冲区中的字节数已定义 。 - - 成功接收的项目仍在队列中,因此将由下一次调用再次返回 或 xQueueReceive () 调用。 - -**参数:** - -- xQueue - - 要从中接收项目的队列的句柄。 - -- pvBuffer - - 指向缓冲区的指针,接收到的项目将被复制到这个缓冲区。它必须至少足够大, 才能容纳创建队列时定义的队列项的大小。 - -- xTicksToWait - - 如果在调用时队列为空, 则任务应阻塞等待项目接收的最长时间。时间是以滴答周期为单位定义的,因此如果需要,应使用常量 portTICK_PERIOD_MS 转换为实时。 - 如果 INCLUDE_vTaskSuspend设置为 “1”,则将阻塞时间指定为 portMAX_DELAY 会导致任务 无限期地阻塞(没有超时) - - - -# 中断级出队函数 - -![1742464560250](C:\Users\Pavan\AppData\Roaming\Typora\typora-user-images\1742464560250.png) - -### xQueueReceiveFromISR - -```c - BaseType_t xQueueReceiveFromISR - ( - QueueHandle_t xQueue, - void *pvBuffer, - BaseType_t *pxHigherPriorityTaskWoken - ); -``` - -此函数是xQueueRecerve()的中断版本,用于在中断服务函数中读取一条消息,读取成功以后就会将队列中的这条数据删除。此函数在读取消息时采用拷贝的方式,所以需要用户提供一个数组或者缓存区来保存读取到的数据,所读的数据长度是创建队列时候所设定的每个队列项目的长度。 - -**参数:** - -- xQueue : 队列句柄 - -- pvBuffer:保存数据的缓存区,读取队列的过程中会将读取到的数据拷贝到这个缓冲区。 - -- pxHigherPriorityTaskWoken :标记退出此函数以后是否进行任务切换,这个变量的值由这三个函数来设置的,用户不用 - - ​ 进行设置,用户只需要提供一个变量来保存这个值就可以了。当此值为pdTRUE的时候 退出中断服务函数之一定进程一次任务切换。 - -**返回值:** - -- pdTRUE : 向消息队列发送成功。 - -- errQUEUE_FULL : 队列已满,消息发送失败。 - - - -### xQueuePeekFromISR - -```c -BaseType_t xQueuePeekFromISR( - QueueHandle_t xQueue, - void *pvBuffer, - ); - -``` - -从队列中接收项目,而无须从队列中删除该项目。 项目通过复制的方式接收,因此必须提供 足够大的缓冲区。复制到缓冲区中的字节数 在创建队列时即已定义。 - -成功接收的项目将保留在队列中,因此会在下一次调用时返回, 或者在调用任何队列接收函数时返回。 - -**参数:** - -- xQueue - - 要从中接收项目的队列的句柄。 - -- pvBuffer - - 指向缓冲区的指针,接收的项目将复制到此缓冲区。缓冲区的大小必须至少足够 存储在创建队列时定义的队列项目的大小。 - -**返回:** - -- 如果成功从队列中接收(窥视)项目,则返回 pdTRUE, -- 否则返回 pdFALSE。 - - - - - -# 队列上锁与解锁 - -队列的上锁和解锁操作是用来保护队列数据的完整性和一致性的。 - -当一个任务要向队列发送数据时,首先需要对队列进行上锁操作。这是为了防止其他任务同时访问队列,从而导致数据的错误读写。在上锁期间,其他任务无法访问队列,直到上锁任务完成发送操作并解锁队列。 - -类似地,当一个任务要从队列接收数据时,也需要对队列进行上锁操作。这是为了保证在接收数据的过程中,队列中的数据不会被其他任务修改。在上锁期间,其他任务无法修改队列中的数据,直到上锁任务完成接收操作并解锁队列。 - -通过对队列进行上锁和解锁操作,可以确保在多任务环境下,队列的数据操作是安全和可靠的。 - -两个API函数 -prvLockQueue() - -prvUnLockQueue() \ No newline at end of file