デジタル入出力


まず、LEDを点灯させる

 まず始めに、LEDを点滅させる事を考えてみる。LEDを点灯させるには、電池などの電源からLEDに電流を流してやることで可能となる。
 具体的には、
       
            図1  LED点灯回路(制御なし)              図2 LED点灯回路(スイッチ付き・マイコン制御)

  のようになる。LEDは図1のように接続すれば、点灯する。要するにLEDの点灯に必要な電流を流してやれば、LEDは光り輝くという事。
  (電池とLEDの間にあるギザギザの部品は抵抗と呼ばれていて、LEDに流 れる電流をコントロールするためのものである。もし、LEDと電池が直結されていたとすると、LEDに過大な電流が流れるため、発熱や破壊を引き起こす。 一見、なんともないように見えたとしても、寿命は確実に短くなる。)

 LEDの点滅を制御したいなら、図2の様にスイッチを回路の途中に挿入すれば良い。
 (その他に、抵抗の値を変化させることで、LEDの明るさをコントロールする事も出来る。)


 図2(上)の回路を実際に作成したものが図3になる。回路図と配線の対応をよく確認しておくこと。
 (この例では、LEDや抵抗、電池などをはんだ付けして接続している。はんだ付けは確実な接続方法だが、回路を変更したい場合は少し手間がかかる。このため、実験や試作を行う場合は、簡単に配線を変更できるブレッドボードを用いると良い。)

                    
                          図3 LED点灯回路(スイッチ付き)の配線例                 図4 機械的なスイッチ(トグルスイッチ)の例

 機械的なスイッチをON/OFFすることでLEDは点滅する事になるが、マイコンにはこのスイッチをエイヤッと切り替えることなんて出来るはずもないので、スイッチ部分を電子的なスイッチであるトランジスタ(MOS-FET)に置き換えることになる。(図2・下)
 マイコンからの指令(信号)によって、トランジスタがON/OFFし、LEDが点滅するようになる。

 以上のように、マイコンを用いて外部のデバイス(装置)を制御する事が出来る。マイコンと外部のデバイスとのデータのやり取りは、
I/Oポート(Input / Outputポート:データの入出力を行う港)を通じて行う事になる。


ブレッドボードによるLED点灯回路の例

 

デジタル信号と入出力

 デジタル信号と
は、電気信号(電圧)を2値化したもの(正確には離散化)。コンピュータの内部では、データはビットを単位として処理されているが、1つのビットは1か0の2つの値を持っている。これを電気信号に対応させたものが、デジタル信号である。 

  例えば図5の様な回路があった場合に、スイッチをON/OFFすることで、電圧センサに加わる電圧(Vsw ~ デジタル信号)が図6の様に変化する。 
                 
                                                                図5 デジタル信号とIOポート

               
                         図6 電圧と閾値、デジタル値の関係

 スイッチを押す(ON)すると、Vswの電圧は5Vとなる。この値は閾値を超えているので、電圧センサによって1のデジタル値に変換される。

 スイッチを離す(OFF)すると、Vswの電圧は0Vとなる。この値は閾値を下回っている、電圧センサによって0のデジタル値に変換される。
  これを表にまとめると、

スイッチの状態 デジタル信号(Vsw) デジタル値(bitの値:2進数1桁分)
ON 5V 1
OFF 0V 0

                                                                          図7 デジタル信号とデジタル値
 の様な対応関係となる。


  図5と図6の例は、マイコンから見るとデジタル信号を外部から入力している。IOポートは、外部にデジタル信号を出力する事も出来る。この関係を表したのが、図8、図9である。
                                  
                 図8  デジタル入力                                                   図9 デジタル出力

 図8は、外部からデジタル信号(この例ではスイッチが生成した)を受け取っているので、IOポートは入力として動作している。
 図9は、CPUが設定したデジタル値をデジタル信号に変換し、LEDに送り出しているので、IOポートは出力として動作している。


 このように、IOポートの機能は、デジタル信号とデジタル値を相互に変換することである。
(デジタル信号→デジタル値:入力、デジタル値→デジタル信号:出力)

 通常、IOポートは複数の入力(もしくは出力)が出来るように設計されている。図5の例ではスイッチは1個だが、1つのIOポートに複数のスイッチを繋ぐことも出来る。実習で使用するAVRマイコン(ATMega32U4)には、複数のIOポートがあるが、例えばPortBは8ビットの入力・出力が可能なので、8個のスイッチやLEDを接続することができる。(それぞれのスイッチ、LEDは各ビットが対応する。図10参照)
             
           図10 IOポートの各ビットにLEDを接続した例(出力設定)、この例では10110001 = 0xB1 = 177を出力している

 もう少し別の面からみた説明は
こちら


