IOポートに関する補足
Copyright(C)12Nov2005
coskx
1.IOポートへの出力
IOポート(入出力ポート)は,メモリと同じアドレス空間にある。(メモリマップトIO)IOポートはメモリの一部のように見え,あるアドレスにあ るメモリに対して読み込み/書き込みをすることでで,8ビットデータをIOポートに書き込んだり読み出したりすることができる。あるIOポートがメモリ上 のどのアドレスに存在するようになっているかは,ハードウェアマニュアルを見ると書いてある。
ある出力ポートに8ビットデータを書き込むと,そのポートにつながるCPUの端子から1と0のビットパターンが電圧のVcc(5V)またはGNDレベル(0V)として出力される。
ある入力ポートから8ビットデータを読み出すと,そのポートにつながるCPUの端子に与えられた電圧のVccまたはGNDレベルを1と0として読み出すことができる。
2.出力ポート5のアドレスとCPUの外側
H8/3048のマザーボードでは,ポート5の第0ビットと第1ビットがそれぞれ2つのLEDにつながれているため,ポート5の第0ビットと第1ビットに1と0を出力することにより,LEDの点灯消灯を実現できる。(図1,2,3参照)
図1 マザーボード,CPUカード,CPUの接続 ポート5の第0ビットと第1ビットはCPUの53番ピン,54番ピンから外部に出ており,CPUカードのコネクタ3(CN3)の31,32ピンに接続されています。 マザーボード上ではCN3の31,32ピンから1.5kΩの抵抗を介してLEDにつながっています。 |
図2 CPUカード 実際のCPUカードのCN3の31,32ピン 右の図とは上下が逆なので注意 |
図3 マザーボード マザーボード上でCN3の31,32ピンから引き出されているLED |
3.ポート5の実体
AKI-H8/3048は,H8/300Hアドバンスモードのモード7で動作しているこの時のアドレスとメモリの関係は表1のようになっている。
メモリ空間は1Mバイト(16進数5桁)になる。
実際のプログラムでは,16Mバイトメモリ空間モードに対応できるような記述となるため,アドレスは16進数6桁で記述している。
1Mバイトメモリ空間の場合,アドレスの上位4ビットは捨てられて16進数5桁でコンパイルされるため,実用上は差し支えない。
特に,ポート5に関しては表2のようになっている。
表1 メモリマップ(H8/300Hアドバンスモードのモード7)
ハードウェアマニュアルから読み取ることができる。
メモリの種類 | アドレス | 備考 |
ROM | 0x00000-0x1FFFF (128kbyte) | 実際にはフラッシュメモリ,書き込みモードでのみ書き込むことが出来る。 電源が切れても,内容は保たれる。 割り込みベクタテーブル,プログラム,定数,変数の初期値の格納場所 |
RAM | 0xFEF10-0xFFF0F ( 4kbyte) | 電源が切れると内容は消滅する。 変数,スタック(オート変数,戻りアドレス,レジスタ退避)の格納場所 |
I/Oレジスタ | 0xFFF1C-0xFFFFF (228byte) | I/Oレジスタ ポート5はこの中の一部である |
表2 IOレジスタポート5
ハードウェアマニュアルから読み取ることができる。
ポート5レジスタの役割 | アドレス |
DDR データディレクションレジスタ(8ビット幅) DR(8ビット)の各ビットを入力用に使うか出力用に使うかを定義する ビットごとに0なら入力,1なら出力の設定となる。 |
0xFFFC8 |
DR データレジスタ(8ビット幅) IOレジスタ |
0xFFFCA |
よくある質問
Q:どうしてポート5の第0ビットと第1ビットにLEDがつながれているのですか。
A:マザーボードの設計者がLEDをつなぐ位置をポート5の第0ビットと第1ビットに決めています。
Q:他のポートの適当なビットにLEDをつなぐことができますか
A:出力に使えるポートのビットであり,他の用途に使われていないのならLEDをつなぐことができます。
ただし,ソフトウェアを対応させないとLEDを点灯することができません。
勘どころ |
ポート5レジスタのアドレスが0xFFFC8であることはCPUの設計者が決めたことなので,使用者は変更することができない。 |
4.ポート5の実体を意識したLED点灯消灯のプログラム
ポート5の下位2ビットからデータを出力するためには,次の手順となる
(1)ポート5は入出力兼用であるため,下位2ビットを出力に使うように設定する
(2)ポート5の下位2ビットに任意のデータを出力する。
(2進法で00,01,10,11の4通りしかない)
具体的な手順は,図4を参考に,次の通りとなる。
(1)ポート5データディレクションレジスタ(8ビット幅)
メモリマップドIOレジスタ アドレス FFFFC8
ビットごとに0なら入力,1なら出力の設定となる。
ここでは下位2ビットを出力とし,他を入力の設定にするため3を書き込む
(2)ポート5データレジスタ(8ビット幅)
メモリマップドIOレジスタ アドレス FFFFCA
下位2ビットにデータを書き込む
図4 CPUがポート5を通してLEDをON−OFFさせる様子 |
動作させるプログラムの手順は上記のとおりだが,Cプログラムでどのように記述するかについてはいろいろ工夫がある。
実用上は「4.4 ヘッダ記載のstruct表現によるプログラム」が使われているが,「4.1」から「4.3」は説明のために表
記方法を工夫している例である。
4.1 ポインタと#defineを用いて,あるアドレス(ポートP5)に直接データを書き込むプログラム
リスト1 |
#define P5DDR *(unsigned char*)0xfffc8 #define P5DR *(unsigned char*)0xfffca void msecwait(int msec) /*mesc間なにもしない時間稼ぎ関数*/ { int i,j; for (i=0;i<msec;i++) { for (j=0;j<1588;j++); /*1588は実測によって求めた値*/ } } main() { /*P5の下位2ビットを出力に設定*/ /* P5のDDRの下位2ビットに1を与えるとこの設定になる*/ /*DDRとはDataDirectionRegister(データ方向設定レジスタ)*/ P5DDR = 0x3; /*0x3 = 00000011(二進数)*/ while(1) {/*これは無限ループ*/ /*LED0をONに,LED1をOFFにする P5のDRの第0ビットを1に,第1ビットを0にする*/ /*DRとはDataRegister(データレジスタ)*/ P5DR=1; msecwait(1000);/*1000msecの間なにもしない*/ /*LED0をOFFに,LED1をONにする P5のDRの第0ビットを0に,第1ビットを1にする*/ /*LED0をOFFにする P5のDRの第0ビットを0にする*/ P5DR=2; msecwait(1000); } } |
4.2 ポインタとstructを用いた表現によるプログラム
リスト2 |
struct port5 { unsigned char DDR; char dummy; /*ここは使われない*/ unsigned char DR; }; #define P5 (*(struct port5*)0xfffc8) /* この設定により,それぞれ1バイトずつなので,次のように配置されることになる P5.DDR FFFC8 P5.dummy FFFC9 ここは使われない P5.DR FFFCA */ void msecwait(int msec) /*mesc間なにもしない時間稼ぎ関数*/ { int i,j; for (i=0;i<msec;i++) { for (j=0;j<1588;j++); /*1588は実測によって求めた値*/ } } main() { /* P5のDDRの下位2ビットに1を与える*/ P5.DDR = 0x3; /*0x3 = 00000011(二進数)*/ while(1) {/*これは無限ループ*/ /*LED0をONに,LED1をOFFにする P5のDRの第0ビットを1に,第1ビットを0にする*/ P5.DR=1; msecwait(1000);/*1000msecの間なにもしない*/ /*LED0をOFFに,LED1をONにする P5のDRの第0ビットを0に,第1ビットを1にする*/ P5.DR=2; msecwait(1000); } } |
4.3 ビットフィールドと共用体を用いた表現によるプログラム
P5.DRをバイト単位で扱ったり,ビット単位で扱えるように工夫している
ビットフィールドと共用体の解説は→Cadd.html
リスト3 |
struct port5 { unsigned char DDR; char dummy; /*ここは使われない*/ union { unsigned char BYTE; struct { unsigned char dummy6 :6; /*6ビット ここは使われない*/ unsigned char B1 :1; /*1ビット*/ unsigned char B0 :1; /*1ビット*/ } BIT; } DR; }; #define P5 (*(struct port5*)0xfffc8) void msecwait(int msec) /*mesc間なにもしない時間稼ぎ関数*/ { int i,j; for (i=0;i<msec;i++) { for (j=0;j<1588;j++); /*1588は実測によって求めた値*/ } } main() { /*P5の下位2ビットを出力に設定*/ P5.DDR = 0x3; /*0x3 = 00000011(二進数)*/ while(1) {/*これは無限ループ*/ /*LED0をONに,LED1をOFFにする P5のDRの第0ビットを1に,第1ビットを0にする*/ P5.DR.BYTE=1; msecwait(1000);/*1000msecの間なにもしない*/ /*LED0をOFFに,LED1をONにする P5のDRの第0ビットを0に,第1ビットを1にする*/ P5.DR.BYTE=2; msecwait(1000); /*LED0をONに,LED1をOFFにする P5のDRの第0ビットを1に,第1ビットを0にする*/ P5.DR.BIT.B0=1; P5.DR.BIT.B1=0; msecwait(250);/*250msecの間なにもしない*/ /*LED0をOFFに,LED1をONにする P5のDRの第0ビットを0に,第1ビットを1にする*/ P5.DR.BIT.B0=0; P5.DR.BIT.B1=1; msecwait(250); } } |
4.4 ヘッダ記載のstruct表現によるプログラム
ヘッダファイル3048f.hにはリスト3のポート定義と同じ記述が書かれている。
リスト4 |
#include <3048f.h> void msecwait(int msec) /*mesc間なにもしない時間稼ぎ関数*/ { int i,j; for (i=0;i<msec;i++) { for (j=0;j<1588;j++); /*1588は実測によって求めた値*/ } } main() { /*P5の下位2ビットを出力に設定*/ P5.DDR = 0x3; /*0x3 = 00000011(二進数)*/ while(1) {/*これは無限ループ*/ /*LED0をONに,LED1をOFFにする P5のDRの第0ビットを1に,第1ビットを0にする*/ P5.DR.BYTE=1; msecwait(1000);/*1000msecの間なにもしない*/ /*LED0をOFFに,LED1をONにする P5のDRの第0ビットを0に,第1ビットを1にする*/ P5.DR.BYTE=2; msecwait(1000); /*LED0をONに,LED1をOFFにする P5のDRの第0ビットを1に,第1ビットを0にする*/ P5.DR.BIT.B0=1; P5.DR.BIT.B1=0; msecwait(250);/*250msecの間なにもしない*/ /*LED0をOFFに,LED1をONにする P5のDRの第0ビットを0に,第1ビットを1にする*/ P5.DR.BIT.B0=0; P5.DR.BIT.B1=1; msecwait(250); } } |