ESP32-IDF的Freertos消息队列的创建与基础应用


前言

写博客是为了总结知识,提炼知识不做知识的搬运者
文章不一定全是原创但是所写内容都有经过自身研读与提炼
喜欢我博客的朋友可以跟我交流与点赞,


一、消息队列是什么?

队列是任务间通信的主要形式。它们可用于在任务之间以及中断和任务之间发送消息。在大多数情况下,它们用作线程安全的 FIFO(先进先出)缓冲区,将新数据发送到队列的后面,尽管数据也可以发送到前面。

在这里插入图片描述

  • 是一种常用于任务间通信的数据结构队列可以在任务与任务间、中断和任务间传递信息,实现了任务接收来自其他任务或中断的不固定长度的消息,任务能够从队列里面读取消息。
  • 当队列中的消息是空时,读取消息的任务将被阻塞,用户还可以指定阻塞的任务时间 xTicksToWait,在这段时间中,如果队列为空,该任务将保持阻塞状态以等待队列数据有效。当队列中有新消息时,被阻塞的任务会被唤醒并处理新消息;当等待的时间超过了指定的阻塞时间,即使队列中尚无有效数据,任务也会自动从阻塞态转为就绪态。
  • 通过消息队列服务,任务或中断服务例程可以将一条或多条消息放入消息队列中。同
    样,一个或多个任务可以从消息队列中获得消息。当有多个消息发送到消息队列时,通常
    是将先进入消息队列的消息先传给任务,也就是说,任务先得到的是最先进入消息队列的
    消息,即先进先出原则(FIFO),但是也支持后进先出原则(LIFO)。

总结:队列是单片机数据结构,用于任务间的通信,任务可以在一个队列(这个队列是自己定义的大小)中发送和收取多个消息,可以设置阻塞状态,超过阻塞状态会自动就绪处于发送或者接收信息

二、消息队列的作用

  • 使用消息队列可以让 RTOS 内核有效地管理任务,而全局数组是无法做到的,任务的超时等机制需要用户自己去实现
  • 使用了全局数组就要防止多任务的访问冲突,而使用消息队列则处理好了这个问题,用户无需担心。
  • 使用消息队列可以有效地解决中断服务程序与任务之间消息传递的问题。
  • FIFO 机制更有利于数据的处理。
  • 消息队列可以应用于发送不定长消息的场合,包括任务与任务间的消息交换,队列是
    FreeRTOS 主要的任务间通讯方式,可以在任务与任务间、中断和任务间传送信息,发送到
    队列的消息是通过拷贝方式实现的,这意味着队列存储的数据是原数据,而不是原数据的
    引用。
    这部分内容由于实践项目应用不够无法多于总结

三、FreeRtos中消息队列API的调用

使用丰富的API23个函数可以实现 FreeRTOS 的消息队列官网链接:API链接

重要函数理解:

函数 xQueueCreate

函数返回类型为指针QueueHandle_t所以需要提前定义一个指针QueueHandle_t queuex;用于函数的返回,即
queue =QueueHandle_t xQueueCreate(x,x)


QueueHandle_t xQueueCreate
	( UBaseType_t uxQueueLength, /* 消息个数 */
        UBaseType_t uxItemSize ); /* 每个消息大小,单位字节 */

函数 xQueueSend


BaseType_t xQueueSend(
       QueueHandle_t xQueue, /* 消息队列句柄 */
       const void * pvItemToQueue, /* 要传递数据地址 */
       TickType_t xTicksToWait /* 等待消息队列有空间的最大等待时间 */
 );

重点:

  • 接收到消息以后可以自动删除当前已经接收的消息接收下一个队列里面的消息,
  • 区分一下中断里面的xQueueSendFromISR,
  • 了解调用API的返回值

在这里插入图片描述

在这里插入图片描述

函数 xQueueReceive


BaseType_t xQueueReceive(
        QueueHandle_t xQueue, /* 消息队列句柄 */
        void *pvBuffer, /* 接收消息队列数据的缓冲地址 */
 	TickType_t xTicksToWait /* 等待消息队列有数据的最大等待时间 */
 );

函数 xQueueSendFromISR


