2017年04月13日

FreeRTOS TICK RATE 調査

今回はSTM32 CubeMXで自動生成できる
FreeRTOSについて紹介させて頂きます。

CubeMXでFreeRTOSにチェックを入れるだけで
簡単にFreeRTOSを導入することが可能です。

ロボット制御やモータ制御を専門としていると
リアルタイム性や高速制御周期が制御性能や制御安定性に
効いてくるため気になるポイントです。


今回は将来的に加速度制御系にFreeRTOSを導入する場合を考え、
標準のFreeRTOSのconfigTICK_RATE_HZをデフォルト1000から
10000~60000まで変更してリアルタイム性やスレッドが動作するかテストしてみました。

FreeRTOSはconfigTICK_RATE_HZをデフォルト1000となっていますが、
この場合、最小スレッド切替が1msとなってしまいます。
加速度制御系や電流制御系を構築する場合、
20us~200us程度の最小スレッド切替が欲しいところです。



環境は
・STM32F303K8
 +SW4STM32(System Workbench for STM32)
 +STM32CubeMX(HAL ライブラリ、F3 ver. 1.7.0)
です。


CubeMXの設定は下記の通りです。
MPU周波数は内臓RCで最大周波数の64MHzに設定しました。
ポイントはデフォルトでは無効のosDelayUntil関数を有効化する点です。

rtos-setting1.png

rtos-setting2.png



作成するスレッドは3つ。

void StartDefaultTask(void const * argument)
{
  uint32_t PreviousWakeTime = osKernelSysTick();
  for(;;)
  {
      HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3);
      osDelayUntil (&PreviousWakeTime, 1);
  }
}

void StartTask02(void const * argument)
{
   uint32_t PreviousWakeTime = osKernelSysTick();
  for(;;)
  {
    HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_4);
    osDelayUntil (&PreviousWakeTime, 1);
  }
 }

void StartTask03(void const * argument)
{
   uint32_t PreviousWakeTime = osKernelSysTick();
  for(;;)
  {
      HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5);
      osDelayUntil (&PreviousWakeTime, 1);
  }
}

スレッドはそれぞれGPIOのトグルを実行し、
トグル実行時間含めて1tick分まで待機します。


スレッドの生成は3段階の優先度で設定しました。
スレッド1つor 2つの場合は2つ目、3つ目のスレッドをコメントアウトして
スレッドを減らして実行しました。

 /* definition and creation of myTask01 */
  osThreadDef(defaultTask, StartDefaultTask, osPriorityRealtime, 0, 128);
  defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);

  /* definition and creation of myTask02 */
  osThreadDef(myTask02, StartTask02, osPriorityHigh, 0, 128);
  myTask02Handle = osThreadCreate(osThread(myTask02), NULL);

  /* definition and creation of myTask03 */
  osThreadDef(myTask03, StartTask03, osPriorityAboveNormal, 0, 128);
  myTask03Handle = osThreadCreate(osThread(myTask03), NULL);




最小スレッド切替の定義は
FreeRTOSConfig.h内の
#define configTICK_RATE_HZ                       ((TickType_t)1000*10)
を変更して実験しました。



◆結果
結果一覧は下記の通り

configTICK_RATE  周期       スレッド1つ   スレッド2つ   スレッド3つ
10000           100us       ○           ○        ○
20000           50us        ○           ○        ○
30000           33us        ○           ○        ×
40000           25us        ○           △       ×
50000           20us        △           ×        ×
60000           17us        ×           ×        ×

○:スレッド通りの動作、△:ジッターが多く、時々スレッド通り動作していない
×:スレッド通り動作していない、他のスレッドが全く動作していない


◆スレッド1つ
スレッド1つの場合、configTICK_RATE_HZ 50000 が限界ラインのようです。
configTICK_RATE_HZ 60000 にすると
下記のように周期が一定でなく、非常にジッターが多い感じです。


1ch60t.png



◆スレッド2つ
スレッド2つの場合、configTICK_RATE_HZ 30000 が限界ラインのようです。
configTICK_RATE_HZ 40000 にすると
下記のようにスレッド1は周期一定ですが、
スレッド2の周期が一定でなく、非常にジッターが多い感じです。


2ch40t.png

更に周期を高く設定して、
configTICK_RATE_HZ 50000 にすると
下記のようにスレッド1は周期一定ですが、
スレッド2が全く動作していません。


2ch50t.png


◆スレッド3つ
スレッド3つの場合、configTICK_RATE_HZ 20000 が限界ラインのようです。
configTICK_RATE_HZ 30000 にすると
下記のようにスレッド1、スレッド2は周期一定ですが、
スレッド3は全く動作していません。

3ch30t.png


更に周期を高く設定して、
configTICK_RATE_HZ 40000 にすると
下記のようにスレッド1は周期一定ですが、
スレッド2の周期が安定せず、
スレッド3は全く動作していません。
3ch40t.png



◆スレッド切替時間
スレッド数2や3の場合は11us程度のようです。
厳密には同時にスレッドスタートできないため、
スレッド切替時間というよりも
スタート時の処理時間の差といった方が正しいかもしれません。

taskswitch.png



◆まとめ

スレッド1つ:25us周期、configTICK_RATE_HZ 40000が限界ライン
スレッド2つ:33us周期、configTICK_RATE_HZ 30000が限界ライン
スレッド3つ:50us周期、configTICK_RATE_HZ 20000が限界ライン
という感じです。
スレッド切替時間は11us程度。

ただ、今回のスレッド内のタスクはLEDトグルの最小限の処理のため、
実際に数値計算の処理やAD変換等の処理が入ると
もう少し限界ラインが変わってくると思います。


理想的な加速度制御系を構築するためには
電流制御周期20us程度が理想で
加速制御周期100us程度となると
スレッド2つでFreeRTOS+F3だとちょっと厳しいかなと思いました。

ということで理想的な加速度制御系を実現するには
MPU周波数が高いF4、F7、H7 を使ってみるか
今まで通り、RTOSなしでタイマ処理で分けるのが良さそうです...



posted by Crescent at 00:00| Comment(0) | TrackBack(0) | 組込ソフト | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

※ブログオーナーが承認したコメントのみ表示されます。

この記事へのトラックバック