FreeRTOS教程——队列(三)

FreeRTOS教程——任务(一)
FreeRTOS教程——定时器(二)
FreeRTOS教程——队列(三)
FreeRTOS教程——二值信号量(四)

队列函数库

1、xQueueCreate

queue. h

xQueueHandle xQueueCreate( 
   unsigned portBASE_TYPE uxQueueLength, 
   unsigned portBASE_TYPE uxItemSize 
);

创建一个新的队列。为新的队列分配所需的存储内存,并返回一个队列处理。

  • 参数
uxQueueLength队列中包含最大项目数量。
uxItemSize队列中每个项目所需的字节数。项目通过复制而不是引用排队,因为,所需的字节数,将复制给每个项目。队列中每个项目必须分配同样大小。
  • 返回值

如果队列成功创建,并返回一个新建队列的处理。如果不能创建队列,将返回0。

  • 使用范例
struct AMessage {
    portCHAR ucMessageID;
    portCHAR ucData[ 20 ];
};
void vATask( void *pvParameters ){
    xQueueHandle xQueue1, xQueue2;
    // 创建一个队列,包含10个unsigned long值
    xQueue1 = xQueueCreate( 10, sizeof( unsigned portLONG ) );
    if( xQueue1 == 0 ){
        // 队列不能创建,就不能使用
    }
    // 创建一个队列,包含10个指向AMessage 结构的指针
    /// 可以通过指针传递,指针可以包含很多数据
    xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
    if( xQueue2 == 0 ){
        // 队列不能创建,就不能使用
    }
    // ... 其余代码
}

2、xQueueSend

queue.h

 portBASE_TYPE xQueueSend( 
   xQueueHandle xQueue, 
   const void * pvItemToQueue, 
   portTickType xTicksToWait 
);

是一个调用xQueueGenericSend()的宏。能向后兼容FreeRTOS.org(没有包括xQueueSendToFront()和xQueueSendToBack() 宏的)版本。与xQueueSendToBack()等效。

传递一个项目到队列。这个项目通过复制而不是通过引用排队。这个函数不能从中断服务程序调用。参考xQueueSendFromISR(),在ISR中交错使用。

xQueueSend() 是全特点任务间通信API接口。xQueueSend() 等效于交叉API。版本不要同样的参数和同样的返回值。

  • Parameters:

    xQueue 处理将项目传递给队列pvItemToQueue 指向队列中放置的项目的指针。项目的大小,由队列创建时定义,因为许多字节可以从 pvItemToQueue复制到队列的储存区域 xTicksToWait 最大时间量(任务应该锁住,等待队列中的可用空间)应该已经满了。如果设置为0,调用将立即返回。时间使用滴答周期来定义,因此如果需要,常量portTICK_RATE_MS应该用来转换实时时间

  • Returns:

    pdTRUE:项目成功传递。否则为:errQUEUE_FULL.

3、xQueueReceive

queue.h

 portBASE_TYPE xQueueSend( 
   xQueueHandle xQueue, 
   const void * pvItemToQueue, 
   portTickType xTicksToWait 
);

一个调用 xQueueGenericReceive() 函数的宏。

从队列接收一个项目。这个项目通过复制接收,因此缓冲器必须提供足够大的空间。复制进缓冲器的字节数,在队列创建时已经定义。

这个函数一定不能在中断服务程序中使用。参考 xQueueReceiveFromISR 获得能够的选择。

xQueueReceive() 是全功能任务间通信API接口。xQueueAltReceive() 相当于API其中之一。版本需要相同的参数和相同的返回值。

  • Parameters
pxQueue将要接收项目的队列句柄
pvBuffer指向将要复制接收项目的缓冲器的指针。
xTicksToWait任务中断并等待队列中可用空间的最大时间,应该是满的。如果设置为0,调用将立刻返回。时间在片区间中定义,如果需要,portTICK_RATE_MS常量用来转换为实际时间。 如果 INCLUDE_vTaskSuspend 定义为1 ,指定的中断时间( portMAX_DELAY) 将导致任务无限期中断(没有时间溢出)。
  • Returns

如果项目成功被队列接收为pdTRUE 。 否则为 pdFALSE.

4、任务与队列实例

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "key.h"

//任务优先级
#define START_TASK_PRIO		1
//任务堆栈大小	
#define START_STK_SIZE 		128  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);

//任务优先级
#define LED0_TASK_PRIO		3
//任务堆栈大小	
#define LED0_STK_SIZE 		50  
//任务句柄
TaskHandle_t LED0Task_Handler;
//任务函数
void led0_task(void *pvParameters);

//任务优先级
#define LED1_TASK_PRIO		2
//任务堆栈大小	
#define LED1_STK_SIZE 		50  
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);

