二桁表示太陽計

数値表示太陽計よりも更に詳しく明るさを表現するには、 表示できる数値の桁数を増やします。 二桁表示の7セグメントLEDを使って、 00から99の百段階の数値で明るさを表現してみましょう。

完成写真

アジェンダ

ハードウェア

このアプリケーションは、 DEMO9S08QG8評価ボードに 7セグメントLEDを二個、 トランジスタを二個、 抵抗を10本追加して製作します。

回路図

アプリケーションに必要な部分の回路は、 以下のようになっています。

太陽計回路図(STEP3)

マイコンの右側にある部分が拡張した部分です。 必要な部品は、以下の通りです。

太陽計(STEP3)部品表
品名型番個数調達先
評価ボードDEMO9S08QG81Freescale
ブレッドボードEIC-8012秋月電子
7セグメントLEDLN516RA2秋月電子
トランジスタ2SA10152千石電商
抵抗220Ω 1/6W8秋月電子
抵抗1kΩ 1/6W2秋月電子
両端オスピンソケット6604P-20G-1211秋月電子

拡張部分は、数値表示太陽計同様にブレッドボードに作りました。

ダイナミック点灯のしくみ

このアプリケーションでは、 ダイナミック点灯方式を使用しています。

前回は、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が起動します。

初めてCodeWarriorを起動したときには、 下のような"Startup"ダイアログが出てきますが、 今はこのダイアログは使用しませんので、 X をクリックしてダイアログを閉じます。

CodeWarrior起動画面

この後、"Tip of the Day"ダイアログが出てくるかも知れませんが、 これも X をクリックして閉じてしまいましょう。

使用言語とディレクトリの設定

メニューから"File → New Project..."を選ぶと、 "HC(S)08 New Project"ダイアログが開きます。 まずは、"Project Parameters"(プロジェクトのパラメータ)を設定します。

新規プロジェクト画面1

使用する言語に"C"を選び、 プロジェクトを作成する場所を設定します。 この例では、"C:\Projects\CW\TaiyouKei3"というディレクトリに "TaiyouKei3.mcp"というプロジェクトファイルを作成しています。 設定を終えたら、「次へ」をクリックします。

デバイスと接続法の設定

画面が変わって、 "Device and Connection"(デバイスと接続方法)の設定に移ります。

新規プロジェクト画面2

デバイスには"HCS08 → HCS08QG Family → MC9S08QG8"を選び、 デフォルトの接続方法には"P&E Multilink/Cyclone Pro"を選び、 「次へ」をクリックします。

追加するファイルの設定

3枚目の画面は、 "Add Additional Files"(追加するファイル)の設定です。 プロジェクトで使用するソースコードなどがあれば、 ここで設定します。

新規プロジェクト画面3

今回は、追加するファイルはありませんので、 そのまま「次へ」をクリックします。

プロセッサエキスパートの設定

4枚目の画面は、 "Processor Expert"(プロセッサエキスパート)の設定です。

新規プロジェクト画面4

"Processor Expert"を選んでこの機能の使用を宣言します。 さらに、「次へ」をクリックします。

C/C++のオプションの設定

5枚目の画面は、"C/C++ Options"の設定です。

新規プロジェクト画面5

すべて、デフォルトのままの設定にします。

startup codeANSI
memory modelSmall
floating point formatNone

設定を確認して 「次へ」をクリックします。

PC-Lintの設定

最後の画面は、"PC-Lint"の設定です。 PC-lint™というのは、 プログラムの書式を検査するためのプログラムです。 今回は使用しません。

新規プロジェクト画面6

"No"をチェックして 「完了」をクリックすると、 新規プロジェクトの作成が始まります。

パッケージを選ぶ

MC9S08QG8マイコンには、 いくつかの異なる種類のパッケージが存在します。 その中から、今から使おうとしているパッケージが どのパッケージなのかを指定するのが次のウィンドウです。

パッケージ選択画面

今回は、評価ボードに搭載されている 16PIN-DIPパッケージを使いますので、 "MC9S08QG8_16"だけを選択して「OK」をクリックします。

使用する設定を選ぶ

これが、新規プロジェクトのための 最後の設定です。 "Select Configurations"(設定を選べ)というのは、 実にあいまいですが、 ここでは、アプリケーション開発の結果を何に使うかを指定します。 もちろん、独立した製品に仕上げるのが最終目的には 違いないのですが、 プログラムの開発中には、デバッガを接続した状態で プログラムを実行する必要もあります。

