[目標]
・スイッチで,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 は,自由に設定できる。
別にデフォルトでいい。
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 件のコメント:
コメントを投稿