Arduino Nano EveryでFreeRTOSを使う

Arduino IDEでインストールできるFreeRTOSライブラリではArduino Nano Everyのサポートがされていません。

本記事では、Arduino Nano EveryにFreeRTOSを導入する方法を紹介します。

具体的な例として、FreeRTOSのサンプルスケッチである「Blink_AnalogRead.ino」をArduino Nano Everyで動かすことを目指します。

環境

  • Arduino Nano Every
  • Arduino IDE 2.2.1(Windows)
  • FreeRTOS@10.5.1-1(Arduino IDEからインストール)
  • FreeRTOS 202210.01 LTS

やり方

作戦はArduino IDEでインストールしたFreeRTOSのライブラリに対して、マイコンに依存するコードをFreeRTOSから移植して、コンパイルスイッチで切り替えられるようにします!

以下の6個のステップを順番に実施します。

  1. Arduino_FreeRTOSをインストールする
  2. AVR_Mega0用のファイルを追加する
  3. TCB_tの名前を変更する
  4. Variantを削除する
  5. timers.hの名前を変更する
  6. FreeRTOSConfig.hを修正する

1. Arduino_FreeRTOSをインストールする

Arduino IDE からFreeRTOSのライブラリをインストールします。

ライブラリマネージャで「FreeRTOS」と検索して、FreeRTOSをインストールします。

Arduino Uno R3 やArduino Nanoはこの作業だけでFreeRTOSが使えるようになります。

インストールしたライブラリのソースコードは、初期設定で
ドキュメント\Arduino\libraries\FreeRTOS
にあります。

Arduino Nano Everyにボードをセットするとエラーが生じて検証できません。上記のソースコードを修正して、生じたエラーをひとつづつ対応して検証できるようにしていきます。

2. AVR_Mega0用のファイルを追加する

Arduino Nano EveryのマイコンであるAVR_Mega0用のファイル(port.c, porthardware.h, portmacro.h)をFreeFTOSからArduino_FreeRTOSに移植します。

FreeRTOS 202210.01 LTSを公式からダウンロードして展開します。
https://www.freertos.org/a00104.html

AVR_Mega0用のファイルは展開したフォルダの以下のパスにあります。
FreeRTOSv202210.01-LTS\FreeRTOS-LTS\FreeRTOS\FreeRTOS-Kernel\portable\ThirdParty\Partner-Supported-Ports\GCC\AVR_Mega0

展開してできたAVR_Mega0フォルダを、中のファイルごとArduinoIDEでインストールしたFreeFTOSのソースコードがあるフォルダ(ドキュメント\Arduino\libraries\FreeRTOS\src)にコピーします。

Arduino Nano Everyの場合、追加したファイルがコンパイルされるようにARDUINO_AVR_NANO_EVERYでコンパイルスイッチを行います。

修正するファイルは3つです。

port.c
  • ファイルの先頭に#ifndef ARDUINO_AVR_NANO_EVERYを追加する
  • ファイルの末尾に#endifを追加する
portmacro.h
  • ファイルの先頭に
    #ifdef ARDUINO_AVR_NANO_EVERY
    #include "AVR_Mega0/portmacro.h"
    #else

    を追加する
  • ファイルの末尾に#endifを追加する
AVR_Mega0/port.c
  • ファイルの先頭に#ifdef ARDUINO_AVR_NANO_EVERYを追加する
  • #include "FreeRTOS.h"#include "Arduino_FreeRTOS.h"に修正する
  • ファイルの末尾に#endifを追加する

この作業でマイコンに依存するコードを追加できました。

3. TCB_tの名前を変更する

TCB_tという構造体はArduino Nano Everyのビルド環境で使われているようで、使えません。
移植したport.cではRTOS_TCB_tという名前に変更されています。

tasks.cで使われているTCB_tをRTOS_TCB_tに置換してください。
変更箇所は以下の行です。

