FreeRTOSをVScode環境で動かす①環境構築


 概要

FreeRTOS をWindows上の VScode+Cmake 環境で動作させるための手順です。

注意事項

  • FreeRTOSはARMマイコン上でを動作させることが本来
  • マイコン環境が無い状況下において、ある程度の動作確認をしたいため環境構築を行った
  • VisualStudio(VScodeではなく)を使用する場合、FreeRTOS.orgから提供されているサンプルがそのまま動くらしい

使用ツール

項目 ツール
OS Windows10
コンパイラ GCC(MingW)
ビルドツール Cmake
実行環境 VScode
開発言語 C

ソースファイルの準備

開発用フォルダ作成

まず、作業用のフォルダを作成します。

.
└── src  (自作ソース置き場)
     ├── lib  (自作のライブラリcファイル置き場)
     └── RTOS (入手したFreeRTOSのソース置き場)

※フォルダ構成は自由なのですが、Cmakeの書き方が不安な人は真似してください

FreeRTOSの入手

②のURLより、Source code using the FreeRTOS Windows port をクリックしてWin32環境サンプル用のFreeRTOSソースファイルを入手します。

No 説明 リンクURL
フル装備版 https://www.freertos.org/a00104.html
Windows向け抜粋版 https://www.freertos.org/Documentation/code/

必要なファイルの抽出

入手したFreeRTOSのファイル構成は↓の通りで、VisualStudio向けのファイル等は不要なので、欲しいファイルだけコピーします。

Win32-simulator-MSVC
├── /Examples      <===多数のExsamplesが入っていますので、今回はExample001の中の FreeRTOSConfig.h をsrc直下へコピー
├── /FreeRTOS_Source    <===このディレクトリの中身を自環境の"RTOS"フォルダにコピー
│    ├── /include
│    ├── /portable
│    ├── event_groups.c
│    ├── list.c
│    ├── queue.c
│    ├── readme.txt
│    ├── tasks.c
│    └── timers.c
├── /License
├── /Supporting_Functions
├── ReadMe-Instructions.txt
├── RTOSDemo.sln
└── RTOSDemo.suo

不足関数(vAssertCalled)の作成

RTOSフォルダ直下に以下のコードを置きます。

vAssertCalled.c
#include "FreeRTOS.h"
#include "portmacro.h"
#include "projdefs.h"
#include "task.h"

void vAssertCalled( uint32_t ulLine, const char * const pcFileName )
{
static portBASE_TYPE xPrinted = pdFALSE;
volatile uint32_t ulSetToNonZeroInDebuggerToContinue = 0;

    /* Parameters are not used. */
    ( void ) ulLine;
    ( void ) pcFileName;

    taskENTER_CRITICAL();
    {
        /* You can step out of this function to debug the assertion by using
        the debugger to set ulSetToNonZeroInDebuggerToContinue to a non-zero
        value. */
        while( ulSetToNonZeroInDebuggerToContinue == 0 )
        {
        }
    }
    taskEXIT_CRITICAL();
}

RTOSの修正

エラー回避のため修正を入れていますので、Gitの差分情報を紹介します。

コンフィグファイルではタイマ使用をONにします

src/FreeRTOSConfig.h
-#define configUSE_TIMERS                       0
+#define configUSE_TIMERS                       1

2つの関数が参照できなかったのでプライベートスコープを外します。

src/RTOS/include/queue.h
-QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet, const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION;
+QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet, const TickType_t xTicksToWait ) ;//PRIVILEGED_FUNCTION;
src/RTOS/include/task.h
-void vTaskPlaceOnEventListRestricted( List_t * const pxEventList, const TickType_t xTicksToWait, const BaseType_t xWaitIndefinitely ) PRIVILEGED_FUNCTION;
+void vTaskPlaceOnEventListRestricted( List_t * const pxEventList, const TickType_t xTicksToWait, const BaseType_t xWaitIndefinitely ) ;//PRIVILEGED_FUNCTION;

メインプログラム作成

