ポートを使いこなし、いざ制御プログラミングの世界へ:イチから作って丸ごと学ぶ! H8マイコン道(8)(2/3 ページ)
制御システムの基本といえるスイッチとLEDによる入出力回路の動作を理解したうえで、C言語によるポート制御を行う!!
――H8Tiny-USBの入出力インターフェイスを理解できたところで、次はC言語プログラミングの解説に入ります。「どのようにI/Oレジスタを使うのか」が、ここでのポイントです。また、ビット制御など、制御プログラミングならではの演算も併せてマスターしてください。
それでは、スイッチ入力回路、7セグメントLED表示回路と接続されている3つのポート(図4)を操作するC言語プログラムを作ってみましょう。
マイコン「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)の解答例 |
どう? 健一君。
ちゃんと理解できたかしら?
はい!!
岡野さんの解答だと、スイッチを同時に押したときは、SW1が一番有効ってことですよね。
そう! if文の深さは機能の優先順位を示しているのよ。
やるわねー。よく読み取ったわ。
へへッ!
でも、まだポート定義(#define)の意味がよく分からないや。
そう。
じゃあ、特別にもう少し説明してあげるわ。
はーい!
Copyright © ITmedia, Inc. All Rights Reserved.