【GPIO】MCP23017でRaspberry PiやArduinoのI/Oを増やす
I/Oピン数が足りない!!…とお嘆きの貴方へ
Raspberry PiやArduinoには「GPIO」というプログラムから制御可能な入出力端子が備わっています。
この端子にリレー・LED・スイッチ・その他の部品や機器を接続することができるのですが、Raspberry Piは30本弱、ArduinoはUNOは十数本程度しかありません。ちょっと凝ったことをしようとして、こんなんじゃ全然足りない!と嘆いている方、世の中そんなもの…と諦めている方も多いのではないでしょうか。
じつはGPIOはわりと簡単に増設することができるのです。この記事では、「I2Cバス」と呼ばれる信号セットにMCP23017を接続して大量のGPIO端子を増設する方法を紹介します。
なお、I2Cバス自体の仕様やら細かいアクセスの仕方やらは他のサイトにいくらでも載っていますので適当にググったらいいです。(投げやり)
I/Oエキスパンダ MCP23017
MCP23017は「I/Oエキスパンダ」と呼ばれるICです。

その名前から想像できる通りGPIOを追加・拡張するためのICで、それほど高速な入出力やタイミング制御は必要ないがGPIOの本数は欲しい、というときによく用いられます。家電の表示パネルのLEDダイナミック制御やキーマトリクススキャンなど、割と身近なところに使われていたりします。
入手性としては秋月電子でも普通に購入できますし、何よりDIPパッケージがラインナップされていることはDIY界隈では大変心強いところですね。
| 秋月電子通商 | http://akizukidenshi.com/catalog/ |
|---|
ならさぞかしネット上でも情報が溢れているだろうと思いきや、
ロクな情報がありゃしねェ!!(迫真)
…というわけで、この記事を書くことにしました。
ちなみにPCA9535も同じようなICです。こちらの方がレジスタの数が少なく理解しやすいので、参考までにどうぞ。
| PCA9535 | PCA9535でRaspberry PiやArduinoのI/Oを増やす |
|---|
とりあえず使ってみる
今回試すにあたり、以下のような環境を用意しました。
ハードウェア環境
お試しということで、Arduino MEGA2560モドキとブレッドボード上にMCP23017を実装した環境を用意しました。
5V系のArduinoのためMCP23017も5Vで動作させていますが、もしRaspberry Piで試す場合は3.3Vで動作させる必要があることに注意してください。

接続は以下のようになってます。

上図のGPA7〜0、GPB7〜0がGPIOです。MCP23017を1つ使うと16本のGPIOが増やせるわけですね。A2〜0の接続を変えることで異なるスレーブアドレスに設定することができるので、1つのI2Cバスに最大8個まで接続して使うことができます。
試しにGPA7〜4にスイッチ、GPA3〜2は空き、GPA1〜0にLEDを接続した例を示します。

