在嵌入式系統領域,嵌入式實時操作系統(RTOS) 的應用正日益廣泛,采用RTOS能夠更合理、更高效地利用CPU資源,FreeRTOS作為一款輕量級且成熟的實時操作系統內核,其核心功能完備,包括任務管理、時間管理(如延時、定時器)、同步機制(信號量、互斥鎖)、進程間通信(消息隊列)等等。這些特性使其能夠很好地滿足資源相對有限的中小型嵌入式系統的需求。
i.MX 9352作為NXP 推出的新一代輕量級邊緣AI處理器,集成2個Cortex-A55核和1個Cortex-M33實時核,其架構設計充分體現了對實時性與復雜任務處理能力的兼顧。為了幫助開發者充分利用i.MX 9352 M33核的實時能力,其配套的M核SDK包提供的FreeRTOS例程分為兩類,一類介紹FreeRTOS系統組件特性,如信號量、互斥量、隊列等,另一類是介紹外設接口如何在FreeRTOS使用,我們分別挑選這兩類下的例程進行演示。
演示平臺:飛凌嵌入式OK-MX9352-C開發板

1、FreeRTOS-generic
飛凌嵌入式OK-MX9352-C開發板支持FreeRTOS功能特性示例代碼如下:
- freertos_event:任務事件演示例程
- freertos_queue:隊列消息實現任務間通信的演示例程
- freertos_mutex:互斥鎖使用例程
- freertos_sem:信號量使用例程
- freertos_swtimer:軟件計數器及其回調的用法。
- freertos_tickless:使用 LPTMR 延時喚醒或者硬件中斷喚醒例程
- freertos_generic:task、queue、swtimer、tick hook 、semaphore 組合利用演示例程。
因FreeRTOS_generic例程使用的FreeRTOS特性較多,我們重點分析此例程。
(1)軟件實現
示例程序內容包括:任務創建、隊列、軟定時器、系統節拍時鐘、信號量、異常處理。具體如下:
任務創建:
主函數創建了隊列發送、接收,信號量三個任務。
// 創建隊列接收任務 if(xTaskCreate(prvQueueReceiveTask,"Rx",configMINIMAL_STACK_SIZE+166,NULL,mainQUEUE_RECEIVE_TASK_PRIORITY,NULL)!=pdPASS) // 創建隊列發送任務 if(xTaskCreate(prvQueueSendTask,"TX",configMINIMAL_STACK_SIZE+166, NULL, mainQUEUE_SEND_TASK_PRIORITY, NULL) !=pdPASS) // 創建信號量任務 if(xTaskCreate(prvEventSemaphoreTask,"Sem",configMINIMAL_STACK_SIZE+166,NULL,mainEVENT_SEMAPHORE_TASK_PRIORITY, NULL) != pdPASS)
隊列:
隊列發送任務,阻塞200ms后向隊列發送數據;隊列接收任務,任務阻塞讀取隊列,數據讀取正確,則打印此時的隊列接收數量。
// 隊列發送任務,阻塞200ms后 向隊列發送數據 static void prvQueueSendTask(void *pvParameters) { TickType_t xNextWakeTime; const uint32_t ulValueToSend = 100UL; xNextWakeTime = xTaskGetTickCount(); for (;;) { // 任務阻塞,直至200ms延時結束 vTaskDelayUntil(&xNextWakeTime, mainQUEUE_SEND_PERIOD_MS); // 向隊列發送數據,阻塞時間為0表示當隊列滿的時候就立即返回 xQueueSend(xQueue, &ulValueToSend, 0); } } // 隊列接收任務,任務阻塞讀取隊列,數據讀取正確,則打印此時的隊列接收數量。 static void prvQueueReceiveTask(void *pvParameters) { uint32_t ulReceivedValue; for (;;) { // 任務一直阻塞,知道隊列內讀取到數據 xQueueReceive(xQueue, &ulReceivedValue, portMAX_DELAY); // 隊列數據和發送一致,隊列接收數量+1 輸出此時的隊列接收數量 if (ulReceivedValue == 100UL) { ulCountOfItemsReceivedOnQueue++; PRINTF("Receive message counter: %d.\r\n", ulCountOfItemsReceivedOnQueue); } } }
軟定時器:
設置軟定時器周期1s,時間到后,調用回調函數,記錄次數并串口打印。
// 創建軟件定時器任務 時間為1s,周期循環 xExampleSoftwareTimer = xTimerCreate( "LEDTimer", mainSOFTWARE_TIMER_PERIOD_MS, pdTRUE, (void *)0, vExampleTimerCallback); // 啟動軟件定時器 xTimerStart(xExampleSoftwareTimer, 0); // 回調函數 static void vExampleTimerCallback(TimerHandle_t xTimer) { // 每1s進入一次回調函數,計數增加 ulCountOfTimerCallbackExecutions++; PRINTF("Soft timer: %d s.\r\n", ulCountOfTimerCallbackExecutions); }
系統節拍時鐘:
通過設置文件 FreeRTOSConfig.h 中 configTICK_RATE_HZ 設置任務節拍中斷頻率, 在啟動任務調度器時,系統會根據另一個變量CPU的頻率configCPU_CLOCK_HZ計算對應寫入節拍計數器的值,啟動定時器中斷。
// 設置系統時鐘節拍為 1000/200=5ms #define configTICK_RATE_HZ ((TickType_t)200)
信號量:
每個系統節拍時鐘中斷中,調用函數vApplicationTickHook,累積500次即500*5ms=2.5s后,發送信號量。信號量任務獲取信號后,計數并打印累積次數。
// 系統節拍為5ms,每個500*5ms=2.5s 釋放事件信號量 void vApplicationTickHook(void) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; static uint32_t ulCount = 0; ulCount++; if (ulCount >= 500UL) { // 在中斷中釋放事件信號量 xSemaphoreGiveFromISR(xEventSemaphore, &xHigherPriorityTaskWoken); ulCount = 0UL; } } // 任務阻塞等待信號量,收到后,接收次數增加,并通過串口打印 static void prvEventSemaphoreTask(void *pvParameters) { for (;;) { // 任務阻塞,直到能獲取信號量 if (xSemaphoreTake(xEventSemaphore, portMAX_DELAY) != pdTRUE) { PRINTF("Failed to take semaphore.\r\n"); } // 接收到信號量的次數累加 ulCountOfReceivedSemaphores++; PRINTF("Event task is running. Get semaphore :%d \r\n",ulCountOfReceivedSemaphores); } }
異常處理:
當內存分配失敗、堆棧發生錯誤或任務空閑時,進入相應的函數,用戶可添加相應的處理函數。
// 內存分配失敗函數,當內存分配失敗時,進入此函數 void vApplicationMallocFailedHook(void) { for (;;) ; } // 堆棧錯誤檢查函數,當堆棧發生溢出時,進入此函數 void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) { (void)pcTaskName; (void)xTask; for (;;) ; } // 空閑任務,優先級最低,沒有實際意義,只是讓CPU有事情做,用戶可以自己添加自己的函數 void vApplicationIdleHook(void) { volatile size_t xFreeStackSpace; xFreeStackSpace = xPortGetFreeHeapSize(); if (xFreeStackSpace > 100) { } }
(2)實驗現象
① 編譯程序:在uboot手動加載M核程序。
② 隊列:每隔200ms,隊列發送任務發送數據,隊列接收任務獲取數據,從阻塞態到運行態,打印計數。
③ 軟定時器:每隔1s,時間到達,調用回調函數,打印計數。
④ 信號量:每隔5ms,系統時鐘節拍中斷調用函數,超過500次后,釋放信號量。信號量任務獲的信號量,從阻塞態到運行態,打印計數。

