STM32F412應用開發筆記之九:移植FreeRTOS到F412ZG平台


在開發實際應用系統時,我們經常需要考慮數據的實時性和多任務,嵌入式實時操作系統的出現為實現這一目的提供了很好的助力。FreeRTOS是近年來比較流行的嵌入式實時操作系統,而且是開源免費的,STM32CubeMX對它也提供了支持。我們可以使用STM32CubeMX很方便的添加上FreeRTOS,只需要在配置界面找到“MiddleWares”並將其下的FreeRTOS選為“Enable”就可添加上FreeRTOS:

配置完成后,更新源碼就會在軟件中添加上FreeRTOS的相關文件,各文件的功能網上已經有很多介紹,就不啰嗦了,而且也不屬於我們需要說明的內容。

 

添加完了之后,源碼中就會添加創建一個缺省任務的代碼。因為最少要有一個任務才有意義。查看main()函數,就會出現如下這樣一段代碼:

  /* Create the thread(s) */

  /* definition and creation of defaultTask */

  osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128);

  defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);

 

  /* USER CODE BEGIN RTOS_THREADS */

  /* add threads, ... */

  /* USER CODE END RTOS_THREADS */

 

  /* USER CODE BEGIN RTOS_QUEUES */

  /* add queues, ... */

  /* USER CODE END RTOS_QUEUES */

 

 

  /* Start scheduler */

  osKernelStart();

其實這段代碼就實現兩個功能:創建任務和情動任務調度。不過ST在FreeRTOS的基礎上做了進一步的封裝。其中osKernelStart()函數用於啟動任務調度,這個函數比較簡單只不過是調用了一下FreeRTOS中的任務調度函數vTaskStartScheduler(),其實現原型如下:

osStatus osKernelStart (void)

{

  vTaskStartScheduler();

 

  return osOK;

}

而實現任務創建的就是osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128);和defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);這兩行代碼。這兩行代碼就是創建了一個任務顯示名稱為defaultTask的StartDefaultTask任務。帶是代碼看起來有點不容易理解,其實是ST進行了進一步封裝的結果。

首先osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128)其實是一個宏,其定義的原型如下:

#define osThreadDef(name, thread, priority, instances, stacksz)  \

const osThreadDef_t os_thread_def_##name = \

{ #name, (thread), (priority), (instances), (stacksz)  }

那么osThreadDef_t又是個什么呢?其實是ST定義的創建任務的參數結構體:

typedef struct os_thread_def  {

  char                   *name;        ///< Thread name

  os_pthread             pthread;      ///< start address of thread function

  osPriority             tpriority;    ///< initial thread priority

  uint32_t               instances;    ///< maximum number of instances of that thread function

  uint32_t               stacksize;    ///< stack size requirements in bytes; 0 is default stack size

} osThreadDef_t;

所以第一句的含義就很明確了,就是定義了一個osThreadDef_t類型的變量用於存儲用於創建任務的參數。第二句很明顯是一個函數調用,但參數與我們見到的FreeRTOS中的創建任務的函數有些不同。其實它的第一個參數osThread(defaultTask)也是一宏:

#define osThread(name)  \

&os_thread_def_##name

其實就是調用第一句中定義的osThreadDef_t類型的結構體變量。這樣就明確了,第二句實際上就是調用FreeRTOS中的任務創建函數創建任務:

osThreadId osThreadCreate (const osThreadDef_t *thread_def, void *argument)

{

  TaskHandle_t handle;

 

  if (xTaskCreate((TaskFunction_t)thread_def->pthread,(const portCHAR *)thread_def->name,

              thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),

              &handle) != pdPASS)  {

    return NULL;

  }

 

  return handle;

}

所以我們要創建其他的任務,只需照此設計即可。當然任務中的內容根據需要修改,STM32CubeMX添加的缺省任務,參數和實現內容也可以更改。在這里我們不對缺省任務做太多修改,在其任務函數中添加部分我們的內容。