"Debug"設定は、このようなデバッガを接続した状態を考慮した 設定を行います。 このため、BKGD端子には、デバッガが接続されアプリケーションでは 使用できなくなります。

一方、"Release"設定は、最終製品での使用を考慮した設定を行うため、 すべての端子がアプリケーションに開放されます。

今回は、デバッグ環境である評価ボードでアプリケーション開発を 行いますので、 "Debug"だけを選択して「OK」をクリックします。

新規プロジェクト最終画面

以上で、プロセッサエキスパートを使用する 新規プロジェクトができました。

新規プロジェクト最終画面

リソース設定

ここから、 プロセッサエキスパートのプロパティを設定していきます。 ここでは、設定箇所だけ示します。

A/Dコンバータの設定

ADCビーンを呼び出したら、以下の項目について設定します。

ADCビーンの設定
項目名設定する値
A/D Channels → Channel0 → A/D Channel (pin) PTA1_KBIP1_ADP1_ACMPMINUS
Conversion Time 46µs

ADCビーンの設定が終了すると、属性は以下のようになります。

ADCビーンの設定終了

ポート出力の設定

このアプリケーションでは、PORT-Bの全8本と PORT-Aの2本を汎用ポートとして使い、 7セグメントLEDを駆動しています。

まず、PORT-Bの設定を行います。 呼び出すビーンは、ByteIOです。 ByteIOビーンを呼び出したら、以下の項目について設定します。

ByteIOビーンの設定
項目名設定する値
Direction Output

ByteIOビーンの設定が終了すると、属性は以下のようになります。

ByteIOビーンの設定終了

次に、PORT-Aの設定を行います。 呼び出すビーンは、BitsIOです。 BitsIOビーンを呼び出したら、以下の項目について設定します。

BitsIOビーンの設定
項目名設定する値
Pins 2
Pin0 → Pin PTA2_KBIP2_SDA_ADP2
Pin1 → Pin PTA3_KBIP3_SCL_ADP3
Direction Output

BitsIOビーンの設定が終了すると、属性は以下のようになります。

BitsIOビーンの設定終了

A/D入力とLED表示のプログラム

プロセッサエキスパートの設定が終わったら、 プログラムのコーディングを行います。

コードの生成

CodeWarriorのウィンドウの左側にあるプロジェクトペインの Makeボタンをクリックして、 ソースコードを生成させます。

Makeボタン

ソースコードの修正

コード生成では、"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 (;;) {
    :
  }

A/D変換指令

