コメを噛め

コメを噛め

rerofumi の電子工作メモ

hatena bookmark


STBee でLEDチカチカやってみました、第二弾。いつまでLチカやってんじゃいと思わずお付き合いくださいまし。
上の動画を見て貰った方が早いのだけれども、今回は 2つのLEDがそれぞれ異なる周期で非同期にチカチカしている。これは FreeRTOS によってそれぞれ別のタスクとして動作しているという成果によるもの。

今回は開発ソースツリーに FreeRTOS を含めたというお話。
STBee を使った電子工作プロジェクトの開発環境を整備するというシリーズも目標としていたところを一通り準備し終えたということで、これで一応の完結となる。

ソースコード。
Download: fumi2_stbee_project-0.2.0.zip


FreeRTOS は基本的な機能を備えた小さな組み込み向けのリアルタイムOS。タスク、メッセージ、セマフォ、MUTEX、ヒープ管理といった機構がある。小さくまとまっているので、PIC32 や AVR にも porting されている。
後閑さんのFreeRTOS解説が非常に良くまとまっていて、RTOSがなんであるかを知っている人はこれを読んだだけですぐに使えると思う。

さて FreeRTOS をダウンロードすると source ディレクトリに 4つの .c ファイルがみつかる。

  • croutine.c
  • list.c
  • queue.c
  • tasks.c

FreeRTOS の本体はこれとヘッダーファイルだけである。
他に必要なファイルは例によって散らばっているので自分のターゲットに合わせて拾い集めていく。

  • port.c
  • portmacro.h

このほかプロセッサ毎に異なる部位をまとめたものが source/portable/ ディレクトリに納められているのでターゲット用の port.c と portmacro.h を拾って先のソースと同じ所に置いておく。
今回の目的である STM32 用の port は Source/portable/GCC/ARM_CM3/ に置いてあるのでそれを用いる。

  • heap_2.c

RTOSにはメモリ管理があって、ヒープからワークエリアを取得する。いわゆる malloc なあれである。
組み込み環境だと潤沢にメモリがあるわけではないし、リッチなヒープ管理を持ってもコードの無駄となる。それに、ほとんどの場合最初にメモリを割り当ててしまえば解放することもなくずっとそれを使い続けることで済んでしまうのでヒープ管理は簡単にすることができる。
そこで FreeRTOS では 3種類のヒープ管理コードがあって目的に合わせたモノを選択するようだ。このコードは Source/portable/MemMang/ に置いてあって heap_(1-3).c というファイル名になっている。
これのどれかを持ってくるのだが、STM32 の場合は 1 か 2 であろう。
heap_1 はヒープを取得したらそれを使い続けるタイプ。heap_2 は取得と解放があり自前でヒープを管理するタイプ。heap_3 は内部で malloc をコールして管理はそちらに任せるタイプ。となっているようだ。

  • FreeRTOSConfig.h

もうひとつ、FreeRTOSの動作を設定するコンフィグファイルが必要なのだけれども、これは source の方ではなく demo ディレクトリの方から拾う。
Demo/CORTEX_STM32F103_GCC_Rowley/ にあるものをコピーしてデバッグログ系を止めるなどの改変を行った。

FreeRTOS のソースは以上の通り。これを開発ソースツリーに追加をする。
FreeRTOS を動作させるためには割り込みベクタを 3つほど設定しなくてはいけない。

      SVC_Handler → vPortSVCHandler
      PendSV_Handler → xPortPendSVHandler
      SysTick_Handler → xPortSysTickHandler

左が CMSIS の割り込みベクタラベル名で、左が FreeRTOS の割り込みエントリー名。
SVC_Handler() という割り込み関数を作ってそこから vPortSVCHandler() をコールしても良いのだけれども、ブランチがワンクッション生じてしまうので startup 内の割り込みベクタ設定に直接書いてしまった方がよさそう。


g_pfnVectors:
	.word	_estack
	.word	Reset_Handler
	.word	NMI_Handler
	.word	HardFault_Handler
	.word	MemManage_Handler
	.word	BusFault_Handler
	.word	UsageFault_Handler
	.word	0
	.word	0
	.word	0
	.word	0
	.word	vPortSVCHandler
	.word	DebugMon_Handler
	.word	0
	.word	xPortPendSVHandler
	.word	xPortSysTickHandler
	.word	WWDG_IRQHandler
(途中を抜粋)

こんなかんじ。

他に留意点と言えば、FreeRTOS は内部で libc 関数を呼ぶのでリンクする必要がある。ほとんどがデバッグログ用なので、コンフィグで未使用にすれば libc コールを減らせる。それでも1つだけ残ってしまうので libc は必須となる。
その1つもヒープ割り当て後デバッグが容易になるように定値でフィルするために memset を呼んでいるというもので、ソース改変して削ってしまっても良いくらいのものだったりするのだけれども。

これで FreeRTOS が動作するようになる。
動いてしまえばタスクという概念で動作するようになるため、コードを書くのがずいぶんと楽になるというものだ。


void vTask1(void *pcParam)
{
  while(1) {
    xSemaphoreTake(mutex_portb, portMAX_DELAY);
    GPIO_ResetBits(GPIOB, _BV(13));
    xSemaphoreGive(mutex_portb);

    vTaskDelay(500 / portTICK_RATE_MS);

    xSemaphoreTake(mutex_portb, portMAX_DELAY);
    GPIO_SetBits(GPIOB, _BV(13));
    xSemaphoreGive(mutex_portb);

    vTaskDelay(500 / portTICK_RATE_MS);
  }
}

LEDチカチカのタスクはこんな感じ。タスク管理ベースの Delay があるのでらくちんである。

ついでに割り込み関数のお話。
割り込みベクタは startup ファイルに定義されているのだけれども、実際の飛び先については weak alias になってる。これは、そのような関数名を持つときはリンクするが、無いときはalias の方にリンクされるという仕組み。


  .weak	NMI_Handler
	.thumb_set NMI_Handler,Default_Handler

というように、NMI_Handler という関数があるとそれが呼ばれるのだが、無い場合は Default_Handler が呼ばれるようになっている。
この Defalut_Handler は startup 内で定義されており、無限ループで停止するようなコードに成っている。つまり、割り込み関数が用意されていないのに割り込みがかかるとだんまりになってしまうということだ。
なんで動作しないのかわからないといった状況の原因になりやすいので覚えておきたい。

今回割り込み関数のスケルトンとなる stm32f10x_it.[c|h] も用意しておいた。
stm32f10x_it_setting.h 内で使いたい割り込みを 1 にすると stm32f10x_it.c 内の関数が有効化するので、そこに処理を書くと良いだろう。

Leave a Reply