FreeRTOS 队列使用
对FreeRTOS来说,队列是一种很基本数据结构,其中计数信号量,二值信号量等都是利用队列来做的;在FreeRTOS中,队列用于任务与任务之间,任务与中断服务子程序之间的数据传输,其作用类似于裸机编程时使用的全局变量,与全局变量不同的是,队列有阻塞超时时间;任务之间使用队列要遵循FIFO的特性
当一个任务读取空队列时,这个任务将进入阻塞态(不消耗cpu,cpu会去运行其他任务),直到队列不为空或者阻塞时长超过设定的阻塞时间,将进入就绪态。
当一个任务向满队列写时,这个任务会进入阻塞态(不消耗cpu,cpu会去运行其他任务),直到队列不为满或者阻塞时长超过设定的阻塞时间,将进入就绪态。
如果有多个任务因为同一个队列而进入阻塞态,当队列满足条件的时候,优先级最高的任务先解除阻塞态,其他任务继续阻塞。
中断里面只能使用后缀带有“FromISR”的API
普通任务队列的初始化
#define MESSAGE_Q_NUM 4 //队列项目数
#define Queue_BUF_SIZE 4 //每个队列项目的大小
QueueHandle_t Message_Queue; //串口信息队列句柄
#define QUE_TASK_NUM 10 //队列深度
QueueHandle_t Task_Queue; //串口信息队列句柄
创建队列在start_task里面
QueueHandle_t xQueueCreate(UBaseType_t uxQueueLength, UBaseType_t uxItemSize);
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
Message_Queue=xQueueCreate(MESSAGE_Q_NUM,Queue_BUF_SIZE); //创建一个消息队列
Task_Queue=xQueueCreate(QUE_TASK_NUM,sizeof(u8)); //创建一个任务队列 队列的深度为10 每个深度的大小为u8的大小,为32位的无符号整形数。
xTaskCreate((TaskFunction_t ) led_task, //任务函数
(const char* )"led_task", //任务名称
(uint16_t )LED_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )LED_TASK_PRIO, //任务优先级
(TaskHandle_t* )&LEDTask_Handler);//任务句柄
普通任务队列入队
void led_task(void *pvParameters)
{
u8 task_val=0;
while(1)
{
u8 i=0;
LED0=~LED0;
for(i=0;i<10;i++)
{
xQueueSend(Task_Queue,&task_val,0);// 入队 一次只能一个深度,循环入队到多个深度 即一次入队一个u8类型的数据,分10次入队,则队列满。
task_val++;
}
vTaskDelay(400);
}
}
普通任务队列出队
void dcm250b_task(void *pvParameters)
{
u8 task_data[10]={0};
while(1)
{
xQueueReceive(Task_Queue,task_data,portMAX_DELAY);// 将出队的数据保存在task_data 中,
xQueueReceive(Task_Queue,task_data+1,portMAX_DELAY);
USART2_data(task_data,2);
}
}
portMAX_DELAY为无限延时,即执行到该函数时队列无数据则无限期阻塞,以下的函数语句不会执行,该任务会处于无限期阻塞状态,直到队列有数据来。
xQueueReceive出队保存数据一次只能保存一个深度的数据,执行一次该函数只能保存该函数sizeof(u8)的最大长度。
即UBaseType_t uxItemSize的长度,长度为4则保存4个长度数据,即使队列只有3个数据,多余的数据为0.
portMAX_DELAY为0 则表示不阻塞,有数据则读取队列数据,无数据则返回。任务接收超时 如果在调用时队列为空,任务应等待等待接收项目的最长时间。 如果队列为空,则将xTicksToWait设置为0将导致该函数立即返回。
中断队列入队
在中断函数头文件声明队列信息句柄
extern QueueHandle_t Message_Queue; //信息队列句柄
void USART2_IRQHandler(void) //串口2中断服务程序
{
BaseType_t xHigherPriorityTaskWoken;
xHigherPriorityTaskWoken=pdFALSE;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
{
RxBuffer2[RxCounter2++] = USART_ReceiveData(USART2);
if(RxCounter2>7||RxBuffer2[7]==0xff)//判断结束标志
{
if(Message_Queue!=NULL)
{
xQueueSendFromISR(Message_Queue,RxBuffer2,&xHigherPriorityTaskWoken); //执行一次该函数只能读取该队列的Queue_BUF_SIZE的长度,下一个深度则需要再次执行该语句,才能读取下一个深度的信息。
xQueueSendFromISR(Message_Queue,&RxBuffer2[4],&xHigherPriorityTaskWoken);
memset(RxBuffer2,0,8);//清除数据接收缓冲区USART_RX_BUF,用于下一次数据接收
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
//USART2_data(RxBuffer2,8);
memset(RxBuffer2,0,8);
RxCounter2=0;
}
}
}
BaseType_t xHigherPriorityTaskWoken; 定义该参数为pdFALSE,则表示用于保存是否有高优先级任务准备就绪。如果函数执行完毕后,此参数的数值是pdTRUE,说明有高优先级任务要执行,否则没有。
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
如果pxHigherPriorityTaskWoken==pdFALSE 的话,不会执行portYIELD_FROM_ISR(),如果等于pdTRUE的话,在中断服务函数执行完之后,进行一次任务切换。
如果不调用portYIELD_FROM_ISR(pxHigherPriorityTaskWoken),那么要等到下一个系统节拍xPortSysTickHandler()中切换。
中断队列出队
void TIM3_IRQHandler(void)
{
u8 *Que_data;
BaseType_t xTaskWokenByReceive=pdFALSE;
BaseType_t err;
if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET) //溢出中断
{
if(Message_Queue!=NULL)
{
Que_data=(u8*)malloc(10);
memset(Que_data,0,10);
err=xQueueReceiveFromISR(Message_Queue,Que_data,&xTaskWokenByReceive); //同样需要读取两次才能读取8个字节的数据,一次只能读取4个长度的字节。
err=xQueueReceiveFromISR(Message_Queue,Que_data+4,&xTaskWokenByReceive);//读取队列的下一个深度的信息。
if(err==pdTRUE)//err为返回值,如果返回为pdTRUE则表示读取成功。
{
USART2_data(Que_data,8);
}
free(Que_data);//记得释放内存
}
portYIELD_FROM_ISR(xTaskWokenByReceive);//如果需要的话进行一次任务切换
}
TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除中断标志位
}