考慮到我們所應用到的功能,我們需要6個任務,利用上缺省任務我們還需要添加5個任務。之所以要添加這么多的任務是因為我們不想讓各種功能相互影響。在某一外設出現操作錯誤時不會影響其他部分的應用,這也是我們使用多任務實時操作系統的原因之一。

缺省任務我們讓它來跑邏輯控制。再增加幾個任務分別來跑各種功能,由於AD和DA都是通過SPI1通訊來完成的,所以采用同一個任務。

首先定義幾個任務操作句柄:

/*定義任務句柄*/

osThreadId defaultTaskHandle;

osThreadId addaTaskHandle;

osThreadId ndirTaskHandle;

osThreadId lcdTaskHandle;

osThreadId ethernetTaskHandle;

osThreadId paraTaskHandle;

在聲明對應的任務處理函數:

/*聲明任務處理函數*/

void StartDefaultTask(void const * argument);

void ADDATask(void const * argument);

void NDIRTask(void const * argument);

void LCDTask(void const * argument);

void EthernetTask(void const * argument);

void ParameterTask(void const * argument);

編寫各任務處理函數,由於各種功能在之前已經實現了所以任務函數比較簡單:

/* 缺省任務,用來處理邏輯控制 */

void StartDefaultTask(void const * argument)

{

  for(;;)

  {

    /*邏輯處理*/

    LogicOperation();

    osDelay(1);

  }

}

 

/* AD/DC數據處理任務函數 */

void ADDATask(void const * argument)

{

  for(;;)

  {

    /*獲取AD采集的測量值*/

    GetMeasuredValue();

    /*設置模擬量輸出*/

    SetOutputValue();

    osDelay(1);

  }

}

 

/* 遠紅外炭氫檢測數據處理任務函數 */

void NDIRTask(void const * argument)

{

  for(;;)

  {

    /*獲取CH4測量值*/

    GetNDIRData();

    osDelay(1);

  }

}

 

/* LCD顯示通訊處理任務函數 */

void LCDTask(void const * argument)

{

  for(;;)

  {

    /*顯示屏數據通訊*/

    LCD_DataExchange();

    osDelay(1);

  }

}

 

/* 以太網通訊處理任務函數 */

void EthernetTask(void const * argument)

{

  for(;;)

  {

    /*以太網通訊處理*/

    EthernetProcess();

    osDelay(1);

  }

}

 

/* 參數存取處理任務函數 */

void ParameterTask(void const * argument)

{

  for(;;)

  {

    /*參數的存儲、恢復等處理*/

    ParameterProcess();

    osDelay(1);

  }

}

再在系統中創建任務:

/* Create the thread(s) */

  /* definition and creation of defaultTask */

  osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128);

  defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);

 

  /* USER CODE BEGIN RTOS_THREADS */

  osThreadDef(addaTask, ADDATask, osPriorityNormal, 0, 128);

  addaTaskHandle = osThreadCreate(osThread(addaTask), NULL);

 

  osThreadDef(ndirTask, NDIRTask, osPriorityNormal, 0, 128);

  ndirTaskHandle = osThreadCreate(osThread(ndirTask), NULL);

 

  osThreadDef(lcdTask, LCDTask, osPriorityNormal, 0, 128);

  lcdTaskHandle = osThreadCreate(osThread(lcdTask), NULL);

 

  osThreadDef(ethernetTask, EthernetTask, osPriorityNormal, 0, 128);

  ethernetTaskHandle = osThreadCreate(osThread(ethernetTask), NULL);

 

  osThreadDef(paraTask, ParameterTask, osPriorityNormal, 0, 128);

  paraTaskHandle = osThreadCreate(osThread(paraTask), NULL);

  /* USER CODE END RTOS_THREADS */

至此在系統中添加FreeRTOS的過程完成,編譯調試看看結果:

 

編譯無錯誤,運行正常。通過IO中斷看看運行結果與沒有操作系統時有沒有區別:

 

在運行一段時間后再看看結果:

 

系統運行無誤,與我們所希望的一樣。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM