FreeRtos——23、任务间通信方式:队列、信号量、事件组、任务通知
第一节、任务间的通信方式xQueue(队列或消息队列):1. 为何使用队列进行任务间的通信,而非全局变量呢?与Linux进程间通信方式类似,FreeRTOS中的通知也采用的队列方式,其本质与Linux一样都是使用的一种环形队列。不过这个数据结构不需要我们去构建,已经封装好了。我们只需要操作上层的接口实现任务间的数据传递即可。(当然任务间在FreeRTOS中也可以使用全局变量传递,但全局变量在多任务中可能会产生竞态的问题)。FreeRTOS中的队列是用于任务间通信和同步的重要机制。它们提供了线程安全的数据传输方式,并支持阻塞和非阻塞的发送和接收操作。通过使用队列,任务和中断服务程序可以高效、安全地共享数据。2.xQueue队列的基本功能介绍:FIFO(先进先出):队列遵循先进先出的原则,即最早进入队列的项目最早被移除。多任务通信:队列可以在多个任务之间共享数据,一个任务可以向队列发送数据,另一个任务可以从队列接收数据。线程安全:FreeRTOS提供的队列机制是线程安全的,确保多任务访问队列时不会出现竞争条件或数据损坏。队列默认使用拷贝赋值方式传递数据,而不是引用。3.xQueue的创建:xQueueCreate:QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize ); 功能:动态创建一个队列,并返回队列的句柄。 参数:uxQueueLength 队列可以容纳的最大项目数。 参数:uxItemSize 存储队列中的每个数据项所需的大小(以字节为单位) 返回值: 如果队列创建成功,则返回所创建队列的句柄。 如果创建队列所需的内存无法 分配 ,则返回 NULL。 与创建任务类似也提供了相应的静态创建队列的方法: xQueueCreateStatic() 创建队列,相应的方法介绍请查看FreeRTOS在线手册。4.向队列中发数据:xQueueSend:BaseType_t xQueueSend(QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait); 功能:在队列中发布项目。该项目按副本排队,而不是按引用排队。 不得从中断服务程序调用此函数。请参阅 xQueueSendFromISR() 以获取 可用于 ISR 的替代方案。 参数:xQueue 队列的句柄,数据项将发布到此队列。 参数:pvItemToQueue 指向待入队数据项的指针 参数:xTicksToWait 如果队列已满,则任务应进入阻塞态等待队列上出现可用空间的最大时间。 等待的最大时间可以使用宏:protMAX_DELAY,也可以使用-1或者~0 如果队列已满,并且 xTicksToWait 设置为0 ,调用将立即返回。 返回:如果成功发布项目,则返回 pdTRUE,否则返回 errQUEUE_FULL。5.从队列中接收数据后删除:xQueueReceive, 只读取不删除数据:xQueuePeek():BaseType_t xQueueReceive(QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait); 功能:从队列中接收项目。该项目通过复制接收,因此必须提供足够大小的缓冲区。 中断服务程序中不得使用此函数。请参阅 xQueueReceiveFromISR() 了解可以选择的替代方案 参数:xQueue 要从中接收项目的队列的句柄。pvBuffer 指向缓冲区的指针,接收到的项目将被复制到这个缓冲区。 参数:xTicksToWait 如果在调用时队列为空,则任务应阻塞等待项目接收的最长时间。 返回: 如果从队列成功接收到项目,返回 pdTRUE,否则返回 pdFALSE。代码实例:freeRTOS.c:在任务前创建队列:/* Includes ------------------------------------------------------------------*/ #include "FreeRTOS.h" #include "task.h" #include "main.h" #include "cmsis_os.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "ldr_driver.h" #include "beep.h" #include "beep_and_ldr_app.h" #include "color_led_app.h" #include stdbool.h #include "led_driver.h" #include "queue.h" /* USER CODE BEGIN Variables */ extern struct Beep_LDR_Task my_beep; extern struct Color_LED_Task color_task; /* USER CODE END Variables */ /* Definitions for defaultTask */ osThreadId_t defaultTaskHandle; const osThreadAttr_t defaultTask_attributes = { .name = "defaultTask", .stack_size = 128 * 4, .priority = (osPriority_t) osPriorityNormal, }; QueueHandle_t glink_queue = NULL; void StartDefaultTask(void *argument); void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */ void MX_FREERTOS_Init(void) { /* USER CODE BEGIN RTOS_QUEUES */ /* add queues,动态创建消息队列 ... */ glink_queue = xQueueCreate(1,sizeof(int)); /* USER CODE END RTOS_QUEUES */ /* Create the thread(s) */ /* creation of defaultTask */ defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, defaultTask_attributes); /* USER CODE BEGIN RTOS_THREADS */ /* 动态创建蜂鸣器测试任务, ... */ xTaskCreate(beep_and_ldr_test, "beep_test_app",256,NULL,osPriorityNormal,my_beep.beep_ldr_task); /* 静态创建小彩灯闪烁任务*/ color_task.task = xTaskCreateStatic(color_led_test,"color_led_app", SIZE,NULL,osPriorityNormal,color_task.stackBuf,color_task.TCB); } /* USER CODE END Header_StartDefaultTask */ void StartDefaultTask(void *argument) { /* USER CODE BEGIN StartDefaultTask */ /* Infinite loop */ while (true) { led_on(); osDelay(1000); led_off(); osDelay(1000); } /* USER CODE END StartDefaultTask */ }任务A:生产者:#include "cmsis_os.h" #include "beep_and_ldr_app.h" #include "color_led_app.h" #include "FreeRTOS.h" #include "task.h" #include "queue.h" struct Beep_LDR_Task my_beep = {0}; extern struct Color_LED_Task color_task; extern osThreadId_t defaultTaskHandle; extern QueueHandle_t glink_queue; void beep_and_ldr_test(void* arg) { int data = 0; //这就是测试应用程序: for (;;) { if (isDark()) { data = 100; //如果天黑,我们就通过消息队列发送数,放入数据100,让小彩灯任务闪烁红灯: //向队列中发出数据 xQueueSend(glink_queue,data,1000); } else { data = 200; //天亮时,放入消息队列200,让小彩灯的蓝灯闪烁。 //向队列中发出数据: xQueueSend(glink_queue,data,1000); } } }任务B:消费者:#include "color_led_app.h" #include stdbool.h #include "FreeRTOS.h" #include "task.h" #include "queue.h" extern QueueHandle_t glink_queue; struct Color_LED_Task color_task; void color_led_test(void *arg) { int data = 0; while (true) { /* color_led的应用 */ // 红灯闪烁: //接收数据,并做出判断: xQueueReceive(glink_queue,data,1000); if(data == 100) { color_led_set(1,0,0); vTaskDelay(1000); color_led_set(0,0,0); vTaskDelay(1000); } if(data == 200) //蓝灯闪烁: { color_led_set(0,0,1); vTaskDelay(1000); color_led_set(0,0,0); vTaskDelay(1000); } } }第二节、任务同步与互斥之信号量Semaphore:信号量,顾名思义就是 传递信号的量。信号量也是一种队列,只不过有时候我们只需要传递一个状态,并不需要传递具体信息,所