連載
» 2009年11月18日 00時00分 公開

ポートを使いこなし、いざ制御プログラミングの世界へイチから作って丸ごと学ぶ! H8マイコン道(8)(2/3 ページ)

[横田一弘 埼玉県立新座総合技術高等学校 教諭,MONOist]

――H8Tiny-USBの入出力インターフェイスを理解できたところで、次はC言語プログラミングの解説に入ります。「どのようにI/Oレジスタを使うのか」が、ここでのポイントです。また、ビット制御など、制御プログラミングならではの演算も併せてマスターしてください。

 それでは、スイッチ入力回路、7セグメントLED表示回路と接続されている3つのポート(図4)を操作するC言語プログラムを作ってみましょう。


H8Tiny-USBのポートの割り当て 図4 H8Tiny-USBのポートの割り当て

 マイコン「H8/3664」のポート操作は、「ポートコントロールレジスタ(PCRn)」と「ポートデータレジスタ(PDRn)」の2種類のI/Oレジスタによって行います。

 ポートコントロールレジスタは、ポートを入力として使うのか、出力として使うのかをビットごとに設定するものです。「0」を設定すると入力に、「1」を設定すると出力になります。なお、ポートの入出力設定はプログラムの初期に行います。

 図4の入出力設定はC言語で、

    PCR1 = 0; /* ポート1の全ビットを入力に設定 */
    PCR5 = 0x03;/* ポート5の下位2ビットを出力に設定 */
    PCR8 = 0xff;/* ポート8の全ビットを出力に設定 */ 

と記述します。なお、C言語で「0x」ではじまる数は16進数です。

 また、ポートデータレジスタは、データを入出力するためのものです。例えば、ポート1の状態を変数「data」に入力するには、

    data = PDR1; 

と記述します。

 逆に出力するには、

    PDR8 = data; 

のように、ポートデータレジスタに値を代入すればよいのです。

晴子さんからの宿題(1)の解説

 それでは、前回出題した晴子さんの宿題を考えてみましょう。

晴子さんからの宿題(1)の解説

SW1を押したら「1」を、SW2を押したら「2」を、SW3を押したら「3」を7セグメントLEDに表示するプログラムを作ってね。ただし、スイッチが1つも押されていないときは「0」を表示すること!



 プログラムは、システムの初期化からはじまります。ポートの初期化を次のように行います。

    PCR1 = 0x0; /* ポート1を入力に設定 */
    PDR5 = 0xfe;/* LED1に表示する */
    PCR5 = 0x03;/* ポート5の下位2ビットを出力に設定 */
    PDR8 = 0xc0;/* 0を表示 */
    PCR8 = 0xff;/* ポート8を出力に設定 */ 

 「使用しないポート・ビットを入力に設定する」「ポートを出力に設定する前に、データを確定する」は、制御プログラムの定石と考えてください。

 さて、宿題を解くには、ポート1に接続された3つのスイッチの状態をビットごとに判定しなければなりません。今回は、ビットごとの「論理積(AND)」演算を使って、この問題を解いてみます。

 C言語で「SW1を押したら『1』を表示する」は、

if ((PDR1 & 0x01) == 0) PDR8 = 0xf9; 

と、if文で書くことができます。上式の「アンパサンド(&)」はビットごとの論理積演算で、「0x01」との論理積により、

図

のように、ビット「0」のSW1を残して、ほかのビットをすべて「0」クリアしてしまいます。さらに、H8Tiny-USBのスイッチ入力は負論理なので、結果が「0」ならばSW1が押されたことになります。

 以上のように、制御プログラムではビットの操作が欠かせません。表1にC言語のビットごとの論理演算子を示します。

演算子 意味 使用例
| ビットごとの論理和(OR) x = a | 0x01; 0ビットを1にする
& ビットごとの論理積(AND) x = a & 0x0f; 下位4ビットを抽出する
^ ビットごとの排他的論理和 x = a ^ 0x0f; 下位4ビットを反転する
~ ビットごとの反転(NOT) x = ~a; 全ビットを反転する
表1 C言語のビットごとの論理演算

 ここまでの解説を踏まえ、さらに工夫した解答例を以下に示します(ソースコード1)。

/* SW1が押されたら「1」、SW2が押されたら「2」、SW3が押されたら「3」を表示 */
#define PCR1(*((volatile unsigned char *)0xffe4))
#define PDR1(*((volatile unsigned char *)0xffd4))
#define PCR5(*((volatile unsigned char *)0xffe8))
#define PDR5(*((volatile unsigned char *)0xffd8))
#define PCR8(*((volatile unsigned char *)0xffeb))
#define PDR8(*((volatile unsigned char *)0xffdb))
 
#define SW1 0x01
#define SW2 0x02
#define SW3 0x04
#define LED1 0x01
#define LED2 0x02
 
int main(void)
{
    unsigned char sw;
 
    PCR1 = 0x0;
    PDR5 = ~LED1;/* LED1に表示 */
    PCR5 = 0x03;
    PDR8 = 0xc0;/* 0を表示 */
    PCR8 = 0xff;
    for (;;) {/* 無限ループ */
        sw = ~PDR1;/* 入力し反転する */
        if (sw & SW1) PDR8 = 0xf9;
        else if (sw & SW2) PDR8 = 0xa4;
        else if (sw & SW3) PDR8 = 0xb0;
        else PDR8 = 0xc0;
    }
    return 0;
}
ソースコード1 宿題(1)の解答例

photo

どう? 健一君。


ちゃんと理解できたかしら?


photo

はい!!


岡野さんの解答だと、スイッチを同時に押したときは、SW1が一番有効ってことですよね。


photo

そう! if文の深さは機能の優先順位を示しているのよ。


やるわねー。よく読み取ったわ。


photo

へへッ!


でも、まだポート定義(#define)の意味がよく分からないや。


photo

そう。


じゃあ、特別にもう少し説明してあげるわ。


photo

はーい!



Copyright © ITmedia, Inc. All Rights Reserved.