一、簡介
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; //定義變量,默認鍵碼值為0if (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()函數,如果讀取完或寫入完隊列里面的數據,自動會使任務由就緒態轉變為阻塞態,知道隊列里面有數據可以寫入或者讀出;