//任务优先级
#define LED2_TASK_PRIO		1
//任务堆栈大小	
#define LED2_STK_SIZE 		50  
//任务句柄
TaskHandle_t LED2Task_Handler;
//任务函数
void led2_task(void *pvParameters);

QueueHandle_t key_Queue;

int main(void)
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4	 
	delay_init();	    				//延时函数初始化	  
	uart_init(115200);					//初始化串口
	LED_Init();		  					//初始化LED
	KEY_Init();
	//创建开始任务
    xTaskCreate((TaskFunction_t )start_task,            //任务函数
                (const char*    )"start_task",          //任务名称
                (uint16_t       )START_STK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )START_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
    vTaskStartScheduler();          //开启任务调度
}

//开始任务任务函数
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //进入临界区
	
	  key_Queue=xQueueCreate( 5, sizeof(u8));
    if(key_Queue==NULL)	printf("创建失败\r\n");//当内存空间不够时会失败
		else
	      printf("创建成功\r\n");
		
	
    //创建LED0任务
    xTaskCreate((TaskFunction_t )led0_task,     	
                (const char*    )"led0_task",   	
                (uint16_t       )LED0_STK_SIZE, 
                (void*          )NULL,				
                (UBaseType_t    )LED0_TASK_PRIO,	
                (TaskHandle_t*  )&LED0Task_Handler);   
    //创建LED1任务
    xTaskCreate((TaskFunction_t )led1_task,     
                (const char*    )"led1_task",   
                (uint16_t       )LED1_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )LED1_TASK_PRIO,
                (TaskHandle_t*  )&LED1Task_Handler);       
		//创建LED2任务
    xTaskCreate((TaskFunction_t )led2_task,     
                (const char*    )"led2_task",   
                (uint16_t       )LED2_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )LED2_TASK_PRIO,
                (TaskHandle_t*  )&LED2Task_Handler);   
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
}

//LED0任务函数 
void led0_task(void *pvParameters)
{
    u8 key=0;
		BaseType_t r;
		while(1) {
			key = KEY_Scan(0);
			if(key){
					r = xQueueSend( key_Queue, &key, 10 ); //portMAX_DELAY 死等
					if(r == pdPASS) printf("入队成功\n\r");
					else printf("入队失败");
			}
			vTaskDelay(10);
		}
}   

//LED1任务函数
void led1_task(void *pvParameters)
{
		u8 kk = 0;
		BaseType_t rr;
    while(1)
    {
				rr = xQueueReceive(key_Queue, &kk, portMAX_DELAY );
        rr == pdPASS ? printf("出队成功\r\n") : printf("出队失败\r\n");
				printf("出队数据是%d\r\n",kk);
        vTaskDelay(10);
    }
}

void led2_task(void *pvParameters)
{
		while(1)
    {
        LED0=0;
        vTaskDelay(1000);
        LED0=1;
        vTaskDelay(1000);
    }
}

5、xQueueSendFromISR

queue.h

portBASE_TYPE xQueueSendFromISR(
  xQueueHandle pxQueue,
  const void *pvItemToQueue,
  portBASE_TYPE *pxHigherPriorityTaskWoken
);

这是一个调用 xQueueGenericSendFromISR() 的宏。是为了兼容 FreeRTOS.org 以后的版本(没有包含xQueueSendToBackFromISR() 和 xQueueSendToFrontFromISR() 宏)

传递一个项到队列的后面。在终端服务程序中可以安全使用。

项在队列中是复制而不是引用,排列小项目更加灵活,特别是当从ISR调用时。在大多数情况下,使用一个指向项目的指针传进队列更加灵活

  • 参数
xQueue将项目传进的队列
pvItemToQueue一个指向将在队列中放置的项目的指针。项目的大小,队列在创建时已经定义了, 将从pvItemToQueue复制许多字节到队列的存储区域
pxHigherPriorityTaskWoken如果传进队列而导致任务解锁,并且解锁的任务的优先级高于当前运行任务的优先级xQueueSendFromISR将设置 *pxHigherPriorityTaskWoken到 pdTRUE 。如果xQueueSendFromISR()设置这个值到 pdTRUE,在中断推出之前将请求任务切换。
  • Returns

pdTRUE:数据成功传递进队列。否则为:errQUEUE_FULL。使用范例是缓冲IO(每次调用ISR能够更多的值)

  • 示例

Example usage for buffered IO (where the ISR can obtain more than one value per call):

void vBufferISR( void ){
  portCHAR cIn;
  portBASE_TYPE xHigherPriorityTaskWoken;

  /* 没有在ISR中唤醒任务 */
  xHigherPriorityTaskWoken = pdFALSE;

  /* 循环直到缓冲器为空 */
  do{
    /*从缓冲器中获得一个字节. *
    cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );						

    /* 传递字节 */
    xQueueSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
  } while ( portINPUT_BYTE( BUFFER_COUNT ) );

  /* 现在缓冲器为空,如果需要可以任务切换 */
  if( xHigherPriorityTaskWoken ){
    /* 实际宏使用了特殊接口 */
    taskYIELD_FROM_ISR ();
  }
}

