コメを噛め

コメを噛め

rerofumi の電子工作メモ

hatena bookmark


やっぱりマイコン開発に LCDモジュールはつきもの。
初期化が若干面倒なので制御処理の初歩にちょうど良いし、表示装置というのはなんだかそれだけでカッコイイ。それに色々数値を表示することで、デバッグ作業が楽になるというもの。
一般的な HD44780 コンパチな LCDモジュールは 5V 駆動だったりする。だけれども最近のマイコンは 3.3V 駆動のものが多くなり STM32 も 2~3.6V ラインである。まあ、液晶駆動用に 5V ラインがあれば信号線は 3.3V でも動作するし、STBee は 5V のラインもあるからその組みあわせでなんとか動作させることができちゃったりもする。
しかしまあ、今後の主流が 3.3V だというのならそれに慣れておくのも良かろうと、3.3V 駆動で有名なストロベリーリナックスの I2C LCD 表示モジュールを用意した。3.3V ってだけじゃなくて I2C でもあるので信号線が少なくて済むというメリットもある。
3.3V 液晶モジュールはストロベリーリナックスだけでなく、共立電子からも買える様になっているので徐々に入手しやすくなってきている感がある。

STM32 で I2C を使ってみようというのは peripheral 使用のトレーニングとしては割と面倒な部類に入るかもしれない。
I2C のサンプルコードは見かけるのだが


    while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT));

などとポーリングで完了待ちをしていたりしていささかよろしくない。
外部通信は存分に遅い処理なのでその間ループでブロックして待つというのは無駄な処理と時間を費やす事になる。
そこで、割り込みと FreeRTOS のタスクを利用してできるだけ無駄な負荷がかからない I2C LCD 表示ルーチンを目指してみた。


少々複雑なれどタスクとキューと割り込みの関係図。

ソースコード
Download: fumi2_stbee_project-0.3.0.zip


■ fm_i2c_lcd の使い方

関数 はたらき
fmI2C_Init(port) I2Cポートと割り込みの初期化、vI2C_LCD_TASK()の前に呼ぶ
vI2C_LCD_TASK() FreeRTOSのタスクとして実行する、LCD初期化はこの中で行われる
fmI2C_LCD_State() タスクの状態を返します、STATE_I2CLCD_READY なら表示リクエストが行えます
fmI2C_LCD_ClearScreen() 画面クリアのリクエスト
fmI2C_LCD_Contrast(cont) LCDモジュールのコントラストを変更する(0-255)
fmI2C_LCD_Position(x,y) 表示位置を指定する
fmI2C_LCD_PutChar(cCode) cCodeの文字を1文字表示する
fmI2C_LCD_Puts(cString,size) 文字列cStringを表示する、表示サイズはsizeを超えない
fmI2C_LCD_DisplayHex_8(val) 2桁のHEXで数値を表示する
fmI2C_LCD_DisplayHex_16(val) 4桁のHEXで数値を表示する
fmI2C_LCD_DisplayHex_32(val) 8桁のHEXで数値を表示する

fmI2C_Init() を呼んで初期化してから vI2C_LCD_TASK() のタスクを生成してください。fmI2C_LCD_State() が STATE_I2CLCD_READY になれば初期化は完了で、表示のリクエストを行うことができます。
LCDモジュールの制御は I2C バスの 2本だけでなく、/RST のモジュールリセット用にもう 1本の GPIO を使用することを期待しています。どのピンを/RSTに繋ぐかは fm_i2c_lcd.h の定義で指定するか、FM_I2CLCD_SW_USERESET を 0 にして /RST を無効にしてください。
電源オンで内部リセットがかかるため正常動作の範囲では /RST を繋がなくても大丈夫ですが、コマンドエラーとか通信を中断した後など正常に通信できない現象があるようなのでできるだけ繋いだ方がよさそうです。

■ ひっかかった点あれこれ
これまではブートシーケンスと main() までの環境を整えてきたので、STM32 のペリフェラルと割り込みをきちんと扱うのは始めての事となる。割とどたばたひっかかりまくったのでメモがき程度に残しておく

・リファレンスマニュアルは読もう
STM32F10x_StdPeriph_Driver があるおかげで C言語のライブラリになってはいるものの、このライブラリを見ただけで扱えるものではないと思った。
RM0008 Reference Manual をダウンロードしてきて、peripheral register の仕様をちゃんと確認しないと使い方がほとんどわからない。逆に Reference Manual から直接レジスタを叩く方法を理解した上で、使えそうにまとまったところだけライブラリを利用するのがよさそうだ。

・割り込みレベルと FreeRTOS
FreeRTOS のタスクマネージ自体も割り込みで動作している。
なのでユーザーが使用する割り込みが FreeRTOS のものより高かったりしたら割り込みの方に全部持って行かれて FreeRTOS のタスクが動作しなくなることがある。
FreeRTOSConfig.h のなかに


  #define configMAX_SYSCALL_INTERRUPT_PRIORITY 	191
   /* equivalent to 0xb0, or priority 11. */

という設定があって、これをそのまま使うのであればユーザーの使う割り込みは 12-15 であれば問題無さそう。

・I2Cの割り込みは通信ステートマシンのためのもの
I2Cペリフェラルの割り込みは各種イベント用の EV とエラー時の ER の 2種類がある。
エラーの方は通常の割り込みフラグと同じで、割り込みハンドラ内でクリアしてやれば良い形なため特に問題は無い。
イベントの方は割り込みに使われるイベントフラグがリードオンリーになっている。これを下げる方法はリファレンスマニュアルをよく読まないと分からないのだが、ステータスを見たりデータレジスタをセットしたりといった特定アクションでクリアされる。
つまり、I2C EV の割り込みは通信ステートマシンを割り込みハンドラの中で作成するために使われる。
当初はこれがわかっていなくてイベントをメッセージで投げて、それを拾うタスクの方でステートマシンにしようとして失敗した。割り込みハンドラの中でステートマシンを作らないとイベントのフラグをクリアできないため、そのイベントで延々と割り込み発生し続けるという状態になってしまうのである。

・I2C LCDモジュールのシンク能力は高くない
通信ができるようになってきたとき、LCDモジュールが ACK を返さなかったり、一応返してくるのだけれども遅くてマイコン側で NACK 扱いされてしまいエラーとなるケースが多発した。
どうにもLCDモジュールが ACK に引き下げる力が弱そうだということで調べたらねむいさんのところでまさにそのものな記述があった。
それで見てみたら同じように私も 1kΩ をプルアップにしようしていた。がっかりである。
普段 I2C のプルアップは 2.2kΩ を使っているんだけれども、なんでこのときだけ 1k にしようとしたかは今ひとつ覚えていない。
てなわけで、プルアップ抵抗には 2.2k 前後の適値なものを使用すること。

色々と手こずったけれども、今回のこれで STM32 のペリフェラルと割り込みの使い方についての流れがつかめたとは思う。

Leave a Reply