FreeRTOS消息队列

【def:队列定义】

  • 队列是任务到任务,任务到中断,中断到任务数据交流的一种机制(消息传递)
  • 队列和全局变量类似:如果有一个全局变量a = 0, 现有两个任务都在写这个全局变量a

全局变量的弊端:数据无保护,导致数据不安全,当多个任务同时对该变量进行操作时,数据易受损(表示的含义是当任务1在执行一个任务且中断发生时,任务1被打断cpu去执行更高优先级的任务此时数据被改变,导致最终执行的结果发生错误)  

注:解决办法是对任务添加临界区,临界区可以不受中断任务的打断

【临界区:相当于是关闭中断,任务调度是由pendsv引起的,而pendsv的优先级设置为最低(相当于中断无法执行相关操作)】

队列项目和队列长度

队列的特点

 队列的阻塞访问:

 队列的阻塞访问:

  1.  只要知道队列的句柄,谁都可以读、写该队列。任务、ISR都可读、写队列。可以多个任务读写队列。
  2. 任务读写队列时,简单地说:如果读写不成功,则阻塞;可以指定超时时间。口语化地说,就是可以定
  3. 个闹钟:如果能读写了就马上进入就绪态,否则就阻塞直到超时。
  4. 某个任务读队列时,如果队列没有数据,则该任务可以进入阻塞状态:还可以指定阻塞的时间。如果队
  5. 列有数据了,则该阻塞的任务会变为就绪态。如果一直都没有数据,则时间到之后它也会进入就绪态。
  6. 既然读取队列的任务个数没有限制,那么当多个任务读取空队列时,这些任务都会进入阻塞状态:有多
  7. 个任务在等待同一个队列的数据。当队列中有数据时,哪个任务会进入就绪态?
  8. 优先级最高的任务
  9. 如果大家的优先级相同,那等待时间最久的任务会进入就绪态
  10. 跟读队列类似,一个任务要写队列时,如果队列满了,该任务也可以进入阻塞状态:还可以指定阻塞的
  11. 时间。如果队列有空间了,则该阻塞的任务会变为就绪态。如果一直都没有空间,则时间到之后它也会
  12. 进入就绪态。
  13. 既然写队列的任务个数没有限制,那么当多个任务写"满队列"时,这些任务都会进入阻塞状态:有多个
  14. 任务在等待同一个队列的空间。当队列中有空间时,哪个任务会进入就绪态?
  15. 优先级最高的任务
  16. 如果大家的优先级相同,那等待时间最久的任务会进入就绪态

eg1:?

队列的基本操作过程

  • 队列的简化操如入下图所示,从此图可知:
  • 队列可以包含若干个数据:队列中有若干项,这被称为"长度"(length)
  • 每个数据大小固定
  • 创建队列时就要指定长度、数据大小
  • 数据的操作采用先进先出的方法(FIFO,First In First Out):写数据时放到尾部,读数据时从头部
  • 也可以强制写队列头部:覆盖头部数据 

 更详细的操作入下图所示:

【队列传输数据的两种方式】

使用队列传输数据时有两种方法:

  1. 拷贝:把数据、把变量的值复制进队列里
  2. 引用:把数据、把变量的地址复制进队列里

FreeRTOS使用拷贝值的方法,这更简单:

  1. 局部变量的值可以发送到队列中,后续即使函数退出、局部变量被回收,也不会影响队列中的数据
  2. 无需分配buffer来保存数据,队列中有buffer
  3. 局部变量可以马上再次使用
  4. 发送任务、接收任务解耦:接收任务不需要知道这数据是谁的、也不需要发送任务来释放数据
  5. 如果数据实在太大,你还是可以使用队列传输它的地址
  6. 队列的空间有FreeRTOS内核分配,无需任务操心
  7. 对于有内存保护功能的系统,如果队列使用引用方法,也就是使用地址,必须确保双方任务对这个
  8. 地址都有访问权限。使用拷贝方法时,则无此限制:内核有足够的权限,把数据复制进队列、再把
  9. 数据复制出队列

def: 队列读取任务时,被读取到的任务会被移出队列,后一个队列项中的数据会向前移动一位

队列结构体介绍

队列结构体介绍(熟悉)
typedef struct QueueDefinition{

     int8_t* pcHead     存储区域起始地址
     int8_t* pcWrite    下一个写入位置

     union{
            QueuePointers_t xQueue;
            SemaphoreData_t xSemaphore;
     }u;

     // 等待发送列表
     List_t xTasksWaitingToSend;
     // 等待接收列表
     List_t xTaskWaitingToReceive;
     // 非空闲队列项目数量
     volatile UBaseType_t uxMessagesWaiting
     // 队列长度
     UBaseType_t uxLength 
     // 队列项目的大小
     UBaseType_t uxItemSize
     // 读取上锁计数器
     volatile int8_t cRxLock
     // 写入上锁计数器
     volatile int8_t cTxLock

}xQueue