6、xQueueReceiveFromISR

queue. h

portBASE_TYPE xQueueReceiveFromISR( 
  xQueueHandle pxQueue, 
  void *pvBuffer, 
  portBASE_TYPE *pxTaskWoken 
); 

从队列接收一个项目。在中断程序中使用此函数是安全的。

Parameters:

pxQueue发送项目的队列句柄
pvBuffer指向缓冲区的指针,将接收的项目被复制进去。
pxTaskWoken任务将锁住,等待队列中的可用空间。如果xQueueReceiveFromISR 引起一个任务解锁,pxTaskWoken 将设置为pdTRUE,否则pxTaskWoken保留不变

Returns: pdTRUE :如果项目成功从队列接收。否则为: pdFALSE

xQueueHandle xQueue;
// Function to create a queue and post some values.
void vAFunction( void *pvParameters ){
  portCHAR cValueToPost;
  const portTickType xBlockTime = ( portTickType )0xff;
  // Create a queue capable of containing 10 characters.
  xQueue = xQueueCreate( 10, sizeof( portCHAR ) );
  if( xQueue == 0 ){
    // Failed to create the queue.
  }
  // ...
  // Post some characters that will be used within an ISR.  If the queue
  // is full then this task will block for xBlockTime ticks.
  cValueToPost = 'a';
  xQueueSend( xQueue, ( void * ) &cValueToPost, xBlockTime );
  cValueToPost = 'b';
  xQueueSend( xQueue, ( void * ) &cValueToPost, xBlockTime );
  // ... keep posting characters ... this task may block when the queue
  // becomes full.
  cValueToPost = 'c';
  xQueueSend( xQueue, ( void * ) &cValueToPost, xBlockTime );
}
// ISR that outputs all the characters received on the queue. 
void vISR_Routine( void ){
  portBASE_TYPE xTaskWokenByReceive = pdFALSE;
  portCHAR cRxedChar;
  while( xQueueReceiveFromISR( xQueue, ( void * ) &cRxedChar, &xTaskWokenByReceive) ){
    // A character was received.  Output the character now.
    vOutputCharacter( cRxedChar );
    // If removing the character from the queue woke the task that was 
    // posting onto the queue xTaskWokenByReceive will have been set to
    // pdTRUE.  No matter how many times this loop iterates only one
    // task will be woken.
  }
  if( xTaskWokenByPost != pdFALSE ){
    // We should switch context so the ISR returns to a different task.
    // NOTE:  How this is done depends on the port you are using.  Check
    // the documentation and examples for your port.
    taskYIELD ();
  }
}

7、队列与中断实例

// usart.c
#include "sys.h"
#include "usart.h"	 

#include "FreeRTOS.h"
#include "task.h" 
#include "queue.h"
//
//加入以下代码,支持printf函数,而不需要选择use MicroLIB	  
#if 1
#pragma import(__use_no_semihosting)             
//标准库需要的支持函数                 
struct __FILE 
{ 
	int handle; 

}; 

FILE __stdout;       
//定义_sys_exit()以避免使用半主机模式    
void _sys_exit(int x) 
{ 
	x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f)
{      
	while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
    USART1->DR = (u8) ch;      
	return ch;
}
#endif 

#if EN_USART1_RX   //如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误   	
u8 USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15,	接收完成标志
//bit14,	接收到0x0d
//bit13~0,	接收到的有效字节数目
u16 USART_RX_STA=0;       //接收状态标记	  
  
void uart_init(u32 bound){
	//GPIO端口设置
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA时钟
  
	//USART1_TX   GPIOA.9
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
   
	//USART1_RX	  GPIOA.10初始化
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  

	//Usart1 NVIC 配置
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 6;//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;		//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器
  
	//USART 初始化设置

	USART_InitStructure.USART_BaudRate = bound;//串口波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式

	USART_Init(USART1, &USART_InitStructure); //初始化串口1
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
	USART_Cmd(USART1, ENABLE);                    //使能串口1 
}

extern QueueHandle_t Key_Odd_Queue;//队列句柄
extern QueueHandle_t Key_Even_Queue;//队列句柄

