在任何RTOS中,都具有一個重要的通信機制----消息隊列。
隊列是任務間通信的主要形式。它們可用於在任務之間、中斷和任務之間發送消息。在大多數情況下,它們被用作線程安全的FIFO(先進先出)緩沖區,新數據被發送到隊列的后面,不過數據也可以發送到前面。
消息隊列的概念及其作用(以下基礎內容轉載自安富萊電子)
消息隊列就是通過RTOS內核提供的服務,任務或中斷服務子程序可以將一個消息(注意,FreeRTOS消息隊列傳遞的是實際數據,並不是數據地址,RTX,uCOS-II和uCOS-III是傳遞的地址)放入到隊列。同樣,一個或者多個任務可以通過RTOS內核服務從隊列中得到消息。通常,先進入消息隊列的消息先傳給任務,也就是說,任務先得到的是最先進入到消息隊列的消息,即先進先出的原則(FIFO),FreeRTOS的消息隊列支持FIFO和LIFO兩種數據存取方式。 也許有不理解的初學者會問采用消息隊列多麻煩,搞個全局數組不是更簡單,其實不然。在裸機編程時,使用全局數組的確比較方便,但是在加上RTOS后就是另一種情況了。相比消息隊列,使用全局數組主要有如下四個問題:
♦使用消息隊列可以讓RTOS內核有效地管理任務,而全局數組是無法做到的,任務的超時等機制需要用戶自己去實現。
♦使用了全局數組就要防止多任務的訪問沖突,而使用消息隊列則處理好了這個問題,用戶無需擔心。
♦使用消息隊列可以有效地解決中斷服務程序與任務之間消息傳遞的問題。
♦FIFO機制更有利於數據的處理。
這里我將創建兩個線程,monitor Task和 control Task 。通過monitor Task發送消息,control Task接受消息,根據消息類型打印不同的信息。
1.基於STM32Cube配置FreeRTOS,我這里是用的STM32F103RCT6,首先Enable FreeRTOS,配置RCC外部高速晶振8M HZ
2.配置打印串口USART1,HAL時鍾基准源TIM1,RTOS將systick作為時鍾源。
3.配置時鍾樹
4.配置FreeRTOS
5.生成代碼配置
生成的main主函數,System,GPIO,USART,FreeRTOS初始化,一旦啟動線程調度,Task開始執行,main中的while不會進入。
1 int main(void) 2 { 3 /* USER CODE BEGIN 1 */ 4 5 /* USER CODE END 1 */ 6 7 /* MCU Configuration----------------------------------------------------------*/ 8 9 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ 10 HAL_Init(); 11 12 /* USER CODE BEGIN Init */ 13 14 /* USER CODE END Init */ 15 16 /* Configure the system clock */ 17 SystemClock_Config(); 18 19 /* USER CODE BEGIN SysInit */ 20 21 /* USER CODE END SysInit */ 22 23 /* Initialize all configured peripherals */ 24 MX_GPIO_Init(); 25 MX_USART1_UART_Init(); 26 27 28 /* Initialize interrupts */ 29 MX_NVIC_Init(); 30 /* USER CODE BEGIN 2 */ 31 printf("Test Message Queue!\n"); 32 /* USER CODE END 2 */ 33 34 /* Call init function for freertos objects (in freertos.c) */ 35 MX_FREERTOS_Init(); 36 37 /* Start scheduler */ 38 osKernelStart(); 39 40 /* We should never get here as control is now taken by the scheduler */ 41 42 /* Infinite loop */ 43 /* USER CODE BEGIN WHILE */ 44 while (1) 45 { 46 47 /* USER CODE END WHILE */ 48 49 /* USER CODE BEGIN 3 */ 50 51 } 52 /* USER CODE END 3 */ 53 54 }
定義消息結構:
1 /* USER CODE BEGIN Variables */ 2 typedef enum _Msg_Type_ 3 { 4 MSG_TYPE_LED, 5 MSG_TYPE_LED_TOGGLE, 6 MSG_TYPE_BUZZER, 7 MSG_TYPE_MAX 8 }MsgType; 9 10 typedef struct _Msg 11 { 12 MsgType msgType; 13 bool bswitch; 14 }Msg; 15 16 17 /* USER CODE END Variables */
定義Debug_Printf用來打印串口信息
void Debug_Printf(char *format, ...) { char buf_str[128]; va_list v_args; va_start(v_args, format); (void)vsnprintf((char *)&buf_str[0], (size_t ) sizeof(buf_str), (char const *) format, v_args); va_end(v_args); /* 互斥信號量 */ osMutexWait(Mutex_printfHandle,osWaitForever); printf("[%ldms]%s",xTaskGetTickCount(),buf_str); osMutexRelease(Mutex_printfHandle); }
FreeRTOS初始化
/* Init FreeRTOS */ void MX_FREERTOS_Init(void) { /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Create the mutex(es) */ /* definition and creation of Mutex_printf */ osMutexDef(Mutex_printf); Mutex_printfHandle = osMutexCreate(osMutex(Mutex_printf)); /* USER CODE BEGIN RTOS_MUTEX */ /* add mutexes, ... */ /* USER CODE END RTOS_MUTEX */ /* Create the semaphores(s) */ /* USER CODE BEGIN RTOS_SEMAPHORES */ /* add semaphores, ... */ /* USER CODE END RTOS_SEMAPHORES */ /* USER CODE BEGIN RTOS_TIMERS */ /* start timers, add new ones, ... */ /* USER CODE END RTOS_TIMERS */ /* Create the thread(s) */ /* definition and creation of vTask1 */ osThreadDef(vTask1, monitor_task, osPriorityNormal, 0, 256); vTask1Handle = osThreadCreate(osThread(vTask1), NULL); /* definition and creation of vTaks2 */ osThreadDef(vTaks2, control_task, osPriorityNormal, 0, 256); vTaks2Handle = osThreadCreate(osThread(vTaks2), NULL); /* USER CODE BEGIN RTOS_THREADS */ /* add threads, ... */ /* USER CODE END RTOS_THREADS */ /* Create the queue(s) */ /* definition and creation of msgQueue */ osMessageQDef(msgQueue, 16, Msg); //定義消息隊列的深度及負載大小 msgQueueHandle = osMessageCreate(osMessageQ(msgQueue), NULL); /* USER CODE BEGIN RTOS_QUEUES */ /* add queues, ... */ /* USER CODE END RTOS_QUEUES */ } /* monitor_task function */ void monitor_task(void const * argument) { /* USER CODE BEGIN monitor_task */ int cnt = 0; Msg msg; Debug_Printf("Enter monitor task!\n"); /* Infinite loop */ for(;;) { cnt++; if(cnt == 1 ) { msg.msgType = MSG_TYPE_LED; msg.bswitch = true; xQueueSend(msgQueueHandle,&msg,10); } else if(cnt == 5) { msg.msgType = MSG_TYPE_LED; msg.bswitch = false; xQueueSend(msgQueueHandle,&msg,10); } else if(cnt == 10) { msg.msgType = MSG_TYPE_LED_TOGGLE; msg.bswitch = true; xQueueSend(msgQueueHandle,&msg,10); } osDelay(1000); } /* USER CODE END monitor_task */ } /* control_task function */ void control_task(void const * argument) { /* USER CODE BEGIN control_task */ Msg tempMsg ; Debug_Printf("Enter control task!\n"); /* Infinite loop */ for(;;) { xQueueReceive(msgQueueHandle,(void*)&tempMsg,osWaitForever); switch(tempMsg.msgType) { case MSG_TYPE_LED: if(tempMsg.bswitch) { Debug_Printf("LED is Open!\n"); } else { Debug_Printf("LED is Closed!\n"); } break; case MSG_TYPE_LED_TOGGLE: if(tempMsg.bswitch) { Debug_Printf("LED is Toggling!\n"); } else { Debug_Printf("LED is off!\n"); } break; default: break; } osDelay(10); } /* USER CODE END control_task */ }
測試結果:通過系統時間戳可以看出,首先monitor Task 發送消息類型MSG_TYPE_LED,
消息負載為Open,過了4秒發送MSG_TYPE_LED,消息負載為Close;再過5秒發送MSG_TYPE_LED_TOGGLE