队列结构体整体示意图

队列函数

使用队列的流程:创建队列、写队列、读队列、删除队列。

队列的创建有两种方法:动态分配内存、静态分配内存,
            动态分配内存:xQueueCreate,队列的内存在函数内部动态分配
函数原型如下

QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );

静态分配内存:xQueueCreateStatic,队列的内存要事先分配好

函数原形:

QueueHandle_t xQueueCreateStatic(
UBaseType_t uxQueueLength,
UBaseType_t uxItemSize,
uint8_t *pucQueueStorageBuffer,
StaticQueue_t *pxQueueBuffer
);

示例代码:

// 示例代码
#define QUEUE_LENGTH 10
#define ITEM_SIZE sizeof( uint32_t )
// xQueueBuffer用来保存队列结构体
StaticQueue_t xQueueBuffer;
// ucQueueStorage 用来保存队列的数据
// 大小为:队列长度 * 数据大小
uint8_t ucQueueStorage[ QUEUE_LENGTH * ITEM_SIZE ];
void vATask( void *pvParameters )
{
QueueHandle_t xQueue1;
// 创建队列: 可以容纳QUEUE_LENGTH个数据,每个数据大小是ITEM_SIZE
xQueue1 = xQueueCreateStatic( QUEUE_LENGTH,
ITEM_SIZE,
ucQueueStorage,
&xQueueBuffer );
}

消息队列创建代码案例

static int sum = 0;
static volatile int flagCalcEnd = 0;
static volatile int flagUARTused = 0;
static QueueHandle_t xQueueCalcHandle;
static QueueHandle_t xQueueUARTcHandle;


int InitUARTLock(void)
{	
	int val;
	// 创建一个队列,第一个是队列的
	xQueueUARTcHandle = xQueueCreate(1, sizeof(int));
	// 判断队列是否创建成功
	if (xQueueUARTcHandle == NULL)
	{
		printf("can not create queue\r\n");
		return -1;
	}
	xQueueSend(xQueueUARTcHandle, &val, portMAX_DELAY);
	return 0;
}

void GetUARTLock(void)
{	
	int val;
	xQueueReceive(xQueueUARTcHandle, &val, portMAX_DELAY);
}

void PutUARTLock(void)
{	
	int val;
	xQueueSend(xQueueUARTcHandle, &val, portMAX_DELAY);
}


void Task1Function(void * param)
{
	volatile int i = 0;
	while (1)
	{
		for (i = 0; i < 10000000; i++)
			sum++;
		//printf("1");
		//flagCalcEnd = 1;
		//vTaskDelete(NULL);
		xQueueSend(xQueueCalcHandle, &sum, portMAX_DELAY);
		sum = 1;
	}
}

void Task2Function(void * param)
{
	int val;
	
	while (1)
	{
		//if (flagCalcEnd)
		flagCalcEnd = 0;
		xQueueReceive(xQueueCalcHandle, &val, portMAX_DELAY);
		flagCalcEnd = 1;
		printf("sum = %d\r\n", val);
	}
}

void TaskGenericFunction(void * param)
{
	while (1)
	{
		GetUARTLock();
		printf("%s\r\n", (char *)param);
		   // task 3 is waiting
		PutUARTLock(); /* task 3 ==> ready, task 4 is running  */
		vTaskDelay(1);
	}
}


/*-----------------------------------------------------------*/

int main( void )
{
	TaskHandle_t xHandleTask1;
		
#ifdef DEBUG
  debug();
#endif

	prvSetupHardware();

	printf("Hello, world!\r\n");

	xQueueCalcHandle = xQueueCreate(2, sizeof(int));
	if (xQueueCalcHandle == NULL)
	{
		printf("can not create queue\r\n");
	}

	InitUARTLock();

	xTaskCreate(Task1Function, "Task1", 100, NULL, 1, &xHandleTask1);
	xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);

	xTaskCreate(TaskGenericFunction, "Task3", 100, "Task 3 is running", 1, NULL);
	xTaskCreate(TaskGenericFunction, "Task4", 100, "Task 4 is running", 1, NULL);

	/* Start the scheduler. */
	vTaskStartScheduler();

	/* Will only get here if there was not enough heap space to create the
	idle task. */
	return 0;
}

【以上内容参考自正点原子和韦东山老师课程以及文件,后面会继续补充完善】

.......

  • 26
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值