0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

STM32のFreeRTOS(not CMSIS)プロジェクトを構築する

Posted at

はじめに

この記事では,STM32でFreeRTOSを作させるプロジェクトの作成を行います.

通常,CubeMXを用いてFreeRTOSプロジェクトを作成すると,CMSISによるFreeRTOSのwrapper(CMSIS-FreeRTOS)を使用することになります.ですが,今回はCMSIS-FreeRTOSは使用せず,より一般的なネイティブのFreeRTOSを用いてプロジェクトの作成を行います.

以下では,主に公式:新規プロジェクト作成を参考にしながら,FreeRTOSプロジェクトの作成を行っていきます

ハードウェアは,STM32F401RE(NUCLEO-F401RE)を使用します.

作成したプロジェクトはGitHubに上げています.

FreeRTOSに必要なファイルの準備

FreeRTOSを動かすのに必要なファイルを用意します.

まずは,公式:FreeRTOSのダウンロードまたはGitHubをダウンロードします.

ダウンロードしたフォルダの中には,サンプルプログラムなどの多くのファイルが含まれています.しかし,プロジェクトに作成には,FreeRTOSvyyyymm.dd/FreeRTOS/SorceまたはFreeRTOS-Kernelフォルダ内にある3つのソースファイルとヘッダファイル群

  • tasks.c
  • queue.c
  • list.c
  • include/*.h

とその更に下の階層にある3つのファイル

  • /portable/MemMang/heap_[x].c
  • /portable/[compiler]/[architecture]/port.c
  • /portable/[compiler]/[architecture]/portmacro.h

のみを使用します.

[compiler][architecture]の組み合わせによっては,/portable/[compiler]/[architecture]/内に,上記に記載がないヘッダファイル*.hやアセンブラファイル*.asmが含まれていることがあります.その場合は,それらも追加で含める必要があります.

ここで,[compiler]は使用するコンパイラ,[architecture]は使用するCPUアーキテクチャーを指定します.
今回は,コンパイラにarm-none-eabi-gcc"を使用するので[compiler] = "GCC",ターゲットCPUとしてCoretex-M4FであるSTM32F401REを使用するので,[architecture] = "C4F"となります.

また,[x]には,FreeRTOSでのheapメモリの確保方法を選択します.
今回は,推奨されているheap4.cを用いるので,[x] = "4"を指定します.
heapメモリについての詳細は,公式:ヒープメモリについてに記載があります.

CubeMXによるプロジェクトの作成

今回,クロックやペリフェラルの設定はCubeMXのGUI上で行います.

その際に,デフォルトから変更する点が3つあります.

  • HALのTimebase Sorceの変更
  • 割込みの優先度グループ割り当て・優先度の変更
  • 例外・割込みハンドラのコード生成の無効化
  • ツールチェーンのMakefileへの変更,etc

HALのTimebase Sorceの変更

FreeRTOSは,Sys Tickを使用するので,それとの競合を避けるために,HALライブラリのTimebase SorceSys Tick以外のタイマを割り当てることが推奨されます.

今回は,TIM2を割り当てます.

systime.png

割込み(NVIC)の優先度グループ割り当て・優先度の変更

Cortexの割込み(NVIC)優先度についての詳細は公式:cortex-m3,4での割込み優先度についてに記載があります

change_nvic_priority.png

  • 優先度グループ割り当て

    Cortex-Mアーキテクチャでは,割込み(NVIC)優先度を8bitで設定します.さらに,8bitをメインorサブにグルーピングできるものもあります.ですが,8bitのうちユーザが設定可能なbit数やメインorサブのグループ分けの有無は,ベンダー依存になります.

    ここで,FreeRTOSを使用する際は,全てメインに割り当てることが推奨されます.

    今回,STM32F401REでは,設定可能bitは上位4bitでメインorサブのグループ分けが有効であるので,4bitすべてをメイングループに割り当てます.

  • 優先度の変更

    Cortex-Mアーキテクチャにおいて,割込み(NVIC)関数から,FreeRTOSのAPIを呼ぶ際には,割込み(NVIC)の優先度をconfigMAX_SYSCALL_INTERRUPT_PRIORITY以下の優先度に設定する必要があります.

    ここで,FreeRTOSでは設定数値が大きいほど優先度が上がるのに対して,Cortex-Mでは設定数値が大きいほど優先度が下がることに注意が必要です.

    今回は,割込み(NVIC)を用いたり,さらにその中からFreeRTOSのAPIは呼ばないので,すべてデフォルト0のままとします.

割込み・例外ハンドラコード生成の無効化

Cortex-M3, M4, M4Fでは,幾つかの割込み・例外ハンドラは,FreeRTOSライブラリ内にある処理を使用するので,CubeMXでのコード生成を無効化します

no_generate_code.png

詳細は公式F&Q集に記載があります

ツールチェーンのMakefileへの変更,etc

Project Manager > ProjectToolchain/IDEMakefileに変更します.

また,好みになりますが,Project Manager > Code Generatorについて,以下にチェックを入れます.

  • Copy only the necessary library files
  • Generate peripheral initialization as ~

設定ファイルFreeRTOSconfigの用意

チュートリアル通り,GitHubをベースにカスタマイズします.詳細は,公式:FreeRTOSConfig.hについてに記載があります.

今回は,以下の変更を行いますが,FreeRTOSの様々な機能を使用・詳細の動作を制御したい際には,更に追加の変更が必要となることに,注意ください.

クロック・タイマーの設定

  • configCPU_CLOCK_HZ 
    • CPUクロックの設定
  • configTICK_TYPE_WIDTH_IN_BITS
    • FreeRTOS内タイマーカウンタのビット長の設定

作成したプロジェクトに沿って,CPUクロックの設定とFreeRTOS内タイマーカウンタのビット長を以下のように設定します.

#define configCPU_CLOCK_HZ    ( ( unsigned long ) 16000000 )
#define configTICK_TYPE_WIDTH_IN_BITS              TICK_TYPE_WIDTH_32_BITS

割込み優先度に関する設定

割込み優先度に関する設定には以下の3つがありますが,ハードウェアによって使用される設定が少し異なります.

  • configKERNEL_INTERRUPT_PRIORITY
    • FreeRTOS自身が使用する割込み優先度
    • 対象:ARM Cortex-M3, IC24, dsPIC, PIC32, SuperH, RX600
  • configMAX_SYSCALL_INTERRUPT_PRIORITY
    • 割込み内でFreeRTOSのAPIを呼び出すことができる最高の割り込み優先度
    • 対象:IC32, RX600, ARM, Cortex-A/M
  • configMAX_API_CALL_INTERRUPT_PRIORITY
    • configMAX_SYSCALL_INTERRUPT_PRIORITYの新しい名称
    • 対象:IC32, RX600, ARM, Cortex-A/M

STM32F401REでは,configMAX_SYSCALL_INTERRUPT_PRIORITYが使用されます.

先述の通り,今回は,割込み(NVIC)を用いたり,さらにその中からFreeRTOSのAPIは呼ばないので,0のままで良さそうですが,この値を16=0x10以上にする必要がありました.

というのも,STM32F401REでは.割込み(NVIC)優先度を決める8bitのうち上位4bitが有効であるので,16=0x10から255=0xFFの範囲で設定する必要があるようです.

// #define configKERNEL_INTERRUPT_PRIORITY          0
#define configMAX_SYSCALL_INTERRUPT_PRIORITY    16
// #define configMAX_API_CALL_INTERRUPT_PRIORITY    0

割込み・例外ハンドラの登録

Cortex-M3, M4, M4Fでは,特定の例外・割込みが起こった際,FreeRTOSの想定する処理が実行されるように,ハンドラを登録します.さきほど,これらのハンドラのコード生成を無効化したのは,このためです.

config.cpp
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler

必要ファイルの配置・Makefileの修正

各ファイルの配置

今回は,/Drivers内にFreeRTOS-Kernelというフォルダを作成し,そこへ先ほど準備したファイルを配置します.

FreeRTOSConfig.hは,Core/Incに置くこととします.

├─Core
│  ├─Inc
|  | └─FreeRTOSConfig.h
│  └─Src
└─Drivers
    ├─CMSIS
    ├─FreeRTOS-Kernel
    │  ├─include
    │  └─portable
    │      ├─GCC
    │      │  └─ARM_CM4F
    │      └─MemMang
    └─STM32F4xx_HAL_Driver
        ├─Inc
        └─Src

Makefileの修正

追加したファイルをbuild対象に含めます.
具体的には,以下の内容をMakefileに追記します.

  • ソースファイルの追加

    # C sources
    # ...
    C_SOURCES += \
    $(wildcard Drivers/FreeRTOS-Kernel/*.c) \
    Drivers/FreeRTOS-Kernel/portable/GCC/ARM_CM4F/port.c \
    Drivers/FreeRTOS-Kernel/portable/MemMang/heap_4.c
    
  • インクルードパスの追加

    # C includes
    # ...
    C_INCLUDES +=  \
    -IDrivers/FreeRTOS-Kernel/include \
    -IDrivers/FreeRTOS-Kernel/portable/GCC/ARM_CM4F
    

スタックオーバーフローチェックフック関数の実装

各タスクで使用するスタックメモリが,予め定められた量を超えた際に,フックされる関数の実装を記述します.

void vApplicationStackOverflowHook( TaskHandle_t xTask, char *pcTaskName )
{
  char* uart_mes = " Stack Over Flow!\r\n";
  HAL_UART_Transmit(&huart2, (uint8_t*)uart_mes, strlen(uart_mes), 1000);
}

このフックを無効化したい場合には,configCHECK_FOR_STACK_OVERFLOW0に設定します.オーバヘッドが生じるので,開発時(デバック時)のみ有効にすることが推奨されます.

詳しくは,公式:スタックオーバーフローチェックを参照ください.

タスクの作成・登録

今回は,main.cの中で,タスクの作成・登録を記述します.

ヘッダの記述

main.cのヘッダを追加して,FreeRTOSライブラリを使用できるようにします.

#include <string.h>は,シリアル通信に使用しているだけで,FreeRTOSと直接関係はありません.

#include "FreeRTOS.h"
#include "task.h"
#include <string.h>

タスクの宣言

二つのタスクLEDBlinkB1Pushedを用意します.

  • LEDBlink
    1secごとにLEDを点滅させるタスク
    void LEDBlink(void* pvParameters)
    {
      while (1)
      {
        HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
        char* uart_mes = "LED Blink!\r\n";
        HAL_UART_Transmit(&huart2, (uint8_t*)uart_mes, strlen(uart_mes), 1000);
        vTaskDelay(1000);   
      }
    }
    
  • B1Pushed
    ボタンが押されているかを50msecのポーリングで監視し,ボタンが押されていることをUARTに通知するタスク
    void B1Pushed(void* pvParameters)
    {
      while (1)
      {
        GPIO_PinState state =  HAL_GPIO_ReadPin(B1_GPIO_Port, B1_Pin);
        if (state == GPIO_PIN_RESET) {
          char* uart_mes = "B1 Pushed!\r\n";
          HAL_UART_Transmit(&huart2, (uint8_t*)uart_mes, strlen(uart_mes), 1000);
        }
        vTaskDelay(50);
      }
    }
    

タスクの登録・起動

用意したタスクを登録したのちに,タスクの起動を行います.
特に,タスクを起動する関数vTaskStartScheduler()は,システムクロックやペリフェラルの初期化が行われた後,mainループ処理を行う前に呼び出します.

  xTaskCreate(LEDBlink, "LEDBlink", configMINIMAL_STACK_SIZE*2, NULL, 3, NULL);
  xTaskCreate(B1Pushed, "B1Pushed", configMINIMAL_STACK_SIZE*2, NULL, 3, NULL);
  vTaskStartScheduler();

動作確認

ここまでの作業で,STM32のFreeRTOSプロジェクトの作成は終了です.
後はコンパイルして,マイコン上に展開してください.

LEDが1secごとに光り,ボタン(B1)を押している間は50msecごとに"B1 Pushed!"がシリアル表示されれば,成功です.

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?