2009年3月27日金曜日

TinyでDRO!(ソフトウェア1)

ソフトウェアの説明です。例によってSourceForgeで公開します。


ヒューズの設定はZIPファイルの中のFuseMapping.jpgにAVRStudioの(中のAVRProgの)設定画面のスクリーンショットを入れてありますので参考にしてください。また、ソースコード中にもコメントで書き込んであります。要は内蔵RC発振器をONにして、1/8分周をOFFにするということです。

今回のプログラムの要点は、スケールから送られてくるデータをクロックに同期して取り込むこと、そして直列で送られてくるデータを切り出し---つまり頭出し---することです。

まずマクロな視点から。データブロックは約0.3秒の間隔でスケールから出力されています。


クロック(CLK)は出っぱなしではなく、データ(DATA)が送られるときのみ出ているのが分かります。クロックが出ていないときは論理"0"(-1.5V電位)にスタックしています。データブロックの部分をクローズアップしてみます。


クロックはデータ出力開始直前に"1"(GND電位)となり、しばらくしてからクロッキングを始めます。クロック周期は約12usですので、周波数にすると約83kHzです。

データは2種類送られてきます。先に絶対座標のデータ、後に相対座標のデータです。絶対座標はそのスケール固有の位置情報で、原点が絶対にずれることはありません。相対座標はスケールのゼロクリアボタンを押すごとに原点を設定するもので、通常はこちらが使われることになります。

これらのデータは24ビット長で、クロックパルスに同期して順次最下位ビットから送り出されます。図でA0~A23が絶対座標、R0~R23が相対座標です。R24(ダミー)と書いてあるビットがありますが、これは本来は取り込まないビットで、後ほど説明します。

図で分かる通りクロックの立下りエッジでデータを取り込むとよさそうな感じです。実際のプログラムでは、AVRに入ってくるときにレベルシフタにより論理反転していますので、クロックの立ち上がりエッジで捉えることにしています。ここでファイル"interrupt.c"の該当部分。(コメント部分で改行されてしまい読みにくいのでコメントをちょっといじってあります。)

// スケールからのデータ取り込み。検出エッジは立上がりエッジ。
ISR(INT1_vect)
{
*Rbufp++ = SCALE_PIN; /* 読んでバッファにためる */
if (Rbufp >= Rbuf + sizeof(Rbuf)) {/*バッファ端*/
Rbufp = Rbuf + sizeof(Rbuf) - 1;
Updated = 1; /* main()に更新を通知 */
}
TCNT1 = 0; /* 送信間隔タイマーをクリア */
}
INT1は立ち上がりエッジを検出するように設定されています。重要なことは割り込みセンスから割り込みプログラムが起動されるまでの時間がかなりかかることです。あまり時間がかかるとデータを読みそこなってしまいます。割り込み処理のシーケンスは、
  1. INT1エッジ認識
  2. 割り込みハンドラに分岐
  3. 使用するレジスタ群を退避
  4. ユーザの定義した割り込み処理プログラムを実行
上記の1.から2.までで約4クロックかそれ以上かかり、これはAVRハードウェアによるオーバーヘッドですので回避不能です。3.はコンパイラの出力するコードによるオーバーヘッドが含まれ、アセンブラで書かない限りは制御はやっかいです。コンパイラのアセンブラ出力を見るとここに16クロックかかっていることが分かります。そこでこの割り込み処理では、スケールのDATAがつながっている入力ポート(上記リストの"SCALE_PIN"=PIND)を何はともあれ読み込んで、バッファにためることだけを行っています(52行目)。その際にそのビットを抽出するなど余計なことはやっていません。ちょっとでも複雑なことをしようとすると、コンパイラはためらうことなくたくさんのレジスタを使おうとして、それらを退避するコードを割り込み処理の冒頭に突っ込んでくるからです。

今回データ読み込みはすべてクロックの立ち上がりエッジ(タイミング図上では立下り)で検出しているので、前出のタイミング図では、データ送出終わった後にクロックが"0"スタックする時点でもうひとつ検出エッジが来てしまいます。これが「R24(ダミー)」と書いてあるゆえんです。プログラムではこの部分も愚直に読み込んでしまうことで、割り込みプログラム中の無用な条件判定を回避しています。読み込んだとしても値を集計するときに使わなければいいのです。

0 件のコメント: