2018年2月28日水曜日

STM32 Nucleo FreeRTOS SW割り込み on mac memo

[目標]

・スイッチで,LEDをON/OFFする。
  ・最初 : LED OFF
  ・1回目 : LED ON
  ・2回目 : LED Blink(500ms)
  ・3回目 : LED OFF
・スイッチのON/OFFには,IRQ割り込みを用いる
 (ポーリングは面白くないよね)

[前提]

・NucleoL152RE を使用
・NucleoにデフォルトでのってるLED(LD2) をLチカ
・開発環境 SW4STM32(AC6)
・コード生成ツールSTM32CubeMX を使用
http://chuchulabo.blogspot.jp/2018/02/stm32-nucleo-freertos-l-on-mac-memo.html
 で作ったプロジェクトを再利用。
 (めんどくさいから)
 (続きとして作るね)

[手順]

http://chuchulabo.blogspot.jp/2018/02/stm32-nucleo-freertos-l-on-mac-memo.html
の続き。

== Cube MXの設定 ==

CubeMXのプロジェクトを開く。
Nucleoに乗ってる青いスイッチを使用する。
で,それに繋がってるピンは,PC13。
「B1[Blue PushButton]」ってなってる。
ピンをクリックすると,そのピンに設定されている機能を設定/参照できる。
PC13をクリックして,「GPIO_EXTI13」であることを確認する。
さっき設定した「GPIO_EXTI13」は,
このPC13ピンは,GPIOで,割り込み使いますよって意味。
GPIO : 汎用入出力。General-purpose input/output の略。
EXTI : 外部割込み/イベントコントローラ。External Interrupt の略。

次に,GPIOの設定を行う。
「Configulation」タブ->「GPIO」ボタンをクリック。
Pin Configulationダイアログが立ち上がる。
「GPIO」タブを選択すると,設定されているGPIOピンが見れる。
今(Nucleoのデフォルト)は,LEDとSWの設定が見える。
「PC13-WKUP2」をクリックすると,下にどんな設定になっているかが表示される。
プルダウン形式で選択可能。
Nucleoのデフォルトは,
  ・立ち上がりエッジで割り込み
  ・プルアップ/プルダウン なし
になってる。
User Label は,自由に設定できる。
別にデフォルトでいい。
今回は,他の項目は確認しなくていい。
「OK」をクリックして,GPIOの設定は終了。

次に,割り込みの設定を行う。
さっきの立ち上がりエッジ割り込みとか,
割り込み設定じゃないのかというツッコミをしたくなったけど,
立ち上がりエッジ/立ち下がりエッジ/両エッジ とかは,汎用I/Oの割り込みだから必要なもの。
というわけで,やっぱりGPIOの設定になるのかな?

割り込みは,NVICで設定できる。
NVIC : ネスト型ベクタ割り込みコントローラ。Nested Vectored Interrupt Controllerの略。

「Configulation」タブ->「NVIC」ボタンをクリック。
NVIC Configulationダイアログが開く。
「NVIC」タブ->「EXTI line[15:10] interrupts」の「Enabled」にチェックを入れる。
「Preemption Priority」とか,「Sub Priority」とかは,とりあえずデフォルトで。
あと,FreeRTOSを使ってるので,とりあえず「Uses FreeRTOS functions」にチェックを入れておく。

「Enabled」にチェックを入れると,Code Generationの項目が勝手に設定される。
「Code Generation」タブ->「EXTI line[15:10] Interrupt」が勝手にできる。
「Generate IRQ handler」 にチェックが入っていることを確認する。
たぶん,これでNVICの設定は終わり。
「OK」をクリックする。

「Project」->「Generate Code」でコードを生成する。
生成したら,コード(もとい,プロジェクト)を開くか聞かれるので,とりあえず開く。


== 設計? ==


スイッチのON/OFFの時の割り込み(EXTI line[15:10] Interrupt)で,LEDのON/OFF/BLINKを切り替えるのもいいけど,それでは,FreeRTOS使わなくてもできる。
なので,少し回りくどいことをする。
せっかくLEDタスクを作っているので,LEDの制御は,全てLEDタスクから行うようにする。(と言っても,BLINKはタイマ機能使うけど。)
というわけで,シーケンス図。
スイッチの割り込みがかかったら,メッセージをLEDタスクに送る。
LEDタスクは,メッセージを受け取るごとに,
ON->OFF->BLINK->・・・
とLEDの光り方を変えていく。
ちなみに,これはSeq Diagで作った。
PowerPointで作ろうかとも思ったけど,線を引くのがいやだったので,Seq Diagを使用。
すごい便利。
参考 : http://blockdiag.com/ja/seqdiag/index.html

割り込みとタスクのメッセージのやりとりは,メッセージキューを使用する。
メッセージキューとメールキュー,用途どう違うのかわからんけど,
  ・メッセージキュー : int型/ポインタ のみを転送可能
  ・メールキュー : どんな型でも転送可能
って書いてある気がする。
ただ,メールキューは,メッセージキューのラッパーな気がする。
メッセージキューは,int型/ポインタ 以外の例えば構造体のデータとかを転送したい場合には,メモリプールを取得->メモリプール領域内にデータを保存->メモリプールのポインタを転送 としなければならない。
メールキューは,この一連の流れを,勝手にやってくれるものというイメージ。

メールキューの方が便利そうだが,
なんとなく,基本を知るために,メッセージキューを使う。

== 割り込みの作業準備(ただの説明) ==


