IOポートの使い方

 I/OポートとはInput/Output Portの略で、コンピュータが、外部のシステムとデータのやり取りをする時に使うハードウエア(回路)の一種。ポート(port)とは、日本語で言う「港」のことで、データのやり取りを行う「港」という意味になる。入出力とも言う。(要するにデータの出入り口と考えれば良い) 以下に、CPUとI/Oポートの関係を以下の図に示す。

 ※注CPUから見て、データを取り込む場合が「入力」、データを出す場合が「出力」となる。

 I/OポートとCPUは、バスコントローラを経由してバス(緑の線)を介して接続されている。バスに接続されている複数のハードウエアは、メモリと同じように、アドレスを使ってアクセスする。つまり、あるアドレスはメモリ(RAMやROM)であり、また、あるアドレスはI/Oだったり、タイマーだったりする。

 実習で用いるマイコンボードでは、ポートAに押しボタンスイッチ(PA3~PA0)、ポートBにLED(PB3~PB0)が接続されている。(下図参照)

●レジスタとアドレス

 単純にデータを記憶するだけでなく、特定の機能を持った記憶領域(素子)の事をレジスタと呼ぶ。例えばこの後で説明する様なDDR(Data Direction Register)やDR(Data Register)など、そのアドレスにアクセスすることで、様々な機能を実現している記憶領域が該当する。

 実習で使用しているマイコンボードでは、ポートAにスイッチ(PA0~PA3)、ポートBにLED(PB0~PB3)が接続されているが、これらの操作に必要なレジスタはPA_DDRとPA_DR、およびPB_DDRとPB_DRの各レジスタとなる。それぞれのレジスタの配置は、以下の図を参照。

                     

※マイコンの様々な機能を使用するためのレジスタ類は、内部I/Oレジスタとして0xFFF1C~0xFFFFFの間に配置されている。

 

●I/Oポートの構造、データの方向

(1)データの方向

I/Oポートは、データの入力、出力を行うため、データの流れに方向がある。

入力では、 CPU ← I/O ← 外部のハードウエア(例えばスイッチなど)

  
※センサやスイッチなど、外部の状態を読み取るために使用する。センサやスイッチは、使用によって状態が
    変化する。(例えばスイッチONとかOFFとか) 状態が変化するという事は、状態=データなので、データ
   を作り出していると言える。作り出されたデータをマイコン側で取り込んでやる必要があるので、入力とす
   る。

出力では、 CPU → I/Oポート → 外部のハードウエア(例えばLEDなど)

  
※外部のLEDやモータなどの装置を制御したい場合に使用する。LEDやモータは、外部からの指示(データ)
    によって動作を変化させる。(例えばLEDの点灯/消灯とか) なので、外部(マイコン)からデータ与え
    てやる必要がある、よって、出力とする。

のようにデータが流れる。

 I/Oポートでの入出力の方向については、以下の様になっている。データの方向を決めるのは、DDR(Data Direction Register)に対して、値を書き込むことで設定する。bitに1を設定すると出力、0を設定すると入力になる。デフォルト(リセット直後や、電源投入直後)は、全て入力になっている。

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

※注意 上記の図は、説明のためのもので、実習に使用しているマイコンボードとは接続が異なる。

 I/Oポートの入出力は、ICのピン(写真)として、外部と接続される。 さらに、ICのピンに接続された配線パターン(基板上の導線)を通じてLEDやスイッチなどの外部のハードウエアと接続されている。