BaseType_t xQueueSendFromISR
	 (
	 QueueHandle_t xQueue, /* 消息队列句柄 */
	 const void *pvItemToQueue, /* 要传递数据地址 */
	 BaseType_t *pxHigherPriorityTaskWoken /* 高优先级任务是否被唤醒的状态保存 */
 );

中断函数里面调用发送队列的信息

函数 uxQueueMessagesWaiting

UBaseType_t uxQueueMessagesWaiting(QueueHandle_t xQueue);

函数 uxQueueSpacesAvailable

UBaseType_t uxQueueSpacesAvailable(QueueHandle_t xQueue);

函数 xQueuePeek


 BaseType_t xQueuePeek(
                          QueueHandle_t xQueue,
                          无效 *pvBuffer,
                          TickType_t xTicksToWait
                       );

函数 xQueueOverwrite

 BaseType_t xQueueOverwrite(
                             QueueHandle_t xQueue,
                           const void * pvItemToQueue
                              );
 

接收到消息以后不会自动删除当前已经接收的消息不会接收下一个队列里面的消息,一直卡在了队头

四、基于esp32-idf实验

这个实验不拘泥于任何单片机,可以在任何单片机上运行

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"

TaskHandle_t myTask1Handle = NULL;
TaskHandle_t myTask2Handle = NULL;
//队列句柄
QueueHandle_t queue1;
 
 void task1(void *arg){
	char txbuff[50];
	queue1= xQueueCreate(5, sizeof(txbuff));
	   //创建队列返回已创建队列的句柄
	if( queue1 == 0 ) 	
{   
 	printf("failed to create queue1= %p \n",queue1);
           // Failed to create the queue.
}

sprintf(txbuff,"hello world! 1");//c语言库赋值
xQueueSend(queue1, (void*)txbuff,(TickType_t)0 );

sprintf(txbuff,"hello world! 2");
xQueueSend(queue1, (void*)txbuff,(TickType_t)0 );

sprintf(txbuff,"hello world! 3");
xQueueSendToFront(queue1, (void*)txbuff,(TickType_t)0 );
 while(1){  
   printf("data waiting to be read : %d  
     available spaces: %d \n",
   uxQueueMessagesWaiting(queue1),//队列中待读取的消息
   uxQueueSpacesAvailable(queue1));//队列剩余空间
   vTaskDelay(pdMS_TO_TICKS(1000));
    }
 }
 void task2(void *arg)
 {
 	char rxbuff[50]; 
        while(1)
	  { //if(xQueuePeek(queue1, &(rxbuff) , (TickType_t)5 ))

		if(xQueueReceive(queue1, &(rxbuff) , (TickType_t)5 )){ 
		printf("got a data from queue!  ===  %s \n",rxbuff); }
     	vTaskDelay(pdMS_TO_TICKS(1000));
     
      }
 }

void app_main(){
     xTaskCreate(task1, "task1", 4096, NULL, 10, &myTask1Handle);  
     xTaskCreatePinnedToCore(task2, "task2", 4096, NULL, 10, &myTask2Handle,1);
 }

在这里插入图片描述

进一步实验

