ThreadX應用開發筆記之一:移植ThreadX到STM32平臺

如今一些小型系統中也每每有多任務處理的需求,這就爲實時操做系統提供了用武之地。事實上國內外各類各樣的RTOS有不少,並且基本都在走開源的路線,ThreadX也不例外,在這一篇中咱們就來學習ThreadX初步應用並將其移植到STM32平臺中。git

1、前期準備

在開始將ThreadX一直到STM32平臺之間咱們須要作一些前期準備。首先咱們須要準備一個硬件平臺,此次咱們採用STM32F407IG控制單元來做爲目標平臺。其次咱們須要準備一個該硬件平臺下能夠正常運行的裸機項目。這兩點其實咱們都已經具有了。github

最主要的咱們須要得到ThreadX的源碼,這是咱們移植它的基礎。ThreadX的源碼已經開源到Github上,其地址爲:https://github.com/azure-rtos/threadx,直接下載源碼就能夠了。目前發佈的最新版本是6.0.1,在咱們的移植中咱們使用6.0.0的版原本實現。api

2、系統移植

首先咱們先來了解一下得到的ThreadX源碼。解壓下載下來的壓縮包,其包含有如下文件及文件夾,咱們先來具體看一看都有哪些文件,以下圖:數據結構

上圖中一目瞭然,無需作太多解釋。咱們須要用到的文件主要存放在common文件夾和ports文件夾。其中common文件夾存放的是內核源碼,ports文件夾存放的是不一樣平臺的接口文件。咱們的硬件採用的是STM32F407IG,軟件開發環境用的是IAR EWARM,因此咱們選擇ports文件夾下cortex_m4下的IAR文件夾中的接口文件。app

接下來咱們須要在項目中添加ThreadX的相關源碼文件。因此咱們在項目下添加ThreadX組、並在ThreadX組下添加Source和Ports兩個組用於添加文件。並將common文件夾和ports文件夾中的文件添加到對應的分組。以下所示:函數

而後要在項目屬性中爲編譯器指定頭文件的引用路徑,主要是內核函數的頭文件以及接口文件的頭文件兩個路徑,在咱們這個項目中配置以下:學習

$PROJ_DIR$\..\..\ThreadX\common\inc測試

$PROJ_DIR$\..\..\ThreadX\ports\cortex_m4\iar\incspa

事實上到這了,咱們已經完成了對ThreadX內核文件以及接口文件的移植,但如今ThreadX不會運行,咱們還須要作一些工做。咱們要將內核與主函數聯繫起來,首先咱們要在調用內核的地方添加頭文件「tx_api.h」,咱們這裏將其添加到主函數文件中。操作系統

而後有兩個函數咱們須要處理,分別是:tx_kernel_enter和tx_application_define,這兩個函數在頭文件「tx_api.h」中被聲明。tx_kernel_enter實際是一個宏,真正的函數是_tx_initialize_kernel_enter,用於啓動內核,這個函數須要咱們在主函數中調用。而tx_application_define函數只有聲明沒有實現,在_tx_initialize_kernel_enter函數中被調用,用於任務的建立。這個函數的實現是咱們的主要工做,後續將詳細說明。

3、任務實現

咱們已經說過了tx_application_define用於任務的建立,它的具體內容須要咱們來實現,接下來咱們就來實現tx_application_define這個函數。

咱們先來規劃一下咱們將要實現的內容。咱們計劃實現5個任務,包括啓動任務、紅燈閃爍任務、綠燈閃爍任務、空閒任務及統計任務。其中爲啓動任務用於初始化一些配置並執行一些如系統心跳、看門狗之類的工做;用於紅燈閃爍任務和綠燈閃爍任務用於實現咱們要操做的指示燈控制;空閒任務在其餘任務不運行時其運行,優先級最低。統計任務再次咱們實現系統空閒率的統計。接下來咱們就按此思路來實現之。

