ℹ️ 関連記事
ESP32でMCP23017を使いたい方や、MCP23017の詳細な仕様・応用例を知りたい方は、こちらの記事もご覧ください:
👉 【2026年版】MCP23017の使い方|I2Cアドレス設定とESP32/Arduino実装例
(I2Cアドレス設定、複数デバイス接続、トラブルシューティングなど詳しく解説しています)
はじめに
Arduinoを使っていて、誰もが一度はぶつかる壁。それが 「GPIO不足」 です。
「ここにLCDをつけて、ここにセンサーを5個並べて、さらにステータスLEDを…」 そう考えた瞬間、Arduino Unoのピンソケットを見つめて絶望したことはありませんか?Arduino Uno R4はCortex-M4搭載で処理能力は爆上がりしましたが、物理的な足の数(ピン数)は増えていません。
今回は、そんなピン不足の悩みをたった2本の信号線で解決するI/Oエキスパンダの決定版、「MCP23017」 を徹底解説します。
さらに、単にLチカするだけでは面白くないので、「全ポート連動ループバックテスト」 という、ちょっと通(ツウ)な実験を行い、入力と出力の仕組みを完全に理解することを目指します。
🔍 1. 今回の主役:MCP23017とは何か?
MCP23017は、Microchip社が製造する 16-bit I/O Expander です。電子工作界隈では「定番中の定番」ICですが、その実力は侮れません。
I2Cで命令を受け取り、8本×2ポート(Port A / Port B)=16本 のGPIOを提供する——役割を図にすると、このICの立ち位置は非常にシンプルです。
GPIO 8本"] MCP --> PB["Port B(GPB0〜GPB7)
GPIO 8本"] MCP -. "INTA / INTB(割り込み通知)" .-> ARD
ピン配置(DIP-28)
ブレッドボードに直挿しできる DIP-28(SPDIP)パッケージのピン配置です。Microchip 公式データシート(DS20001952C・Table 2-1)に基づいています。
| ピン | 名称 | 機能 |
|---|---|---|
| 1〜8 | GPB0〜GPB7 | Port B の入出力ピン(状態変化割り込み・内部プルアップ設定可) |
| 9 | VDD | 電源 |
| 10 | VSS | グランド |
| 11 | NC | 未接続(SPI版 MCP23S17 では CS) |
| 12 | SCK(SCL) | I2Cクロック入力 |
| 13 | SDA | I2Cデータ |
| 14 | NC | 未接続(SPI版 MCP23S17 では SO) |
| 15〜17 | A0 / A1 / A2 | I2Cアドレス設定ピン(必ず High か Low に固定する) |
| 18 | RESET | ハードウェアリセット(Lowアクティブ・必ず外部で電位を固定) |
| 19 | INTB | Port B の割り込み出力 |
| 20 | INTA | Port A の割り込み出力 |
| 21〜28 | GPA0〜GPA7 | Port A の入出力ピン(状態変化割り込み・内部プルアップ設定可) |
MCP23017 には SPI接続の兄弟チップ「MCP23S17」 が存在します(データシートも共通)。ピン配置はほぼ同じで、I2C版では未接続の11番・14番ピンが、SPI版では CS / SO として使われます。型番の「S」が SPI の目印です。
💪 2. なぜ MCP23017 が選ばれるのか?
「ピンを増やすだけなら、安いシフトレジスタ(74HC595など)でいいのでは?」 そう思う方もいるかもしれません。しかし、MCP23017が電子工作から産業用途まで幅広く愛されるのには、明確な技術的理由があります。
まず、代表的なライバルである 74HC595(シフトレジスタ)と並べてみると、キャラクターの違いがはっきりします。
| 項目 | MCP23017 | 74HC595 |
|---|---|---|
| 通信方式 | I2C(SDA/SCL の2本) | シフトレジスタ(DATA/CLK/LATCH の3本) |
| 入出力 | 16本・ピンごとに入出力を設定可 | 出力専用・8本 |
| 割り込み | あり(INTA/INTB) | なし |
| 内部プルアップ | あり | なし(外付けが必要) |
| 増設方法 | I2Cアドレスで最大8個(=128ピン) | カスケード接続で無制限 |
| 得意分野 | スイッチ入力とLED出力の混在・状態監視 | 大量のLEDを高速に振り回す出力特化 |
「入力もほしい・状態変化を知りたい」なら MCP23017、「とにかく出力だけ増やしたい」なら 74HC595。ここからは前者の実力を、5つの観点で掘り下げます。
① 「入力」と「出力」を自由に切り替え可能
シフトレジスタの多くは「出力専用」または「入力専用」ですが、MCP23017はArduinoのGPIOと同様、ピンごとに INPUT / OUTPUT を設定可能です。
「半分を入力スイッチ、半分をLED制御」といった柔軟な使い方が、たった一つのチップで完結します。
② 割り込み(Interrupt)機能が優秀
これが最大の強みです。 通常、スイッチの状態を監視するには、Arduino側で常に「押された?」と確認し続ける(ポーリング)必要がありますが、これはCPUリソースの無駄遣いです。 MCP23017は、入力ピンの状態が変化した瞬間に 「INTピン」から信号を出してArduinoに知らせる機能 を持っています。これにより、Arduinoは普段は別の計算をしておき、スイッチが押された時だけ反応する、といった高度な制御が可能になります。
③ 内部プルアップ(約100kΩ相当)を内蔵
地味ですが、回路を組む際に最強の機能です。 スイッチを接続する場合、通常は外付けのプルアップ抵抗が必要ですが、MCP23017はコマンド一つで内部のプルアップをONにできます。データシート上は抵抗値ではなくプルアップ電流で規定されており(typ 75µA・VDD=5V時)、俗に言う「約100kΩ相当」の強さです。 これにより、16個のスイッチをつける際、16本の抵抗パーツとハンダ付け作業が不要になります。配線が劇的にスッキリします。
④ 5V完全対応 & ハイパワー駆動
最近のI2Cデバイスは3.3V専用が増えていますが、MCP23017は 1.8V〜5.5V の広い電圧範囲で動作します(I2C を 400kHz モードで使う場合の規定範囲は 2.7V〜5.5V)。つまり、Arduino Uno R4(5V系)とレベル変換なしで直結可能です。 また、出力電流は 1ピンあたり25mA。これはArduinoのGPIOと同等の強さで、LED程度ならトランジスタなしで直接駆動できます。
「1ピン25mAなら、16本で400mA流せる?」と考えるとチップが燃えます。 データシートによると、VSSピン(GND)に流せる電流の合計は 最大150mA です(VDD側に流し込める合計は最大125mA)。 LEDを16個全点灯させる場合は、1個あたり数mA(抵抗を大きめにする)に抑える設計が必要です。ここがプロとアマチュアの分かれ道です。
⑤ アドレス設定で無限の拡張性
I2Cバス上に最大8個まで接続可能です(アドレス 0x20 〜 0x27)。
理論上、16ピン × 8個 = 128ピン まで増設可能。Arduino Megaをも凌駕するIO数を、たった2本の線で実現できます。
🧪 3. 実験の構成:ただのLチカではない「ループバックテスト」
今回は、技術的な信頼性を担保するためのテスト手法を採用します。 MCP23017の16本のピンを以下のように分割します。
- Port A (8本): 出力用(LEDを点灯)
- Port B (8本): 入力用(信号を受け取る)
そして、「Arduino本体から出した信号をMCPのPort Bで受け取り、その内容を即座にPort AにコピーしてLEDを光らせる」 という構成にします。
信号の流れを図にすると、こうなります。
8bitパターンを出力"] SER["シリアルモニタ
送信値と受信値を突き合わせ"] end subgraph MCP["MCP23017"] PB["Port B(GPB0〜GPB7)
入力として受信"] PA["Port A(GPA0〜GPA7)
出力としてコピー"] end D -- "ジャンパ線8本" --> PB PB -- "readGPIOAB() で一括読み取り" --> PA PA --> LED["LED×8
受信パターンを表示"] PB -. "受信値をシリアルで報告" .-> SER
なぜこんなことをするのか?
通常、入力のテストにはスイッチを使いますが、チャタリング(接点のブレ)や配線ミスが混入しがちです。 信号源として「正確無比なArduinoの出力」を使うことで、「MCP23017が正しく信号を読み取れているか」 と 「正しく出力できているか」 を同時に、かつ高速に検証できるのです。これは実際の組み込み開発でも行われる「ループバック(折り返し)テスト」の応用です。
🔌 4. ハードウェア接続と「アドレス設定」の掟
ブレッドボード上での配線は以下の通りです。Arduino Uno R4はI2C周りが強化されているので接続はスムーズです。
基本配線
| Arduino Uno R4 | MCP23017 (DIP28) | 役割 |
|---|---|---|
| 5V | VDD (Pin 9) | 電源 |
| GND | VSS (Pin 10) | グランド |
| SDA | SDA (Pin 13) | I2Cデータ |
| SCL | SCL (Pin 12) | I2Cクロック |
| 5V | RESET (Pin 18) | リセット解除(重要!) |
MCP23017の RESETピンは「Lowアクティブ」 です。つまり、ここを繋ぎ忘れて電圧が不定になると、勝手にリセットがかかり動作しません。必ず5Vに接続(プルアップ)してください。データシートにも「必ず外部で電位を固定すること(Must be externally biased)」と明記されています。アドレスピン(A0〜A2)も同様です。
I2Cアドレスの設定 (Pin 15, 16, 17)
I2Cデバイスは「アドレス」で識別されます。MCP23017は A0, A1, A2 ピンのHigh/Lowの組み合わせでアドレスが決まります。
データシート上、アドレスの上位4ビットは 0100 に固定されており、下位3ビットが A2/A1/A0 ピンの状態そのものです。つまりアドレスは 0100 + A2A1A0 の7ビット構成で、組み合わせは次の8通りになります。
| A2 | A1 | A0 | I2Cアドレス |
|---|---|---|---|
| GND | GND | GND | 0x20(デフォルト) |
| GND | GND | 5V | 0x21 |
| GND | 5V | GND | 0x22 |
| GND | 5V | 5V | 0x23 |
| 5V | GND | GND | 0x24 |
| 5V | GND | 5V | 0x25 |
| 5V | 5V | GND | 0x26 |
| 5V | 5V | 5V | 0x27 |
今回はシンプルに全てGNDに接続します。
- A0, A1, A2 → 全て GND
- アドレス:
0x20(デフォルト)
アドレスが8通りあるということは、同じI2Cバスに最大8個までぶら下げられるということです。バスを増やさず、配線2本のままピンだけが増えていきます。
0x20"] BUS --- M2["MCP23017 その2
0x21"] BUS --- M3["…"] BUS --- M8["MCP23017 その8
0x27"] M8 --> SUM["合計 16本 × 8個 = 128ピン"]
入出力ループバック配線
ここが今回のキモです。
- Arduino D0〜D7 を MCP GPB0〜GPB7 (Pin 1〜8) に接続。
- MCP GPA0〜GPA7 (Pin 21〜28) にLED(と抵抗)を接続。
実際の組み立て:シールド基板を使った場合
今回の実験では、専用設計した MCP23017搭載Arduinoシールド を使用しています。
Arduino Uno R4に装着したMCP23017シールド
ブレッドボードでの配線作業は学習には最適ですが、何度も実験を繰り返す場合や、作品として仕上げる際には配線ミスや接触不良のリスクがつきまといます。
このシールドは、Arduino Uno R4/R3に直接装着するだけで以下が完結します:
- MCP23017とArduinoのI2C接続 (SDA/SCL/電源/GND)
- 16個のLED (Port A/Bの状態を即座に視覚確認)
- 16個の入力用DIPスイッチ (Port B入力のテスト用)
- アドレス設定用ジャンパピン (複数個接続時のアドレス切り替え)
配線不要でワンタッチ装着できるため、「回路を組む時間」ではなく「プログラムを書く時間」に集中できるのが最大のメリットです。
💡 このシールド基板について
今回使用したシールドはこちらのショップページで購入可能です。回路図やサンプルコードも公開しているので、自作したい方もぜひご活用ください。
💻 5. ライブラリの使い方:基本から「一括制御」まで
今回は標準的な Adafruit MCP23017 Arduino Library を使用します。 このライブラリには、初心者向けの「Arduino互換モード」と、上級者向けの「高速ポート操作モード」の2つの顔があります。
① インストールと初期化
まずはインスタンスを作成し、setup() 内で初期化します。
#include <Adafruit_MCP23X17.h>
Adafruit_MCP23X17 mcp;
void setup() {
// I2Cアドレスを指定して開始(A0-A2がGNDなら0x20)
if (!mcp.begin_I2C(0x20)) {
Serial.println("Error.");
while (1);
}
}
② ピン番号の「罠」を理解する
ライブラリ内では、MCP23017の16本のピンは 0番〜15番 の通し番号で管理されます。データシートのピン名(GPA0など)との対応関係を頭に入れておく必要があります。
| ライブラリのピン番号 | ポート | 物理ピン(DIP-28) |
|---|---|---|
| 0 〜 7番 | Port A(GPA0 〜 GPA7) | 21 〜 28番 |
| 8 〜 15番 | Port B(GPB0 〜 GPB7) | 1 〜 8番 |
③ 基本的な使い方(1ピン操作)
Arduino標準の関数と同じ感覚で使えます。LEDを1つつけたり、スイッチを1つ読むだけならこれで十分です。
// 0番ピン(GPA0)を出力に設定
mcp.pinMode(0, OUTPUT);
// 8番ピン(GPB0)を入力(プルアップ有効)に設定
// ※これで外付け抵抗なしでスイッチを繋げます!便利!
mcp.pinMode(8, INPUT_PULLUP);
// 書き込みと読み込み
mcp.digitalWrite(0, HIGH);
int val = mcp.digitalRead(8);
④ 【重要】「一括読み書き」で高速化する
ここが今回のテストのキモです。
I2C通信は、CPU内部の処理に比べると非常に低速です。mcp.digitalWrite を16回ループさせると、通信が16回発生してしまい、動作が目に見えて遅くなることがあります。
そこで、16本分(または8本分)を1回の通信でまとめて処理する関数を使います。
まとめて読む: readGPIOAB()
16本のピン状態を uint16_t(16ビット整数)として一度に取得します。
// 1回の通信で全ピンの状態を取得
uint16_t allPins = mcp.readGPIOAB();
// ビット演算で特定のピンの状態を取り出す
// 例:10番ピン(GPB2)の状態を知りたい場合
int pin10_state = (allPins >> 10) & 1;
まとめて書く: writeGPIOAB(value)
16本のピン状態を一度に書き換えます。
// 0x00FF = 00000000 11111111
// 上位8ビット(Port B)は全てLOW、下位8ビット(Port A)は全てHIGHにする
mcp.writeGPIOAB(0x00FF);
今回のサンプルコードで readGPIOAB() を使用しているのは、Arduinoからの高速なパルス信号を取りこぼさずにキャッチするためです。
⑤ 割り込み(Interrupt)の設定
(今回は使いませんが)スイッチ入力を作る際に便利な割り込み設定も簡単です。
// 8番ピンが変化したら、MCPのINTピン信号を出す設定
mcp.setupInterrupts(true, false, LOW); // ミラーリング, オープンドレイン, 極性
mcp.attachInterrupt(8, CHANGE); // ピン8の変化を監視
ソースコード解説
このプログラムは、Arduino Uno R4のパワーとMCP23017の機能をフル活用します。
#include <Adafruit_MCP23X17.h>
Adafruit_MCP23X17 mcp;
// Arduino側の送信ピン定義 (Uno R4のD0-D7を使用)
// 配列にしておくことでループ処理をスマートにします
const int arduinoPins[] = {0, 1, 2, 3, 4, 5, 6, 7};
void setup() {
Serial.begin(115200);
while (!Serial); // R4特有:シリアル接続待ち
Serial.println(">>> MCP23017 System Test Initiated");
// --- 1. MCP23017の初期化 ---
// begin_I2C() 引数なしの場合はデフォルトの0x20アドレスを探しに行きます
if (!mcp.begin_I2C()) {
Serial.println("Fatal Error: MCP23017 not found. Check wiring!");
while (1);
}
// --- 2. ポートの方向定義 ---
// MCP23017のライブラリ仕様:
// 0-7番ピン = Port A (GPA0-7)
// 8-15番ピン = Port B (GPB0-7)
// Port A (0-7) -> LED駆動のため OUTPUT
for (int i = 0; i <= 7; i++) {
mcp.pinMode(i, OUTPUT);
}
// Port B (8-15) -> Arduinoからの信号監視のため INPUT
// 必要に応じて INPUT_PULLUP も使えますが、今回はArduino側が確実にドライブするのでINPUTでOK
for (int i = 8; i <= 15; i++) {
mcp.pinMode(i, INPUT);
}
// --- 3. Arduino側の準備 ---
// MCPへ信号を送るホスト側の設定
for (int i = 0; i < 8; i++) {
pinMode(arduinoPins[i], OUTPUT);
}
}
void loop() {
static uint8_t counter = 0; // 0〜255をカウントアップする変数
// --- Phase 1: Arduinoから信号を出力 ---
// ビット演算を使って、counterの数値を8本のピンに展開します
// 例: counterが 3 (00000011) の場合、Pin0とPin1だけHIGHにする
for (int i = 0; i < 8; i++) {
int state = (counter >> i) & 1;
digitalWrite(arduinoPins[i], state);
}
// 信号が安定するまでごくわずかに待機(高速なICですが念のため)
delay(10);
// --- Phase 2: MCP23017で入力状態を一括取得 ---
// ここが重要! digitalReadを8回やるより、レジスタをガバっと読むほうが圧倒的に速い。
// readGPIOAB() は16bit全てのピン状態を返します。
uint16_t allPins = mcp.readGPIOAB();
// 上位8bit (Port B) の部分だけを取り出します
uint8_t inputVal = (allPins >> 8) & 0xFF;
// デバッグ表示:正しく通信できているかシリアルモニタで確認
Serial.print("Sent: "); Serial.print(counter);
Serial.print(" \t Received: "); Serial.println(inputVal);
// --- Phase 3: 受け取った値をそのまま出力ポートへ ---
// 入力されたパターンと同じ形でLEDを光らせます。
// ここでもビット演算で分解してセットします。
for(int i=0; i<8; i++){
int val = (inputVal >> i) & 1;
mcp.digitalWrite(i, val); // Port A (0-7) に書き込み
}
counter++; // 次のパターンへ
delay(200); // 目視確認用ウェイト
}
技術的な見どころ
コードの中で注目してほしいのは mcp.readGPIOAB() です。
初心者は mcp.digitalRead(8)、mcp.digitalRead(9)… と書きがちですが、I2C通信はオーバーヘッド(通信の手間)が大きいため、1ピンずつ通信すると処理落ちの原因になります。
このように 「16ピン分まとめて読み書きする」 のが、IOエキスパンダを使いこなすプロの作法です。
🛠️ 6. Arduino Uno R4 でのトラブルシューティング
開発中に 「書き込みエラー (exit status 74)」 が出ることがあります。特に今回のようにGPIOに多くの配線をしていると、起動時の電流やノイズの影響で、Uno R4がUSBを見失うことがあります。
そんな時は、R4特有の 「救済儀式」 を行います。
- USBを繋いだままにする。
- Arduino上のリセットボタンを素早く「2回」カチカチッと押す。
- 「L」LEDがフワッフワッと明滅し始めたら成功(DFUモード)。
- この状態で書き込めば復活します。
R4ユーザーならこの技は必須スキルなので、覚えておきましょう。
✅ 7. まとめ
今回はMCP23017を使って、Arduino Uno R4の入出力を拡張し、さらにループバックテストでその動作を詳細に検証しました。
- 16本のピン増設はI2Cを使えば簡単。
- アドレス設定で複数個の同時使用も可能。
- ポート単位(8bit/16bit)での一括読み書きが高速化の鍵。
このICを使いこなせれば、巨大なLEDマトリクス、自作キーボード、あるいは複雑なロボット制御など、Arduino Uno R4の可能性は無限に広がります。
「ピンが足りない?」 これからは、そんな悩みとはサヨナラです。MCP23017で、あなたのArduinoを最強にアップグレードしてあげましょう!
📦 今回使用したMCP23017シールドについて
今回の実験で使用した MCP23017搭載Arduinoシールド は、配線不要でArduino Uno R4/R3に直接装着可能な専用基板です。
🎯 シールドの特徴
- ワンタッチ装着: ブレッドボード配線が不要で、初心者でもすぐに使えます
- 16個のLED搭載: 出力状態が一目で確認できるステータスLED付き
- プロトタイピングエリア: カスタマイズ用のユニバーサル基板エリアも完備
- 堅牢な設計: 実験だけでなく、作品制作にもそのまま使用可能
🛒 購入方法
このシールドは以下のサイトから購入できます:
📖 オープンソース
このシールドは オープンソースハードウェア として公開されています。
回路図やガーバーデータは以下のGitHubリポジトリから入手できます:
回路図・基板データ・サンプルコードを自由に利用できます
自作したい方や、カスタマイズしたい方はぜひご活用ください!
関連記事
そもそも I2C とは何か(通信規格の基礎):
- I2C・SPI・UART の違いと使い分け完全ガイド|配線本数・速度・距離で選ぶマイコン通信規格:本記事の MCP23017 が使う I2C を、SPI・UART と並べて比較しています。なぜプルアップ抵抗が必須なのか(オープンドレイン出力の構造)、アドレスで相手を呼び分ける仕組み、ACK による応答確認まで、本記事の前提になっている部分を解説しています。
同じMCP23017をESP32で使う:
- 【2026年版】MCP23017の使い方|I2Cアドレス設定とESP32/Arduino実装例:I2Cアドレスの決まり方とA0〜A2ピンによるアドレス変更を詳しく扱っています。同じICを8個並べたいときはこちら。
別方式でピンを増やす(SPI系):
- 74HC595 使い方完全ガイド【2026年版】|ピン配線とArduinoサンプルコード:I2CのMCP23017に対し、こちらはシフトレジスタで出力専用に拡張する方式。速度が要るなら74HC595、入出力混在ならMCP23017という使い分けの比較表もあります。
- 74HC595でドットマトリクスLED入門|Arduino 3ピンで64個のLEDを制御:拡張したピンで実際に何ができるかの実例。
ピンの状態を目で確認する:
- 【Arduino開発を加速】全ピンLED搭載デバッグシールドの設計と実装:拡張したピンが本当に意図どおり動いているか、本記事の自己診断テストと同じ発想で可視化するツールです。
Arduinoの基礎に戻る:
- 【Arduino入門】Arduino IDE 2 のインストールからLチカまで完全ガイド:開発環境の準備から。
シリーズ「IC実験室」のほかのIC:
- 加速度センサKXR94-2050で作るデジタル水平器【IC実験室 #2】:アナログ加速度センサをADCで読んで傾斜角を求める回です。
- NE555N(CMOS版555タイマーIC)の使い方とLチカ回路【IC実験室 #4】:マイコンを使わずにLチカする定番タイマーIC。
- 2SC1815で学ぶトランジスタの基本【IC実験室 #5】:ICの中身そのもの、トランジスタ1石から味わう回。