このアプリケーションでも、変換開始指令と結果の受け取りを 続けて記述しています。 この部分は、三値デジタル太陽計と同じです。

 (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から得た値は、以下のように変換されます。

アナログ値と明るさの対応表
valuelevel明るさ
最小値最大値
065599明るい
656131198
::::
321443279951
328003345550
334563411149
:::
64288649441暗い
64944655350

数値パターンの出力

表示すべき数値が決まったら、そのコードを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評価ボードを接続する

DEMO9S08QG8評価ボードをUSBケーブルで接続します。

デバッガ兼書き込みプログラムを呼び出す

ドロップダウンリストが"P&E Multilink/Cyclone Pro"になっているのを 確認して、 Debug アイコンをクリックすると コンパイル、リンクの後、デバッガが立ち上がります。

デバッガの呼び出し

USBインターフェースの設定

接続方法の指定

USBインターフェースの設定を確認したら、 "Connect"をクリックします。

マイコンに書き込む

消去確認

「Yes」をクリックして、マイコンに書き込みます。

プログラムを実行させる

ツールボタンの Start/Continue ボタンをクリックするとマイコンは自律走行を始めます。

表示がおかしい

ところが、このプログラムでは、LEDがぼんやりと光るだけで 一向に数値を表示してくれません。 そこで、デバッガの Halt ボタンをクリックして、マイコンを停止させます。 さらに Step Out ボタンを何度かクリックして"main()"関数のレベルに戻り、 Step Over ボタンをクリックしてステップ動作をさせます。

十の桁を表示
一の桁を表示

すると、十の桁と一の桁を交互に表示しているらしいことは 確認できます。

この問題は、LEDの表示が間に合っていないのが原因です。 マイコンの動作速度が速過ぎるので、 LEDが十分に電流を流しきらないうちに次の桁の表示に移ってしまいます。 このため、すべてのセグメントが同時に発光しているように見える というわけです。 一の桁のほうが明るく見えるのは、 A/D変換を行っている時間は一の桁のLEDが発光しているので、 十の桁のLEDに比べて長く光っているためです。

プログラムの修正と再実行

どうやら、表示時間が足りなかったのが原因のようなので、 時間稼ぎをしましょう。

待ち時間を入れる

LEDが安定して光っていられる時間を作るために LEDが発光を始めてからしばらくの間、 待ち時間を挿入してやります。 ここでは、単純にfor文によるループを使います。

時間待ち関数 delay(void)

まず、待ち時間関数を作ります。 以下のように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"に値を設定した後の二箇所です。

再書き込み、再実行

再書き込みの方法は、一回目と同じです。 Debug アイコンをクリックして、 デバッガを呼び出し、書き込み、実行を行います。

数値を表示

ちょっと、落ち着きがないようですが、数値が表示されました。

時間の調整

今度は、待ち時間の初期値として与えた"1000"が妥当であるかどうかを 確認します。 デバッガのウィンドウに"Data:1"と表示されたサブウィンドウがあります。

待ち時間の調整箇所

このサブウィンドウの"nDelay"と表示された行をダブルクリックすると、 "nDelay"変数を変更することが出来ます。

大きい値を入れる

まずは、大きめの値として"20000"を入力して、 最後に"Enter"キーを押します。 すると、目で点滅がわかる4Hz程度の周期になりました。 これでは人間の目には数値として見えません。

小さい値を入れる

次に、小さめの値として"2"を入力して、 最後に"Enter"キーを押します。 すると、最初に待ち時間が無い状態に近い表示になります。 これも人間の目には数値として見えません。

推定60Hzの値を入れる

普通のテレビの画面は、30秒に一回書き換えられています。 つまり、30Hz程度の点滅であれば人間の目には点滅としては 感じられません。

また、蛍光灯は、家庭用交流電源の周波数、60Hzまたは50Hzで 点滅しています。 しかし、普通は点滅としては感じられません。

これらの事実と、 待ち時間として"20000"を入れると4Hzになったという事から、 60Hz相当の待ち時間として"1300"を入れてみます。 このぐらいだと、ちらつきも無く数字として読めるようです。

他の時間稼ぎ方法は?

for文で待ち時間を作る方法を紹介しましたが、 実際にプログラムを実行させてみないと待ち時間がわかりません。 特にコンパイラを使っている場合には、 コンパイラの品質によって待ち時間が変わってくる場合があります。

もっと正確な時間が必要であれば、 マイコンに内蔵されたタイマを使いましょう。 タイマは、プログラムの動作とは無関係に時を刻んでくれますから、 プログラムの実行時間が長くても短くても 常に同じ時間を提供してくれます。

タイマにも色々と種類があります。 詳しい使い方については、またの機会に書きます。

表示が落ち着かないのはなぜ?

この太陽計アプリケーションは、二桁の数値で明るさを細かく表示します。 このため、小さな明るさのゆらぎでも数値がコロコロと変わってしまうことが 予想されていました。 自宅での実験でも落ち着き無く数値が変化していました。

ところが、このアプリケーションをオフィスで動作させたところ、 ピタッと安定した数値が表示されることがわかりました。 はて、なぜだろう。

自宅もオフィスも蛍光灯照明です。 ただし、同じ蛍光灯でも自宅は昔ながらの安定器を使った、 しかもかなり古めの蛍光灯です。 かたや、オフィスの蛍光灯は、 おそらくインバータ制御されていると思われます。 つまり、人間の目には見えなくても、 機械の目は「自宅の蛍光灯は点滅している」と感じていたらしいのです。 結果として、明るいときと暗いときの数値を交互に表示する 落ち着かない表示になっていたようです。

回路図を見るとわかるように、 光センサには、推定10Hzのローパスフィルタが入っています。 このため、自宅の蛍光灯の点滅はフィルタできず、 オフィスの蛍光灯の点滅はフィルタできたと思われます。

課題

表示のちらつきを無くす

このプログラムでは、60Hz周期でA/D変換が行われるため、 アナログ入力にノイズが乗る環境では、 表示がちらついてしまいます。 ちらつきを無くすには、どんな方法が考えられるでしょうか。

マイコンを休ませながら使う

このプログラムは、無限ループの中で実行されているため、 常にフルスピードでプログラムを実行しています。

LEDの表示を見るのは人間なので、そんなに忙しくLEDの表示を 更新する必要はありません。 どのようにしたら、適当にサボりながら表示を更新していくことが できるでしょうか。

2006-02-23 「表示が落ち着かないのはなぜ?」を加筆。
2006-02-22 発行。
Copyright (C) 2006 noritan.org ■