Arduino運行FreeRTOS操作系統

在這裏插入圖片描述

我們從一開始接觸Arduino編程就知道,Arduino程序結構由setup()和loop()兩部分組成,我們需要反覆執行的代碼要放在loop()中,並且這些代碼一般都是順序執行的。

隨着我們需要實現的功能越來越複雜,這種順序執行的方式很難達到實時性,這個時候就需要使用操作系統了,就類似於我們的PC機,可以同時運行多個軟件,你可以一邊聊QQ一邊看電影,或者你用手機一邊聽歌一邊看這篇文章。當然PC機和手機的處理器要強大的太多太多了,而我們的Arduino UNO開發板上使用的是一顆8位的AVR單片機。

接觸過嵌入式的朋友都知道,我們會在ARM處理器上使用Linux系統,而在STM32這種較ARM低端而又比單片機強大的MCU上一般會使用更輕量級的實時操作系統,類似的如UCOS、FreeRTOS、RTThread等。習慣了STM32上運行FreeRTOS,真的沒有想過在Arduino上來運行,最近發現了被移植到Arduino上運行的FreeRTOS實時操作系統,趕緊來嘗試下。

1. 安裝Arduino FreeRTOS庫

在Arduino IDE中,點擊「項目」—「加載庫」—「管理庫」,在搜索欄輸入"FreeRTOS",查找並安裝庫。

安裝庫

2. Arduino FreeRTOS的使用

Arduino FreeRTOS庫可運行於Arduino AVR設備,如Uno、Leonardo、Mega等。本篇使用Uno開發板。

首先要包含Arduino FreeRTOS庫的頭文件。

#include <Arduino_FreeRTOS.h>

我們使用xTaskCreate()函數來創建任務,函數原型爲:

xTaskCreate(TaskFunction_t pvTaskCode,const char * const pcName,uint16_t usStackDepth,void * pvParameters,UBaseType_t uxPriority,TaskHandle_t * pxCreatedTask)

創建任務時需要傳入6個參數:

  • pvTaskCode:任務函數。
  • pcName:任務名稱,一般用於調試和追蹤。
  • usStackDepth:任務堆棧,內核在創建任務時將其分配給任務。該值指定堆棧可以容納的字數,而不是字節數。例如,如果堆棧爲32位寬,並且usStackDepth作爲100傳入,那麼將在RAM中分配400字節的堆棧空間(100 * 4字節)。合理使用此項,因爲Arduino Uno只有2KB的RAM。
  • pvParameters:任務輸入參數(可以爲NULL)。
  • uxPriority:任務優先級(0是最低優先級)。
  • pxCreatedTask:可用於向正在創建的任務傳遞句柄。然後,可以使用此句柄在API調用中引用任務,例如,更改任務優先級或刪除任務(可以爲NULL)。

本次實驗創建兩個串口打印任務:

xTaskCreate(TaskPrint1, "Print1", 128, NULL, 1, NULL);
xTaskCreate(TaskPrint2, "Print2", 128, NULL, 2, NULL);

其中任務2有更高的優先級,會首先執行。

創建任務後,使用**vTaskStartScheduler()**函數啓動任務調度。

創建任務實現函數。一般結構如下:

void task(void *param)
{
    while(1)
    {
        ....//需要執行的代碼
    }
}

大多數代碼都需要延遲函數來停止正在運行的任務,但是在RTOS中,不建議使用**Delay()**函數,因爲它會停止CPU,因此RTOS也將停止工作。因此,FreeRTOS具有內核API,可以在特定時間內阻止任務:

vTaskDelay(const TickType_t xTicksToDelay)

例如延時1秒:

vTaskDelay(1000 / portTICK_PERIOD_MS)

其中portTICK_PERIOD_MS與實際MCU的時鐘頻率相關。

3. 本實驗代碼如下,拷貝編譯下載。

#include <Arduino_FreeRTOS.h>

void TaskPrint1(void *param); //聲明打印任務1
void TaskPrint2(void *param); //聲明打印任務2

void setup() {

  Serial.begin(9600);
  while (!Serial);//等待串口連接後執行

  xTaskCreate(TaskPrint1, "Print1", 128, NULL, 1, NULL); //創建任務1
  xTaskCreate(TaskPrint2, "Print2", 128, NULL, 2, NULL); //創建任務2
  vTaskStartScheduler(); //啓動任務調度
}

void TaskPrint1(void *param)
{
  while (1)
  {
    Serial.println("TaskPrint1...");
    vTaskDelay(1000 / portTICK_PERIOD_MS ); // 等待1秒
  }
}


void TaskPrint2(void *param)
{
  while (1)
  {
    Serial.println("TaskPrint2...");
    vTaskDelay(2000 / portTICK_PERIOD_MS ); // 等待2秒
  }
}

void loop() {

}

程序下載

4. 實驗現象

打開串口監視器,波特蘭設置與程序中一致的9600,會看到任務2先運行打印,由於任務1等待1秒,任務2等待2秒,所以每次打印任務1兩次,打印任務2一次。

實驗現象


關注公衆號「TonyCode」,回覆「1024」獲取1000G學習資料。
個人博客
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章