AVRマイコンのIOポート

 AVRマイコン(ATMega32U4)には、以下のIOポートがある。

  1. PortB (PB0~PB7:8bit)
  2. PortC (PC6、7:2bit)
  3. PortD (PD0~PD7:8bit)
  4. PortE (PE2、6:1bit)
    ※PE2はブートローダの起動選択用。
  5. PortF (PF0、1、4~7:6bit)
    ※JTAGポートと共用(デフォルトでJTAG用に設定されているので、ユーザが使用する場合は設定を変更する必要がある)

 いずれのポートも入出力が可能。

 一般にマイコンのI/Oポートは、入出力を切り替えて使うようになっている。(そうでないものもあるが...)入力専用ピンや出力専用ピンを設けてしまうと、設計に融通が利かなくなる事が多い。(また、ICパッケージのピン数が増える) 場合によっては同一のピンである時は入力ある時は出力と切り替えて処理を行う必要がある。このような場面にも対応できるように入力・出力のプログラミングが可能になっている。
 

AVRマイコン(ATmega32U4)のデジタル入出力を使うには

 IOポートを利用するには、まず、IOポートの各ビットをどのように動作させるか設定する必要がある。設定には、各ポートごとに設置されているレジスタ※1に値を書き込む必要がある。IOポートを制御するレジスタには、以下の4つがある。

レジスタ名 機能
 DDRx         

I/Oポートの各ビットの入力・出力を決める。
ビットを1にセットすると出力、0にクリアすると入力となる。
 ex) DDRBを0b01000001に設定したとすると、PB6とPB0が出力となる。他のビットは入力。
      初期状態では0b00000000となっている。

 PORTx                

①ポートが出力に設定されている場合(DDRxの該当ビットが1)
  PORTxに書き込まれた値が外部ピンに出力される。
②ポートが入力に設定されている場合(DDRxの該当ビットが0)
  PORTxのビットが1にセットされた外部ピンがプルアップされる。
  (同時にMCUCRのPUDビットが0にする必要がある)

 PINx ①ポートが出力に設定されている場合(DDRxの該当ビットが1)
 無効
②ポートが入力に設定されている場合(DDRxの該当ビットが0)
 外部ピンから電流が流れ込む時(電圧が加わっている時)、該当するビットが1
 そうでない時は0
 MCUCR
(PUDビット)

IOポートが入力に設定されている場合に、PORTxによるプルアップ設定を有効にするか無効にするかを決める。
 ・1にセットすると、全てのプルアップ設定(PORTxによる)が無効になる。
     ex)   MCUCR |= (1 << PUD);
 ・0クリアすると、全てのプルアップ設定が有効になる。
           ex)   MCUCR &= ~(1 << PUD);

※1レジスタとは、ある特定の機能を持ったメモリの事。通常は変数と同様に使用する事が出来るが、場合によっては書き込みしか
  できなかったり、読み込みしかできない事もあるので、全く変数と同じではない
事に注意。

※2DDRxなどのレジスタ名の末尾のxは、B・C・D・Eに読み替えること。各ポートの構造は基本的に同じである。
 であるので、実際にはDDRBとかPORTBというレジスタ名があるのであって、DDRx = 0x1B; とかやって、「コンパイルエラーが出た。よくわからん」などという事は言わないように。

 なお、各IOポートのピン配置は、以下の様になっている。

       
                            図11  IOポートのピン配置

※図中に現れる記号で、PD0のPPortDポートの名前を表す。 ... なので、PD0PortDの0bitを表す。他も同様。


デジタル出力を使う場合

  1. PortBからPortFの中で、出力に使用するピンを選択する。
  2. DDRxの当該ビットを1に設定する。
  3. PORTxに出力するデータを書きこむ。

 以上の操作で、外部ピンにデータが出力される。ここでいうデータとは、デジタル信号のことで、ビットの1は5V(5ボルト)であり、ビットの0は0V(ゼロボルト)である。

 IOポートの出力動作を模式的にあらわしたのが図12(この図については、完全に理解する必要は無い)である。DDRxのビットに1を書き込むと、Tr1とTr2が有効になる。PORTxに書き込まれたビットの状態によって、各ビットごとに設置されたTr1とTr2がON/OFFする。
 Tr1がON、Tr2がOFF(つまりPORTxのビットが1)では、外部ピンに5Vが出力される。(LEDが点灯)
 Tr1がOFF、Tr2がON(つまりPORTxのビットが0)では、外部ピンに0Vが出力される。(LEDは消灯)

 

            
                 図12 IOポートの構造と動作(出力時)