/*tx_application_define函數實現*/
void  tx_application_define(void *first_unused_memory)
{

  /**************建立啓動任務*********************/
  tx_thread_create(&AppTaskStartTCB,              /* 任務控制塊地址 */  
                "App Task Start",              /* 任務名 */
                AppTaskStart,                  /* 啓動任務函數地址 */
                0,                             /* 傳遞給任務的參數 */
                &AppTaskStartStk[0],            /* 堆棧基地址 */
                APP_CFG_TASK_START_STK_SIZE,  /* 堆棧空間大小 */ 
                APP_CFG_TASK_START_PRIO,      /* 任務優先級*/
                APP_CFG_TASK_START_PRIO,      /* 任務搶佔閥值 */
                TX_NO_TIME_SLICE,              /* 不開啓時間片 */
                TX_AUTO_START);                /* 建立後當即啓動 */
 
  /**************建立紅燈閃爍任務*********************/
  tx_thread_create(&AppTaskRedLedTCB,           /* 任務控制塊地址 */
                "App Msp Pro",                 /* 任務名 */
                AppTaskRedLED,              /* 啓動任務函數地址 */
                0,                           /* 傳遞給任務的參數 */
                &AppTaskMsgProStk[0],        /* 堆棧基地址 */
                APP_CFG_TASK_RedLED_STK_SIZE,  /* 堆棧空間大小 */
                APP_CFG_TASK_REDLED_PRIO,      /* 任務優先級*/
                APP_CFG_TASK_REDLED_PRIO,     /* 任務搶佔閥值 */
                TX_NO_TIME_SLICE,               /* 不開啓時間片 */
                TX_AUTO_START);                /* 建立後當即啓動 */
 
  /**************建立綠燈閃爍任務*********************/
  tx_thread_create(&AppTaskGreenLEDTCB,        /* 任務控制塊地址 */
                "App Task UserIF",              /* 任務名 */
                AppTaskGreenLED,             /* 啓動任務函數地址 */
                0,                         /* 傳遞給任務的參數 */
                &AppTaskUserIFStk[0],         /* 堆棧基地址 */
                APP_CFG_TASK_GreenLED_STK_SIZE, /* 堆棧空間大小 */
                APP_CFG_TASK_GREENLED_PRIO,     /* 任務優先級*/
                APP_CFG_TASK_GREENLED_PRIO,   /* 任務搶佔閥值 */
                TX_NO_TIME_SLICE,               /* 不開啓時間片 */
                TX_AUTO_START);             /* 建立後當即啓動 */
 
  /**************建立統計任務*********************/
  tx_thread_create(&AppTaskStatTCB,               /* 任務控制塊地址 */   
                   "App Task STAT",              /* 任務名 */
                   AppTaskStat,                  /* 啓動任務函數地址 */
                   0,                           /* 傳遞給任務的參數 */
                   &AppTaskStatStk[0],           /* 堆棧基地址 */
                   APP_CFG_TASK_STAT_STK_SIZE,  /* 堆棧空間大小 */ 
                   APP_CFG_TASK_STAT_PRIO,      /* 任務優先級*/
                   APP_CFG_TASK_STAT_PRIO,     /* 任務搶佔閥值 */
                   TX_NO_TIME_SLICE,            /* 不開啓時間片 */
                   TX_AUTO_START);             /* 建立後當即啓動 */
 
  /**************建立空閒任務*********************/
  tx_thread_create(&AppTaskIdleTCB,               /* 任務控制塊地址 */   
                   "App Task IDLE",              /* 任務名 */
                   AppTaskIDLE,               /* 啓動任務函數地址 */
                   0,                          /* 傳遞給任務的參數 */
                   &AppTaskIdleStk[0],          /* 堆棧基地址 */
                   APP_CFG_TASK_IDLE_STK_SIZE,  /* 堆棧空間大小 */
                   APP_CFG_TASK_IDLE_PRIO,       /* 任務優先級*/
                   APP_CFG_TASK_IDLE_PRIO,     /* 任務搶佔閥值 */
                   TX_NO_TIME_SLICE,           /* 不開啓時間片 */
                   TX_AUTO_START);            /* 建立後當即啓動 */
}

咱們實現了tx_application_define函數,在其中建立了任務,理所固然咱們還須要實現相應的任務函數。

/*系統啓動任務*/
static  void  AppTaskStart (ULONG thread_input)
{
  (void)thread_input;
 
  /* 任務統計前先掛起其它任務 */
  tx_thread_suspend(&AppTaskRedLedTCB);
  tx_thread_suspend(&AppTaskGreenLEDTCB);
 
  OSStatInit();
 
  /* 任務統計完畢後,恢復其它任務 */      
  tx_thread_resume(&AppTaskRedLedTCB);
  tx_thread_resume(&AppTaskGreenLEDTCB);
 
  /* 內核開啓後,恢復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);
  }
}

/*統計任務函數*/
static void AppTaskStat(ULONG thread_input)
{
  (void)thread_input;
 
  while (OSStatRdy == false)
  {
    tx_thread_sleep(200);     /* 等待統計任務就緒 */
  }
 
  OSIdleCtrMax /= 100uL;
  if (OSIdleCtrMax == 0uL)
  {
    OSCPUUsage = 0u;
  }
 
  OSIdleCtr = OSIdleCtrMax * 100uL;  /* 設置初始CPU利用率 0% */
 
  for (;;)
  {
    OSIdleCtrRun = OSIdleCtr;    /* 得到100ms內空閒計數 */
    OSIdleCtr    = 0uL;          /* 復位空閒計數 */
    OSCPUUsage   = (100uL - (float)OSIdleCtrRun / OSIdleCtrMax);
    tx_thread_sleep(100);        /* 每100ms統計一次 */
  }
}

/*空閒任務函數*/
static void AppTaskIDLE(ULONG thread_input)
{    
  TX_INTERRUPT_SAVE_AREA
   
  (void)thread_input;
 
  while(1)
  {
    TX_DISABLE
      OSIdleCtr++;
    TX_RESTORE
  }                          
}

實現了上面這些函數後,咱們一個基於ThreadX的最基礎的系統就創建起來了,對於更復雜的系統也沒有問題,其實現的基本思路也是與此相同的。

4、最後測試

完成前述的所有內容後,咱們編譯下載到目標平臺,兩個指示燈按照咱們的預期正常閃爍,說明的們的移植是成功的。

事實上ThreadX的移植相對簡單,接下來咱們總結一下移植ThreadX的步驟。咱們以爲大致可分爲以下過程進行:

首先,將ThreadX的文件及引用,包括內核文件和接口文件,添加到咱們的項目中,並作好相關的項目配置。

其次,將 tx_api.h 文件包含於全部使用 ThreadX 服務和數據結構的應用程序。如前面咱們將其包含在主函數文件中。

而後,在主函數中調用 tx_kernel_enter函數以達到啓動ThreadX內核的目的。若是沒有通過ThreadX特定的初始化,能夠經過增長其優先權而進入到內核中。

再其次,創建 tx_application_define 函數。這是初始系統資源建立的地方。這些資源包括線程、隊列、內存緩衝池、事件標誌組以及信號。

最後,編譯下載到目標平臺測試。

歡迎關注:

相關文章
相關標籤/搜索