按键中断通信里面发送消息给队列与增加发送检测
【if(xQueueSend(queue1, (void*)txbuff,(TickType_t)0 ) !=1】为什么这么写呢?根据官方解释返回值

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#define ESP_INTR_FLAG_DEFAULT 0
#define CONFIG_LED_PIN 32
#define CONFIG_BUTTON_PIN 0
TaskHandle_t myTask1Handle = NULL;
TaskHandle_t myTask2Handle = NULL;
QueueHandle_t queue1;
TaskHandle_t ISR = NULL;
// interrupt service routine, called when the button is pressed

 void IRAM_ATTR button_isr_handler(void* arg) { 
	//响应中断服务程序的函数:
	//发送数据需要用xQueueSendFromISR函数
	//不可以实验xQueuesend
		char txbuff[50]; 
		sprintf(txbuff,"hello from ISR!");    
  		xQueueSendFromISR(queue1, &txbuff, NULL);
}

 void task1(void *arg){
		char txbuff[50];
		queue1= xQueueCreate(1, sizeof(txbuff));
		 if( queue1 == 0 ) {   		
    printf("failed to create queue1= %p \n",queue1); // Failed to create the queue.
 }

sprintf(txbuff,"hello world! 1");
if(xQueueSend(queue1, (void*)txbuff,(TickType_t)0 ) !=1){ 
printf("could not sended this message = %s \n",txbuff);  }

sprintf(txbuff,"hello world! 2");
if(xQueueSend(queue1, (void*)txbuff , (TickType_t)0 ) !=1)	{ 
printf("could not sended this message = %s \n",txbuff);  }

sprintf(txbuff,"hello world! 3");
if(xQueueOverwrite(queue1, (void*)txbuff ) !=1)	{
 printf("could not sended this message = %s \n",txbuff);  }


 while(1){ 
    vTaskDelay(pdMS_TO_TICKS(10));   
     printf("data waiting to be read : %d  available spaces: %d \n",uxQueueMessagesWaiting(queue1),uxQueueSpacesAvailable(queue1));
    vTaskDelay(pdMS_TO_TICKS(1000)); }}
 void task2(void *arg){char rxbuff[50];while(1)
  { if(xQueueReceive(queue1, &(rxbuff) , 
  (TickType_t)5 ))
  {
  printf("got a data from queue!  ===  %s \n",rxbuff); }

   vTaskDelay(pdMS_TO_TICKS(1000));  
    }
  }
   
 void app_main(){  
  
   	gpio_pad_select_gpio(CONFIG_BUTTON_PIN);
        gpio_pad_select_gpio(CONFIG_LED_PIN);   
        gpio_set_direction(CONFIG_BUTTON_PIN, 
      			  GPIO_MODE_INPUT);    			
      	gpio_set_direction(CONFIG_LED_PIN, 
   			  GPIO_MODE_OUTPUT);    // enable interrupt on falling (1->0) edge for button pin  		
   	gpio_set_intr_type(CONFIG_BUTTON_PIN,  					
   			   GPIO_INTR_NEGEDGE);
    //Install the driver’s GPIO ISR handler service, which allows per-pin GPIO interrupt handlers.  // install ISR service with default configuration  
   
      	 gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);// attach the interrupt service routine  
         gpio_isr_handler_add(CONFIG_BUTTON_PIN, 
  		  button_isr_handler, NULL);		
         xTaskCreate(task1, 
   	     "task1", 4096, NULL, 10, &myTask1Handle);  
         xTaskCreatePinnedToCore(task2,
       	      "task2", 4096, NULL, 10, &myTask2Handle,1);
}

五、总结

  • API的调用注重调用机制与流程,入口
  • 需要记忆一些常见函数的返回值,入库参数的用法
  • 作者Lee1989写于2021.09.10喜欢文章欢迎点赞关注
FreeRTOS是一个开源的实时操作系统内核,被广泛应用于嵌入式系统中。ESP32是一款具有双核处理器和Wi-Fi功能的芯片,通过使用ESP-IDF开发框架可以进行软件开发。在ESP32-IDF开发中,使用FreeRTOS消息队列可以实现不同任务之间的通信。 在ESP32开发中,可以通过中断服务程序(Interrupt Service Routine,ISR)来发送消息到消息队列,并在任务中通过接收方法响应。 首先,我们需要创建一个全局的消息队列句柄,可以使用xQueueCreate函数来创建一个消息队列。例如,可以使用以下代码创建一个大小为10的消息队列: xQueueHandle messageQueue = xQueueCreate(10, sizeof(int)); 然后,在中断服务程序中,可以使用xQueueSendFromISR方法将消息发送到消息队列中。例如,可以使用以下代码将一个整数值发送到消息队列中: int value = 100; xQueueSendFromISR(messageQueue, &value, NULL); 在任务中,可以使用xQueueReceive方法从消息队列中接收消息并进行响应。例如,可以使用以下代码从消息队列中接收一个整数值并打印出来: int receivedValue; xQueueReceive(messageQueue, &receivedValue, portMAX_DELAY); printf("Received value: %d\n", receivedValue); 需要注意的是,在接收消息时,可以通过指定第三个参数来设置等待时间。例如,使用portMAX_DELAY表示无限等待,即直到接收到消息为止。 通过以上步骤,我们可以实现在ESP32开发中使用FreeRTOS消息队列进行中断服务消息发送与响应。这种方式可以实现不同任务之间的通信和同步,提高系统的并发性和实时性。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值