tasks.c:
   236: #define prvGetTCBFromHandle( pxHandle )    ( ( ( pxHandle ) == NULL ) ? pxCurrentTCB : ( TCB_t * ) ( pxHandle ) )
   325: } TCB_t;
   329: PRIVILEGED_DATA TCB_t * volatile pxCurrentTCB __attribute__ ((used)) = NULL;
   442:     static void prvDeleteTCB( TCB_t * pxTCB ) PRIVILEGED_FUNCTION;
   482:     static TCB_t * prvSearchForNameWithinSingleList( List_t * pxList,
   540:                                   TCB_t * pxNewTCB,
   547: static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB ) PRIVILEGED_FUNCTION;
   572:         TCB_t * pxNewTCB;
   584:             configASSERT( xSize == sizeof( TCB_t ) );
   593:             pxNewTCB = ( TCB_t * ) pxTaskBuffer; /*lint !e740 !e9087 Unusual cast is ok as the structures are designed to have the same alignment, and the size is checked by an assert. */
   594:             memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) );
   624:         TCB_t * pxNewTCB;
   635:             pxNewTCB = ( TCB_t * ) pxTaskDefinition->pxTaskBuffer;
   636:             memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) );
   672:         TCB_t * pxNewTCB;
   682:             pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
   686:                 memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) );
   728:         TCB_t * pxNewTCB;
   739:             pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
   743:                 memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) );
   768:                 pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e9087 !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack, and the first member of TCB_t is always a pointer to the task's stack. */
   772:                     memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) );
   822:                                   TCB_t * pxNewTCB,
  1026: static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB )
  1114:         TCB_t * pxTCB;
  1352:         const TCB_t * const pxTCB = ( TCB_t * ) xTask;
  1447:         TCB_t const * pxTCB;
  1469:         TCB_t const * pxTCB;
  1511:         TCB_t * pxTCB;
  1676:         TCB_t * pxTCB;
  1783:         const TCB_t * const pxTCB = ( TCB_t * ) xTask;
  1828:         TCB_t * const pxTCB = ( TCB_t * ) xTaskToResume;
  1883:         TCB_t * const pxTCB = ( TCB_t * ) xTaskToResume;
  2178:     TCB_t * pxTCB = NULL;
  2344:     TCB_t * pxTCB;
  2356:     static TCB_t * prvSearchForNameWithinSingleList( List_t * pxList,
  2359:         TCB_t * pxNextTCB;
  2360:         TCB_t * pxFirstTCB;
  2361:         TCB_t * pxReturn = NULL;
  2430:         TCB_t * pxTCB;
  2496:         TCB_t * pxTCB;
  2693:         TCB_t * pxTCB = xTask;
  2771:     TCB_t * pxTCB;
  2951:         TCB_t * xTCB;
  2957:             xTCB = ( TCB_t * ) pxCurrentTCB;
  2980:         TCB_t * pxTCB;
  3004:         TCB_t * pxTCB;
  3030:         TCB_t * xTCB;
  3220:     TCB_t * pxUnblockedTCB;
  3289:     TCB_t * pxUnblockedTCB;
  3431:         TCB_t const * pxTCB;
  3454:         TCB_t * pxTCB;
  3649:         TCB_t * pxTCB;
  3669:         TCB_t * pxTCB;
  3693:         TCB_t * pxTCB;
  3743:         TCB_t * pxTCB;
  3772:         TCB_t * pxTCB;
  3874:         configLIST_VOLATILE TCB_t * pxNextTCB;
  3875:         configLIST_VOLATILE TCB_t * pxFirstTCB;
  3936:         TCB_t * pxTCB;
  3967:         TCB_t * pxTCB;
  3995:     static void prvDeleteTCB( TCB_t * pxTCB )
  4118:         TCB_t * const pxMutexHolderTCB = ( TCB_t * ) pxMutexHolder;
  4208:         TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder;
  4289:         TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder;
  4904:         TCB_t * pxTCB;
  4910:         pxTCB = ( TCB_t * ) xTaskToNotify;
  5028:         TCB_t * pxTCB;
  5054:         pxTCB = ( TCB_t * ) xTaskToNotify;
  5164:         TCB_t * pxTCB;
  5189:         pxTCB = ( TCB_t * ) xTaskToNotify;
  5252:         TCB_t * pxTCB;
  5287:         TCB_t * pxTCB;

4. Variantを削除する

ValiantはArduino Nano Everyのビルド環境で使われているようで、使えません。

ARDUINO_AVR_NANO_EVERYでコンパイルスイッチを行いコメントアウトします。

FreeRTOSVariant.h
  • ファイルの先頭に#ifndef ARDUINO_AVR_NANO_EVERYを追加する
  • ファイルの末尾に#endifを追加する
variantHooks.cpp
  • ファイルの先頭に#ifndef ARDUINO_AVR_NANO_EVERYを追加する
  • ファイルの末尾に#endifを追加する

この作業により、task.hやqueue.hのヘッダファイルのインクルードを明記する必要が増えたり、vTaskStartScheduler()をsetup()関数で実行する必要がありますので注意してください。

5. timers.hの名前を変更する

timer.hというヘッダファイルはArduino Nano Everyのビルド環境で使われているようで、使えません。

ドキュメント\Arduino\libraries\FreeRTOS\src\timers.hをドキュメント\Arduino\libraries\FreeRTOS\src\RTOS_timers.hにファイル名を変更してください。

また、ライブラリ内でtimers.hを利用している以下のファイルのインクルードもRTOS_timers.hに修正してください

event_groups.c:
  40: #include "timers.h"

event_groups.h:
  37: #include "timers.h"

tasks.c:
  41: #include "timers.h"

timers.c:
  40: #include "timers.h"

variantHooks.cpp:
  38: #include "timers.h"

6. FreeRTOSConfig.hを修正する

ファイルを修正したことによりFreeFTOSの使えなくなった機能を停止させる必要があります。

FreeRTOSConfig.hの最後の#endifの前に以下のコードを追記してください。

#ifdef ARDUINO_AVR_NANO_EVERY
    #define configUSE_TIMER_INSTANCE 4
    #define configTICK_RATE_HZ  ((TickType_t)1000)

    #undef configCHECK_FOR_STACK_OVERFLOW
    #undef configUSE_IDLE_HOOK
    #undef configUSE_MALLOC_FAILED_HOOK
    #undef configUSE_PORT_DELAY

    #define configCHECK_FOR_STACK_OVERFLOW 0
    #define configUSE_IDLE_HOOK 0
    #define configUSE_MALLOC_FAILED_HOOK 0
    #define configUSE_PORT_DELAY 0
#endif

動作確認

ドキュメント\Arduino\libraries\FreeRTOS\examples\Blink_AnalogRead\Blink_AnalogRead.ino
にあるサンプルスケッチをArduino Nano Everyで実行してみます。

Variantを削除したので2点修正が必要です。

  1. #include <task.h>を追加する
  2. サンプルスケッチのsetup()関数の最後にvTaskStartScheduler();を追加する

Board TypeにArduino Nano Everyを選択してUploadを行うと、基板のLEDが点滅し、並行してシリアルモニタに数字が出力されていることが確認できました。

作業は以上となります。

タスクやQueueを使ってみましたが、いまのところ正常に動いています。

コメント

タイトルとURLをコピーしました