先週は、時計+タイマーの大まかな仕様を決めました。あれ 機能分割した仕様を決めよう
から1週間が経過しています。仕様も虫干しされて、少しは
より単純な仕様に落とせるようになったでしょう。ロボの足
の爪を獣医さんに切ってもらい、哲さんは、啓二さんの仕事
部屋にやってきました。すると、何やら鍋でグツグツと音が
しています。
哲 :こんちは。今日は、ロボを獣医さんのところへ連れて 行った帰りなんで、遅くなった。 啓二:うん、まあ、いいよ。 哲 :鍋でグツグツやっているのは何だ。 啓二:温泉卵を作っているんだよ。ガスを使わず、高周波加熱 だから、火が出ないし安心さ。火を使うと、Aiboの 動きが激しく、危ないんだ。 哲 :そうか。今日は何をするんだ。 啓二:時計の機能を、分割してソフトウエア仕様を決めよう。 哲 :この前の仕様で、できるだろう。 啓二:まだ、駄目だよ。あれでコーディングできるか。 哲 :ちょっと苦しいか。 啓二:そうだろう。まず、出力から仕様を決めていこう。 最初に、4個のシフトレジスタにビットパターンの16 ビットを転送する仕様をきめるか。 哲 :実現できるかな。どれどれ、動作を書き出してみるか。 タイマー割込みで、時刻が確定したとする。 時刻は、メモリのhour、minuteに10進数で入っている と仮定しよう。 啓二:時、分ともに10進数をBCDコードに変換して、ビット パターンを引き出すようにするか。 例えば、12:34であれば、時は12、分は34が 入っている。1けたにつき、8ビットのビットパターンを テーブル参照で引き出すか。 哲 :テーブル参照なんて、エレガントじゃないなあ。 啓二:そんなことはないさ。ビットパターンを確実に定義して おけば、数値をオフセットにして、簡単にパターンを引き 出して使える。 Pgfedcba 0 : 00111111 1 : 00000110 2 : 01011011 3 : 01001111 4 : 01100110 5 : 01101101 6 : 01111101 7 : 00000111 8 : 01111111 9 : 01101111 哲 :12:34のときは、まず、4つの変数を用意して、数値を 分割して入れておくか。まあ、配列を使うと出来るな。 12:34 -> digit[0] = 1 / digit[1] = 2 / digit[2] = 3 / digit[3] = 4 のように、入れるのか。 ああ、そうか。ここで、論理演算とシフト演算が必要なんだ。 啓二:そう言うこと。 哲 :digit[0]の内容を決めるときは、変数hourの値を10で 割って、商を格納する。 digit[0] = hour / 10 ; digit[1]は、10で割り余りを格納する。 同じ方式で、digit[2]、digit[3]を決定できる。 結局、こうだな。 digit[0] = hour / 10 ; digit[1] = hour % 10 ; digit[2] = minute / 10 ; digit[3] = minute % 10 ; 啓二:ビットパターンは、digitの内容で取り出すといい。 哲 :後でアセンブリ言語で記述するとして、まずは、C言語で 論理だけを考えるとするか。じゃあ、便利な関数get_pattern があるとして、上の処理に続けて digit[0] = get_pattern(digit[0]) ; digit[1] = get_pattern(digit[1]) ; digit[2] = get_pattern(digit[2]) ; digit[3] = get_pattern(digit[3]) ; でいいさ。 啓二:あまり、関心しないなあ。 哲 :ソフト開発を生業にしている人間に、ケチつけるのか。 啓二:関数get_patternや他の処理にバグがあったとき、どう やって見つけるんだよ。お前のやり方だと、必要となる 情報を破壊してしまうから、デバッグの効率が下がる。 もうひとつ配列を用意して、次のようにする。 pdigit[0] = get_pattern(digit[0]) ; pdigit[1] = get_pattern(digit[1]) ; pdigit[2] = get_pattern(digit[2]) ; pdigit[3] = get_pattern(digit[3]) ; 哲 :これじゃ、メモリを食ってしまうだろう。 啓二:うん、でも、2kバイト程度の中の4バイト利用 しても害はないさ。むしろ、デバッグのことを考えたら、 4バイトをケチってしまう方が問題じゃないか。 哲 :そうか。78kのメモリに制約が多いというので、4バイト でも削れればと思ったんだが、デバッグを考えると、この方 がいいなあ。 関数の使用前後を比較すると、不具合を見つけやすいしな。 啓二:pdigitの内容を、出力していけば、7セグメントLEDに 数字が出てくる。8ビットごとに送信するルーチンを考えて みろよ。 哲 :まず、8ビット出力するから、for文でループを回すよな。 MSBから出力すると仮定するか。あっ、MSBから出力する としたら、ハードウエアをそうしないと駄目なんだ。 啓二:そう。だから、ラフなプログラムを作成してみることも 必要なんだ。 哲 :まず、8ビット出力するから、for文でループを回すよな。 for ( i = 7 ; i > -1 ; i-- ) { } 次に、pdigitの値を、出力して、クロックに1、0を与える。 for ( i = 7 ; i > -1 ; i-- ) { P2.2 = (pdigit[0] >> i) & 1 ; P2.3 = 1; P2.3 = 0; } これは、時の上位けただけだから、分の上位けたの処理も入れる。 for ( i = 7 ; i > -1 ; i-- ) { P2.2 = (pdigit[0] >> i) & 1 ; P2.0 = (pdigit[2] >> i) & 1 ; P2.3 = 1; P2.1 = 1; P2.3 = 0; P2.1 = 0; } 次に、下位けたのビットパターンを送ればいいな。 for ( i = 7 ; i > -1 ; i-- ) { P2.2 = (pdigit[1] >> i) & 1 ; P2.0 = (pdigit[3] >> i) & 1 ; P2.3 = 1; P2.1 = 1; P2.3 = 0; P2.1 = 0; } 啓二:2回のfor文は、醜いぞ。一つにまとめた方がいい。 哲 :そうか。そうすると、16ビットを転送すればいいから、 時と分の16ビットを作って、ループで回すか。 変数を2つ用意してと。 tmpH = pdigit[0] * 256 + pdigit[1] ; tmpL = pdigit[2] * 256 + pdigit[3] ; for ( i = 16 ; i > -1 ; i-- ) { P2.2 = (tmpH >> i) & 1 ; P2.0 = (tmpL >> i) & 1 ; P2.3 = 1; P2.1 = 1; P2.3 = 0; P2.1 = 0; } 啓二:C言語だから掛け算処理を簡単にできるけれど、 アセンブリ言語じゃ面倒だ。 シフトを使って書き直せよ。 哲 :あれ、そういうもんか。ならば、こうか。 tmpH = pdigit[0] ; tmpH <<= 8 ; tmpH &= 0xff00 ; tmpH |= pdigit[1] ; tmpL = pdigit[2] ; tmpL <<= 8 ; tmpL &= 0xff00 ; tmpL |= pdigit[3] ; 啓二:いんでないのかい。次は、動作表示LEDの点滅だなあ。 これは、タイマー割込みで起動してしまえばいい。 タイマー割込みは、後から定義するとして、点灯、消灯を 実行させるコードを考えよう。 哲 :まず、3つの状態があるんだよな。 点灯、消灯、点滅。これを、まず2つにわけるか。 点滅とそれ以外。 啓二:場合分けに出たな。数学の余事象の考え方を使うと、結構 楽だ。 哲 :2つの場合を分けるには、フラグを利用すればいいか。 よし、blk_flgというフラグを用意して、こうするか。 if ( blk_flag == ON ) {} else {} あれ、動作中でなければ、消灯だけにすればいいんだよなあ。 だとすると、こうか。 if ( blk_flag == ON ) { /* 点滅 */ } else { /* 消灯 */ } おい、点灯と消灯って、1と0にどう対応させるんだ。 啓二:普通は、0で点灯、1で消灯にする。その方がマイコンの 出力回路の負担が少ないから。 哲 :じゃ、こうすればいいのか。 if ( blk_flag == ON ) { /* 点滅 */ } else { P13.0 = 1 ; } ああ、点滅はどうしようか。 啓二:簡単だ。変数を用意して、次のようにすればいい。 blk_cnt++ ; blk_cnt %= 2 ; if ( blk_flag == ON ) { P13.0 = (blk_cnt & 1) ; } else { P13.0 = 1 ; } 哲 :あらら、タイマー割込みごとに、変数blk_cntが0か1に なるから、それを出力するってか。 啓二:こういうときの定石なんだ。フラグblk_flagで点滅か 否かを決めてあるので、使う方は、フラグだけに気を つければよし。そして、ハードウエアに点滅をやらせて しまえば、後は知らん顔できる。 哲 :ハードを知っているから、こんな芸当ができるのか。 残りは、何だっけ。 啓二:タイマー動作中の出力を決めるところ。これは、単純に オンかオフの指令を与えだけでいい。 哲 :じゃあ、こんな程度でいいのか。 P4.5 = 0 ; if ( out_flg == ON ) P4.5 = 1; 啓二:いいんじゃないかあ。 今日は、ここまでにする。温泉卵ができたようだし。 哲 :あれ、あの高周波調理器は、自作なのか。 啓二:そう。誘導コイルを小さい寸胴にまいて、タイマーを つけてある。それで、時間がきたら高周波をかけるのを やめるのさ。温泉卵なら30分でできるぞ。 哲 :じゃあ、今日は、温泉卵を頂いていくか。 啓二:何だよ。自分の家で作れよ。 (料理のレシピ その4) A 食材の用意 豆腐:1丁 かつお節:少々 なめ茸:少々 B 料理方法 豆腐は、一口大に包丁で切れ目をいれる。 小鉢に豆腐をいれる。 かつお節、なめ茸を豆腐の上にトッピング。 C 盛り付け 皿に、フライパンに納豆をうつす 好みで、醤油をかける