目录
1. 队列的理论讲解
1.1 常规操作
2. 队列的常规使用
3. 队列集
队列的简化操如入下图所示,从此图可知:
队列可以包含若干个数据:队列中有若干项,这被称为"长度"(length)
每个数据大小固定
创建队列时就要指定长度、数据大小
数据的操作采用先进先出的方法(FIFO,First In First Out):写数据时放到尾部,读数据时从头部
读
也可以强制写队列头部:覆盖头部数据
本节源码:13_freertos_example_queue
,基于12_freertos_example_sync_exclusion
修改。
猜测队列中应该有:
以下为xQueueCreate 队列源码:
创建环形队列的图解:
写队列
读队列
往头部写队列,xQueueSendToFront 这个函数写的数据,会最先被读出来,因为数据被插到了队列的头部。
假如已经在0、1位置已经写入了数据,之后再头部写入数据会在N-1③位置,在向后移动指针pcReadFrom,pcReadFrom -= ItemSize,因为pcReadFrom表示的是上一次的位置,所以向后移动之后再次读到的数据为N-1③位置。
有很多个写队列,或者有很多个读队列时,都有等待函数,那现在该唤醒谁呢?
一般会先执行优先级最高的那个,或者等待时间最长的那个,理所应当是这样的。
本节源码:13_freertos_example_queue
,基于12_freertos_example_sync_exclusion
修改。
队列使用了同步:
任务1、任务2使用队列实现了同步。
任务1计算完数值之后,写入队列中,任务2读取队列,当队列中有数据的时候就会打印出来,当队列中没有数据的时候就会进入到阻塞状态,任务2在等待数据的过程中,就不会参与cpu的调度。
static int sum = 0;
static volatile int flagCalcEnd = 0;
static QueueHandle_t xQueueCalcHandle;
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);
}
}
/*-----------------------------------------------------------*/
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);
/* Start the scheduler. */
vTaskStartScheduler();
/* Will only get here if there was not enough heap space to create the
idle task. */
return 0;
}
运行结果,在 “四、freeRTOS_同步互斥与通信概述” 中,任务2与任务1竞争cpu的资源,导致任务1实现同步花了4s,现在使用队列同步的方法来做,任务2在等待数据的过程中,就不会参与cpu的调度,此时任务2大概花费了2s。
URAT1串口打印
队列使用了互斥:
任务3、任务4使用队列实现了互斥。
临界资源是 TaskGenericFunction() 函数,使用初始化串口锁,上锁,下锁,来完成队列的互斥。
/*-----------------------------------------------------------*/
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;
}
运行结果:
任务3、任务4在互斥的交替执行。
传输大块数据:
FreeRTOS的队列使用拷贝传输,也就是要传输uint32_t时,把4字节的数据拷贝进队列;要传输一个8字节的结构体时,把8字节的数据拷贝进队列。
如果要传输1000字节的结构体呢?写队列时拷贝1000字节,读队列时再拷贝1000字节?不建议这么做,影响效率!
这时候,我们要传输的是这个巨大结构体的地址:把它的地址写入队列,对方从队列得到这个地址,使用地址去访问那1000字节的数据。
使用地址来间接传输数据时,这些数据放在RAM里,对于这块RAM,要保证这几点:
程序会创建一个队列,然后创建1个发送任务、1个接收任务:
这个程序故意设置接收任务的优先级更高,在它访问数组的过程中,接收任务无法执行、无法写这个数组。
结构体读写队列:
字符串读写队列:
本节源码:14_freertos_example_queue_set
从多个队列中得到数据,就使用队列集。
在实际工作中需要从多个队列中得到数据吗?是需要的。
如果你的鼠标既支持鼠标、键盘、触摸屏,任意一个队列有数据,都可以唤醒app去工作,这时候就需要用到队列集合。
让Task1、Task2往Queue1、Queue2中写数据,使用Task3,监测两个Queue。
main()函数
任务执行过程
static volatile int flagCalcEnd = 0;
static volatile int flagUARTused = 0;
static QueueHandle_t xQueueHandle1;
static QueueHandle_t xQueueHandle2;
static QueueSetHandle_t xQueueSet;
void Task1Function(void * param)
{
int i = 0;
while (1)
{
xQueueSend(xQueueHandle1, &i, portMAX_DELAY);
i++;
vTaskDelay(10);
}
}
void Task2Function(void * param)
{
int i = -1;
while (1)
{
xQueueSend(xQueueHandle2, &i, portMAX_DELAY);
i--;
vTaskDelay(20);
}
}
void Task3Function(void * param)
{
QueueSetMemberHandle_t handle;
int i;
while (1)
{
/* 1. read queue set: which queue has data */
handle = xQueueSelectFromSet(xQueueSet, portMAX_DELAY);
/* 2. read queue */
xQueueReceive(handle, &i, 0);
/* 3. print */
printf("get data : %d\r\n", i);
}
}
/*-----------------------------------------------------------*/
int main( void )
{
TaskHandle_t xHandleTask1;
#ifdef DEBUG
debug();
#endif
prvSetupHardware();
printf("Hello, world!\r\n");
/* 1. 创建2个queue */
xQueueHandle1 = xQueueCreate(2, sizeof(int));
if (xQueueHandle1 == NULL)
{
printf("can not create queue\r\n");
}
xQueueHandle2 = xQueueCreate(2, sizeof(int));
if (xQueueHandle2 == NULL)
{
printf("can not create queue\r\n");
}
/* 2. 创建queue set */
xQueueSet = xQueueCreateSet(3);
/* 3. 把2个queue添加进queue set */
xQueueAddToSet(xQueueHandle1, xQueueSet);
xQueueAddToSet(xQueueHandle2, xQueueSet);
/* 4. 创建3个任务 */
xTaskCreate(Task1Function, "Task1", 100, NULL, 1, &xHandleTask1);
xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);
xTaskCreate(Task3Function, "Task3", 100, NULL, 1, NULL);
/* Start the scheduler. */
vTaskStartScheduler();
/* Will only get here if there was not enough heap space to create the
idle task. */
return 0;
}