在嵌入式系統(tǒng)領(lǐng)域,嵌入式實(shí)時(shí)操作系統(tǒng)(RTOS) 的應(yīng)用正日益廣泛,采用RTOS能夠更合理、更高效地利用CPU資源,F(xiàn)reeRTOS作為一款輕量級(jí)且成熟的實(shí)時(shí)操作系統(tǒng)內(nèi)核,其核心功能完備,包括任務(wù)管理、時(shí)間管理(如延時(shí)、定時(shí)器)、同步機(jī)制(信號(hào)量、互斥鎖)、進(jìn)程間通信(消息隊(duì)列)等等。這些特性使其能夠很好地滿(mǎn)足資源相對(duì)有限的中小型嵌入式系統(tǒng)的需求。
i.MX 9352作為NXP 推出的新一代輕量級(jí)邊緣AI處理器,集成2個(gè)Cortex-A55核和1個(gè)Cortex-M33實(shí)時(shí)核,其架構(gòu)設(shè)計(jì)充分體現(xiàn)了對(duì)實(shí)時(shí)性與復(fù)雜任務(wù)處理能力的兼顧。為了幫助開(kāi)發(fā)者充分利用i.MX 9352 M33核的實(shí)時(shí)能力,其配套的M核SDK包提供的FreeRTOS例程分為兩類(lèi),一類(lèi)介紹FreeRTOS系統(tǒng)組件特性,如信號(hào)量、互斥量、隊(duì)列等,另一類(lèi)是介紹外設(shè)接口如何在FreeRTOS使用,我們分別挑選這兩類(lèi)下的例程進(jìn)行演示。
1、FreeRTOS-generic
飛凌嵌入式OK-MX9352-C開(kāi)發(fā)板支持FreeRTOS功能特性示例代碼如下:
-freertos_event:任務(wù)事件演示例程
-freertos_queue:隊(duì)列消息實(shí)現(xiàn)任務(wù)間通信的演示例程
-freertos_mutex:互斥鎖使用例程
-freertos_sem:信號(hào)量使用例程
-freertos_swtimer:軟件計(jì)數(shù)器及其回調(diào)的用法。
-freertos_tickless:使用 LPTMR 延時(shí)喚醒或者硬件中斷喚醒例程
-freertos_generic:task、queue、swtimer、tick hook 、semaphore 組合利用演示例程。
因FreeRTOS_generic例程使用的FreeRTOS特性較多,我們重點(diǎn)分析此例程。
(1)軟件實(shí)現(xiàn)
示例程序內(nèi)容包括:任務(wù)創(chuàng)建、隊(duì)列、軟定時(shí)器、系統(tǒng)節(jié)拍時(shí)鐘、信號(hào)量、異常處理。具體如下:
任務(wù)創(chuàng)建:
主函數(shù)創(chuàng)建了隊(duì)列發(fā)送、接收,信號(hào)量三個(gè)任務(wù)。
// 創(chuàng)建隊(duì)列接收任務(wù)
if(xTaskCreate(prvQueueReceiveTask,"Rx",configMINIMAL_STACK_SIZE+166,NULL,mainQUEUE_RECEIVE_TASK_PRIORITY,NULL)!=pdPASS)
// 創(chuàng)建隊(duì)列發(fā)送任務(wù)
if(xTaskCreate(prvQueueSendTask,"TX",configMINIMAL_STACK_SIZE+166, NULL, mainQUEUE_SEND_TASK_PRIORITY, NULL) !=pdPASS)
// 創(chuàng)建信號(hào)量任務(wù)
if(xTaskCreate(prvEventSemaphoreTask,"Sem",configMINIMAL_STACK_SIZE+166,NULL,mainEVENT_SEMAPHORE_TASK_PRIORITY, NULL) != pdPASS)
隊(duì)列:
隊(duì)列發(fā)送任務(wù),阻塞200ms后向隊(duì)列發(fā)送數(shù)據(jù);隊(duì)列接收任務(wù),任務(wù)阻塞讀取隊(duì)列,數(shù)據(jù)讀取正確,則打印此時(shí)的隊(duì)列接收數(shù)量。
// 隊(duì)列發(fā)送任務(wù),阻塞200ms后 向隊(duì)列發(fā)送數(shù)據(jù)
static void prvQueueSendTask(void *pvParameters)
{
TickType_t xNextWakeTime;
const uint32_t ulValueToSend = 100UL;
xNextWakeTime = xTaskGetTickCount();
for (;;)
{
// 任務(wù)阻塞,直至200ms延時(shí)結(jié)束
vTaskDelayUntil(&xNextWakeTime, mainQUEUE_SEND_PERIOD_MS);
// 向隊(duì)列發(fā)送數(shù)據(jù),阻塞時(shí)間為0表示當(dāng)隊(duì)列滿(mǎn)的時(shí)候就立即返回
xQueueSend(xQueue, &ulValueToSend, 0);
}
}
// 隊(duì)列接收任務(wù),任務(wù)阻塞讀取隊(duì)列,數(shù)據(jù)讀取正確,則打印此時(shí)的隊(duì)列接收數(shù)量。
static void prvQueueReceiveTask(void *pvParameters)
{
uint32_t ulReceivedValue;
for (;;)
{
// 任務(wù)一直阻塞,知道隊(duì)列內(nèi)讀取到數(shù)據(jù)
xQueueReceive(xQueue, &ulReceivedValue, portMAX_DELAY);
// 隊(duì)列數(shù)據(jù)和發(fā)送一致,隊(duì)列接收數(shù)量+1 輸出此時(shí)的隊(duì)列接收數(shù)量
if (ulReceivedValue == 100UL)
{
ulCountOfItemsReceivedOnQueue++;
PRINTF("Receive message counter: %d.rn", ulCountOfItemsReceivedOnQueue);
}
}
}
軟定時(shí)器:
設(shè)置軟定時(shí)器周期1s,時(shí)間到后,調(diào)用回調(diào)函數(shù),記錄次數(shù)并串口打印。
// 創(chuàng)建軟件定時(shí)器任務(wù) 時(shí)間為1s,周期循環(huán)
xExampleSoftwareTimer = xTimerCreate(
"LEDTimer",
mainSOFTWARE_TIMER_PERIOD_MS,
pdTRUE,
(void *)0,
vExampleTimerCallback);
// 啟動(dòng)軟件定時(shí)器
xTimerStart(xExampleSoftwareTimer, 0);
// 回調(diào)函數(shù)
static void vExampleTimerCallback(TimerHandle_t xTimer)
{
// 每1s進(jìn)入一次回調(diào)函數(shù),計(jì)數(shù)增加
ulCountOfTimerCallbackExecutions++;
PRINTF("Soft timer: %d s.rn", ulCountOfTimerCallbackExecutions);
}
系統(tǒng)節(jié)拍時(shí)鐘:
通過(guò)設(shè)置文件 FreeRTOSConfig.h 中 configTICK_RATE_HZ 設(shè)置任務(wù)節(jié)拍中斷頻率, 在啟動(dòng)任務(wù)調(diào)度器時(shí),系統(tǒng)會(huì)根據(jù)另一個(gè)變量CPU的頻率configCPU_CLOCK_HZ計(jì)算對(duì)應(yīng)寫(xiě)入節(jié)拍計(jì)數(shù)器的值,啟動(dòng)定時(shí)器中斷。
// 設(shè)置系統(tǒng)時(shí)鐘節(jié)拍為 1000/200=5ms
#define configTICK_RATE_HZ ((TickType_t)200)
信號(hào)量:
每個(gè)系統(tǒng)節(jié)拍時(shí)鐘中斷中,調(diào)用函數(shù)vApplicationTickHook,累積500次即500*5ms=2.5s后,發(fā)送信號(hào)量。信號(hào)量任務(wù)獲取信號(hào)后,計(jì)數(shù)并打印累積次數(shù)。
// 系統(tǒng)節(jié)拍為5ms,每個(gè)500*5ms=2.5s 釋放事件信號(hào)量
void vApplicationTickHook(void)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
static uint32_t ulCount = 0;
ulCount++;
if (ulCount >= 500UL)
{
// 在中斷中釋放事件信號(hào)量
xSemaphoreGiveFromISR(xEventSemaphore, &xHigherPriorityTaskWoken);
ulCount = 0UL;
}
}
// 任務(wù)阻塞等待信號(hào)量,收到后,接收次數(shù)增加,并通過(guò)串口打印
static void prvEventSemaphoreTask(void *pvParameters)
{
for (;;)
{
// 任務(wù)阻塞,直到能獲取信號(hào)量
if (xSemaphoreTake(xEventSemaphore, portMAX_DELAY) != pdTRUE)
{
PRINTF("Failed to take semaphore.rn");
}
// 接收到信號(hào)量的次數(shù)累加
ulCountOfReceivedSemaphores++;
PRINTF("Event task is running. Get semaphore :%d rn",ulCountOfReceivedSemaphores);
}
}
異常處理:
當(dāng)內(nèi)存分配失敗、堆棧發(fā)生錯(cuò)誤或任務(wù)空閑時(shí),進(jìn)入相應(yīng)的函數(shù),用戶(hù)可添加相應(yīng)的處理函數(shù)。
// 內(nèi)存分配失敗函數(shù),當(dāng)內(nèi)存分配失敗時(shí),進(jìn)入此函數(shù)
void vApplicationMallocFailedHook(void)
{
for (;;)
;
}
// 堆棧錯(cuò)誤檢查函數(shù),當(dāng)堆棧發(fā)生溢出時(shí),進(jìn)入此函數(shù)
void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName)
{
(void)pcTaskName;
(void)xTask;
for (;;)
;
}
// 空閑任務(wù),優(yōu)先級(jí)最低,沒(méi)有實(shí)際意義,只是讓CPU有事情做,用戶(hù)可以自己添加自己的函數(shù)
void vApplicationIdleHook(void)
{
volatile size_t xFreeStackSpace;
xFreeStackSpace = xPortGetFreeHeapSize();
if (xFreeStackSpace > 100)
{
}
}
(2)實(shí)驗(yàn)現(xiàn)象
① 編譯程序:在uboot手動(dòng)加載M核程序。
② 隊(duì)列:每隔200ms,隊(duì)列發(fā)送任務(wù)發(fā)送數(shù)據(jù),隊(duì)列接收任務(wù)獲取數(shù)據(jù),從阻塞態(tài)到運(yùn)行態(tài),打印計(jì)數(shù)。
③ 軟定時(shí)器:每隔1s,時(shí)間到達(dá),調(diào)用回調(diào)函數(shù),打印計(jì)數(shù)。
④ 信號(hào)量:每隔5ms,系統(tǒng)時(shí)鐘節(jié)拍中斷調(diào)用函數(shù),超過(guò)500次后,釋放信號(hào)量。信號(hào)量任務(wù)獲的信號(hào)量,從阻塞態(tài)到運(yùn)行態(tài),打印計(jì)數(shù)。
2、FreeRTOS-外設(shè)
飛凌嵌入式OK-MX9352-C開(kāi)發(fā)板支持外設(shè)使用FreeRTOS完成相應(yīng)功能,示例代碼如下:
-freertos_uart:freertos串口演示例程
-freertos_lpi2c_b2b:freertos I2C演示例程
-freertos_lpspi_b2b:freertos SPI演示例程
因freertos_uart例程使用的FreeRTOS特性比較典型,我們重點(diǎn)分析此例程。
(1)軟件實(shí)現(xiàn)
示例程序內(nèi)容包括:串口初始化任務(wù)、串口發(fā)送任務(wù)、串口接收任務(wù)。具體如下:
串口初始化任務(wù):
主要包含串口外設(shè)初始化,發(fā)送、接收互斥量,發(fā)送和接收事件組。串口外設(shè)初始化在裸跑串口例程中已展現(xiàn),此處不再詳述。
// 創(chuàng)建串口發(fā)送互斥量
handle->txSemaphore = xSemaphoreCreateMutex();
// 創(chuàng)建串口接收互斥量
handle->rxSemaphore = xSemaphoreCreateMutex();
// 創(chuàng)建發(fā)送事件標(biāo)志組
handle->txEvent = xEventGroupCreate();
// 創(chuàng)建接收事件標(biāo)志組
handle->rxEvent = xEventGroupCreate();
串口發(fā)送:
發(fā)送前獲取信號(hào)量,啟動(dòng)發(fā)送流程,在中斷中置位發(fā)送完成事件標(biāo)志。發(fā)送任務(wù)獲取到事件后,釋放發(fā)送信號(hào)量。
// 1 獲取發(fā)送信號(hào)量
if (pdFALSE == xSemaphoreTake(handle->txSemaphore, 0))
{
return kStatus_Fail;
}
handle->txTransfer.data = (uint8_t *)buffer;
handle->txTransfer.dataSize = (uint32_t)length;
// 2 阻塞式發(fā)送
status = UART_TransferSendNonBlocking(handle->base, handle->t_state, &handle->txTransfer);
if (status != kStatus_Success)
{
(void)xSemaphoreGive(handle->txSemaphore);
return kStatus_Fail;
}
// 3 等待發(fā)送完成的事件
ev = xEventGroupWaitBits(handle->txEvent, RTOS_UART_COMPLETE, pdTRUE, pdFALSE, portMAX_DELAY);// 等待并判斷多個(gè)事件位
if ((ev & RTOS_UART_COMPLETE) == 0U)
{
retval = kStatus_Fail;
}
// 4 發(fā)送完成,釋放發(fā)送信號(hào)量
if (pdFALSE == xSemaphoreGive(handle->txSemaphore)) // 釋放信號(hào)量
{
retval = kStatus_Fail;
}
串口接收:
接收前獲取信號(hào)量,調(diào)用串口接收函數(shù),在中斷中置位獲取事件標(biāo)志。接收任務(wù)獲取到事件后,釋放接收信號(hào)量。
// 1獲取接收信號(hào)量
if (pdFALSE == xSemaphoreTake(handle->rxSemaphore, portMAX_DELAY))
{
return kStatus_Fail;
}
handle->rxTransfer.data = buffer;
handle->rxTransfer.dataSize = (uint32_t)length;
// 2 串口接收函數(shù)
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 硬件接收錯(cuò)誤
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 接收緩沖區(qū)過(guò)載錯(cuò)誤
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 釋放接收信號(hào)量
if (pdFALSE == xSemaphoreGive(handle->rxSemaphore))
{
retval = kStatus_Fail;
}
(2)實(shí)驗(yàn)現(xiàn)象
① 編譯程序,在uboot手動(dòng)加載M核程序。
② 裝置上電后,串口打印程序信息,此時(shí)通過(guò)鍵盤(pán)輸入4個(gè)字符,M核調(diào)試串口將回顯,重復(fù)輸入和回顯字符,證明程序運(yùn)行成功。
以上就是在飛凌嵌入式i.MX 9352開(kāi)發(fā)板M核上軟件設(shè)計(jì)FreeRTOS的例程演示,希望能夠?qū)Ω魑?a class="article-link" target="_blank" href="/tag/%E5%B7%A5%E7%A8%8B%E5%B8%88/">工程師朋友有所幫助。