传奇背景图网站怎么做,合肥工程建设信息平台,网站成品作业,企业网站优化咨询提示#xff1a;文章写完后#xff0c;目录可以自动生成#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、队列是什么#xff1f;而在freertos中#xff0c;队列是什么呢#xff1f;①如果要进行中断、任务的交流#xff0c;那我用全局变量行吗#xff1f;②… 提示文章写完后目录可以自动生成如何生成可参考右边的帮助文档 文章目录 前言一、队列是什么而在freertos中队列是什么呢①如果要进行中断、任务的交流那我用全局变量行吗②那为什么队列就可以代替全局变量的功能呢③看一看在freertos中队列的结构④问题当多个任务写入消息给一个“满队列”时这些任务都会进入阻塞状态也就是说有多个任务 在等待同一 个队列的空间。那当队列中有空间时哪个任务会进入就绪态⑤数据写队列、读队列操作过程 二、队列的结构体1.结构体内容2.结构体示意图 三、队列相关API函数介绍1.创建队列2.写消息入队列(1)前三个函数2后面的函数 3.从队列中读取消息代码例子 四、队列入队和出队操作实验1.实验目标2.例程①main.c②freertos_demo();③操作队列和存储大数据块④任务一实现入队⑤任务二小数据出队⑥任务三大数据出队 3.例程运行结果 五、队列相关API函数解析1.队列的创建API函数xQueueCreate( )2.往队列写入数据API函数入队xQueueSend( )3.从队列读取数据API函数出队 xQueueReceive( ) 前言
本文包括以下内容
一、队列是什么
综述队列是一种特殊的数据结构它遵循先进先出FIFO的原则。队列中的元素按照其插入的顺序进行访问和处理新元素被插入到队列的末尾而已存在的元素则在队列的前端进行操作和删除。队列的操作包括入队enqueue和出队dequeue入队表示将元素插入到队列的末尾而出队则表示将队列的前端元素移除并返回。队列常用于需要按照先后顺序处理元素的场景例如任务调度、消息传递等。
而在freertos中队列是什么呢
队列是任务到任务、任务到中断、中断到任务数据交流的一种机制消息传递
①如果要进行中断、任务的交流那我用全局变量行吗
答不行。以这个图为例比如一个全局变量a0两个任务里面都有对全局变量a的自增如果两个任务优先级不同当运行任务1时a可能运行到读数据-修改数据但还没有写数据时就已经被高优先级的任务二打断导致a≠1执行任务二后才1这样的话执行两次任务却让全局变量a的值是错误的。
所以全局变量的弊端数据无保护导致数据不安全当多个任务同时对该变量操作时数据易受损
②那为什么队列就可以代替全局变量的功能呢
首先来看队列的结构 可以看到任务A和B是写队列操作写队列这个函数呢它会进入临界区完成实际操作后再退出临界区所以写队列时实际关闭了系统中断使得临界区代码可以完整的运行不被打断而读队列也是同理。
所以读写队列具有程序保护功能防止多任务同时访问造成的数据冲突。
③看一看在freertos中队列的结构 在队列中可以存储数量有限、大小固定的数据。队列中的每一个数据叫做“队列项目”队列能够存储“队列项目”的最大数量称为队列的长度。 所以队列的核心特征队列长度和每个队列项目大小。需要我们自己创建时设置。 在Freertos中队列的特点 1FIFO是First-In First-Out的缩写意为先进先出。在队列中新元素被插入到队列的末尾而已存在的元素则在队列的前端进行操作和删除。当需要访问或处理队列中的元素时先访问或处理队列中最早插入的元素然后按照插入的先后顺序依次访问或处理其他元素。这种先进先出的特性使得队列成为一种常用的数据结构在任务调度、缓存管理、消息传递等场景中得到广泛应用。 2在FreeRTOS中队列可以采用实际值传递或者传递指针的方式进行数据传递。实际值传递是指将数据的副本拷贝到队列中进行传递这样操作的是数据的副本对原始数据没有影响。而传递指针则是将指向实际数据的指针放入队列中这样可以避免复制大量的数据但需要注意在使用指针传递时确保不会出现指针指向无效数据的情况。 在传递较大的数据时采用指针传递可以避免频繁的数据复制提高效率。但需要注意在使用指针传递时要确保数据的有效性即确保指针指向的数据在传递过程中不会被修改或释放以免导致数据错误或悬挂指针的情况。 (3)队列在FreeRTOS中是一种通用的机制可以被任何任务或中断使用来发送和读取消息。这是因为队列是一种共享的数据结构用于在不同的任务或中断之间传递数据。任何任务或中断都可以使用队列的API函数来发送消息到队列或从队列中读取消息无论它们属于哪个任务。 这种灵活性使得队列成为一种常用的通信机制在多任务或多中断的系统中可以方便地进行任务间的数据传递和同步。通过队列任务和中断可以安全地共享数据避免竞争条件和数据冲突的问题。同时任务和中断可以根据需要进行阻塞或唤醒以实现有效的同步和通信。 (4)当任务向一个队列发送消息时可以通过指定一个阻塞时间来控制任务的行为。 如果队列已满无法将消息入队任务可以选择以下几种行为 ①阻塞等待任务可以指定一个阻塞时间如果队列已满则任务会在队列有空闲位置之前被阻塞。任务将等待直到队列有空闲位置并成功将消息入队或者等待的超时时间到达。 ②非阻塞立即返回任务可以选择在队列已满时立即返回而不进行阻塞等待。任务可以根据返回的结果来判断是否消息成功发送到队列中。 ④问题当多个任务写入消息给一个“满队列”时这些任务都会进入阻塞状态也就是说有多个任务 在等待同一 个队列的空间。那当队列中有空间时哪个任务会进入就绪态 ⑤数据写队列、读队列操作过程
二、队列的结构体
1.结构体内容
如下
typedef struct QueueDefinition
{int8_t * pcHead /* 存储区域的起始地址 */int8_t * pcWriteTo; /* 下一个写入的位置 */union{QueuePointers_t xQueue; SemaphoreData_t xSemaphore; } u ;List_t xTasksWaitingToSend; /* 等待发送列表 */List_t xTasksWaitingToReceive; /* 等待接收列表 */volatile UBaseType_t uxMessagesWaiting; /* 非空闲队列项目的数量 */UBaseType_t uxLength /* 队列长度 */UBaseType_t uxItemSize; /* 队列项目的大小 */volatile int8_t cRxLock; /* 读取上锁计数器 */volatile int8_t cTxLock /* 写入上锁计数器 *//* 其他的一些条件编译 */
} xQUEUE;这段代码是一个队列的定义具体的结构体成员解释如下
int8_t * pcHead: 存储区域的起始地址即队列的存储空间的首地址。
int8_t * pcWriteTo: 下一个写入位置的指针用于指示下一个要写入数据的位置。
union { QueuePointers_t xQueue; SemaphoreData_t xSemaphore; } u: 一个联合体用于保存队列指针或信号量数据。
List_t xTasksWaitingToSend: 等待发送列表用于存储等待向队列发送消息的任务。
List_t xTasksWaitingToReceive: 等待接收列表用于存储等待从队列接收消息的任务。
volatile UBaseType_t uxMessagesWaiting: 非空闲队列项目的数量用于记录当前队列中等待接收的消息数量。
UBaseType_t uxLength: 队列长度表示队列可以容纳的最大项目数量。
UBaseType_t uxItemSize: 队列项目的大小表示每个项目占用的字节数。
volatile int8_t cRxLock: 读取上锁计数器用于记录当前队列被读取操作锁定的次数。
volatile int8_t cTxLock: 写入上锁计数器用于记录当前队列被写入操作锁定的次数。
2.结构体示意图 三、队列相关API函数介绍
使用队列的主要流程创建队列 -写队列 - 读队列。主要包括创建队列、写队列、读队列三个部分。
1.创建队列 参数说明
uxQueueLength队列的长度即队列可以容纳的最大项目数量。 uxItemSize队列中每个项目的大小即每个项目占用的字节数。 返回值
成功创建队列时返回一个有效的队列句柄QueueHandle_t。 创建队列失败时返回 NULL。 示例用法
#include FreeRTOS.h
#include queue.h// 创建一个长度为10每个项目大小为4字节的队列
QueueHandle_t xQueue xQueueCreate(10, sizeof(int));
if (xQueue ! NULL) {// 队列创建成功
} else {// 队列创建失败
}xQueueCreate() 函数用于在运行时动态创建一个队列并返回一个队列句柄该句柄可用于后续对队列进行操作如发送消息和接收消息。注意在使用完队列后需要使用 vQueueDelete() 函数来删除队列以释放相关的资源。
2.写消息入队列 写消息到队列里只能往队列头部、队列尾部、覆写方式写入队列这三种方法覆写只有在队列的队列长度为 1 时才能够使用
(1)前三个函数
xQueueSend()、xQueueSendToBack() 和 xQueueSendToFront() 函数都是用于向队列中写入消息的函数它们的作用类似但有一些细微的差别。
这些函数的函数原型如下
BaseType_t xQueueSend(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait);BaseType_t xQueueSendToBack(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait);BaseType_t xQueueSendToFront(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait);参数说明 xQueue要写入的队列的句柄。 pvItemToQueue指向要写入队列的消息的指针。 xTicksToWait写入操作的超时时间如果队列已满则等待一段时间再尝试写入。可以使用 portMAX_DELAY 来表示无限等待。
返回值 如果成功写入消息到队列则返回 pdPASS。 如果写入消息失败如队列已满并且在指定的超时时间内未能成功写入则返回 errQUEUE_FULL。
示例用法
#include FreeRTOS.h
#include queue.h// 创建一个长度为10每个项目大小为4字节的队列
QueueHandle_t xQueue xQueueCreate(10, sizeof(int));int message 42;// 向队列尾部写入消息
if (xQueueSend(xQueue, message, portMAX_DELAY) pdPASS) {// 消息写入成功
} else {// 消息写入失败
}// 向队列尾部写入消息与 xQueueSend() 等效
if (xQueueSendToBack(xQueue, message, portMAX_DELAY) pdPASS) {// 消息写入成功
} else {// 消息写入失败
}// 向队列头部写入消息
if (xQueueSendToFront(xQueue, message, portMAX_DELAY) pdPASS) {// 消息写入成功
} else {// 消息写入失败
}这些函数用于将消息写入队列中xQueueSend() 和 xQueueSendToBack() 将消息写入队列的尾部而 xQueueSendToFront() 将消息写入队列的头部。如果队列已满则写入操作将会阻塞直到队列有可用空间或超时。
2后面的函数
xQueueOverwrite() 和 xQueueOverwriteFromISR() 函数用于覆写队列中的消息仅适用于队列长度为1的情况。
这些函数的函数原型如下
BaseType_t xQueueOverwrite(QueueHandle_t xQueue, const void *pvItemToQueue);BaseType_t xQueueOverwriteFromISR(QueueHandle_t xQueue, const void *pvItemToQueue, BaseType_t *pxHigherPriorityTaskWoken);参数说明
xQueue要操作的队列的句柄。 pvItemToQueue指向要写入队列的消息的指针。 pxHigherPriorityTaskWoken一个指向 BaseType_t 类型变量的指针用于指示是否有更高优先级的任务需要唤醒。在 xQueueOverwriteFromISR() 中使用可以设置为 NULL。 返回值
如果成功覆写队列中的消息则返回 pdPASS。 如果队列为空或队列长度不为1则返回 errQUEUE_FULL。 示例用法
#include FreeRTOS.h
#include queue.h// 创建一个长度为1每个项目大小为4字节的队列
QueueHandle_t xQueue xQueueCreate(1, sizeof(int));int message 42;// 覆写队列中的消息
if (xQueueOverwrite(xQueue, message) pdPASS) {// 消息覆写成功
} else {// 消息覆写失败
}// 在中断中覆写队列中的消息
BaseType_t xHigherPriorityTaskWoken pdFALSE;
if (xQueueOverwriteFromISR(xQueue, message, xHigherPriorityTaskWoken) pdPASS) {// 消息覆写成功
} else {// 消息覆写失败
}xQueueOverwrite() 和 xQueueOverwriteFromISR() 函数用于覆写队列中的消息。在队列长度为1的情况下可以使用这些函数来覆盖队列中的现有消息而不需要等待或创建新的消息。请注意这些函数只适用于队列长度为1的情况。
在中断处理程序中使用 xQueueOverwriteFromISR() 函数时需要将 pxHigherPriorityTaskWoken 参数设置为非空指针并且根据实际情况判断是否需要唤醒更高优先级的任务。
3.从队列中读取消息 xQueueReceive() 从队列头部读取消息并将消息从队列中删除。 如果队列为空任务将进入阻塞状态直到队列中有消息可读取。 返回pdPASS表示成功读取到消息返回errQUEUE_EMPTY表示队列为空。 xQueuePeek() 从队列头部读取消息但不删除消息。 如果队列为空任务将进入阻塞状态直到队列中有消息可读取。 返回pdPASS表示成功读取到消息返回errQUEUE_EMPTY表示队列为空。 xQueueReceiveFromISR() 在中断中从队列头部读取消息并将消息从队列中删除。 与xQueueReceive()函数类似但是特别适用于在中断服务例程ISR中使用。 返回pdPASS表示成功读取到消息返回errQUEUE_EMPTY表示队列为空。 xQueuePeekFromISR() 在中断中从队列头部读取消息但不删除消息。 与xQueuePeek()函数类似但是特别适用于在中断服务例程ISR中使用。 返回pdPASS表示成功读取到消息返回errQUEUE_EMPTY表示队列为空。 这些函数都是用于读取队列中的消息并根据需要删除消息或者保留消息。其中xQueueReceiveFromISR()和xQueuePeekFromISR()函数专门用于在中断服务例程中使用。这些函数将任务或中断服务例程阻塞直到队列中有消息可读取。如果队列为空任务或中断服务例程将进入阻塞状态直到队列中有消息可读取。返回值用于指示读取操作是否成功。
代码例子
下面是一个简单的示例代码展示了如何使用xQueueReceive()和xQueuePeek()函数从队列中读取消息
#include stdio.h
#include FreeRTOS.h
#include task.h
#include queue.h// 定义一个全局队列
QueueHandle_t queue;// 任务函数1向队列中发送消息
void task1(void *pvParameters) {int msg 100;while(1) {// 发送消息到队列中xQueueSend(queue, msg, 0);// 任务延时1秒vTaskDelay(pdMS_TO_TICKS(1000));}
}// 任务函数2从队列中读取消息并删除
void task2(void *pvParameters) {int receivedMsg;while(1) {// 从队列中读取并删除消息if(xQueueReceive(queue, receivedMsg, portMAX_DELAY) pdPASS) {printf(Received message: %d\n, receivedMsg);}// 任务延时500毫秒vTaskDelay(pdMS_TO_TICKS(500));}
}int main() {// 创建队列queue xQueueCreate(5, sizeof(int));// 创建任务1xTaskCreate(task1, Task 1, configMINIMAL_STACK_SIZE, NULL, 1, NULL);// 创建任务2xTaskCreate(task2, Task 2, configMINIMAL_STACK_SIZE, NULL, 2, NULL);// 启动调度器vTaskStartScheduler();// 如果调度器启动失败则打印错误信息printf(Failed to start FreeRTOS scheduler!\n);return 0;
}在这个示例中我们创建了一个全局队列queue然后创建了两个任务task1和task2。task1任务通过xQueueSend()函数将消息发送到队列中而task2任务使用xQueueReceive()函数从队列中读取并删除消息。每个任务都使用vTaskDelay()函数延时一定的时间以模拟任务执行的过程。
当task2任务成功从队列中读取到消息时将打印消息内容。这里使用了portMAX_DELAY作为阻塞时间参数表示如果队列为空任务将一直阻塞直到有消息可读取。
这个示例展示了如何使用xQueueReceive()函数从队列中读取消息并删除以及如何使用xQueueSend()函数向队列中发送消息。实际应用中可以根据需要在任务中使用这些函数来实现消息传递和同步。
四、队列入队和出队操作实验
1.实验目标 这次例程我会进行一个非常细致的讲解
2.例程
①main.c
int main(void)
{HAL_Init(); /* 初始化HAL库 */sys_stm32_clock_init(360, 25, 2, 8); /* 设置时钟,180Mhz */delay_init(180); /* 延时初始化 */usart_init(115200); /* 串口初始化为115200 */led_init(); /* 初始化LED */key_init(); /* 初始化按键 */sdram_init(); /* SRAM初始化 */lcd_init(); /* 初始化LCD */my_mem_init(SRAMIN); /* 初始化内部内存池 */my_mem_init(SRAMEX); /* 初始化外部内存池 */my_mem_init(SRAMCCM); /* 初始化CCM内存池 */freertos_demo();
}除了那些裸机也用到的函数外还有这些内存初始化设置 sdram_init()用于初始化SRAM静态随机存取存储器。SRAM是一种高速的存储器通常用于存储数据、变量或者代码。
my_mem_init(SRAMIN)用于初始化内部内存池。内部内存池是指在芯片内部的一块存储空间用于存储数据、变量或者代码。
my_mem_init(SRAMEX)用于初始化外部内存池。外部内存池是指连接在芯片外部的一块存储空间通常是使用外部存储器如SDRAM、NOR Flash扩展的存储器。
my_mem_init(SRAMCCM)用于初始化CCMCore-Coupled Memory内存池。CCM内存是一种与CPU核心紧密耦合的存储器它具有低延迟和高带宽的特点适用于存储关键数据和代码。
最重要的当然就是freertos_demo();函数了。
②freertos_demo(); /*** brief FreeRTOS例程入口函数* param 无* retval 无*/
void freertos_demo(void)
{ /* 队列的创建 */key_queue xQueueCreate( 2, sizeof(uint8_t) );if(key_queue ! NULL){printf(key_queue队列创建成功\r\n);}else printf(key_queue队列创建失败\r\n);big_date_queue xQueueCreate( 1, sizeof(char *) );if(big_date_queue ! NULL){printf(big_date_queue队列创建成功\r\n);}else printf(big_date_queue队列创建失败\r\n);xTaskCreate((TaskFunction_t ) start_task,(char * ) start_task,(configSTACK_DEPTH_TYPE ) START_TASK_STACK_SIZE,(void * ) NULL,(UBaseType_t ) START_TASK_PRIO,(TaskHandle_t * ) start_task_handler );vTaskStartScheduler();
}这是一个名为freertos_demo()的函数用于演示FreeRTOS中队列的创建和任务的创建与调度。
key_queue xQueueCreate(2, sizeof(uint8_t))创建一个容量为2元素大小为uint8_t的队列用于存储按键值。如果队列创建成功会打印key_queue队列创建成功“否则打印key_queue队列创建失败”。
big_date_queue xQueueCreate(1, sizeof(char *))创建一个容量为1元素大小为char指针的队列用于存储大数据块。如果队列创建成功会打印big_date_queue队列创建成功“否则打印big_date_queue队列创建失败”.
xTaskCreate(start_task, “start_task”, START_TASK_STACK_SIZE, NULL, START_TASK_PRIO, start_task_handler)创建一个名为start_task的任务使用start_task()函数作为任务函数。该任务的堆栈大小为START_TASK_STACK_SIZE优先级为START_TASK_PRIO。任务句柄start_task_handler用于后续操作。 vTaskStartScheduler()启动FreeRTOS调度器开始执行任务。
③操作队列和存储大数据块
QueueHandle_t key_queue; /* 小数据句柄 */
QueueHandle_t big_date_queue; /* 大数据句柄 */
char buff[100] {我是一个大数组大大的数组 124214 uhsidhaksjhdklsadhsaklj};key_queue和big_date_queue是队列的句柄或称为队列的指针用于在程序中引用这两个队列。key_queue是一个指向小数据队列的句柄big_date_queue是一个指向大数据队列的句柄。
另外还定义了一个名为buff的字符数组长度为100用于存储大数据块。该数组中包含了一个字符串表示一个大数据块的内容。
④任务一实现入队 /* 任务一实现入队 */
void task1( void * pvParameters )
{uint8_t key 0;char * buf;BaseType_t err 0;buf buff[0]; /* buf buff[0] */while(1) {key key_scan(0);if(key KEY0_PRES || key KEY1_PRES){err xQueueSend( key_queue, key, portMAX_DELAY );if(err ! pdTRUE){printf(key_queue队列发送失败\r\n);}}else if(key WKUP_PRES){err xQueueSend( big_date_queue, buf, portMAX_DELAY );if(err ! pdTRUE){printf(key_queue队列发送失败\r\n);}}vTaskDelay(10);}
}在任务函数开始时定义了一个key变量和一个buf指针变量。buf指针指向了之前提到的buff数组的第一个元素。
在任务的主循环中首先调用key_scan(0)函数来获取按键值并将其赋值给key变量。然后通过条件判断判断按键值是否为KEY0_PRES或KEY1_PRES如果是则将key值发送到key_queue队列中使用xQueueSend()函数来实现。如果发送失败则打印key_queue队列发送失败。
另外如果按键值为WKUP_PRES则将buf指针发送到big_date_queue队列中同样使用xQueueSend()函数来实现。如果发送失败则打印key_queue队列发送失败。
最后通过vTaskDelay(10)函数来延时10个系统时钟周期然后继续下一次循环。
这个任务函数的功能是根据按键值将数据发送到不同的队列中实现了数据入队的操作。
⑤任务二小数据出队 /* 任务二小数据出队 */
void task2( void * pvParameters )
{uint8_t key 0;BaseType_t err 0;while(1){err xQueueReceive( key_queue,key,portMAX_DELAY);if(err ! pdTRUE){printf(key_queue队列读取失败\r\n);}else {printf(key_queue读取队列成功数据%d\r\n,key);}}
}在任务函数开始时定义了一个key变量和一个err变量。
在任务的主循环中调用xQueueReceive()函数从key_queue队列中接收数据并将接收到的数据保存在key变量中。使用portMAX_DELAY作为阻塞时间表示如果队列为空任务将一直阻塞直到有数据可用。
接收数据后通过条件判断判断数据接收是否成功。如果接收失败则打印key_queue队列读取失败。如果接收成功则打印key_queue读取队列成功数据并打印出接收到的key值。
这个任务函数的功能是从key_queue队列中接收数据并进行处理
⑥任务三大数据出队 /* 任务三大数据出队 */
void task3( void * pvParameters )
{char * buf;BaseType_t err 0;while(1){err xQueueReceive( big_date_queue,buf,portMAX_DELAY);if(err ! pdTRUE){printf(big_date_queue队列读取失败\r\n);}else {printf(数据%s\r\n,buf);}}
}
根据你提供的代码这是一个名为task3()的任务函数用于实现大数据出队操作。
在任务函数开始时定义了一个buf指针变量和一个err变量。
在任务的主循环中调用xQueueReceive()函数从big_date_queue队列中接收数据并将接收到的数据保存在buf指针变量中。使用portMAX_DELAY作为阻塞时间表示如果队列为空任务将一直阻塞直到有数据可用。
接收数据后通过条件判断判断数据接收是否成功。如果接收失败则打印big_date_queue队列读取失败。如果接收成功则打印数据并打印出接收到的字符串数据。
这个任务函数的功能是从big_date_queue队列中接收大数据块并进行处理。
3.例程运行结果 五、队列相关API函数解析
1.队列的创建API函数xQueueCreate( )
xQueueCreate()函数是一个FreeRTOS中用于创建队列的API函数。它的内部实现过程如下
首先函数会检查传入的队列长度和队列元素大小是否合法。如果不合法函数会返回NULL表示队列创建失败。
接着函数会为队列分配内存空间包括队列控制块和队列存储区。队列控制块是一个结构体用于管理队列的各种属性和状态信息队列存储区是一个连续的内存块用于存储队列中的元素。
然后函数会初始化队列控制块的各个字段。例如设置队列的长度、元素大小、存储区的起始地址等。
接下来函数会初始化队列的信号量用于实现队列的同步和互斥访问。这个信号量用于控制任务对队列的读取和写入操作确保只有一个任务在访问队列的时候。
最后函数会返回创建的队列的指针。如果队列创建失败函数将返回NULL。
需要注意的是xQueueCreate()函数只是创建了队列的数据结构并没有分配队列存储区的内存空间。实际的内存分配是在调用xQueueSend()和xQueueReceive()等函数时进行的。这是因为队列的存储区大小是根据队列长度和元素大小动态计算的所以需要在运行时动态分配内存空间。
2.往队列写入数据API函数入队xQueueSend( )
xQueueSend()函数是一个FreeRTOS中用于往队列写入数据的API函数也被称为入队操作。它的内部实现过程如下
首先函数会检查传入的队列指针和待写入的数据指针是否合法。如果队列指针或数据指针为空函数会返回一个错误码表示写入操作失败。
接着函数会尝试获取队列的信号量。这是为了确保只有一个任务在访问队列的时候避免多个任务同时写入队列导致数据混乱。
如果成功获取到队列的信号量函数会将待写入的数据复制到队列的存储区中。具体的复制方式取决于队列的类型。例如如果是一个字节队列直接将数据复制到存储区即可如果是一个结构体队列需要按照结构体的大小逐个成员进行复制。
写入数据后函数会更新队列的相关属性例如队列中的元素数量、读取和写入指针等。
最后函数会释放队列的信号量表示写入操作完成。
需要注意的是xQueueSend()函数在写入数据时有两种写入模式可以选择阻塞模式和非阻塞模式。在阻塞模式下如果队列已满写入操作将会阻塞当前任务直到队列有空闲位置可写入在非阻塞模式下如果队列已满写入操作将会立即返回一个错误码表示写入操作失败。这种模式由函数调用时传入的阻塞时间参数决定。
3.从队列读取数据API函数出队 xQueueReceive( )
xQueueReceive()函数是一个FreeRTOS中用于从队列读取数据的API函数也被称为出队操作。它的内部实现过程如下
首先函数会检查传入的队列指针和接收数据的指针是否合法。如果队列指针或接收数据的指针为空函数会返回一个错误码表示读取操作失败。
接着函数会尝试获取队列的信号量。这是为了确保只有一个任务在访问队列的时候避免多个任务同时读取队列导致数据混乱。
如果成功获取到队列的信号量函数会从队列的存储区中读取数据并将读取到的数据复制到接收数据的指针中。具体的复制方式取决于队列的类型。例如如果是一个字节队列直接从存储区中读取数据即可如果是一个结构体队列需要按照结构体的大小逐个成员进行复制。
读取数据后函数会更新队列的相关属性例如队列中的元素数量、读取和写入指针等。
最后函数会释放队列的信号量表示读取操作完成。
需要注意的是xQueueReceive()函数在读取数据时有两种读取模式可以选择阻塞模式和非阻塞模式。在阻塞模式下如果队列为空读取操作将会阻塞当前任务直到队列有数据可读取在非阻塞模式下如果队列为空读取操作将会立即返回一个错误码表示读取操作失败。这种模式由函数调用时传入的阻塞时间参数决定。