HC08ミニマイコン扇風機の A-D変換器の使われ方を説明したあと、同じハードウェアでA-D変換器と入出力ポートだけを使った比較的シンプルなプログラムを紹介します。
HC08ミニマイコン扇風機にテスト・プログラムを書き込む実験のようす
書籍の第3部で製作する USB-HC08デバッグ・ツールを使用した
内容見本 p.28 図3-1 操作の面から見たブロック図
今回の内容は書籍 「試しながら学ぶHC08マイコン入門」 (CQ出版) のサポート記事です。 第2部と第3部を一通り実施したあとで、改めてプログラムについて勉強したい、という方にピッタリです。
上の図にある D-A変換回路については、この連載の 第20回~第25回 で詳しく説明しました。 R-2Rラダー型 D-A変換器と言えば聞き覚えがあるでしょう。 今回の後半で紹介するプログラムでは、D-A変換回路の先にある モータ駆動回路、モータと羽 を含めたほとんどのハードウェアを利用します。 圧電サウンダについては、もっと後のほうでタイマの学習をするときに取り上げます。
◆ HC08ミニマイコン扇風機における A-D変換器の使われ方
HC08ミニマイコン扇風機のプログラムから、A-D変換器に関する初期設定の部分を抜き出してみます(プログラム全体のファイルについては書籍を参照してください)。
void init_ADC( void ) {
ADCLK = 0x50; // 3.2 ÷ 4 = 0.8MHz ( 0.4 < fADCK < 2MHz )
ADSCR = 0x22; // 連続変換、AD2(PTA4)
}
ADC10クロック・レジスタ(ADCLK) については 第45回 で説明しました。 この作品における設定値を見ておきましょう。
・ ADLPC ADC10低電力設定ビット 0 : 高速設定
本機の動作中はモータという重い負荷を動かしているくらいですから、たとえ 1 に設定して省電力にしてもたいした差はないという判断で、デフォルトのままにしています。
・ ADIV1~ADIV0 ADC10クロック分周ビット 10b : Input Clock ÷ 4
入力クロックとして 3.2MHz を選択しているので、10b にすると 0.8MHz となります(古い Aなしの QY4 では ADC内部クロック周波数範囲が 0.5 MHz~ 1.048MHz だったため、0.8MHz を選んでいた。 別に気にする必要はないので、ここは ÷2 を選んで 1.6MHz としてもかまわない)。
・ ADICLK 入力クロック選択ビット 1 : バス・クロックを選択
ADICLK = 1 にするとA-D変換器のクロックを作る分周器のクロック源としてバス・クロックを選択します。 このビットは ACLKEN が 0 のときのみ意味をもちます。
・ MODE1~MODE0 10ビット/ 8ビット・モード選択 00b : 8ビット・モード
これらのビットに 00b を設定すると、8ビット・モードを選択します。 逐次比較変換の結果は、この選択に基づいて8ビット値になります。
・ ADLSMP 長期サンプリング時間設定 0 : 短期サンプリング時間
この作品では A-D入力端子の外部に接続された回路のインピーダンス(抵抗のようなもの)が 5kΩ前後と決して小さくはないので、長期サンプリング時間を選択して変換精度を上げるのがセオリといえます。 しかし、必要な変換精度が非常にラフでかまわないため、これはどちらでもいいだろう、という判断でデフォルトのままにしています。
・ ACLKEN 非同期クロック源イネーブル
0 : 非同期クロック源を無効にし、入力クロック源を ADICLK で指定する
ADICLK = 1 を選んだので、内蔵発振器 12.8MHz の 4分周3.2MHz が A-D変換器のクロックになります。
ADC10ステータス/ 制御レジスタ(ADSCR) については 第44回 で説明しました。 この作品における設定値を見ておきましょう。
・ COCO 変換完了ビット 読み出し専用ビット (書き込む場合は 0 を書き込む)
・ AIEN ADC10割り込みイネーブル・ビット 0 : ADC10 割り込み禁止
・ ADCO ADC10連続変換ビット 1 : ADSCR への書き込み後に連続変換を実行
ACLKEN = 0 なので、マイコンがストップ・モードに移行すると連続変換が停止します。 ストップ・モードへの移行によって連続変換を停止した場合、ADSCR への書き込みによってのみ連続変換を再起動できます。
・ ADCH4~ADCH0 チャネル選択ビット 00010b : AD2 入力を選択
HC08ミニマイコン扇風機における A-D変換器に関する処理内容
MC908QY4A マイコンに内蔵されている A-D変換器は ADC10 という名称で、10ビット・モードと 8ビット・モードが選択できます。 この作品ではモータ駆動回路の前のポート出力部分が 4ビット・D-A変換回路になっており、10ビットの精度は不要ですから 8ビット・モードを選択しています。 また、プログラム全体を タイムド・ループ(Timed Loop) (*1) と呼ばれる構成にしていて、10ms(ミリ・セカンド : ミリ秒) ごとに A-D変換値を読み出すようにしています。
8ビット・モードですから上位バイト・下位バイトの読み出しタイミングを気にする必要がないことから、いつでも好きなタイミングで勝手に読み出せるように連続変換モードを選択しています。 もしも単独変換モードにして 10msごとのループで毎回 「A-D変換開始~変換完了待ち~読み込み」 という構成にした場合は、10ms以内に終わらせる処理全体の時間が A-D変換完了待ちの分だけ長くなってしまいます(気にするほどの時間ではない)。
「オフ」 のプッシュ・スイッチを押したとき、ピーピ! と音を出したあとマイコンはストップ・モードに移行します。 ADCO の説明にあるとおり、そのとき連続変換が停止します。 「オン」 のプッシュ・スイッチを押すとストップ・モードから抜け出しますので、そのタイミングで A-D変換器に関する初期設定関数を実行してやることにより、連続変換を再起動します。
入力処理では、ADRL レジスタを勝手に読み出すだけ。 ここは 10msに1回しか実行されないということと、連続変換モードを使っているため、こんなに簡単に済ますことができました。
//*************************
// 入力処理
//*************************
void input( void ){
U1_addata = ADRL;
U1_yuragi_sw = 0;
if ( PORT_YURAGI_SW == PHY_ON ){
U1_yuragi_sw = 1;
}
}
パワー状態処理では、STOP を抜け出したところで A-D変換器初期化処理を実行。
asm( "STOP" );
// 【条件1】 STOP解除
KBSCR_IMASKK = 1;init_ADC( );
簡単ですが HC08ミニマイコン扇風機の A-D変換器に関する解説を終わります。
◆ HC08ミニマイコン扇風機を使って簡単なプログラムを試してみる
HC08ミニマイコン扇風機のプログラム全体は結構複雑なことをやっているので、いっぺんに理解することは難しいと思います。 そこで、比較的シンプルなテスト・プログラムを作って少しずつ理解していくことにしましょう。 test1 という名前は書籍で使っていますので、ここで扱うテスト・プログラムは 2から枝番を使って番号を振ることにします。
下記に示すのが test2-1 の仕様です。
【 test2-1 仕様 】 入出力ポートだけを使ったシンプルなプログラム
スイッチ、ツマミの機能は次の通り。
ON スイッチ ・・・・・ モータ「強」 になる。
OFF スイッチ ・・・・・ モータ「オフ」 になる。
ただしマイコンはパワーダウンしない。
TIMER スイッチ ・・・・・ 機能なし。
TIMER LED ・・・・・ 機能なし。
YURAGI スイッチ ・・・・・ モータ「弱」 になる。
ただしモータ「オフ」 のときは機能しない。
SPEED ツマミ ・・・・・ 機能なし。
電源を入れたときはモータ「強」 とする。
POWER LED は常時点灯とする(電源切り忘れ防止のため)。
これを元にして、下記のような設計を行いました。 与えられた仕様を満たす設計方法はいくつか考えられますが、今回は 「状態遷移」 という手法を取り入れています。 この例では状態を表す変数が一つで、取り得る状態(状態番号)が三つあります。 状態を表す変数というのは、常にどれか一つの状態番号を保持しています。 そして、プログラムはそのときそのときの状態の処理と遷移条件だけを気にしていればよく、それ以外の状態の処理はまったく気にする必要がないため、プログラムの読み書きが機械的でよく整理がしやすいという利点があります。
ほかの設計方法の一つに、何でもフラグを使って管理して if 文で処理を切り分けるというのがありますが、これは仕様の複雑さが増すにつれて非常に管理しにくくなるという欠点があります。 この機会に、状態遷移の手法に触れてみるとよいでしょう。
【 test2-1 設計 】
状態は 三つ。 初期状態は 「モータ強状態」。
POWER LED は常に点灯させる。
モータ強状態の処理
モータに 「強」 出力する。
OFF スイッチが押されたら、待機状態へ遷移。
YURAGI スイッチが押されたら、モータ弱状態へ遷移。
待機状態の処理
モータに 「停止」 出力する。
ON スイッチが押されたら、モータ強状態へ遷移。
モータ弱状態の処理
モータに 「弱」 出力する。
OFF スイッチが押されたら、待機状態へ遷移。
ON スイッチが押されたら、モータ強状態へ遷移。
この設計内容が仕様に合っているかどうか、よく読んで確認しておいてください。
ところで、状態を表す変数と書きましたが、これは何も特別なものではありません。 普通の変数を利用して、状態番号を管理するようにプログラムを作るのです。 つまり C言語のような高級言語に備わった機能ではなく、あくまで人間がそのように利用しているというだけなのです。 ですから、アセンブリ言語で状態遷移の手法を用いることも、もちろん可能です。
test2-1 のマクロ定義を示します。
スイッチ・オンのマクロは SW_ON にしようかと思いましたが、ONスイッチやOFFスイッチというモノがありますから if ( PORT_OFF_SW == ON_SW ) のようになってしまうと ONスイッチと読み間違えるので、物理的な ON という意味で PHY_ON にしました。
実験の結果、モータ「強」 は 0x0F 、モータ「弱」 は 0x08 にしてあります。
#define STATE_MAX ( 0 )
#define STATE_READY ( 1 )
#define STATE_MIN ( 2 )#define PORT_ON_SW PTA_PTA3
#define PORT_OFF_SW PTB_PTB4
#define PORT_YURAGI_SW PTB_PTB6
#define PHY_ON ( 0 )
#define PHY_OFF ( 1 )#define PORT_DAC PTB
#define MORTOR_OFF ( 0x00 )
#define MORTOR_MIN ( 0x08 )
#define MORTOR_MAX ( 0x0F )#define PORT_POWER_LED PTA_PTA5
#define LED_ON ( 1 )
#define LED_OFF ( 0 )
test2-1 のメイン関数を示します。
void main( void ) {
U1 state = STATE_MAX;
init_hardware( );
init_PORT( );
for ( ; ; ){
PORT_POWER_LED = LED_ON;
switch ( state ){
case STATE_MAX:
PORT_DAC = MORTOR_MAX;
if ( PORT_OFF_SW == PHY_ON ){
state = STATE_READY;
} else if ( PORT_YURAGI_SW == PHY_ON ){
state = STATE_MIN;
}
break;
case STATE_READY:
PORT_DAC = MORTOR_OFF;
if ( PORT_ON_SW == PHY_ON ){
state = STATE_MAX;
}
break;
case STATE_MIN:
PORT_DAC = MORTOR_MIN;
if ( PORT_OFF_SW == PHY_ON ){
state = STATE_READY;
} else if ( PORT_ON_SW == PHY_ON ){
state = STATE_MAX;
}
}
}
}
このように、state という変数と switch文を利用して状態を管理しています。
プログラム全体をプロジェクトごと圧縮したものを test2-1.zip として保存しました。 ご利用ください。 これはユーザ・モード・モニタ入りマイコン対応のプログラムです。 書き込み方法は書籍の第3部を参照してください。
switch 文 は今回初めて出てきたので、軽く説明しておきましょう。 switch 文は、ふつう次のような形をしています(break; を書かない説明のほうが本来の意味を正確に表現できるが、実際の使い方に近付けて解説を行った)。
switch ( 式 ){
case ラベル1:
文1;
break;
case ラベル2:
文2;
break;
default:
文3;
}
式を評価して、その値が ラベル1 と等しければ 文1 を実行し、終了します。 ただし、break; を書かない場合は終了せず、続けて 文2 も実行します。 これはバグのように見えるかもしれませんが、そういう使い方も OK なのです。 実際は break; が必要な場合がほとんどだと思います。 書き忘れに注意しましょう。
式の評価が ラベル1 と一致しなかったときは ラベル2 と比較します。 あと、ラベル3、ラベル4 ・・・ と続けてもかまいません。 順番に比較していき、一致したところの中身の文を実行します。 もし全部のラベルと一致しなかったときは default: のところに書かれた文が実行されます。 default: とその中身の文は、なくてもかまいません(*1)。
実際に書き込んで動かしてみると、これだけでも簡単なマイコン工作として充分に使えると感じました。 ぜひ試してみてください。
さて今度はツマミを回してモータの速さを変化させるプログラムを作ってみます。 ただし、なるべくシンプルなものにしたいので操作感を良くする工夫までは考えないことにします。
下記に示すのが test2-2 の仕様です。
【 test2-2 仕様 】 入出力ポートと A-D変換器を使ったシンプルなプログラム
スイッチ、ツマミの機能は次の通り。
ON スイッチ ・・・・・ モータ「オン」 になる。
OFF スイッチ ・・・・・ モータ「オフ」 になる。
ただしマイコンはパワーダウンしない。
TIMER スイッチ ・・・・・ 機能なし。
TIMER LED ・・・・・ 機能なし。
YURAGI スイッチ ・・・・・ 機能なし。
SPEED ツマミ ・・・・・ モータ「オン」 のとき、モータの速さを決める。
電源を入れたときはモータ「オン」 とする。
POWER LED は常時点灯とする(電源切り忘れ防止のため)。
簡単のため、ツマミを左に回したときモータが回らなくても良いことにする。
これを元にして、下記のような設計を行いました。 今回は 「状態」 というほどのものではないため、必要な処理をただ順番に繰り返すだけの構造にしています。 専門用語では ポーリング といいます。
【 test2-2 設計 】
以下の処理を繰り返す。
POWER LED は常に点灯させる。
可変抵抗器の電圧を 8ビットA-D変換する。
得られたデータを 4ビット右シフトして 4ビット・データとする。
モータに 4ビット・データを出力する。
もしも OFF スイッチが押されたらモータに 「停止」 出力して、
ON スイッチが押されるまでその場で待機。
ON スイッチが押されたら待機を解除、処理を続行する。
この設計内容が仕様に合っているかどうか、よく読んで確認しておいてください。
さっそく実装してみました。 仕様決めの段階からシンプルさを意識して作っていますので、ただ読めばわかるようなプログラムになりました。
test2-2 のメイン関数を示します。
void main( void ) {
init_hardware( );
init_PORT( );
init_ADC( );
for ( ; ; ){
PORT_POWER_LED = LED_ON;
ADSCR_ADCH = ADCH_VR;
while ( !ADSCR_COCO );
PORT_DAC = ( ADRL >> 4 );
if ( PORT_OFF_SW == PHY_ON ){
PORT_DAC = MORTOR_OFF;
while ( PORT_ON_SW == PHY_OFF );
}
}
}
プログラム全体をプロジェクトごと圧縮したものを test2-2.zip として保存しました。 ご利用ください。
これを動かしてみると、確かにツマミを左のほうに回したときモータの回転が止まってしまいます。 これは予見できたことなので、止まってもかまわないという仕様にしてあるわけです。
もし止まらないようにしたいのであれば、8ビット・データを単純に 16分の1 (4ビット右シフトは 16で割るのと同じ)して 4ビット・データを作るのではなく、一次関数で処理した上で 4ビット・データにすれば改善されます。 またはもっと簡単に、下限値を決めてそれ以下にならないようにするという方法もあります。
次回は 【製作】 抵抗分圧式リモコンとマイコン・カー の予定です。
『関連宣伝』
HC08ミニマイコン扇風機製作部品セット はマルツパーツ館で販売中です。
また、マルツパーツ館でプリント基板付き書籍 試しながら学ぶHC08マイコン入門 をご購入いただくと、CodeWarrior CD-ROM がもらえます(数に限りあり)。
『参考文献』
(*1) 「組み込みソフトウェアの設計&検証」 (CQ出版) 藤倉俊幸 著
「試しながら学ぶHC08マイコン入門」 (CQ出版) 川野亮輔 著
第10章 統合開発環境CodeWarriorを使ってみる
Appendix F KMC908QY4Aユーザ・モード・モニタ入りマイコンの知識
筆者のホームページ 『マイコン工作の実験室』
組み込みエンジニア KAWANO
[2009-07-14 追記]
(*1) default 節に関する注意を 第56回 に書きました。
