はちゅねタコメーター製作(ソフトウェア編)

画面表示用のデータ生成

ニコ動からゲットした画像を元に、40x32のモノクロミニはちゅねをぷちぷちと描く。
2値なので微妙なグラデーションが出せずディザを使い倒す羽目なり、さらにサイズが限界に近い小ささなので、細かいディテールは再現不能
見た目のはちゅねっぽさを維持できるレベルまでガンガンデフォルメしていく。
 
出来上がった数パターン×2コマのはちゅねアニメのBMPを、自作ソフトで液晶用のバイナリデータに変換。それをソフトウェア先頭で定数化しておく。
同じ要領で、実際に画面リソースを組み込んでしまう。数字の文字データ、タコメータ用のバーグラフ、REVアラーム用の画像などなど。手間ばかりかかって面倒な作業だが、直接見た目に関わる作業なので黙々と進める。
 
トータルでは結構なデータ量になるのだが、幸いチップがmega644なのでROMに余裕があり、サイズ的な最適化は考えずに作業できた。
 

グラフィックLCD駆動処理

LCDからデータを読むことはないので、出力のみと割り切ってLCD制御処理を単純化していく。チップ(画面の左右でチップが分かれている)とPageとAddressを指定してデータを渡すと、任意の位置にデータを書き込める関数が完成。
細かく見ると「連続描画でも毎回座標指定している」というスマートではない処理になってしまっているが、液晶自体の反応速度を遙かに超える速度は出ているので無視。
 

rpm計測処理の設定

Timer0は液晶駆動に使用中なので、Timer1を使用する。
当初は、タイマーの値(TCNTx)を元に計測するつもりだったが、精度を確保するために20MHzで回すと、16bit幅では足りない(低rpm時にタイマーが1巡以上してしまう)。なので、タイマーが0xFFFFを超えた回数もカウントしておく必要が出てきた。
 
無論、タイマーの速度を落とせばTCNTxだけで計算できるが、エクセル上で計算してみると高rpm時の分解能が大幅に低下する事が判明。仕組みの単純化も重要だけど、タコメーターとしての基本的な精度は無視するわけにもいかない。
 
結局、Timer1を20MHz(CPUクロック分周無し)で駆動、カウンタが0xFFFFを溢れるたびに割り込みをかけてグローバル変数をカウントアップさせるように仕込むことにした。
同時にパルス入力用のPCINT28のピン状態変化割り込みを有効化し、割り込み内でH→Lの時だけ反応するように設定。その瞬間のカウンタ溢れ回数とタイマーカウントをグローバル変数へ取り出し、溢れ回数とタイマーをリセットさせる。
 
これでパルスとパルスの間を20MHz刻みのカウンタ値で検知できるようになるので、このカウンタ値を元にパルス間のμ秒を計算、それをrpmへ換算することでrpm計測処理が完成。
 

実際の動作

rpmに応じて表示が動くようにプログラムを修正し、早速テストしてみる。
 
すると、割り込みを有効化した瞬間や、動作の途中でハードウェアリセットがかかるようになってしまった。プログラムコードを部分的に変更するとリセットまでの時間が延びたり縮んだりする(ただし、起動からリセットまでの間隔は一定なので外的・環境的要因ではない)という謎っぷり。
割り込みを無効にするとリセットが発生しないので、どうも割り込み関連らしいというトコロまでは分かったのだが、具体的な原因までは分からない。
ただ、cli();とsei();できちんと割り込み制限をかけてあげたら安定してきた。ウェイト以外の計測と描画の処理に細かく制限を設定したら、ほぼリセットしなくなった。
 
割り込み使用時のcli();とsei();は重要らしい。
ググッたら、割り込み時にレジスタの中身が書き換わり、割り込まれた方の処理が吹き飛ぶとかなんとか、なかなか難しい話みたい。
 
主にウェイト(_wait_usとか)に割り込みがかかるようなプログラムデザインにするのが良いみたい。
 

検証用回路

実際にパルスを打ち込んでちゃんとrpm表示するか見たいのだが、チャタリング対策されていないボタンスイッチをカチカチすると時々トンでもない回転数が出てくる。
感覚的にはちゃんと動いているっぽい(1秒間隔でカチカチすると、概ね60rpmになる)のだが、任意の周波数のパルスを発生する回路を準備しないと・・・。
 
ファンクションジェネレータの存在意義がやっと分かってきたwww