プログラミングの実際(デジタル出力)

  マイコンボード上には2つのLEDが実装されているが、赤色のLEDはPortCのbit7に接続されており、ユーザが作成したプログラムでアクセスが可能である。まず、このLEDの点滅を制御するプログラムを考えてみる。

              
                  図13 AVRマイコンボード上のLED(PC7に接続されている)

 プログラムの作成で示した手順に従って、プロジェクトの作成(samplel_output)・ソースコードの入力を行う。入力が終了したらビルドを実行し、実行形式をflipを使ってダウンロードする。入力するソースは以下の通り。    

#define F_CPU 16000000UL   //16MHz動作 _delay_ms()に必要 delay.hで使用
 
#include  < avr/io.h >      // 入出力のポートアドレスを定義してある
#include  < util/delay.h >  // _delay_ms()を使うために必要
 
int main()
{
  CLKPR = 0x80; CLKPR = 0;   //16MHzにする設定
 
  DDRC = 0x80;      // PC7を出力に設定
 
  while(1)             // 無限ループ
  {
    PORTC = 0x80;     // PC7をON
    _delay_ms(200);   // 200m秒待ち
 
    PORTC = 0x00;     // PC7をOFF
    _delay_ms(200);   // 200m秒待ち
  }
}

 プログラムが正常に動作すると、PC7が0.2秒周期で点滅する。
 ※ 幾つかの数値が、0b00000001のように記述されているが、これは2進数で数値を表現したものである。2進、10進、16進の表記法による
   対応関係は、以下の通り。

2進表記 10進表記 16進表記
0b00000000 0 0x0
0b00000001 1 0x1
.... .... ....
0b00001000 8 0x8
.... .... ....
0b11111111 255 0xFF

(注意)0b000...の様な2進数の表記法は、AVRマイコンのCコンパイラ(AVR-GCC)の方言である。従って、他のCコンパイラで は使えない。

 

デジタル入力を使う場合

  1. MCUCRのPUDビットに0を書き込む(全ての内蔵プルアップ抵抗使用の初期設定)
  2. PortBからPortFの中で、入力に使用するピンを選択する
  3. DDRxの当該ビットに0を書き込む(入力を設定)
  4. PORTxの当該ビットに1を書き込む(プルアップ抵抗使用を設定)
  5. PINxの当該ビットの値を読み込む

 以上の操作で、外部ピンからデータを読み込むことができる。以上を模式的にあらわしたものが、図14(この図については、完全に理解する必要は無い)である。なお、3のプルアップについては、外付けのプルアップ抵抗を用いる場合は、内蔵プルアップ抵抗の設定は不要となる。逆に、内蔵プルアップ抵抗を用いる場合は、外付けプルアップ抵抗及び電源が不要となり、スイッチを外部ピンとGNDの間に接続すればよい。(回路の簡略化)
  
           
               図14 IOポートの構造と動作(入力時)


※プルアップとは...

 IOポートを入力に設定した際に使用する。
 例えば、IOポートにスイッチが繋がっているとして、図5、図6の説明は、原理としては間違っていないのだが、実際の動作では、スイッチを押してもいないのに押されたように誤動作するなどの問題が生じる。この問題を回避する手段がプルアップ(プルダウンもある)である。 詳細は、こちら



プログラミングの実際(デジタル入力)

   続いて、デジタル入力についても実験してみる。新たにプロジェクトを作成(sample_input)し、ビルド、ダウンロードして動作を確認する。
  ソースは以下の通り。   

#define F_CPU 16000000UL   //16MHz動作 _delay_ms()に必要 delay.hで使用
 
#include  < avr/io.h >       //入出力のポートアドレスを定義してある
#include  < util/delay.h >   //_delay_ms()を使うために必要
 
int main()
{
  CLKPR = 0x80; CLKPR = 0;     //16MHzにする設定
  MCUCR &= ~(1<<PUD); // 全てのプルアップ設定を有効
 
  DDRC = 0x80;        //PC7を出力に設定
  DDRD = 0x00;        //PD0~PD7すべて入力に設定
  PORTD = 0x01;       //PD0をプルアップする
 
  while(1)             // 無限ループ
  {
    if((PIND & 0x01) == 0x00) //PD0が0の時(SWを押しているとき)
    {
      PORTC = 0x80;         //PC7(ONボードLED)をON
    }
    else                     //PD0が0の時(SWを押していないとき)
    {
      PORTC = 0x00;         //PC7(ONボードLED)をOFF
    }
  }
}


 動作確認は、以下の様に行う。PD0の外部ピンの位置は、番ピンになるのでここにジャンパ線を挿しこむ。この状態だと、PD0はプルアップされているので、PD0の電圧は5V(デジタル値の1)になる。(スイッチを離した状態)結果として、PC7が点灯する。
 ジャンパ線をGND(17番ピン)に差し込むと、PD0の電圧が0V(デジタル値の0)になるため(スイッチを押した状態)、結果として、PC7が消灯する。


    

ビット演算のやり方は、こちらを参照。

最終更新:2017年10月04日 16:20