I/Oエキスパンダー
I/Oエキスパンダー(MCP23017-E/SP)の確認を行い I2C接続についての調査はとりあえず終了したいと思います。今回確認するI/Oエキスパンダーは、I2C接続により入出力端子を16点拡張できるICです。外部入力からデバイスアドレスを変更し、同じICを8個まで接続出来るので、最大128点の入出力端子を拡張することができます。
MCP23017-E/SP仕様
今回扱うI/Oエキスパンダーの仕様について簡単に確認してみます。下図の左側にMCP23017-E/SPのピンアサイン、右側に今回プログラム内で書き込み、読み込みを行うレジスタのアドレスと内容を示しています。このI/Oエキスパンダーは、Aポート(GPA0~7)とBポート(GPA0~7)を入出力端子として使用します。入力/出力は、IODIRA、IODIRB レジスタ設定によって変更します。入力設定時は、GPPUA、GPPUB レジスタにて内部プルアップ抵抗を利用できる様になり、外部プルアップ抵抗は不要となります。これらの設定後、出力設定時は、GPIOA、GPIOB に書き込み、入力設定時は、GPIOA、GPIOB の値を読み込みます。

デバイスアドレス設定
ICのA0,A1,A2は、I2Cバス上のデバイスアドレスを設定する外部端子です。各端子2通り(High or Low)の設定ができますので、全8通り(= 2 X 2 X 2 )の異なるアドレスを設定できます。デバイスアドレスは下記の様になっていて、灰色の固定部とA0,A1,A2の外部端子で設定する部分で構成されます。従って、0x20~0x27 までのデバイスアドレスを設定出来ます。
| 上位ビット | 下位ビット | |||||
| 0 | 1 | 0 | 0 | A2 | A1 | A0 |
接続図
接続図です。2つのI/Oエキスパンダーと既に使用したLCDモジュールを I2Cバス接続しています。また、それぞれのI/OエキスパンダーのAポートにトグルスィッチ、BポートにLEDを2個づつ接続しています。RESET(18番PIN)は、通常Highにし、リセット時Lowとのことですので、とりあえず5V接続しています。
下図より、I/Oエキスパンダーのデバイスアドレスは、0x20、0x21です。LCDは0x3E(固定)です。
実配線は下の写真の通りです。