void USART1_IRQHandler(void)                	//串口1中断服务程序
{
	u8 len,i;
	BaseType_t x_o = 0;
	BaseType_t x_e = 0;
	BaseType_t addr;
	u8 Res;
	char oddchars[20];
	int o_i = 0;
	char evenchars[20];
	int e_i = 0;

	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) 
		{
		Res =USART_ReceiveData(USART1);	 	//读取接收到的数据
		if((USART_RX_STA&0x8000)==0)		//接收未完成    1000 0000 0000 0000
			{
			if(USART_RX_STA&0x4000) //接收到了0x0d     0100 0000 0000 0000
			{
				if(Res!=0x0a)USART_RX_STA=0;  //接收错误,重新开始--接收到了0X0D但是没有接受到0x0A
				else{
					USART_RX_STA|=0x8000;	//接收完成了--0x0d后面是0x0a 
					len=USART_RX_STA&0x3fff; // 0011 1111 1111 1111 获取长度
				  for(i=0;i<len;i++)
					{
							Res=USART_RX_BUF[i]; 
							if(Res %2 == 0){
									evenchars[o_i] = Res;
									o_i++;
							}
							if(Res %2 == 1){
									oddchars[e_i] = Res;
									e_i++;
							}
					}
					oddchars[o_i] = '\0';
					evenchars[e_i] = '\0';
					x_o = xQueueSendFromISR(Key_Odd_Queue,oddchars,&addr);
					x_e = xQueueSendFromISR(Key_Even_Queue,evenchars,&addr);
					USART_RX_STA=0;
					// portMAX_DELAY 死等 0不等
					if(x_o==pdPASS && x_e==pdPASS)	printf("入队成功\r\n");
					else 	printf("入队失败\r\n");
				}
			}
			else
				{	
				if(Res==0x0d)USART_RX_STA|=0x4000;
				else
					{
					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
					USART_RX_STA++;

					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	  
					}		 
				}
			} 
    } 
} 
#endif	
// main.c
//LED0任务函数 
void led0_task(void *pvParameters)
{
    printf("LED0任务函数开始运行!\r\n");
		char oddchar[20];
		char evenchar[20];
		char deschar[40];
		u8 len_o = 0;
		u8 len_e = 0;
		BaseType_t rs_o;
		BaseType_t rs_e;
		int i = 0;
		while(1){
			rs_o=xQueueReceive(Key_Odd_Queue,oddchar,0); //奇数出队后删除
			rs_e=xQueueReceive(Key_Even_Queue,evenchar,0); //偶数出队后删除
			if(rs_o == pdTRUE && rs_e == pdTRUE){
				printf("\r\n字符串出队%s%s",oddchar,evenchar);
			}
			vTaskDelay(20);
		}
}   
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FreeRTOS中的队列是一种用于任务之间通信的机制。它允许任务在队列中发送和接收数据项目。队列可以用于任务与任务之间的通信,也可以用于任务与中断之间的通信。队列中可以存储有限的、大小固定的数据项目。 要使用FreeRTOS中的队列,首先需要创建一个队列对象,并指定数据项目的大小和队列的长度。然后,任务可以使用队列的API函数将数据项目发送到队列中,或从队列中接收数据项目。 在向队列发送数据之前,任务通常需要检查队列是否已满。如果队列已满,则无法发送数据。当队列未满或者是覆写入队列时,任务可以将消息入队。 以下是一个示例代码,演示了如何在FreeRTOS中使用队列: ```c #include "FreeRTOS.h" #include "task.h" #include "queue.h" // 定义队列长度和数据项目大小 #define QUEUE_LENGTH 5 #define ITEM_SIZE sizeof(int) // 创建队列句柄 QueueHandle_t queueHandle; // 任务1,向队列发送数据 void Task1(void *pvParameters) { int data = 123; while (1) { // 检查队列是否已满 if (uxQueueSpacesAvailable(queueHandle) > 0) { // 发送数据到队列 xQueueSend(queueHandle, &data, 0); } vTaskDelay(pdMS_TO_TICKS(1000)); } } // 任务2,从队列接收数据 void Task2(void *pvParameters) { int receivedData; while (1) { // 从队列接收数据 if (xQueueReceive(queueHandle, &receivedData, portMAX_DELAY) == pdTRUE) { // 处理接收到的数据 // ... } vTaskDelay(pdMS_TO_TICKS(1000)); } } int main(void) { // 创建队列 queueHandle = xQueueCreate(QUEUE_LENGTH, ITEM_SIZE); // 创建任务1和任务2 xTaskCreate(Task1, "Task1", configMINIMAL_STACK_SIZE, NULL, 1, NULL); xTaskCreate(Task2, "Task2", configMINIMAL_STACK_SIZE, NULL, 2, NULL); // 启动调度器 vTaskStartScheduler(); return 0; } ``` 在上面的示例中,任务1通过调用`xQueueSend`函数将数据发送到队列中,任务2通过调用`xQueueReceive`函数从队列中接收数据。任务1在发送数据之前会检查队列是否已满,以确保数据能够成功发送。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值