今回動作させるプログラムです。
2つのタスクをクリエイトし、Task1が1秒周期、Task2が2秒周期でprintするプログラムです。

main.c
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>

void vTask1( void *pvParameters );
void vTask2( void *pvParameters );

//===================================================================
int main(){
    /* Create one of the two tasks. */
    xTaskCreate(vTask1,     /* Pointer to the function that implements the task. */
                "Task 1",   /* Text name for the task.  This is to facilitate debugging only. */
                1000,       /* Stack depth - most small microcontrollers will use much less stack than this. */
                NULL,       /* We are not using the task parameter. */
                1,          /* This task will run at priority 1. */
                NULL );     /* We are not using the task handle. */

    xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, NULL );

    vTaskStartScheduler();
    for( ;; );
    return 0;
}
//==================================================================
void vTask1( void *pvParameters ){
    const portTickType xDelay = 1000 / portTICK_RATE_MS;//1000ms
    while(1){
        printf("task1\n");
        vTaskDelay( xDelay );
    }
}
//==================================================================
void vTask2( void *pvParameters ){
    const portTickType xDelay = 2000 / portTICK_RATE_MS;//2000ms
    while(1){{
        printf("\ttask2\n");
        vTaskDelay( xDelay );
    }
}

Cmake環境構築

ここまで用意したすべてのファイルをGCCに食わせれば(恐らく)動作するのですが、毎回フルビルドするのは効率が悪いのでCmakeを記述していきます。

今回Cmakeの記述は初心者でも理解しやすいように必要最小限の記述のみ とします

各フォルダに CMakeLists.txt を配置します。(ファイル名はCMakeLists.txtと同名にする必要がありますので、識別のため★+番号を図示します)

.
├── CMakeLists.txt ★1
└── src
     ├── CMakeLists.txt ★2
     ├── lib
     └── RTOS
          ├── CMakeLists.txt ★3
          ├── /include
          ├── /portable
          └── *.c

記述内容

CMakeLists.txt★1
add_subdirectory(./src)

★1はsrcのフォルダ見てねという指示だけです。

CMakeLists.txt★2
add_subdirectory(./RTOS)

#==============================================================
# include hedder 
#==============================================================
include_directories(./)
include_directories(./RTOS/include)
include_directories(./RTOS/portable/MSVC-MingW)
#==============================================================
# genarate executable object
#==============================================================
set(SOURCE_FILES
    main.c
    )

add_executable(EXE_OBJECT ${SOURCE_FILES})
#==============================================================
# LINK objects.
#==============================================================
# windows dll 
set(DLL_LIB
    winmm 
    gdi32
)
# own library
set(STATIC_LIB
    RTOS
)
target_link_libraries(EXE_OBJECT  PUBLIC ${STATIC_LIB} ${DLL_LIB} )

★2はRTOSフォルダが配下にありますよという指示をして、main.cを実行ファイルに変換、最後にLinkする指示です。
FreeRTOSがWindowsのAPIを参照するので winmm gdi32 という名前のライブラリをリンクする必要があります。

CMakeLists.txt★3
set(THIS_NAME "RTOS")

include_directories(../)                    #include FreeRTOSConfig.h 
include_directories(./include)              #include Hedder file
include_directories(./portable/MSVC-MingW)

add_library(${THIS_NAME}  OBJECT
    portable/MemMang/heap_4.c
    portable/MSVC-MingW/port.c
    event_groups.c
    list.c
    queue.c
    timers.c
    vAssertCalled.c
    tasks.c
)

★3では、RTOSのフォルダ配下のソースを全てコンパイルしてRTOSという名前のライブラリを生成するように指示します。

VSコードの設定

  • C,C++、Cmake関連のプラグインをインストールします。
  • GCCとMAKEのパスも通しておきます。

ビルド&実行

VScodeのGUIから、ビルドして実行します。

実行すると task1とtask2が設定した周期で呼ばれている様子が分かります。

停止方法

今回紹介した部分までではタスク停止まで実装していません。
プログラム実行停止したい場合は、タスクマネージャーで殺してください。。。