動作状態
スィッチ切り換えで点灯LEDの切り換えとLCDにスィッチ状態を表示します。
プログラム
プログラムは以下の通りです。
#include <Wire.h>
// ★ IO-EXPANDER(MCP23017)定数・変数
#define DVC_MCP23017_00 0x20 // IO-EXPANDER_00 アドレス(外部端子設定)
#define DVC_MCP23017_01 0x21 // IO-EXPANDER_01 アドレス(外部端子設定)
uint8_t U1_PA, U2_PA =0; // Define variables to hold I/O port readings.
// LCD(AQM0802A) 定数・変数
#define DVC_AQM0802A 0x3e // LCD(AQM0802A)アドレス(固定)
byte contrast = 20; // コントラスト(0~63)
// ★ LCD(AQM0802A)初期化
void LCD_INIT(){
lcd_cmd(0b00111000); // function set
lcd_cmd(0b00111001); // function set
lcd_cmd(0b00000100); // EntryModeSet
lcd_cmd(0b00010100); // interval osc
lcd_cmd(0b01110000 | (contrast & 0xF)); // contrast Low
lcd_cmd(0b01011100 | ((contrast >> 4) & 0x3));// contast High/icon/power
lcd_cmd(0b01101100); // follower control
delay(200);
lcd_cmd(0b00111000); // function set
lcd_cmd(0b00001100); // Display On
lcd_cmd(0b00000001); // Clear Display
delay(2);
}
// ★ LCD(AQM0802A)コマンド書き込み(I2C)
void lcd_cmd(byte x) {
Wire.beginTransmission(DVC_AQM0802A);
Wire.write(0b00000000); // CO = 0,RS = 0
Wire.write(x);
Wire.endTransmission();
}
// ★ 連続文字
void lcd_contdata(byte x) {
Wire.write(0b11000000); // CO = 1, RS = 1
Wire.write(x);
}
// ★ 最終文字
void lcd_lastdata(byte x) {
Wire.write(0b01000000); // CO = 0, RS = 1
Wire.write(x);
}
// ★ 文字の表示
void lcd_printStr(const char *s) {
Wire.beginTransmission(DVC_AQM0802A);
while (*s) {
if (*(s + 1)) {
lcd_contdata(*s);
} else {
lcd_lastdata(*s);
}
s++;
}
Wire.endTransmission();
}
// ★ 表示位置の指定
void lcd_setCursor(byte x, byte y) {
lcd_cmd(0x80 | (y * 0x40 + x));
}
// ★ 数字表示
void lcd_printInt(int num){
char int2str[10];
sprintf(int2str,"%d",num);
lcd_printStr(int2str);
}
// 指定レジスタにデータ書き込み
void wrt_reg(byte dvc_adrs , uint8_t reg , uint8_t value) {
Wire.beginTransmission(dvc_adrs); // デバイス指定、通信開始
Wire.write((uint8_t)reg); // レジスタ指定
Wire.write((uint8_t)(value)); // データ書込
Wire.endTransmission(); // 送信完了
}
// 指定レジスタからデータ読み出し
uint8_t read_reg(byte dvc_adrs , uint8_t reg) {
Wire.beginTransmission(dvc_adrs); // 送信処理開始
Wire.write(reg); // レジスタ指定
Wire.endTransmission(false); // 送信完了(コネクション維持)
Wire.requestFrom(dvc_adrs , 1); // 1byteデータに要求
if (! Wire.available()) return -1; // データ有無判定
return (Wire.read()); // 1byteデータ
}
// 起動時処理
void setup() {
Wire.begin(); // マスタとしてI2Cバスに接続する
// DVC_MCP23017_00(DVCアドレス:0x20)
wrt_reg( DVC_MCP23017_00 , 0x00 , 0xFF ); // DVC00 I/O-PortA 入力設定
wrt_reg( DVC_MCP23017_00 , 0x0C , 0xFF ); // DVC00 I/O-PortA 入力プルアップ設定
wrt_reg( DVC_MCP23017_00 , 0x01 , 0x00 ); // DVC00 I/O-PortB 出力設定
wrt_reg( DVC_MCP23017_00 , 0x13 , 0x00 ); // DVC00 I/O-PortB 全OFF
// DVC_MCP23017_01(DVCアドレス:0x21)
wrt_reg( DVC_MCP23017_01 , 0x00 , 0xFF ); // I/O-PortA 入力設定
wrt_reg( DVC_MCP23017_01 , 0x0C , 0xFF ); // I/O-PortA 入力プルアップ設定
wrt_reg( DVC_MCP23017_01 , 0x01 , 0x00 ); // I/O-PortB 出力設定
wrt_reg( DVC_MCP23017_01 , 0x13 , 0x00 ); // I/O-PortB 全OFF
LCD_INIT(); // ★ LCD初期化
}
// 繰り返し処理
void loop() {
U1_PA = read_reg( DVC_MCP23017_00 , 0x12 ); // デバイス・レジスタ指定しデータ読取
U2_PA = read_reg( DVC_MCP23017_01 , 0x12 ); // デバイス・レジスタ指定しデータ読取
lcd_cmd(0b00000001); // 表示クリア
lcd_setCursor(0, 0); // 表示座標設定
if ((U1_PA & 1 )!=0){
lcd_printStr("SW1 ON"); // 表示内容(文字)
wrt_reg( DVC_MCP23017_00 , 0x13 , 0x02 ); // DVC00 I/O-PortB 出力設定
}else{
lcd_printStr("SW1 OFF"); // 表示内容(文字)
wrt_reg( DVC_MCP23017_00 , 0x13 , 0x01 ); // DVC00 I/O-PortB 出力設定
}
lcd_setCursor(0, 1); // 表示座標設定
if ((U2_PA & 1 )!=0){
lcd_printStr("SW2 ON"); // 表示内容(文字)
wrt_reg( DVC_MCP23017_01 , 0x13 , 0x02 ); // DVC01 I/O-PortB 出力設定
}else{
lcd_printStr("SW2 OFF"); // 表示内容(文字)
wrt_reg( DVC_MCP23017_01 , 0x13 , 0x01 ); // DVC01 I/O-PortB 出力設定
}
delay(100);
}
以上で I2C 接続に関する確認は一旦完了としようと思います。
「ARDUINOでI2C接続 I/Oエキスパンダーを使う」への1件のフィードバック