現(xiàn)在一些小型系統(tǒng)中也往往有多任務(wù)處理的需求,這就為實時操作系統(tǒng)提供了用武之地。事實上國內(nèi)外各種各樣的RTOS有很多,而且基本都在走開源的路線,ThreadX也不例外,在這一篇中我們就來學(xué)習(xí)ThreadX初步應(yīng)用并將其移植到STM32平臺中。
1 、前期準備
在開始將ThreadX一直到STM32平臺之間我們需要做一些前期準備。首先我們需要準備一個硬件平臺,這次我們采用STM32F407IG控制單元來作為目標平臺。其次我們需要準備一個該硬件平臺下可以正常運行的裸機項目。這兩點其實我們都已經(jīng)具備了。
最主要的我們需要獲得ThreadX的源碼,這是我們移植它的基礎(chǔ)。ThreadX的源碼已經(jīng)開源到Github上,其地址為:https://github.com/azure-rtos/threadx,直接下載源碼就可以了。目前發(fā)布的最新版本是6.0.1,在我們的移植中我們使用6.0.0的版本來實現(xiàn)。
2 、系統(tǒng)移植
首先我們先來了解一下獲得的ThreadX源碼。解壓下載下來的壓縮包,其包含有以下文件及文件夾,我們先來具體看一看都有哪些文件,如下圖
上圖中一目了然,無需做太多解釋。我們需要用到的文件主要存放在common文件夾和ports文件夾。其中common文件夾存放的是內(nèi)核源碼,ports文件夾存放的是不同平臺的接口文件。我們的硬件采用的是STM32F407IG,軟件開發(fā)環(huán)境用的是IAR EWARM,所以我們選擇ports文件夾下cortex_m4下的IAR文件夾中的接口文件。
接下來我們需要在項目中添加ThreadX的相關(guān)源碼文件。所以我們在項目下添加ThreadX組、并在ThreadX組下添加Source和Ports兩個組用于添加文件。并將common文件夾和ports文件夾中的文件添加到對應(yīng)的分組。如下所示:
然后要在項目屬性中為編譯器指定頭文件的引用路徑,主要是內(nèi)核函數(shù)的頭文件以及接口文件的頭文件兩個路徑,在我們這個項目中配置如下:
PROJ_DIR....\\ThreadX\\common\\inc
PROJ_DIR....\\ThreadX\\ports\\cortex_m4\\iar\\inc
事實上到這了,我們已經(jīng)完成了對ThreadX內(nèi)核文件以及接口文件的移植,但現(xiàn)在ThreadX不會運行,我們還需要做一些工作。我們要將內(nèi)核與主函數(shù)聯(lián)系起來,首先我們要在調(diào)用內(nèi)核的地方添加頭文件“tx_api.h”,我們這里將其添加到主函數(shù)文件中。
然后有兩個函數(shù)我們需要處理,分別是:tx_kernel_enter和tx_application_define,這兩個函數(shù)在頭文件“tx_api.h”中被聲明。tx_kernel_enter實際是一個宏,真正的函數(shù)是_tx_initialize_kernel_enter,用于啟動內(nèi)核,這個函數(shù)需要我們在主函數(shù)中調(diào)用。而tx_application_define函數(shù)只有聲明沒有實現(xiàn),在_tx_initialize_kernel_enter函數(shù)中被調(diào)用,用于任務(wù)的創(chuàng)建。這個函數(shù)的實現(xiàn)是我們的主要工作,后續(xù)將詳細說明。
3 、任務(wù)實現(xiàn)
我們已經(jīng)說過了tx_application_define用于任務(wù)的創(chuàng)建,它的具體內(nèi)容需要我們來實現(xiàn),接下來我們就來實現(xiàn)tx_application_define這個函數(shù)。
我們先來規(guī)劃一下我們將要實現(xiàn)的內(nèi)容。我們計劃實現(xiàn)5個任務(wù),包括啟動任務(wù)、紅燈閃爍任務(wù)、綠燈閃爍任務(wù)、空閑任務(wù)及統(tǒng)計任務(wù)。其中為啟動任務(wù)用于初始化一些配置并執(zhí)行一些如系統(tǒng)心跳、看門狗之類的工作;用于紅燈閃爍任務(wù)和綠燈閃爍任務(wù)用于實現(xiàn)我們要操作的指示燈控制;空閑任務(wù)在其他任務(wù)不運行時其運行,優(yōu)先級最低。統(tǒng)計任務(wù)再次我們實現(xiàn)系統(tǒng)空閑率的統(tǒng)計。接下來我們就按此思路來實現(xiàn)之。
/*tx_application_define函數(shù)實現(xiàn)*/
void tx_application_define(void *first_unused_memory)
{
/**************創(chuàng)建啟動任務(wù)*********************/
tx_thread_create(&AppTaskStartTCB, /* 任務(wù)控制塊地址 */
"App Task Start", /* 任務(wù)名 */
AppTaskStart, /* 啟動任務(wù)函數(shù)地址 */
0, /* 傳遞給任務(wù)的參數(shù) */
&AppTaskStartStk[0], /* 堆?;刂?*/
APP_CFG_TASK_START_STK_SIZE, /* 堆棧空間大小 */
APP_CFG_TASK_START_PRIO, /* 任務(wù)優(yōu)先級*/
APP_CFG_TASK_START_PRIO, /* 任務(wù)搶占閥值 */
TX_NO_TIME_SLICE, /* 不開啟時間片 */
TX_AUTO_START); /* 創(chuàng)建后立即啟動 */
/**************創(chuàng)建紅燈閃爍任務(wù)*********************/
tx_thread_create(&AppTaskRedLedTCB, /* 任務(wù)控制塊地址 */
"App Msp Pro", /* 任務(wù)名 */
AppTaskRedLED, /* 啟動任務(wù)函數(shù)地址 */
0, /* 傳遞給任務(wù)的參數(shù) */
&AppTaskMsgProStk[0], /* 堆?;刂?*/
APP_CFG_TASK_RedLED_STK_SIZE, /* 堆??臻g大小 */
APP_CFG_TASK_REDLED_PRIO, /* 任務(wù)優(yōu)先級*/
APP_CFG_TASK_REDLED_PRIO, /* 任務(wù)搶占閥值 */
TX_NO_TIME_SLICE, /*不開啟時間片 */
TX_AUTO_START); /* 創(chuàng)建后立即啟動 */
/**************創(chuàng)建綠燈閃爍任務(wù)*********************/
tx_thread_create(&AppTaskGreenLEDTCB, /* 任務(wù)控制塊地址 */
"App Task UserIF", /* 任務(wù)名 */
AppTaskGreenLED, /* 啟動任務(wù)函數(shù)地址 */
0, /* 傳遞給任務(wù)的參數(shù) */
&AppTaskUserIFStk[0], /* 堆?;刂?*/
APP_CFG_TASK_GreenLED_STK_SIZE, /* 堆棧空間大小 */
APP_CFG_TASK_GREENLED_PRIO, /* 任務(wù)優(yōu)先級*/
APP_CFG_TASK_GREENLED_PRIO, /* 任務(wù)搶占閥值 */
TX_NO_TIME_SLICE, /*不開啟時間片 */
TX_AUTO_START); /* 創(chuàng)建后立即啟動 */
/**************創(chuàng)建統(tǒng)計任務(wù)*********************/
tx_thread_create(&AppTaskStatTCB, /* 任務(wù)控制塊地址 */
"App Task STAT", /* 任務(wù)名 */
AppTaskStat, /* 啟動任務(wù)函數(shù)地址 */
0, /* 傳遞給任務(wù)的參數(shù) */
&AppTaskStatStk[0], /* 堆?;刂?*/
APP_CFG_TASK_STAT_STK_SIZE, /* 堆??臻g大小 */
APP_CFG_TASK_STAT_PRIO, /* 任務(wù)優(yōu)先級*/
APP_CFG_TASK_STAT_PRIO, /* 任務(wù)搶占閥值 */
TX_NO_TIME_SLICE, /* 不開啟時間片 */
TX_AUTO_START); /* 創(chuàng)建后立即啟動 */
/**************創(chuàng)建空閑任務(wù)*********************/
tx_thread_create(&AppTaskIdleTCB, /* 任務(wù)控制塊地址 */
"App Task IDLE", /* 任務(wù)名 */
AppTaskIDLE, /* 啟動任務(wù)函數(shù)地址 */
0, /* 傳遞給任務(wù)的參數(shù) */
&AppTaskIdleStk[0], /* 堆?;刂?*/
APP_CFG_TASK_IDLE_STK_SIZE, /* 堆棧空間大小 */
APP_CFG_TASK_IDLE_PRIO, /* 任務(wù)優(yōu)先級*/
APP_CFG_TASK_IDLE_PRIO, /* 任務(wù)搶占閥值 */
TX_NO_TIME_SLICE, /* 不開啟時間片 */
TX_AUTO_START); /* 創(chuàng)建后立即啟動 */
}
我們實現(xiàn)了tx_application_define函數(shù),在其中創(chuàng)建了任務(wù),理所當然我們還需要實現(xiàn)相應(yīng)的任務(wù)函數(shù)。
/*系統(tǒng)啟動任務(wù)*/
static void AppTaskStart (ULONGthread_input)
{
(void)thread_input;
/* 任務(wù)統(tǒng)計前先掛起其它任務(wù) */
tx_thread_suspend(&AppTaskRedLedTCB);
tx_thread_suspend(&AppTaskGreenLEDTCB);
OSStatInit();
/* 任務(wù)統(tǒng)計完畢后,恢復(fù)其它任務(wù) */
tx_thread_resume(&AppTaskRedLedTCB);
tx_thread_resume(&AppTaskGreenLEDTCB);
/* 內(nèi)核開啟后,恢復(fù)HAL里的時間基準 */
HAL_ResumeTick();
while (1)
{
sysHeartBeat++;
tx_thread_sleep(1000);
}
}
/*紅燈閃爍控制*/
static void AppTaskRedLED(ULONG thread_input)
{
(void)thread_input;
while(1)
{
HAL_GPIO_TogglePin(GPIOI,GPIO_PIN_8);
tx_thread_sleep(500);
}
}
/*綠燈閃爍控制*/
static void AppTaskGreenLED(ULONG thread_input)
{
(void)thread_input;
while(1)
{
HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_13);
tx_thread_sleep(2000);
}
}
/*統(tǒng)計任務(wù)函數(shù)*/
static void AppTaskStat(ULONG thread_input)
{
(void)thread_input;
while(OSStatRdy == false)
{
tx_thread_sleep(200); /* 等待統(tǒng)計任務(wù)就緒 */
}
OSIdleCtrMax /= 100uL;
if(OSIdleCtrMax == 0uL)
{
OSCPUUsage = 0u;
}
OSIdleCtr= OSIdleCtrMax * 100uL; /* 設(shè)置初始CPU利用率 0% */
for (;;)
{
OSIdleCtrRun = OSIdleCtr; /* 獲得100ms內(nèi)空閑計數(shù) */
OSIdleCtr = 0uL; /* 復(fù)位空閑計數(shù) */
OSCPUUsage = (100uL -(float)OSIdleCtrRun / OSIdleCtrMax);
tx_thread_sleep(100); /* 每100ms統(tǒng)計一次 */
}
}
/*空閑任務(wù)函數(shù)*/
static void AppTaskIDLE(ULONG thread_input)
{
TX_INTERRUPT_SAVE_AREA
(void)thread_input;
while(1)
{
TX_DISABLE
OSIdleCtr++;
TX_RESTORE
}
}
實現(xiàn)了上面這些函數(shù)后,我們一個基于ThreadX的最基礎(chǔ)的系統(tǒng)就建立起來了,對于更復(fù)雜的系統(tǒng)也沒有問題,其實現(xiàn)的基本思路也是與此相同的。
4 、最后測試
完成前述的全部內(nèi)容后,我們編譯下載到目標平臺,兩個指示燈按照我們的預(yù)期正常閃爍,說明的們的移植是成功的。
事實上ThreadX的移植相對簡單,接下來我們總結(jié)一下移植ThreadX的步驟。我們覺得大體可分為如下過程進行:
首先,將ThreadX的文件及引用,包括內(nèi)核文件和接口文件,添加到我們的項目中,并做好相關(guān)的項目配置。
其次,將 tx_api.h 文件包含于所有使用 ThreadX 服務(wù)和數(shù)據(jù)結(jié)構(gòu)的應(yīng)用程序。如前面我們將其包含在主函數(shù)文件中。
然后,在主函數(shù)中調(diào)用 tx_kernel_enter函數(shù)以達到啟動ThreadX內(nèi)核的目的。如果沒有經(jīng)過ThreadX特定的初始化,可以通過增加其優(yōu)先權(quán)而進入到內(nèi)核中。
再其次,建立 tx_application_define 函數(shù)。這是初始系統(tǒng)資源創(chuàng)建的地方。這些資源包括線程、隊列、內(nèi)存緩沖池、事件標志組以及信號。
最后,編譯下載到目標平臺測試。
-
threadx
+關(guān)注
關(guān)注
0文章
15瀏覽量
13828 -
STM32
+關(guān)注
關(guān)注
2270文章
10895瀏覽量
355722 -
移植
+關(guān)注
關(guān)注
1文章
379瀏覽量
28124
發(fā)布評論請先 登錄
相關(guān)推薦
評論