STM32-FreeRTOS消息队列
STM32—FreeRTOS消息队列
一、简介
1、队列简介:
队列 :是任务到任务,任务到中断、中断到任务数据交流的一种机制(消息传递)。
FreeRTOS基于队列,实现了多种功能,其中包括队列集、互斥信号量、计数型信号量、二值信号量、递归互斥信号量,因此很有必要深入了解FreeRTOS的队列。
(中断一关闭,就不会出现任务切换,以防多个任务同时操作队列)
2、FreeRTOS队列特点:
1.数据入队出队方式:先进先出
2.数据传递方式:实际值
3.多任务访问
4 出队、入队堵塞
问题:当多个任务写入消息给一个“满队列”时,这些任务都会进入阻塞状态,也就是说有多个任务 在等待同一
个队列的空间。那当队列中有空间时,哪个任务会进入就绪态?
答: 1、优先级最高的任务 2、如果大家的优先级相同,那等待时间最久的任务会进入就绪态
注:我始终认为自己不是一个很聪明的人,所以这些理论知识,我都是浅尝辄止,量力而行。
3、往队列写入消息API函数 :
4、从队列读取消息API函数:
二、实验
1、实验步骤
2、代码:
main.c #include “stm32f10x.h” #include “FreeRTOS.h” #include “task.h” #include “freertos_demo.h” #include “Delay.h” #include “sys.h” #include “usart.h” #include “LED.h” #include “Key.h” int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组 4 uart_init(115200); delay_init(); Key_Init(); LED_Init(); // 创建任务 FrrrRTOS_Demo(); } freertos_demo.c #include “FreeRTOS.h” #include “task.h” #include “queue.h” #include “LED.h” #include “Key.h” #include “usart.h” #include “delay.h” /**************任务配置/ //任务优先级 #define START_TASK_PRIO 1 //任务堆栈大小 #define START_TASK_STACK_SIZE 128 //任务句柄 TaskHandle_t StartTask_Handler; //任务函数 void start_task(void *pvParameters); //任务优先级 #define TASK1_PRIO 2 //任务堆栈大小 #define TASK1_STACK_SIZE 128 //任务句柄 TaskHandle_t Task1_Handler; //任务函数 void task1(void *pvParameters); //任务优先级 #define TASK2_PRIO 3 //任务堆栈大小 #define TASK2_STACK_SIZE 128 //任务句柄 TaskHandle_t Task2_Handler; //任务函数 void task2(void *pvParameters); //任务优先级 #define TASK3_PRIO 4 //任务堆栈大小 #define TASK3_STACK_SIZE 128 //任务句柄 TaskHandle_t Task3_Handler; //任务函数 void task3(void *pvParameters); char task_buffer[500]; //用于存储系统中任务信息表格 /****任务函数/ QueueHandle_t key_queue; //小数据句柄 QueueHandle_t big_data_queue; //大数据 句柄 char buff[100] = {“苍天已死,黄天当立;岁在甲子,天下大吉”}; void FrrrRTOS_Demo(void) { key_queue = xQueueCreate(2, sizeof(uint8_t)); if(key_queue != NULL) { printf("\r\nkey_queue队列创建成功!!!\r\n"); }else{ printf(“key_queue队列创建失败!!!\r\n”); } big_data_queue = xQueueCreate(1, sizeof(char )); if(big_data_queue != NULL) { printf(“big_data_queue队列创建成功!!!\r\n”); }else{ printf(“big_data_queue队列创建失败!!!\r\n”); } //创建开始任务 xTaskCreate((TaskFunction_t )start_task, //任务函数 ( char )“start_task”, //任务名称 (uint16_t )START_TASK_STACK_SIZE, //任务堆栈大小 (void )NULL, //传递给任务函数的参数 (UBaseType_t )START_TASK_PRIO, //任务优先级 (TaskHandle_t )&StartTask_Handler); //任务句柄 // 启动任务调度 vTaskStartScheduler(); } void start_task(void pvParameters) { taskENTER_CRITICAL(); //进入临界区 //创建1任务 xTaskCreate((TaskFunction_t )task1, (const char )“task1”, (uint16_t )TASK1_STACK_SIZE, (void )NULL, (UBaseType_t )TASK1_PRIO, (TaskHandle_t )&Task1_Handler); //创建2任务 xTaskCreate((TaskFunction_t )task2, (const char )“task2”, (uint16_t )TASK2_STACK_SIZE, (void )NULL, (UBaseType_t )TASK2_PRIO, (TaskHandle_t )&Task2_Handler); //创建3任务 xTaskCreate((TaskFunction_t )task3, (const char )“task3”, (uint16_t )TASK3_STACK_SIZE, (void )NULL, (UBaseType_t )TASK3_PRIO, (TaskHandle_t )&Task3_Handler); vTaskDelete(NULL); //删除开始任务 taskEXIT_CRITICAL(); //退出临界区 } //1 任务函数 void task1(void *pvParameters) { uint8_t key = 0; BaseType_t err; char *buf; buf = &buff[0]; while(1) { key = Key_GetNum(); if(key == 1 || key == 2) { err = xQueueSend( key_queue, &key, portMAX_DELAY ); if(err != pdTRUE) { printf(“key_queue队列发送失败\r\n”); } }else if(key == 3) { err = xQueueSend( big_data_queue, &buf, portMAX_DELAY ); if(err != pdTRUE) { printf(“key_queue队列发送失败\r\n”); } } vTaskDelay(50); } } // 任务2 小数据出队函数 void task2(void pvParameters) { uint8_t key = 0; BaseType_t err = 0; // 任务主循环 while (1) { err = xQueueReceive( key_queue,&key,portMAX_DELAY ); if(err != pdTRUE) { printf(“key_queue队列读取失败\r\n”); }else{printf(“key = %d\r\n”,key);}; } } //不调用系统延时函数,因为xQueueReceive()函数如果读取完队列里面的数据,就会由就绪态转变为阻塞态; // 任务3 大数据出队函数 void task3(void pvParameters) { char * buf; BaseType_t err = 0; // 任务主循环 while (1) { err = xQueueReceive( big_data_queue, &buf, portMAX_DELAY); if(err != pdTRUE) { printf(“big_data_queue队列读取失败\r\n”); }else{printf(“key = %s\r\n”,buf);}; } } key.c #include “stm32f10x.h” // Device header #include “FreeRTOS.h” #include “task.h” #include “usart.h” #include “Delay.h” /
- 函 数:按键初始化
- 参 数:无
- 返 回 值:无
- 按键:PB4/PB12/PB14 / void Key_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; /开启时钟/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟 /GPIO初始化/ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_12 | GPIO_Pin_14; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); } /*
- 函 数:按键获取键码
- 参 数:无
- 返 回 值:按下按键的键码值,范围:0~3,返回0代表没有按键按下
- 注意事项:此函数是阻塞式操作,当按键按住不放时,函数会卡住,直到按键松手 */ uint8_t Key_GetNum(void) { uint8_t KeyNum = 0; //定义变量,默认键码值为0 if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_4) == 0) //读PB4输入寄存器的状态,如果为0,则代表按键1按下 { KeyNum= GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_4); printf(“KeyNum = %d\r\n”,KeyNum); delay_xms(20); //延时消抖 while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_4) == 0); //等待按键松手 delay_xms(20); //延时消抖 KeyNum = 1; //置键码为1 } if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12) == 0) { KeyNum= GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12); printf(“KeyNum = %d\r\n”,KeyNum); delay_xms(20); while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12) == 0); delay_xms(20); KeyNum = 2; } if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0) { KeyNum= GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14); printf(“KeyNum = %d\r\n”,KeyNum); delay_xms(20); while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0); delay_xms(20); KeyNum = 3; } return KeyNum; //返回键码值,如果没有按键按下,所有if都不成立,则键码为默认值0 }
3、实验结果解析
开始运行:
按下按键1(PB4):
按下按键1,就会往队列key_queue里面写入key值(1),然后任务切换到task2将队列key_queue里面的数据读取出来;;
按下按键2(PB12):
按下按键2,就会往队列key_queue里面写入key值(2),然后任务切换到task2将队列key_queue里面的数据读取出来;
按下按键3(PB14) :
按下按键2,就会往队列big_data_queue里面写入key值(3),然后任务切换到task3将队列big_data_queue里面的数据读取出来;
三、重点
使用队列相关函数时需要将下面宏置1(默认是1):
#define configSUPPORT_DYNAMIC_ALLOCATION 1 队列创建函数: xQueueCreate( uxQueueLength, uxItemSize ) ; //uxQueueLength:队列长度;uxItemSize 队列参数的大小 队列写入消息函数: xQueueSend( xQueue, pvItemToQueue, xTicksToWait ); //xQueue:待写入的队列;pvItemToQueue:待写入的消息;xTicksToWait:阻塞超时时间 队列读取消息函数: xQueueReceive( QueueHandle_t xQueue,void * const pvBuffer,TickType_t xTicksToWait ) ; //xQueue:待读取的队列;pvBuffer:信息读取缓冲区;xTicksToWait:阻塞超时时间 问题:任务2(task2)和任务3(task3)没有系统延时函数(xTaskDelay()),按优先级来说应该一直执行任务3(task3),复位后却先执行了任务1(task1)? 答:因为xQueueReceive()和xQueueSend()函数,如果读取完或写入完队列里面的数据,自动会使任务由就绪态转变为阻塞态,知道队列里面有数据可以写入或者读出;