先週は、時計+タイマーの出力仕様を細かく作成しました。 仕様はトップダウン、制作はボトムアップ
今週から哲さんの子供は、夏休みです。夏休みの自由研究と
して、LEDを使い何かを子供に作らせようと、哲さんの頭
にはあるのですが、アイデアが浮かびません。で、啓二さん
に聞いてみることにしました。さて、どんなアイデアが出て
くるのでしょうか。
哲 :押忍!今日から、子供は夏休みなんだ。 啓二:そうか。でも、フリーランスの技術者には、休みはないんだ。 哲 :夏休みの自由研究で何かLEDで作らせたいのさ。 何かアイデアないかな。 啓二:暗くなったら、LEDを点滅させてみるのは、どうだ。 車に入れておくと、防犯装置になるぞ。それにキャンプに 行ったときには、自分のテントの位置を示すマーカーに なるし。 哲 :そうか。この78kで作れるかな。 啓二:作れるけれど、何でもマイコンでやるのは、関心しないな。 哲 :どうしてだ、簡単だろう。 啓二:お前、SEだろう。同じ結果を得るにも、複数の方法を検討 しておかなけれりゃ、提案もできないだろさ。 哲 :まあ、そうだが。 啓二:マイコンの場合は、ハードウエアを作って、プログラムを 入れないと動かない。だから、LEDで作るようなマーカー には、大袈裟だし、時間が掛かりすぎる。それに電力を使い 過ぎてしまう。 哲 :じゃあ、どんな方法があるのか、教えてくれよ。 啓二:トランジスタやコンデンサを使うのさ。これが、その回路だ。 哲 :あれま、懐かしい。高校の文化祭でやった劇のときの灯台だ。 あの点滅回路じゃないか。 啓二:そうだ。これを、本屋で「初歩のラジオ」か「ラジオの製作」で 見つけて作っただろう。あのときは、LEDは高かったけれど、 担任に物理実験で使うのを、拝み倒して譲ってもらった。 哲 :そうそう、結局そのまま点滅回路を、担任のところに置いてきたんだった。 実験に使ってくださいって。まだあるのかなあ。 啓二:さあ、なあ。これだと、トランジスタが2個、コンデンサが2個、 LEDが4個、抵抗が2種類5本、電池ホルダー1個に電池2本 CdSが1個でできる。大体、予算は¥500程度。ワンコインだ。 哲 :じゃあ、これで作らせてみるか。安上がりの方がいいしなあ。 啓二:現金な奴だな。今日は、時計+タイマーの頭に相当する部分の 仕様を決めていくぞ。 哲 :先週までのは、下位レベルの部分だったからなあ。 マイコンのプログラムの決まりきったカタチってあるのか。 啓二:他の人はどうやっているかは知らんが、自分は、次のようにしている。 void main(void) { initialize(); enable_interrupt(); while ( 1 ) { /* ? */ } } 哲 :おっと、C言語できたか。初期化処理して、割込みを許可して、 あとは無限ループにするのか。 啓二:そう。で、関数に相当する部分を、サブルーチンで作っていくのさ。 初期化ならば、ポートの出力値を決めて、プルアップの有無、ポートの 方向設定、さらに内蔵ハードウエアの初期化をする。 内蔵ハードウエアは、タイマーやA/Dコンバータだ。 その後、割込みを許可して、最上位のルーチンは、フラグで報告を受けて 必要な指示を出す。 これが、自分にとって、最も考えやすい。 哲 :最上位のルーチンは、プロジェクトリーダかマネージャみたいだな。 会社組織に似ているようにも見えるし。 啓二:まあ、OSが入っていると、無限ループの部分は、OSが 肩代わりしてくれるから、見えないかもなあ。 哲 :本当は、それじゃ駄目なんだが。 啓二:よしと、それじゃ、まず初期化から考えてこう。 ポート2の初期化だが、Appliletがやってくれる。 でも、サポートしてくれるだけだから、予め考えておく。 全部出力に設定だ。 ポート3は全部入力で、ポート4は、 P45だけが出力で、他は入力だな。 入力に設定するときは、プルアップしておけばいい。 プルアップを指定すると、外付けの抵抗を省略できる。 哲 :残りは、何かあるかな。 啓二:あるある、タイマー割り込みを利用するのだから、どの タイマーを使うかを考える。時計用タイマーとスイッチの チャタリング除去にで2個必要だな。 哲 :クロックその他も決めないとならないじゃないか。 啓二:そうだな、内蔵の8MHzのクロックを利用しよう。 システム設定のところで指定すればよい。 哲 :78kのようなマイコンのプログラムは、ファームウエアって 呼ぶが、作成手順のイロハはあるのか。 啓二:特にないと思うが、やはりトップダウンで作っていくことが 多いなあ。ボトムアップだと、先が見えないから。 じゃあ、まずは、スイッチから割込みが入ったとして、動作 を記述してみてくれ。 哲 :いきなりかい。まずは、スイッチ入力があったことをフラグで 知って、何かすればいいんだな。 じゃあ、こうしよう。 while ( 1 ) { if ( swi_flg == ON ) { swi_flg = OFF ; /* ? */ } } モードを確認して、対応する処理に分岐すればいいな。 switch文で、分岐するようにしよう。 while ( 1 ) { if ( swi_flg == ON ) { swi_flg = OFF ; mode = get_mode(); /* シフトボタン処理 */ /* +1ボタン処理 */ /* 確定 */ } } 啓二:これじゃ、頭が重くなるぞ。関数を定義してしまえよ。 哲 :そうだな、じゃ、関数sw_handlingとでもしておくか。 while ( 1 ) { if ( swi_flg == ON ) { swi_flg = OFF ; sw_handling(); } } 啓二:おお、すっきりしたじゃないか。 先に作ったswitch文の内容を、新しい関数で定義しようぜ。 哲 :わかった。 void sw_handling(void) { unsigned char mode ; mode = get_mode(); /* シフトボタン処理 */ /* +1ボタン処理 */ /* 確定 */ } 処理ごとに、すでに関数が定義されていると仮定するか。 void sw_handling(void) { unsigned char mode ; mode = get_mode(); /* シフトボタン処理 */ if ( (sw_state & 1) == 0 ) update_digit_logation(); /* +1ボタン処理 */ if ( (sw_state & 2) == 0 ) increment_digit(); /* 確定 */ if ( (sw_state & 4) == 0 ) confirm_digit(); } 啓二:モードによって、表示する内容も違うから、メイン処理の 中に、表示処理が必要だな。 while ( 1 ) { if ( swi_flg == ON ) { swi_flg = OFF ; sw_handling(); } show_digit(); } とでもしておくか。 関数show_digitの内部では、表示するデータを生成するだけにして 実際の処理は、タイマー割込みに任せてしまおう。 哲 :確か、ブリンクをするとか言っていたよなあ。どうやればいいんだ。 啓二:難しく考えなくていいよ。対応けたのビットパターンを、すべて 0にするか、スルーするかを、タイマー割込みごとに変えればいい。 こんな風にさ。 *(qdigit+0) = *(pdigit+0); *(qdigit+1) = *(pdigit+1); *(qdigit+2) = *(pdigit+2); *(qdigit+3) = *(pdigit+3); blk_cnt++; blk_cnt %= 2 ; if ( blk_flg == ON && blk_cnt > 0 ) { *(qdigit+index) = 0 ; } send_pattern(); 出力する4バイトを、最初に決めてしまう。 ブリンク指定かつカウンタの値が正ならば、対応けたのパターンを すべて0にしておく。そして、出力する。簡単だろう。 変数indexで、ブリンクするけたを指定すると、何も難しくない。 哲 :indexは、タクトスイッチで変更するだけか。 例によって、一度スイッチを押すとインクリメントして、剰余系で サイクリックになるようにするんだ。やっぱり、理系の頭だよなあ。 啓二:そうかなあ、通信でリングバッファを使うときには、出てくると 思うけれどなあ。 哲 :確かに出てきそうだが、256とかで、2のべき乗にしてカウンタ が自動的に0に戻るようにしちゃうからなあ。 啓二:なるほど。じゃあ、また来週にしようぜ。