2、FreeRTOS-外設
飛凌嵌入式OK-MX9352-C開發板支持外設使用FreeRTOS完成相應功能,示例代碼如下:
因freertos_uart例程使用的FreeRTOS特性比較典型,我們重點分析此例程。
(1)軟件實現
示例程序內容包括:串口初始化任務、串口發送任務、串口接收任務。具體如下:
串口初始化任務:
主要包含串口外設初始化,發送、接收互斥量,發送和接收事件組。串口外設初始化在裸跑串口例程中已展現,此處不再詳述。
// 創建串口發送互斥量 handle->txSemaphore = xSemaphoreCreateMutex(); // 創建串口接收互斥量 handle->rxSemaphore = xSemaphoreCreateMutex(); // 創建發送事件標志組 handle->txEvent = xEventGroupCreate(); // 創建接收事件標志組 handle->rxEvent = xEventGroupCreate();
串口發送:
發送前獲取信號量,啟動發送流程,在中斷中置位發送完成事件標志。發送任務獲取到事件后,釋放發送信號量。
// 1 獲取發送信號量 if (pdFALSE == xSemaphoreTake(handle->txSemaphore, 0)) { return kStatus_Fail; } handle->txTransfer.data = (uint8_t *)buffer; handle->txTransfer.dataSize = (uint32_t)length; // 2 阻塞式發送 status = UART_TransferSendNonBlocking(handle->base, handle->t_state, &handle->txTransfer); if (status != kStatus_Success) { (void)xSemaphoreGive(handle->txSemaphore); return kStatus_Fail; } // 3 等待發送完成的事件 ev = xEventGroupWaitBits(handle->txEvent, RTOS_UART_COMPLETE, pdTRUE, pdFALSE, portMAX_DELAY);// 等待并判斷多個事件位 if ((ev & RTOS_UART_COMPLETE) == 0U) { retval = kStatus_Fail; } // 4 發送完成,釋放發送信號量 if (pdFALSE == xSemaphoreGive(handle->txSemaphore)) // 釋放信號量 { retval = kStatus_Fail; }
串口接收:
接收前獲取信號量,調用串口接收函數,在中斷中置位獲取事件標志。接收任務獲取到事件后,釋放接收信號量。
// 1獲取接收信號量 if (pdFALSE == xSemaphoreTake(handle->rxSemaphore, portMAX_DELAY)) { return kStatus_Fail; } handle->rxTransfer.data = buffer; handle->rxTransfer.dataSize = (uint32_t)length; // 2 串口接收函數 status = UART_TransferReceiveNonBlocking(handle->base, handle->t_state, &handle->rxTransfer, &n); if (status != kStatus_Success) { (void)xSemaphoreGive(handle->rxSemaphore); return kStatus_Fail; } // 3 獲取接收事件 ev = xEventGroupWaitBits(handle->rxEvent,RTOS_UART_COMPLETE | RTOS_UART_RING_BUFFER_OVERRUN | RTOS_UART_HARDWARE_BUFFER_OVERRUN, pdTRUE, pdFALSE, portMAX_DELAY); // 等待并判斷接收完成事件位 // 3.1 硬件接收錯誤 if ((ev & RTOS_UART_HARDWARE_BUFFER_OVERRUN) != 0U) { UART_TransferAbortReceive(handle->base, handle->t_state); (void)xEventGroupClearBits(handle->rxEvent, RTOS_UART_COMPLETE); // 將接收完成的事件位清零, retval = kStatus_UART_RxHardwareOverrun; local_received = 0; } // 3.2 接收緩沖區過載錯誤 else if ((ev & RTOS_UART_RING_BUFFER_OVERRUN) != 0U) { UART_TransferAbortReceive(handle->base, handle->t_state); (void)xEventGroupClearBits(handle->rxEvent, RTOS_UART_COMPLETE); // 將接收完成的事件位清零, retval = kStatus_UART_RxRingBufferOverrun; local_received = 0; } // 3.3 接收完成 else if ((ev & RTOS_UART_COMPLETE) != 0U) { retval = kStatus_Success; local_received = length; } else { retval = kStatus_UART_Error; local_received = 0; } // 4 釋放接收信號量 if (pdFALSE == xSemaphoreGive(handle->rxSemaphore)) { retval = kStatus_Fail; }
(2)實驗現象
① 編譯程序,在uboot手動加載M核程序。
② 裝置上電后,串口打印程序信息,此時通過鍵盤輸入4個字符,M核調試串口將回顯,重復輸入和回顯字符,證明程序運行成功。

以上就是在飛凌嵌入式i.MX 9352開發板M核上軟件設計FreeRTOS的例程演示,希望能夠對各位工程師朋友有所幫助。
-
ARM
+關注
關注
134文章
9321瀏覽量
375385 -
嵌入式
+關注
關注
5142文章
19561瀏覽量
315399 -
NXP
+關注
關注
61文章
1336瀏覽量
187632 -
FreeRTOS
+關注
關注
12文章
492瀏覽量
63825
發布評論請先 登錄
評論