第4回 I/Oプログラミング
今までは、マイコンからの出力をプログラミングしてきました。AVR マイコンからの配線図を図16.1 に示します。電圧の出力はPORTB から行われ、[5V] あるいは1[0V] が出力されます。LED 発光時の消費電流は表示灯用途では数mA~50mA 程度なので、抵抗を使うことによって電流を小さくしています。
Figure:AVR マイコンからLED への出力
スイッチからの入力とLEDの点灯
今回は、入出力システムを構築します。PORTC を入力用の端子として使用します。スイッチのオン・オフの状態をPORTC からの入力情報によって監視します。トグルスイッチはPC 2とPC 3に接続されています。プッシュスイッチはPC 4とPC 5に接続されています。
Figure:AVR マイコンからLED への出力
I/O ポートの設定
DDRB = 0xFF; はPORTB をすべて出力として使うことを宣言しています。データを出力したいポートはDDRB コマンドで、[1] を指定します。例えば、B ポートの0,1,3,4,6 番を「出力」に設定して、外部のLED などを「駆動」したいときは、DDRB=0x91 となります。2,5,7 は必然的に[0] となり、入力端子になります DDRB=0xFF ならすべての端子が出力端子になります。B ポートにはLED が接続されていますので、この LED に電気信号を出力して、LED を点灯させます。DDRC = 0x00; はPORTC をすべて出力として使うことを宣言してます。データを「入力」したいポートはDDRC コマンドで、[0] を指定します。例えば、Cポートの0,1,3,4,6 番を「入力」に設定して、外部のスイッチからくる電気信号が[1](5V に対応)なのか、 [0](0V に対応)なのかを検知したい場合は、DDRC=0xA4 となります。2,5,7 は必然的に[0] となり、入力端子になります。DDRC=0xFF ならすべての端子が出力端子になります。授業で使用しているマイコンボードの場合はC ポートにはスイッチが接続されていますので、このスイッチからくる電気信号を検知して、[1]か[0] かを判別することになります。
Figure:入力ポートの設定
マスクを使ったI/O プログラミング
#include <avr/io.h> typedef unsigned char byte; typedef unsigned int word; int main() { byte data; DDRB= 0xFF; //PORTB はすべて出力 DDRC= 0x00; //PORTC はすべて入力 for(;;){ data= PINC; //PORTC の状態を読み込む data&= 0x3C; //スイッチ以外のポートをマスク PORTB= data; //結果をPORTB に出力 } return 0; }
ビット演算
マイコンボードにおいて、マイコンのポートからインプットアウトプットを行うことをソフトウェアで実現するにはビット演算というプログラミングが必要になる。そこでビット演算について簡単にまとめる。ビット演算とは、2 進数を個々のビットの列として論理演算することである。論理演算とは論理回路を使って、2 進数を演算する方法である。まず論理回路について説明する。論理回路とは、[1](H レベル) と[0](L レベル) の2つの状態によって機能する回路であり、上の図のように入力と出力が対応している。 電子回路の場合は電気的な信号でやりとりされが、論理演算としても理解することができ、それがまさに2 進数を使ったビット演算のことである。どんなに複雑な論理回路もNOT,AND,OR の3つの論理回路の組み合わせで構成される。
Figure:基本論理回路と論理演算
ビット単位ANDは、ふたつの同じ長さのビットパターンを入力とし、同じ位置のビット毎に論理的ANDを行って同じ長さのビットパターンを出力する操作である。各ビット位置で、入力するふたつのビットがどちらも[1] であれば、出力ビットは[1] となる/C++では、x = y & z と&を使って表される。y AND zの演算の結果をxに格納する。ビット単位AND はビットマスク操作として使われることもあります。これは、ビット列の一部を取り出すのに使われたり、ある特定のビットが[1] か[0] かを調べるのにも使わます。
Figure:ビット毎の論理積
図16.5 の例で、PORTC=11001010 という入力を考える。4 番目のビットが[1] かどうかを調べるには、ビット単位AND に対して、その調べたいビット位置だけを[1] にしたビットパターンを入力します。この結果は[1] なので、4 番目のビットは[1] であったことがわかります。このようなビット単位AND の使い方はビットマスクと呼ばれ、ある特定のポートの状態を知りたいときに使われます。
ビット単位OR は、ふたつの同じ長さのビットパターンを入力とし、同じ位置のビット毎に論理的ORを行って同じ長さのビットパターンを出力する操作です。各ビット位置で、入力のふたつのビットのどちらかでも[1] であれば、出力ビットは[1] となります。C/C++では、ビット単位OR 演算子は"." (縦棒)で表されます。x = yz;. 図16.6 例では、"y OR z" の結果をx に格納する。
マスク
Figure:ビット毎の論理和
data &= 0x3C; という命令文はスイッチ以外のポートをマスクしています。図17.3 の8 ビットのうち上段が示しているのは[3C] で、真ん中の4 つを[1] にしています。スイッチが接続されているのは、 この4つのPORT になります。この4つのスイッチの[1][0] という状態だけを監視して、LED に表示することが目的ですが、スイッチがつながっていないビットは[1] なのか[0] なのかわからず、 マイコンはどちらか適当な値を出力してしまう可能性があります。そこで、入力ポートのC ポートにはスイッチが接続されていますが、入力されたデータの中から、真ん中の4つのビットに対応する情報だけを残し、 他のビットを[0] にすることが必要になります。図17.3 では中断の7 ビットにはスイッチがつながっていないにもかかわらず、[1](赤)が表示されています。これを0x3C とのAND をとることで消し去ります。これはビット演算という方法で、各ビット毎に論理和がとられます。
Figure:ビット演算によるマスク.
課題
- 1. 論理を反転させてスイッチを押したら、LED が点灯する、あるいは消灯するプログラムを書こう
- 2. 押したスイッチの二つとなりのLED が、点灯するプログラムを書こう
その他のビット演算
data = 0x3c;//スイッチが接続されているポートの論理を反転 data <<= 2;//ビットを2ビット左シフト
if 構文を使ったスイッチからの入力とLED の点灯
ポートC のポートにスイッチからの情報がはいる場合、スイッチはオフのときは、「1」を発信し、スイッチを押されると「0」を発信します。この信号を論理回路であるAND 回路を用い、PORTC の情報を取り出します。したがって、センサーからの入力を条件分岐に使うには、以下のプログラムで行います。トグルスイッチのS1 とS2 からの入力情報を取り出して、LED にアウトプットするプログラムです。
入力情報の条件分岐
#include <avr/io.h> int main() { int data; int output; DDRB= 0xFF; //PORTB はすべて出力 DDRC= 0x00; //PORTC はすべて入力 for(;;){ output = 0; data = PINC; //PORTC の状態を読み込んでdata に格納 if((data & 0x04) !=0 ) output= 0xf0; //S1 がオフ if((data & 0x08) !=0 ) output= 0x0f; //S2 がオフ if((data & 0x04) ==0 ) output= 0xf0; //S1 がオン if((data & 0x08) ==0 ) output= 0x0f; //S2 がオン PORTB = output; //結果をPORTB に出力 } return 0; }
Figure:各スイッチとPORTC の接続
論理積の計算をした結果、スイッチのオン・オフの状態に従って、[0] あるいは[1] の出力がでます。スイッチがオンのときは、[0] という状態なので、if 構文の中で、"[0]==0 "が「TRUE」になり、スイッチがオンである情報を取り出します。スイッチがオフのときは、[1] という状態なので、if 構文の中で、[1] は[0]ではないので、"[1]!=0 "が「TRUE」になり、スイッチがオフである情報を取り出すことができます。
- 課題1 押しボタンスイッチ3 と4 を使って、LED が点滅するプログラムを書こう
- 課題2 押しボタンスイッチ3 と4 を押すと、ブザーが鳴るプログラムを書こう。注)ブザーはPORTD の4 番ピンに接続されています。PORTD を「出力」に設定してから、PORTD の4 番目の端子に[1] と[0] を交互に送ることで、ブザーの膜が振動し、音が鳴ります。
他の方法:if 構文
センサからの入力情報
if(PORTC & 0x08 != 0){ //4 番目のフラグのビット情報を監視する }
入力情報の条件分岐
#include <avr/io.h> typedef unsigned char byte; typedef unsigned int word; int main() { byte data; DDRB= 0xFF; //PORTB はすべて出力 DDRC= 0x00; //PORTC はすべて入力 for(;;){ data = PINC; //PORTC の状態を読み込む data &= 0x3C; //スイッチ以外のポートをマスク data = 0x3c; //スイッチが接続されてるポートの論理の反転 outputdata = 0x00; if(data & 0x04){ outdata |=0x81;} if(data & 0x08){ outdata |=0x42;} if(data & 0x10){ outdata |=0x24;} if(data & 0x20){ outdata |=0x18;} PORTB = outdata; //結果をPORB に出力 } return0; }