数値表示太陽計よりも更に詳しく明るさを表現するには、 表示できる数値の桁数を増やします。 二桁表示の7セグメントLEDを使って、 00から99の百段階の数値で明るさを表現してみましょう。
このアプリケーションは、 DEMO9S08QG8評価ボードに 7セグメントLEDを二個、 トランジスタを二個、 抵抗を10本追加して製作します。
アプリケーションに必要な部分の回路は、 以下のようになっています。
マイコンの右側にある部分が拡張した部分です。 必要な部品は、以下の通りです。
品名 | 型番 | 個数 | 調達先 |
---|---|---|---|
評価ボード | DEMO9S08QG8 | 1 | Freescale |
ブレッドボード | EIC-801 | 2 | 秋月電子 |
7セグメントLED | LN516RA | 2 | 秋月電子 |
トランジスタ | 2SA1015 | 2 | 千石電商 |
抵抗 | 220Ω 1/6W | 8 | 秋月電子 |
抵抗 | 1kΩ 1/6W | 2 | 秋月電子 |
両端オスピンソケット | 6604P-20G-121 | 1 | 秋月電子 |
拡張部分は、数値表示太陽計同様にブレッドボードに作りました。
このアプリケーションでは、 ダイナミック点灯方式を使用しています。
前回は、7セグメントLEDを点灯させるために8本の出力端子を使用していました。 そのため、二桁の7セグメントLEDを点灯させるためには、 16本の出力端子が必要です。 ところが、MC9S08QG8マイコンには、そんなに多くの端子はありません。 そこで、二つのLEDの点灯を高速に切り替えて表示させる ダイナミック点灯と呼ばれる方法が考え出されました。 この方式では、LEDは点滅を繰り返しています。 ところが、高速に点滅させると、 人間の目の残像現象によって点滅が見えなくなります。 結果として、二桁のLEDを同時に点灯しているように見えることになります。
下の図は、ダイナミック点灯の原理を表しています。
ある時刻には、左上のスイッチSW1がONし、右上のスイッチSW2はOFFします。 このとき、左側のLEDだけが点灯しますが、点灯のパターンは、 下に並んだスイッチ群によって決められます。
時間が経つと、今度は、 左上のスイッチSW1がOFFし、右上のスイッチSW1はONします。 すると、右側のLEDだけが下に並んだスイッチ群のパターンに従って点灯します。
この動作を繰り返して左右のLEDのどちらも点灯しているように見せます。
上の原理図では、スイッチが使われていました。 ところが、マイコンにスイッチを直接操作させる事はできません。 そこで、原理図をを回路図に落とし込む作業が必要です。 これを「回路設計」と呼んでいます。
下の図は、回路設計を行った際の各部分の電圧と電流の図です。
まず、LEDには5mAの電流を流すことにします。 この値は、前回の数値表示太陽計と同じです。 この電流は、マイコン内部のN型MOSFETトランジスタ(NMOSFET)に 流れ込みますので、 8本のLEDを同時に点灯させたときには、 マイコンのVSSに40mAの電流が流れます。 この値は、マイコンに流れ込む全電流が マイコンの全ポートに流すことの出来る電流の値(60mA)を 超えないように採用された値です。
次に上部のスイッチに流れる電流を考えます。 すべてのLEDが点灯すると、 上部のスイッチから40mAの電流を供給する必要があります。 ところが、マイコンの出力端子の最大電流は20mAです。 このため、40mAの電流が供給できるように電流を増幅する素子、 つまりトランジスタを追加します。
プログラムの開発には、CodeWarriorを使います。 CodeWarriorでは、 一つのプログラムの単位を「プロジェクト」と呼んでいます。 ここでは、新規にプロジェクトを作成します。 ここでは、V5.0を使って説明します。
スタートメニューからCodeWarriorを起動するとCodeWarriorが起動します。
初めてCodeWarriorを起動したときには、 下のような"Startup"ダイアログが出てきますが、 今はこのダイアログは使用しませんので、 をクリックしてダイアログを閉じます。
この後、"Tip of the Day"ダイアログが出てくるかも知れませんが、 これも をクリックして閉じてしまいましょう。
メニューから"File → New Project..."を選ぶと、 "HC(S)08 New Project"ダイアログが開きます。 まずは、"Project Parameters"(プロジェクトのパラメータ)を設定します。
使用する言語に"C"を選び、 プロジェクトを作成する場所を設定します。 この例では、"C:\Projects\CW\TaiyouKei3"というディレクトリに "TaiyouKei3.mcp"というプロジェクトファイルを作成しています。 設定を終えたら、「次へ」をクリックします。
画面が変わって、 "Device and Connection"(デバイスと接続方法)の設定に移ります。
デバイスには"HCS08 → HCS08QG Family → MC9S08QG8"を選び、 デフォルトの接続方法には"P&E Multilink/Cyclone Pro"を選び、 「次へ」をクリックします。
3枚目の画面は、 "Add Additional Files"(追加するファイル)の設定です。 プロジェクトで使用するソースコードなどがあれば、 ここで設定します。
今回は、追加するファイルはありませんので、 そのまま「次へ」をクリックします。
4枚目の画面は、 "Processor Expert"(プロセッサエキスパート)の設定です。
"Processor Expert"を選んでこの機能の使用を宣言します。 さらに、「次へ」をクリックします。
5枚目の画面は、"C/C++ Options"の設定です。
すべて、デフォルトのままの設定にします。
startup code | ANSI |
memory model | Small |
floating point format | None |
設定を確認して 「次へ」をクリックします。
最後の画面は、"PC-Lint"の設定です。 PC-lint™というのは、 プログラムの書式を検査するためのプログラムです。 今回は使用しません。
"No"をチェックして 「完了」をクリックすると、 新規プロジェクトの作成が始まります。
MC9S08QG8マイコンには、 いくつかの異なる種類のパッケージが存在します。 その中から、今から使おうとしているパッケージが どのパッケージなのかを指定するのが次のウィンドウです。
今回は、評価ボードに搭載されている 16PIN-DIPパッケージを使いますので、 "MC9S08QG8_16"だけを選択して「OK」をクリックします。
これが、新規プロジェクトのための 最後の設定です。 "Select Configurations"(設定を選べ)というのは、 実にあいまいですが、 ここでは、アプリケーション開発の結果を何に使うかを指定します。 もちろん、独立した製品に仕上げるのが最終目的には 違いないのですが、 プログラムの開発中には、デバッガを接続した状態で プログラムを実行する必要もあります。
"Debug"設定は、このようなデバッガを接続した状態を考慮した 設定を行います。 このため、BKGD端子には、デバッガが接続されアプリケーションでは 使用できなくなります。
一方、"Release"設定は、最終製品での使用を考慮した設定を行うため、 すべての端子がアプリケーションに開放されます。
今回は、デバッグ環境である評価ボードでアプリケーション開発を 行いますので、 "Debug"だけを選択して「OK」をクリックします。
以上で、プロセッサエキスパートを使用する 新規プロジェクトができました。
ここから、 プロセッサエキスパートのプロパティを設定していきます。 ここでは、設定箇所だけ示します。
ADCビーンを呼び出したら、以下の項目について設定します。
項目名 | 設定する値 |
---|---|
A/D Channels → Channel0 → A/D Channel (pin) | PTA1_KBIP1_ADP1_ACMPMINUS |
Conversion Time | 46µs |
ADCビーンの設定が終了すると、属性は以下のようになります。
このアプリケーションでは、PORT-Bの全8本と PORT-Aの2本を汎用ポートとして使い、 7セグメントLEDを駆動しています。
まず、PORT-Bの設定を行います。 呼び出すビーンは、ByteIOです。 ByteIOビーンを呼び出したら、以下の項目について設定します。
項目名 | 設定する値 |
---|---|
Direction | Output |
ByteIOビーンの設定が終了すると、属性は以下のようになります。
次に、PORT-Aの設定を行います。 呼び出すビーンは、BitsIOです。 BitsIOビーンを呼び出したら、以下の項目について設定します。
項目名 | 設定する値 |
---|---|
Pins | 2 |
Pin0 → Pin | PTA2_KBIP2_SDA_ADP2 |
Pin1 → Pin | PTA3_KBIP3_SCL_ADP3 |
Direction | Output |
BitsIOビーンの設定が終了すると、属性は以下のようになります。
プロセッサエキスパートの設定が終わったら、 プログラムのコーディングを行います。
CodeWarriorのウィンドウの左側にあるプロジェクトペインの ボタンをクリックして、 ソースコードを生成させます。
コード生成では、"TaiyouKei3.c"というファイルも同時に生成されています。
このファイルには、 マイコンがリセットされた後で実行される "main()"関数が記述されています。 ここをダブルクリックすると、 "TaiyouKei3.c"ファイルを編集するテキストエディタが開きます。
メインルーチンのうち、変更する箇所は、 以下の通りです。
最初に7セグメントLEDに数字を表示させるための パターンが定義されています。 この記述そのものは、数値表示太陽計で書いたものと同じです。
const byte digitPattern[10] = { // Pattern for 7-segment LED 0x5C, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x27, 0x7F, 0x6F };
このプログラムでは、二つの変数が宣言されています。 一つ目は、A/Dコンバータから16ビットのデータを受け取る変数 valueです。 この変数の使い方は、三値デジタル太陽計と同じです。
二つ目は、表示すべき数値を保持する変数levelです。 この変数には、0から99までの数値が入り、 LEDに表示すべき数値を示します。
word value; // Value from ADC. byte level; // Level of Brightness.
このプログラムの処理は、 繰り返し処理を続ける無限ループで出来ています。 無限ループを記述するには、for文を使います。
/* Write your code here */ for (;;) { : }
このアプリケーションでも、変換開始指令と結果の受け取りを 続けて記述しています。 この部分は、三値デジタル太陽計と同じです。
(void)AD1_Measure(TRUE); // wait for a conversion. (void)AD1_GetValue16(&value); // get a converted value.
ここでは、ADCから得たアナログ電圧を数値表現に変換します。 数値表示太陽計の時に作成した記述を二桁に拡張します。
level = 99 - (value / 656); // translate to brightness level.
この計算式によって、ADCから得た値は、以下のように変換されます。
value | level | 明るさ | |
---|---|---|---|
最小値 | 最大値 | ||
0 | 655 | 99 | 明るい |
656 | 1311 | 98 | |
: | : | : | : |
32144 | 32799 | 51 | |
32800 | 33455 | 50 | |
33456 | 34111 | 49 | |
: | : | : | |
64288 | 64944 | 1 | 暗い |
64944 | 65535 | 0 |
表示すべき数値が決まったら、そのコードをLEDに表示します。 まず、十の桁からです。 十の桁のトランジスタをONするためには、PTA3=0, PTA2=1 を出力します。 ビーン"Bits1"は、(PTA3:PTA2)を表現しますから、 "Bits1"に"01"を設定すればよいことになります。
Bits1_PutVal(0b01); // Select 10's digit.
また、"PORTB"には十の桁の数値に対応するパターンを出力します。 十の桁に表示する数値は、 "level / 10"(10で割って、あまりを切り捨てる)で得られます。 表示パターンは、数値表示太陽計と同じように 反転したパターンを設定します。
Byte1_PutVal(~digitPattern[level / 10]); // Show 10's digit.
次は一の桁です。 一の桁のトランジスタをONするためには、 "Bits1"に"10"を設定します。 また、一の桁に表示する数値は、 "level % 10"(10で割ったあまり)で得られます。
Bits1_PutVal(0b10); // Select 1's digit. Byte1_PutVal(~digitPattern[level % 10]); // Show 1's digit.
プログラムが完成したら、いよいよ自律制御をさせます。
三値デジタル太陽計の時と同じようにマイコンにプログラムを書き込みます。
DEMO9S08QG8評価ボードをUSBケーブルで接続します。
ドロップダウンリストが"P&E Multilink/Cyclone Pro"になっているのを 確認して、 アイコンをクリックすると コンパイル、リンクの後、デバッガが立ち上がります。
USBインターフェースの設定を確認したら、 "Connect"をクリックします。
「Yes」をクリックして、マイコンに書き込みます。
ツールボタンの
ところが、このプログラムでは、LEDがぼんやりと光るだけで 一向に数値を表示してくれません。 そこで、デバッガの ボタンをクリックして、マイコンを停止させます。 さらに ボタンを何度かクリックして"main()"関数のレベルに戻り、 ボタンをクリックしてステップ動作をさせます。
すると、十の桁と一の桁を交互に表示しているらしいことは 確認できます。
この問題は、LEDの表示が間に合っていないのが原因です。 マイコンの動作速度が速過ぎるので、 LEDが十分に電流を流しきらないうちに次の桁の表示に移ってしまいます。 このため、すべてのセグメントが同時に発光しているように見える というわけです。 一の桁のほうが明るく見えるのは、 A/D変換を行っている時間は一の桁のLEDが発光しているので、 十の桁のLEDに比べて長く光っているためです。
どうやら、表示時間が足りなかったのが原因のようなので、 時間稼ぎをしましょう。
LEDが安定して光っていられる時間を作るために LEDが発光を始めてからしばらくの間、 待ち時間を挿入してやります。 ここでは、単純にfor文によるループを使います。
まず、待ち時間関数を作ります。 以下のようにdigitPattern配列宣言の直前に delay(void)関数を定義します。
/* Wait for a while specified by a variable "nDelay." */ word nDelay = 1000; void delay(void) { word i; for (i = 0; i < nDelay; i++) ; }
待ち時間nDelayを変数としたのは、 この後、デバッガを使ってプログラムを走らせながら調整を行うためです。 初期値としてとりあえず"1000"を入れておきます。
作成した時間待ち関数は、表示が確定した後の部分に挿入します。
Bits1_PutVal(0b01); // Select 10's digit. Byte1_PutVal(~digitPattern[level / 10]); // Show 10's digit. delay(); // Wait for a while. Bits1_PutVal(0b10); // Select 1's digit. Byte1_PutVal(~digitPattern[level % 10]); // Show 1's digit. delay(); // Wait for a while. }
挿入箇所は、"Byte1"に値を設定した後の二箇所です。
再書き込みの方法は、一回目と同じです。 アイコンをクリックして、 デバッガを呼び出し、書き込み、実行を行います。
ちょっと、落ち着きがないようですが、数値が表示されました。
今度は、待ち時間の初期値として与えた"1000"が妥当であるかどうかを 確認します。 デバッガのウィンドウに"Data:1"と表示されたサブウィンドウがあります。
このサブウィンドウの"nDelay"と表示された行をダブルクリックすると、 "nDelay"変数を変更することが出来ます。
まずは、大きめの値として"20000"を入力して、 最後に"Enter"キーを押します。 すると、目で点滅がわかる4Hz程度の周期になりました。 これでは人間の目には数値として見えません。
次に、小さめの値として"2"を入力して、 最後に"Enter"キーを押します。 すると、最初に待ち時間が無い状態に近い表示になります。 これも人間の目には数値として見えません。
普通のテレビの画面は、30秒に一回書き換えられています。 つまり、30Hz程度の点滅であれば人間の目には点滅としては 感じられません。
また、蛍光灯は、家庭用交流電源の周波数、60Hzまたは50Hzで 点滅しています。 しかし、普通は点滅としては感じられません。
これらの事実と、 待ち時間として"20000"を入れると4Hzになったという事から、 60Hz相当の待ち時間として"1300"を入れてみます。 このぐらいだと、ちらつきも無く数字として読めるようです。
for文で待ち時間を作る方法を紹介しましたが、 実際にプログラムを実行させてみないと待ち時間がわかりません。 特にコンパイラを使っている場合には、 コンパイラの品質によって待ち時間が変わってくる場合があります。
もっと正確な時間が必要であれば、 マイコンに内蔵されたタイマを使いましょう。 タイマは、プログラムの動作とは無関係に時を刻んでくれますから、 プログラムの実行時間が長くても短くても 常に同じ時間を提供してくれます。
タイマにも色々と種類があります。 詳しい使い方については、またの機会に書きます。
この太陽計アプリケーションは、二桁の数値で明るさを細かく表示します。 このため、小さな明るさのゆらぎでも数値がコロコロと変わってしまうことが 予想されていました。 自宅での実験でも落ち着き無く数値が変化していました。
ところが、このアプリケーションをオフィスで動作させたところ、 ピタッと安定した数値が表示されることがわかりました。 はて、なぜだろう。
自宅もオフィスも蛍光灯照明です。 ただし、同じ蛍光灯でも自宅は昔ながらの安定器を使った、 しかもかなり古めの蛍光灯です。 かたや、オフィスの蛍光灯は、 おそらくインバータ制御されていると思われます。 つまり、人間の目には見えなくても、 機械の目は「自宅の蛍光灯は点滅している」と感じていたらしいのです。 結果として、明るいときと暗いときの数値を交互に表示する 落ち着かない表示になっていたようです。
回路図を見るとわかるように、 光センサには、推定10Hzのローパスフィルタが入っています。 このため、自宅の蛍光灯の点滅はフィルタできず、 オフィスの蛍光灯の点滅はフィルタできたと思われます。
このプログラムでは、60Hz周期でA/D変換が行われるため、 アナログ入力にノイズが乗る環境では、 表示がちらついてしまいます。 ちらつきを無くすには、どんな方法が考えられるでしょうか。
このプログラムは、無限ループの中で実行されているため、 常にフルスピードでプログラムを実行しています。
LEDの表示を見るのは人間なので、そんなに忙しくLEDの表示を 更新する必要はありません。 どのようにしたら、適当にサボりながら表示を更新していくことが できるでしょうか。
2006-02-23 「表示が落ち着かないのはなぜ?」を加筆。
2006-02-22 発行。