(2)入力の場合の動作

 外部のスイッチやセンサなどの状態を読み取るには、I/Oポートを入力にする必要がある。(入力電圧とデジタル値の関係は、こちら

 

VPA0はスイッチPA0がOFFであることから5Vとなるため、PA_DRのbit0は1になる。また、VPA1は、スイッチPA1がONであるため、0Vとなる。したがって、PA_DRのbit1は0となる。

※スイッチのON/OFFと電圧の関係は、こちら。

 

(3)出力の場合の動作

 例えば、LEDを点灯、消灯させる動作について考えてみる。LEDの点灯/消灯を制御するということは、データをI/Oポートに出力することで可能となる。実習で使用しているボードでは、LEDはポートBに接続されている。下の図では、ポートBとLEDの接続を模式的に表したもの。

 LEDの点滅を制御するには、DRレジスタ(Data Registor)に値を書き込んでやるとできる。DRレジスタに値を書き込むと、(例えば0xFEを書き込む)最下位のビットだけが0になる。

I/Oポートの詳細な構造についてはこちら。
 

●LEDとI/Oポートの関係について

I/OポートPBにデータを書き込むことでLEDを点灯/消灯させる事が出来る。PBの各ビットとLEDの対応関係は、下図のとおり。


 

●スイッチとI/Oポートの関係について

マイコンボードのスイッチを押すことで、マイコンにデータを渡す事が出来る。スイッチの状態を読み取るにはI/Oにアクセスする必要がある。

マイコンボード上の4つのスイッチは、I/Oポート PAに接続されている。スイッチとPAの関係は、以下のとおり。

●プログラミングの実際

 I/Oポートを使ってCPUと外部の様々な機器(例えばスイッチ、LED、その他)との間でデータをやり取りするには、以下のような手順でプログラミングを行う。

(1)各I/Oポートの、データの入力/出力を切り替え

・I/Oポートを出力に設定すると、LEDの点灯/消灯などが制御できる。また、入力に設定すると、スイッチの状態を読み取ることができる。

・入力と出力の切り替えは、ビット毎にDDRレジスタを設定することで可能。

(2)DRレジスタにデータを書き込み(もしくは読み込み)

・DRレジスタにデータを書き込む(出力)

・DRレジスタのデータを読み込む(入力)

【 PortBに出力にしてLEDを点滅する例】

#include "monitor.h"

#define PB_DDR (*(volatile unsigned char *)0xFFFFD4) // アドレス0xFFFFD4へのポインタを定義
#define PB_DR (*(volatile unsigned char *)0xFFFFD6) // アドレス0xFFFFD6へのポインタを定義

int main()
{
    PB_DDR = 0x0F; // PBのbit7~4を入力、PBのbit3~0を出力に設定
             // ポートBのbit7~bit4にはLEDは繋がっていないので、入力として設定しておく。
             // I/Oポートの先に何が繋がっているかわからない場合は、入力にしていたほうが安全。
    /****************************************************************
     * LEDの点滅を制御する処理
     * 該当するbitを0にすると、LEDは点灯
     *       bitを1にすると、LEDは消灯する。
     ****************************************************************/
    PB_DR = 0x0E; // PortBのbit3~bit0に0xEを出力(LEDのPB0が点灯)
//    PB_DR = 0x02; // PortBのbit3~bit0に0x2を出力(LEDのPB3,2,0が点灯)

    return 0;
}

【PortAを入力にして、スイッチの状態(ON/OFF)を読み込む例】

#include "monitor.h"

#define PA_DDR (*(volatile unsigned char *)0xFFFFD1) // アドレス0xFFFFD1へのポインタを定義
#define PA_DR (*(volatile unsigned char *)0xFFFFD3) // アドレス0xFFFFD3へのポインタを定義

int main()
{
    int c;
    long int w;   // 4byteのint型

    PA_DDR = 0x00; // PortAのbit7~0を入力に設定

    while(1) // whileで無限ループ。こうしないとスイッチの読み取りが一瞬で終わってしまって、動作が確認できない。
    {
        /****************************************************************
         * スイッチの状態を読み取る処理
         * スイッチが押されると、該当するbitが0
         *       離されると、該当するbitが1になる。
         ****************************************************************/
        c = PA_DR & 0x01; // スイッチのON/OFFの状態はPA_DRレジスタを読み取ることで確認できる。
                                   // スイッチPA0はbit0の値で判断できるので、他のbitはAND演算で0にする。
                 // なぜ、この様にするかと言うと、if文などで条件判断する時に、PA0以外のスイッチの
                // 影響を取り除くため
        printf("SW-PA0=%d\n", c); // PA0の値を表示。表示される値が0の時にON、1の時にOFF。

        for(w=0;w<99999;w++);   // printfの表示が大量に出過ぎないための待ち時間を挿入。
    }
    return 0;
}

 

違う書き方として、ビットフィールドを使う方法もある。

#include "monitor.h"
#include "iodefine.h" // 以下のような記述(ビットフィールドという)を使う場合に設定

int main()
{
    PA.DDR = 0xFF; // ポートAの全てのbitを出力に設定
    PA.DDR.BIT.B0 = 1; // PAのbit0を出力に設定
    PA.DDR.BIT.B1 = 0; // PAのbit1を入力に設定

    PB.DDR.BIT.B1 = 1; // PBのbit1を出力に設定

    PB.DR.BIT.B1 = 1; // PBのbit1を1にする。LEDのPB1が消灯

    /* 設定していないポートについては、設定以前の状態をキープする */
}

 

最終更新:2014年11月17日 13:45