今更やけど,main.cから呼ばれてるのは,
  ・HAL_Init() : stm32l1xx_hal.cに記述。
  ・SystemClock_Config() : main.cに記述。
  ・MX_GPIO_Init() : gpio.cに記述。
  ・MX_USART2_UART_Init() : usart.cに記述。
  ・MX_FREERTOS_Init() : freertos.cに記述。
  ・osKernelStart() : cmsis_os.cに記述。
で,このあと,while(1)でループ。

でも,このループにはこないはず。(たぶん)
なぜなら,タスクを起動して,そのタスクにディスパッチされるから。


今回,記述が必要なのは,LedTaskと,スイッチ割り込みがかかった時の処理。
で,これらをどこに書くかというと,
  ・LedTask -> freertos.c
  ・スイッチ割り込み -> stm32l1xx_it.c?

LedTaskに関しては,過去(https://chuchulabo.blogspot.jp/2018/02/stm32-nucleo-freertos-l-on-mac-memo.html)に記述した通り,void StartLedTask(void const * argument) 関数が,タスクの実体。

では,スイッチ割り込み(EXTI line[15:10] Interrupt)の処理の実体はどこにあるかというと,stm32l1xx_it.cにある,void EXTI15_10_IRQHandler(void) という関数がそう。
割り込みがかかると,この関数に飛んでくる。
さらに,中でHAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13); を実行している。

P13は,スイッチの繋がってるピン。
つまり,スイッチ押された時に,HAL_GPIO_EXTI_IRQHandler(); 内に書いてる処理が実行される!
じゃあ,そのHAL_GPIO_EXTI_IRQHandler() はどこに書かれているのか?
これは,stm32l1xx_hal_gpio.c に書かれている。

HAL_GPIO_EXTI_IRQHandler() の中で,さらにコールバック関数(HAL_GPIO_EXTI_Callback(GPIO_Pin);)が呼び出されている。

たぶん,自分で記述すべきは,このコールバック関数だと思う。

ちなみに,割り込みかかってから,ずっと,
全部の関数で,uint16_t GPIO_Pinを引数として渡している。
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);  こんな感じで。

EXTI line[15:10] Interruptは,みたらわかるけど,10〜15 の割り込みがかかった時,呼び出される。じゃあ,さらにわけた個別の処理(10なら10の,13なら13の異なる処理)を行いたい場合,どうするか?
これはユーザーがif文とかで分岐させてやらないといけない。
最後のコールバック関数まで分けてくれている様子はないので,コールバック関数内で自分たちで書かないといけないっぽい。

さらに,自動生成のコールバック関数のコメントに,こんなことが書いてある。

/* NOTE : This function Should not be modified, when the callback is needed,
the HAL_GPIO_EXTI_Callback could be implemented in the user file
*/

どういうことかといえば,コールバック関数が必要な場合,ここに定義してる関数は使用するな。別のとこで(ユーザーが作ったファイルで),HAL_GPIO_EXTICallback関数を定義して,その関数を使え。
ってこと。関数の最初に weak ってついてるから,もし,他の場所で同じ名前の関数があった場合には,そっちが使われるようになっている。

長くなったけど,
割り込みでやることは,
  ・ユーザーファイルを作成
  ・ユーザーファイルにHAL_GPIO_EXTICallback関数を定義
  ・定義した関数ないで,GPIO_PIN_13 の時の処理を記述(条件分岐)
  ・処理内容は,メッセージの送信
以上!

== タスクの作業準備(ほぼ割り込み)(ただの説明) ==

タスクは,何も考えずに,割り込みからメッセージが送られてきた時の処理を書けばいいんやけど,一つ問題が。

メッセージキューでメッセージを割り込みハンドラ -> タスク へ送るわけだが,メッセージキューのIDどうすんの?
extern使ったらいいのかもしれんけど,個人的にexternはあんまり好かん。
というわけで,割り込みかかった時の処理と,LEDタスクの処理は同じファイルに記述する。

でも,freertos.cにLEDの割り込みの処理書くのもなぁ・・・と思うので,
LED制御用のファイルを一個作って,そこに,割り込みの処理と,LEDタスクの処理を書くことに。
freertos.cのStartLedTask()関数からは,このLED制御用のファイルに書いた,LEDタスクの処理関数を呼び出す形にする。
図にするとこんな感じ。
気持ち悪いのが,LedCtrl.cの中に,HdrSwitch()関数が書かれていること。
SW制御ようのタスクを作って,タスク間通信をするのが正解なのかもしれない。
でも,今回はこのまま行く。(過去のログを変更するのが大変面倒だから)
次,いつかなおす!(たぶん直さない)

説明はこれくらいにしとこう。力尽きた。

== コーディング ==

書き始めたけど,すっごい書きづらいなぁ。
LED点滅用のタイマーをCubeMXで自動生成してるから,freertos.cにタイマーが書かれてるわけやけど,LedCtrl.cからタイマーの開始/停止をしたい。
freertos.cで生成してるリソースをLedCtrl.cからいじりたい場合,externするしかない気がする。
考え直した方がいいかなぁ。
CubeMXの自動生成,すごい便利やけど,FreeRTOSのリソースに関しては,自分で書いた方がいいかもしれない。タスクくらいは生成してもいいけど,その他のタイマー,メッセージキュー,メモリプールとかは自分で書いた方がいい。
externあまりしたくなければ,ユーザ作成ファイルに結局メッセージキューもメモリプールもタイマーも書くから。

・・・コーディングするのやめよっかな・・・。

コーディングやめる。
大体考えなあかんことはこれまでに書いたから,まぁ大丈夫でしょ。
何より,書きにくいし。

もっと設計ちゃんと考えなあかんなぁ・・・。




0 件のコメント:

コメントを投稿