ソフトウェア環境
ライブラリもどきとして、Arduino上に以下のようなアクセス関数と定義を実装しました。
もしこのページのコードをそのままコピペして使いたい場合はコメント部のURLを削除せずご利用くださいませ。商業用途でも構いません。サポートはありませんけどね…。
#include <Wire.h>
#define MCP23017_IODIRA 0x00
#define MCP23017_IODIRB 0x01
#define MCP23017_IPOLA 0x02
#define MCP23017_IPOLB 0x03
#define MCP23017_GPINTENA 0x04
#define MCP23017_GPINTENB 0x05
#define MCP23017_DEFVALA 0x06
#define MCP23017_DEFVALB 0x07
#define MCP23017_INTCONA 0x08
#define MCP23017_INTCONB 0x09
#define MCP23017_IOCON 0x0a
#define MCP23017_GPPUA 0x0c
#define MCP23017_GPPUB 0x0d
#define MCP23017_INTFA 0x0e
#define MCP23017_INTFB 0x0f
#define MCP23017_INTCAPA 0x10
#define MCP23017_INTCAPB 0x11
#define MCP23017_GPIOA 0x12
#define MCP23017_GPIOB 0x13
#define MCP23017_OLATA 0x14
#define MCP23017_OLATB 0x15
//---------------------------------------------------------
// I2CバスにWriteアクセスする
//---------------------------------------------------------
//[Arguments]
// slave_addr : スレーブデバイスのアドレス
// device_addr : デバイス内部のレジスタアドレス
// data : 書き込むデータ
//[Returns]
// なし
//--------------- https://ss1.xrea.com/tmct.s1009.xrea.com/
void i2cWriteByte(int slave_addr, int device_addr, int data) {
Wire.beginTransmission(slave_addr);
Wire.write(device_addr);
Wire.write(data);
Wire.endTransmission();
}
//---------------------------------------------------------
// I2CバスにReadアクセスする
//---------------------------------------------------------
//[Arguments]
// slave_addr : スレーブデバイスのアドレス
// device_addr : デバイス内部のレジスタアドレス
//[Returns]
// unsigned char : 読みだしたデータ
//--------------- https://ss1.xrea.com/tmct.s1009.xrea.com/
unsigned char i2cReadByte(int slave_addr, int device_addr) {
unsigned char r;
Wire.beginTransmission(slave_addr);
Wire.write(device_addr);
Wire.endTransmission();
Wire.requestFrom(slave_addr, 1);
if (Wire.available()) r = Wire.read();
Wire.endTransmission();
return(r);
}
たとえば上記に挙げた回路で使う場合は以下のように使います。
unsigned char work;
//---------------------------------------------------------
// 初期化
//---------------------------------------------------------
// 出力初期値を設定する
// 各ビット 1 = High, 0 = Low になります
// 入力に設定するビットは何でも構いませんが初期値の0に設定します
i2cWriteByte(0x20, MCP23017_OLATA, 0x00); // GPA[7:0] = 'LLLL_LLLL'
i2cWriteByte(0x20, MCP23017_OLATB, 0x00); // GPB[7:0] = 'LLLL_LLLL'
// 入力論理を設定する
// 各ビット 1 = 負論理, 0 = 正論理 になります
// 出力に設定するビットは何でも構いませんが初期値の0に設定します
i2cWriteByte(0x20, MCP23017_IPOLA, 0x00); // GPA[7:0] = 全ビット正論理
i2cWriteByte(0x20, MCP23017_IPOLB, 0x00); // GPB[7:0] = 全ビット正論理
// 入力PullUpを設定する
// 各ビット 1 = PullUp ON, 0 = PullUp OFF になります
// 出力に設定するビットは何でも構いませんが初期値の0に設定します
i2cWriteByte(0x20, MCP23017_GPPUA, 0xf0); // GPA[7:4] = 内部PullUp ON, GPA[3:0] = 内部PullUp OFF
i2cWriteByte(0x20, MCP23017_GPPUB, 0xff); // GPB[7:0] = 内部PullUp ON
// 入出力方向を設定する
// 各ビット 1 = 入力, 0 = 出力 になります
// 出力ポートの場合、出力に設定した瞬間に出力初期値で設定した論理レベルが出力されます
i2cWriteByte(0x20, MCP23017_IODIRA, 0xfc); // GPA[7:2] = 入力, GPA[1:0] = 出力
i2cWriteByte(0x20, MCP23017_IODIRB, 0xff); // GPB[7:0] = 全ビット入力
//---------------------------------------------------------
// LEDを点灯
//---------------------------------------------------------
// GPA0に繋いだLEDを点灯
i2cWriteByte(0x20, MCP23017_OLATA, 0x01); // GPA[7:0] = 'LLLL_LLLH'
delay(300); // 300ms wait
// GPA1に繋いだLEDを点灯
i2cWriteByte(0x20, MCP23017_OLATA, 0x02); // GPA[7:0] = 'LLLL_LLHL'
delay(300); // 300ms wait
// GPA0,GPA1に繋いだLEDを点灯
i2cWriteByte(0x20, MCP23017_OLATA, 0x03); // GPA[7:0] = 'LLLL_LLHH'
delay(300); // 300ms wait
// LEDを消灯
i2cWriteByte(0x20, MCP23017_OLATA, 0x00); // GPA[7:0] = 'LLLL_LLLL'
delay(300); // 300ms wait
//---------------------------------------------------------
// スイッチの状態を取得
//---------------------------------------------------------
work = i2cReadByte(0x20, MCP23017_GPIOA); // GPA[7:0]の状態を取得
if ((work & 0x10) == 0x10) {
// GPA4に繋いだスイッチが開放されている
}
else {
// GPA4に繋いだスイッチが押されている
}
if ((work & 0x20) == 0x20) {
// GPA5に繋いだスイッチが開放されている
}
else {
// GPA5に繋いだスイッチが押されている
}
if ((work & 0x40) == 0x40) {
// GPA6に繋いだスイッチが開放されている
}
else {
// GPA6に繋いだスイッチが押されている
}
if ((work & 0x80) == 0x80) {
// GPA7に繋いだスイッチが開放されている
}
else {
// GPA7に繋いだスイッチが押されている
}
未使用の端子は以下のいずれかの処理を行うようにしましょう。
最も推奨されるのは1.です。なぜかというと、電源ON〜プログラムでMCP23017を初期化するまでの間、GPIOの状態が不定値となるためです。
3.は制御がバグったときに大電流が流れてICが壊れてしまう可能性があるのでお勧めしません。
- PullDownして入力端子にする。
※上記の例ではこの処理を行っています。 - 内部PullUpを有効にして入力端子にする。
- VSSかVDDに直結する。
(※非推奨)
もうちょっと詳しく知る
上記の例で何をやっているのか理解するためにも、もう少し詳しく解説していきます。
初期化する
I/Oエキスパンダはまず初期化しないと期待通りに動いてくれません。以下のような順で初期化します。
- 出力初期値を設定する
GPIOレジスタまたはOLATレジスタに初期値を書き込みます。
入力に設定するビットの初期値は何でも構いません。 - 入力論理を設定する
IPOLレジスタに入力ポートの正論理・負論理の設定をします。
出力に設定するビットの値は何でも構いませんが0(正論理)に設定します。 - 入力PullUpを設定する
GPPUレジスタに入力ポートの内蔵PullUpの設定をします。
出力またはPullUpしないビットの値は0(PullUpなし)に設定します。 - 入出力方向を設定する
IODIRレジスタに入出力の方向を設定します。
ネット上のコードではいきなり入出力設定をしているケースがほとんどですが、その場合だと外部に接続している負論理の回路が誤動作することがあるためお勧めしません。上記のようにきちんと初期値を設定してから入出力を切り替えるようにしましょう。
初期化のコード例を以下に示します。I2Cスレーブアドレスは[A2][A1][A0]=‘000’(‘010_0000’=0x20)を想定しています。もちろん設定値などは回路に応じて変更が必要です。
// 出力初期値を設定する
// 各ビット 1 = High, 0 = Low になります
// 入力に設定するビットは何でも構いませんが初期値の0に設定します
i2cWriteByte(0x20, MCP23017_OLATA, 0x00); // GPA[7:0] = 'LLLL_LLLL'
i2cWriteByte(0x20, MCP23017_OLATB, 0x00); // GPB[7:0] = 'LLLL_LLLL'
// 入力論理を設定する
// 各ビット 1 = 負論理, 0 = 正論理 になります
// 出力に設定するビットは何でも構いませんが初期値の0に設定します
i2cWriteByte(0x20, MCP23017_IPOLA, 0x00); // GPA[7:0] = 全ビット正論理
i2cWriteByte(0x20, MCP23017_IPOLB, 0x00); // GPB[7:0] = 全ビット正論理
// 入力PullUpを設定する
// 各ビット 1 = PullUp ON, 0 = PullUp OFF になります
// 出力に設定するビットは何でも構いませんが初期値の0に設定します
i2cWriteByte(0x20, MCP23017_GPPUA, 0x00); // GPA[7:0] = 内部PullUp OFF
i2cWriteByte(0x20, MCP23017_GPPUB, 0x0f); // GPB[7:4] = 内部PullUp OFF, GPB[3:0] = 内部PullUp ON
// 入出力方向を設定する
// 各ビット 1 = 入力, 0 = 出力 になります
// 出力ポートの場合、出力に設定した瞬間に出力初期値で設定した論理レベルが出力されます
i2cWriteByte(0x20, MCP23017_IODIRA, 0x00); // GPA[7:0] = 全ビット出力
i2cWriteByte(0x20, MCP23017_IODIRB, 0xff); // GPB[7:0] = 全ビット入力
スレーブアドレスはA2・A1・A0の端子状態によって以下のように変動します。(1=VDDに接続,0=GNDに接続)
| A2 | A1 | A0 | スレーブアドレス |
|---|---|---|---|
| 0 | 0 | 0 | ‘010_0000’=0x20 |
| 0 | 0 | 1 | ‘010_0001’=0x21 |
| 0 | 1 | 0 | ‘010_0010’=0x22 |
| 0 | 1 | 1 | ‘010_0011’=0x23 |
| 1 | 0 | 0 | ‘010_0100’=0x24 |
| 1 | 0 | 1 | ‘010_0101’=0x25 |
| 1 | 1 | 0 | ‘010_0110’=0x26 |
| 1 | 1 | 1 | ‘010_0111’=0x27 |
入力ポートの状態を取得する
入力ポートの状態はGPIOレジスタを読むことで取得することができます。以下に例を示します。
int a, b;
a = (int)i2cReadByte(0x20, MCP23017_GPIOA); // GPA[7:0]の状態を取得
b = (int)i2cReadByte(0x20, MCP23017_GPIOB); // GPB[7:0]の状態を取得
ICの仕様上、ポート単位(8ビット単位)でないと状態を取得することができません。1ビットずつ判定したい場合は以下のようにビット演算でビットマスクして判定することができます。
// 0x40(0100_0000)と取得値aをAnd演算し、結果が0x40ならGPA[6]='1'です
if ((a & 0x40) == 0x40) {
// GPA[6]='1'の場合
}
else {
// GPA[6]='0'の場合
}
// 0x02(0000_0010)と取得値bをAnd演算し、結果が0x02ならGPB[1]='1'です
if ((b & 0x02) == 0x02) {
// GPB[1]='1'の場合
}
else {
// GPB[1]='0'の場合
}
複数ビットをまとめて判定したい場合…たとえばGPA[5:3]にDipスイッチを接続して設定状態によって処理を分岐したい場合は以下のように記述することができます。
// 0x38(0011_1000)と取得値aをAnd演算し、結果によって処理を分岐します
switch (a & 0x38) {
case 0x00: // GPA[5:3] = '000'
break;
case 0x08: // GPA[5:3] = '001'
break;
case 0x10: // GPA[5:3] = '010'
break;
case 0x18: // GPA[5:3] = '011'
break;
case 0x20: // GPA[5:3] = '100'
break;
case 0x28: // GPA[5:3] = '101'
break;
case 0x30: // GPA[5:3] = '110'
break;
case 0x38: // GPA[5:3] = '111'
break;
default: // 上記のどれでもない場合(ありえないが)
break;
}
なお、GPIOレジスタから読まれる値はIPOLレジスタ設定(正負論理設定)の影響を受けるので負論理に設定している場合は気をつけましょう。
出力ポートの状態を設定する
出力ポートの状態はGPIOレジスタかOLATレジスタに値を書き込むことで設定することができます。どちらに書き込んでも同じです。以下に例を示します。
入力に設定しているビットには何を書き込んでも影響しません。
i2cWriteByte(0x20, MCP23017_OLATA, 0x12); // GPA[7:0]の出力状態を'0001_0010'(LLLH_LLHL)に設定する
i2cWriteByte(0x20, MCP23017_OLATB, 0x34); // GPB[7:0]の出力状態を'0011_0100'(LLHH_LHLL)に設定する
ICの仕様上、ポート単位(8ビット単位)でないと状態を設定することができません。1ビットずつ設定したい場合は以下のようにいったん現在の設定値を読み、ビット演算でビットマスク・ビット加算した結果を書き戻すことで設定します。
int a, b;
a = (int)i2cReadByte(0x20, MCP23017_OLATA); // GPA[7:0]の設定状態を取得
// たとえばGPA[5]だけを0に設定したい場合、
// 5ビット目だけが0の値'1101_1111'=0xdfとAnd演算することで該当ビットだけ0にします
a &= 0xdf;
// 演算した値を書き戻して設定します
i2cWriteByte(0x20, MCP23017_OLATA, (int)a);
b = (int)i2cReadByte(0x20, MCP23017_OLATB); // GPB[7:0]の設定状態を取得
// たとえばGPB[7]だけを1に設定したい場合、
// 7ビット目だけが1の値'1000_0000'=0x80とOr演算することで該当ビットだけ1にします
b |= 0x80;
// 演算した値を書き戻して設定します
i2cWriteByte(0x20, MCP23017_OLATB, (int)b);
主なハードウェア仕様と注意事項
主なハードウェア仕様と注意事項を以下に載せます。
スイッチやLEDを繋ぐ程度ならあまり問題になりませんが、そもそも作ろうとしている物に使えない場合もあるのでうまく動かない場合は下記も参照してみてください。
| 項目 | 仕様 |
|---|---|
| 動作電源* | 1.8V〜5.5V (-40〜85℃環境) |
| I/O本数 | 16本 ※入出力の方向はプログラムから自由に変更できます。 ※電源ONおよびリセット直後は全部入力端子・PullUp OFFになります。 ※入力端子はプログラムの設定でPullUpすることができます。 |
| 入力許容電圧(全端子)* | -0.6V〜電源電圧+0.6V ※3.3V電源で動作させているとき5V系の信号を入力できません。I2Cバスも同様。 ※電源OFF時も信号がかかってはいけません。 |
| 出力電流* | Highレベル・Lowレベルとも25mA(±0.025A) ※ただし全ピンの合計が125mAを超えてはなりません。 |
| Highレベルとして認識する最低電圧 | 電源電圧の80% 5.0V動作時: 4.0V 3.3V動作時: 2.6V ※5V動作時に3.3V系のI2Cバスに接続した場合、動作しません。 ※3.3V動作時に5V系のI2Cバスに接続できません。 |
| Lowレベルとして認識する最高電圧 | 電源電圧の20% 5.0V動作時: 1.0V 3.3V動作時: 0.7V |
| I2C仕様 | - 100KHz - 400KHz - 1.7MHz |
| スレーブアドレス | [A2][A1][A0]端子の状態により8種類から選択可能 ※上位4bitは0100固定、下位3bitを[A2][A1][A0]で設定できます。 |
| 割り込み出力 | 可能 ※Push-Pull出力(H/L出力) or オープンドレイン出力、および出力論理をプログラムから選択できます。 ※電源ONおよびリセット直後は負論理Push-Pull出力に初期化されます。 |
* この値を超えて使用した場合、ICが焼損する可能性があります。何事もなく正常に動作するように見えることもありますが、IC内部にダメージが蓄積されていくためいずれ正常に動作しなくなります。
とくに気をつけないといけないのが 入力許容電圧 と 出力電流 です。接続先と電源系統を合わせないといけませんし、大電流を消費するもの(たとえばモーター・リレー・照明用LEDなど)を直結することはできません。
各ピンの仕様
一応各ピンの仕様も書いておきます。ピン番号はDIP/SOP/TSSOPパッケージのものです。
| ピン名称 | ピン番号 | 入出力 | 機能 |
|---|---|---|---|
| GPB0 | 1 | 入出力 | GPIO端子Bです。 入出力方向および入力時のPullUp状態はプログラムから自由に変更できます。 信号状態の変化に伴ってINTB端子に割り込みトリガ信号を出力するよう設定できます。 電源電圧を超える信号は入力できません。 |
| GPB1 | 2 | 入出力 | (同上) |
| GPB2 | 3 | 入出力 | (同上) |
| GPB3 | 4 | 入出力 | (同上) |
| GPB4 | 5 | 入出力 | (同上) |
| GPB5 | 6 | 入出力 | (同上) |
| GPB6 | 7 | 入出力 | (同上) |
| GPB7 | 8 | 入出力 | (同上) |
| VDD | 9 | 電源入力 | 電源入力です。 |
| VSS | 10 | GND | GNDを接続してください。 |
| (NC) | 11 | - | 未接続端子です。 GNDに接続することをお勧めします。 |
| SCK | 12 | 入力 | I2Cクロック入力端子です。必ずVDDでPullUpしてください。 電源電圧を超える信号は入力できません。 |
| SDA | 13 | 入出力 | I2Cデータ入出力端子です。必ずVDDでPullUpしてください。 電源電圧を超える信号は入力できません。 |
| (NC) | 14 | - | 未接続端子です。 GNDに接続することをお勧めします。 |
| A0 | 15 | 入力 | I2Cスレーブアドレス設定端子(0ビット目)です。 必ずVDDまたはVSSに接続してください。未接続のままだと誤動作します。 |
| A1 | 16 | 入力 | I2Cスレーブアドレス設定端子(1ビット目)です。 必ずVDDまたはVSSに接続してください。未接続のままだと誤動作します。 |
| A2 | 17 | 入力 | I2Cスレーブアドレス設定端子(2ビット目)です。 必ずVDDまたはVSSに接続してください。未接続のままだと誤動作します。 |
| ~RESET | 18 | 入力 | 負論理のリセット入力です。 必ずVDDに接続するかPullUpしてください。未接続のままだと動作しません。 |
| INTB | 19 | 出力 | GPIO端子Bの割り込み信号出力です。 プログラムからPush-Pull出力(H/L出力) or オープンドレイン出力、および出力論理(正論理・負論理)を選択できます。 |
| INTA | 20 | 出力 | GPIO端子Aの割り込み信号出力です。 プログラムからPush-Pull出力(H/L出力) or オープンドレイン出力、および出力論理(正論理・負論理)を選択できます。 |
| GPA0 | 21 | 入出力 | GPIO端子Aです。 入出力方向および入力時のPullUp状態はプログラムから自由に変更できます。 信号状態の変化に伴ってINTA端子に割り込みトリガ信号を出力するよう設定できます。 電源電圧を超える信号は入力できません。 |
| GPA1 | 22 | 入出力 | (同上) |
| GPA2 | 23 | 入出力 | (同上) |
| GPA3 | 24 | 入出力 | (同上) |
| GPA4 | 25 | 入出力 | (同上) |
| GPA5 | 26 | 入出力 | (同上) |
| GPA6 | 27 | 入出力 | (同上) |
| GPA7 | 28 | 入出力 | (同上) |
マニアックな使い方
ここまでは単にGPIOを増やすための機能の説明でした。ここからは少々マニアックな機能の紹介です。
割り込みを使ってみる
MCP23017は端子の状態が変化した瞬間、その変化を示す信号(割り込み信号)を出力することができます。この機能を利用すると、ソフト的に変化を監視しなくてもハードが勝手に比較して結果を通知してくれるのでプログラム側の負担が少なくなります。
このICでは以下のいずれかの変化に反応させることができます。ただし反応するのは入力に設定したビットのみです。自分が出力する信号の変化には反応しないので注意してください。
- あらかじめ設定したデフォルト値と異なる場合
- 現在の値から変化した場合
どちらの条件で反応させるかは、INTCONレジスタの各ビットに設定する値で変更することができます。INTCONレジスタのビットの値が1なら設定したデフォルト値と比較、0なら現在の値と比較されます。
以下に設定コード例を載せます。以下の例ではGPAの場合はINTA、GPBの場合はINTBがLowになります。なお、割り込み禁止・許可や割り込み条件は各ポート・ビットごとに設定することができますが、以下の例では全ポートを「デフォルト値と比較して異なる場合」に設定しています。
// 割り込み出力設定など
i2cWriteByte(0x20, MCP23017_IOCON, 0x00); // INTA,INTB独立出力・Push-Pull・負論理
// 設定変更するのでいったん割り込み禁止
i2cWriteByte(0x20, MCP23017_GPINTENA, 0x00); // GPA割り込み禁止
i2cWriteByte(0x20, MCP23017_GPINTENB, 0x00); // GPB割り込み禁止
// デフォルト値の設定
i2cWriteByte(0x20, MCP23017_DEFVALA, 0x00); // GPAデフォルト値
i2cWriteByte(0x20, MCP23017_DEFVALB, 0x00); // GPBデフォルト値
// 割り込み発生要因設定
i2cWriteByte(0x20, MCP23017_INTCONA, 0xff); // GPA割り込み発生要因
i2cWriteByte(0x20, MCP23017_INTCONB, 0xff); // GPB割り込み発生要因
// 割り込み許可
i2cWriteByte(0x20, MCP23017_GPINTENA, 0xff); // GPAの割り込み許可
i2cWriteByte(0x20, MCP23017_GPINTENB, 0xff); // GPBの割り込み許可
これでポート状態に変化が生じるとINTAかINTB出力が変化するのですが、具体的にどのビットの状態がどのように変化したかはINTF・INTCAPレジスタの値を読んで調べる必要があります。
unsigned char fa, fb;
unsigned char ca, cb;
// 割り込み発生要因取得
// 割り込み発生要因となったビットのみ1が立ちます
// 1が立っているビットを調べることで割り込みが発生した要因を調べることができます
// このレジスタは読み取り専用です
fa = i2cReadByte(0x20, MCP23017_INTFA); // GPA割り込みフラグ
fb = i2cReadByte(0x20, MCP23017_INTFB); // GPB割り込みフラグ
// 割り込み発生時の状態取得
// 割り込みが発生したときの端子状態がキャプチャされます
// このレジスタをReadするとINTF・INTCAPの両方がクリアされます
ca = i2cReadByte(0x20, MCP23017_INTCAPA); // GPAキャプチャ
cb = i2cReadByte(0x20, MCP23017_INTCAPB); // GPBキャプチャ
色々設定をいじくる
IOCONレジスタで色々設定をいじくることができます。主にINTピンの出力信号仕様を変更したい場合などに使用します。
バンク設定を変更するとIOCONレジスタを含めてレジスタのアドレスマッピングが変わってしまうので注意してください。(イマサラですがこの記事はすべてデフォルト値のバンク0を前提として記載してきました。)
IOCONレジスタのビットの意味を以下に示します。★印はデフォルト設定です。
| bit | 意味 |
|---|---|
| 7 | バンク設定 : レジスタの並び方を設定します。 0 = ★バンク0のレジスタ並び(レジスタ機能でまとめられた並び)になります。 1 = バンク1のレジスタ並び(ポートごとにまとめられた並び)になります。 ※この設定を行った直後、IOCONレジスタのアドレスも変更されるので注意してください。 |
| 6 | INT端子ミラー設定 : GPA/GPBの割り込み端子を1つにまとめるか設定します。 0 = ★GPAの割り込みはINTA出力、GPBの割り込みはINTB出力に紐付けられます。 1 = INTAとINTBはIC内部で接続され、両方とも同じ信号が出力されます。 |
| 5 | バーストアクセス設定 : レジスタに連続アクセスする際の挙動を設定します。 0 = ★レジスタアクセス後、IC内部のアドレスポインタを自動で+1します。 1 = レジスタアクセス後もIC内部のアドレスポインタをそのまま保持します。 ※この記事のアクセス方法ではこの設定は無意味です。 |
| 4 | スルーレート制御設定 : SDA信号をドライブする際のドライブ強度を設定します。 0 = ★ドライブ強度を適切に制御します。 1 = ドライブ強度を制限しません。 ※ドライブ強度を制限しない場合、SDA信号の立ち下がりエッジが急しゅんになりノイズが生じる可能性があります。 |
| 3 | MCP23017では未使用 値を書き込む際は常に'0’を書き込んでください。 |
| 2 | INT出力ドライバ設定 : INT端子の出力仕様を設定します。 0 = ★Push-Pull出力(H/L出力) 1 = オープンドレイン出力 |
| 1 | INT出力論理設定 : INT端子の出力論理を設定します。 0 = ★負論理 1 = 正論理 |
| 0 | 未使用(常に0) 値を書き込む際は常に'0’を書き込んでください。 |
まとめ
MCP23017の使い方について記載しました。
レジスタの数が多いので取っつきにくいですが、安価な割にはそれなりに高性能なICです。I/Oポートが足りなくなった!!という場合はこの記事を参考にMCP23017を使ってみてください。