「IOエキスパンダーシールド(Arduino I/O増設 デジタル28点 アナログ16点)」を使うと、比較的簡単にArduinoにデジタル入出力、アナログ入力を拡張増設することができます。
お求めは、下記のサイトでどうぞ。
http://www.elefine.jp/SHOP/IOEXPANDERSHLD.html
Arduinoとの通信は、I2Cを利用しますので、A4(SDA)、A5(SCL)、およびアナログ入力としてA0を使い、合計3点のI/Oを使うだけで済みます。
I2Cで通信を行うデジタル入出力用デバイスMCP23017が2個搭載されています。その各MCP23017のI2Cアドレスは、基板上のジャンパーピンで設定します。
アドレスの上位4ビットは、0x20(16進数) で固定されていますので、下位3ビットをジャンパーピンで設定します。
AD1、AD2、AD3はU2用、AD4、AD5、AD6はU4用です。
AD3、AD2、AD1の順でそれぞれ1、2、4の重みです。同様にAD6、AD5、AD4の順でそれぞれ1、2、4の重みです。
よって、AD1、AD2、AD3のジャンパーをすべて0側(真ん中と0をまたぐようにジャンパーピンを挿す)に挿すとU2のアドレスは、0x20になります。また、AD4、AD5を0側、AD6を1側に挿すとU4のアドレスは、0x21になります。
デジタル入出力ピンとMCP23017のピンの対応
MCP23017の入出力ピンとIOエキスパンダーシールドのピンの対応は、以下のとおりです。
0 – U2 PORT B ビット0
1 – U2 PORT B ビット1
2 – U2 PORT B ビット2
3 – U2 PORT B ビット3
4 – U2 PORT B ビット4
5 – U2 PORT B ビット5
6 – U2 PORT B ビット6
7 – U2 PORT B ビット7
8 – U2 PORT A ビット0
9 – U2 PORT A ビット1
10 – U2 PORT A ビット2
11 – U2 PORT A ビット3
12- U2 PORT A ビット 4
13 – U2 PORT A ビット5
14 – U2 PORT A ビット6
15 – U2 PORT A ビット7
16 – U4 PORT B ビット0
17 – U4 PORT B ビット1
18 – U4 PORT B ビット2
19 – U4 PORT B ビット3
20 – U4 PORT B ビット4
21 – U4 PORT B ビット5
22 – U4 PORT B ビット6
23 – U4 PORT B ビット7
24- U4 PORT A ビット 4
25 – U4 PORT A ビット5
26 – U4 PORT A ビット6
27 – U4 PORT A ビット7
U4 PORT Aの下位4ビットは、アナログマルチプレクサのアナログ入力切替に使用しているため、デジタル入出力としては使用できません。
デジタル入力
サンプルスケッチhttps://github.com/numato/samplecode/blob/master/Arduino/shields/IOExpander/DigitalIn.ino
このサンプルでは、ジャンパーピンをAD6のみ1にして、ほかはすべて0に設定して使用します。よって、U2のI2Cアドレスは0x20、U4は0x21です(7ビット表記アドレス)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
/* * There are two on board MCP23017(U2 and U4) IO Expander ICs on this Shield * Select jumper positions of AD1=AD2=AD3=0, to set the Address of U2 as 0x20 * Select jumper positions of AD4=0; AD5=0; AD6=1, to set the Address of U4 as 0x21 * Digital Pins 0 - 7 belongs to PORTA and 8 - 15 belongs to PORTB of U2 * Digital Pins 24 - 27 belongs to PORTA and 16 - 23 belongs to PORTB of U4 */ #include "Wire.h" byte U2PortA, U2PortB, U4PortA, U4PortB =0; // Define variables to hold I/O port readings. void setup() { Serial.begin(9600); // Begin Serial Communication Wire.begin(); // Begin I2C Communication /* * The following code starts I2C communication with MCP23017(U2).Please refer MCP23017 datasheet * for register addresses. Sets all GPIOs of this IC to INPUT */ Wire.beginTransmission(0x20); // Starts communication with MCP23017(U2) IC Wire.write(0x00); // Set MCP23017 memory pointer to IODIRA address Wire.write(0xFF); // Set all pins of PORTA to outputs Wire.endTransmission(); // Ends I2C communication with MCP23017(U2) IC Wire.beginTransmission(0x20); // Starts communication with MCP23017(U2) IC Wire.write(0x01); // Set MCP23017 memory pointer to IODIRB address Wire.write(0xFF); // Set all pins of PORTB to outputs Wire.endTransmission(); // Ends I2C communication with MCP23017(U2) IC /* * The following code starts I2C communication with MCP23017(U4).Please refer MCP23017 datasheet * for register addresses. Sets all GPIOs of this IC to INPUT */ Wire.beginTransmission(0x21); // Starts communication with MCP23017(U4) IC Wire.write(0x00); // Set MCP23017 memory pointer to IODIRA address Wire.write(0xFF); // Set all PORTA pins to OUTPUT Wire.endTransmission(); // Ends I2C communication with MCP23017(U4) IC Wire.beginTransmission(0x21); // Starts communication with MCP23017(U4) IC Wire.write(0x01); // Set MCP23017 memory pointer to IODIRB address Wire.write(0xFF); // Set all PORTB pins to OUTPUT Wire.endTransmission(); // Ends I2C communication with MCP23017(U4) IC } void loop() { Wire.beginTransmission(0x20); // Starts I2C Communication with MCP23017(U2) IC Wire.write(0x12); // Set MCP23017 memory pointer to PORTA address Wire.endTransmission(); // Ends I2C communication with U2 Wire.requestFrom(0x20, 1); // Request one byte of data from MCP20317(U2) U2PortA=Wire.read(); // Store the incoming byte of PORTA into "U2PortA" Wire.beginTransmission(0x20); // Start I2C communication with U2 Wire.write(0x13); // Set MCP23017 memory pointer to PORTB address Wire.endTransmission(); // Ends I2C communication with U2 Wire.requestFrom(0x20, 1); // Request one byte of data from MCP20317(U2) U2PortB=Wire.read(); // Store the incoming byte of PORTB into "U2PortB" Wire.beginTransmission(0x21); // Start I2C communication with MCP23017(U4) Wire.write(0x12); // Set MCP23017 memory pointer to PORTA address Wire.endTransmission(); // Ends I2C communication with U4 Wire.requestFrom(0x21, 1); // Request one byte of data from MCP20317(U4) U4PortA=Wire.read(); // Store the incoming byte of PORTA into "U4PortA" Wire.beginTransmission(0x21); // Start I2C communication with U4 Wire.write(0x13); // Set MCP23017 memory pointer to PORTB address Wire.endTransmission(); // Ends I2C communication with U4 Wire.requestFrom(0x21, 1); // Request one byte of data from MCP20317 U4PortB=Wire.read(); // Store the incoming byte of PORTB into "U4PortB" Serial.print("U2 PORTA: " ); Serial.println(U2PortA, BIN); // Print the contents of the PORTA register in binary Serial.print("U2 PORTB: " ); Serial.println(U2PortB, BIN); // Print the contents of the PORTB register in binary delay(500); Serial.print("U4 PORTA: " ); Serial.println(U4PortA, BIN); // Print the contents of the PORTA register in binary Serial.print("U4 PORTB: " ); Serial.println(U4PortB, BIN); // Print the contents of the PORTB register in binary delay(500); } |
入出力の方向設定
setup()内で各MCP23017の入出力の方向を設定しています。
MCP23017には、8ビットのポートが二つあり、PORT A、PORT Bの名称がつけられています。
下記の部分でU2(アドレス0x20)の設定を行っています。
初めの0x00は、MCP23017の内部アドレスで0x00は、PORT Aの入出力方向設定のためのレジスターです。
次の行の0xFFは、入出力の方向を設定しています。8ビットの各ビットが入出力ピンに対応し、0で出力、1で入力です。Outなので0、Inなので1と覚えると覚えやすいです(Z80で8255を使っていた頃の覚え方)。
よって、0xFFは、すべて入力です。
Wire.beginTransmission(0x20);
Wire.write(0x00);
Wire.write(0xFF);
Wire.endTransmission();
同様に次の行で内部アドレス指定を0x01にして、U2のPORT Bの設定を行っています。
Wire.beginTransmission(0x20);
Wire.write(0x01);
Wire.write(0xFF);
Wire.endTransmission();
さらに次の行でI2Cアドレスを0x21にして、U4のPORT A、PORT B の設定を行っています。
U4のPORT Aの設定
Wire.beginTransmission(0x21);
Wire.write(0x00);
Wire.write(0xFF);
Wire.endTransmission();
厳密には、U4のPORT Aの下位4ビットは、アナログ入力のマルチプレクサーの切り替えに使用しているため、0xFFではなく、0xF0のほうがいいでしょう。
U4のPORT Bの設定
Wire.beginTransmission(0x21);
Wire.write(0x01);
Wire.write(0xFF);
Wire.endTransmission();
デジタル入力値の取得
デジタル入力の読み出しは、loop() 内で行っています。
下記は、U2のPORT Aを読み出す例です。8ビット分まとめて読み出されます。
Wire.beginTransmission(0x20); //I2CのアドレスでU2を指定
Wire.write(0x12); // MCP23017の内部アドレスの指定
Wire.endTransmission();
Wire.requestFrom(0x20, 1); // 読み出すアドレスを指定する
U2PortA=Wire.read(); // 読み出す
上記の0x20を0x21にするとU4、0x12を0x13にするとPORT Bのデータを読み出すことができます。
読み出した値は、8ビットをまとめた数値ですので、ビットごと、例えばビット0のH,L を確認したい場合は、下記のように &でマスク(対象以外を0にする)して、0以外かどうかを確認します。1がビットを表し、2進数の重みです。
つまり、ビット0からビット7まで、それぞれ1, 2, 4, 8 ,16, 32, 64, 128 です。
if ((U2PortA & 1) != 0) {
// true (H)の処理
}
else {
// false (L)の処理
}
入力のプルアップ
サンプルスケッチには、記述されておりませんが、MCP23017には、入力を内部の抵抗でプルアップする機能があります。これを使うと、スイッチ入力時、外部でプルアップ抵抗を接続する必要がなく、配線の手間が省けます。プルアップ設定のレジスターのアドレスは、PORT Aが0x0C、PORT Bが0x0Dです。8ビットのビットを1にするとそれに対応したピンがMCP23017の内部の抵抗100kΩでVDD(5V)にプルアップされます。
U2のPORT Aの全端子をプルアップする例
Wire.beginTransmission(0x20);
Wire.write(0x0C);
Wire.write(0xFF);
Wire.endTransmission();
これをsetup()内に入れるとよいでしょう。
デジタル出力
サンプルスケッチhttps://github.com/numato/samplecode/blob/master/Arduino/shields/IOExpander/DigitalOut.ino
このサンプルでは、ジャンパーピンをAD6のみ1にして、ほかはすべて0に設定して使用します。よって、U2のI2Cアドレスは0x20、U4は0x21です(7ビット表記アドレス)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
/* * There are two on board MCP23017(U2 and U4) IO Expander ICs on this Shield * Select jumper positions of AD1=AD2=AD3=0, to set the Address of U2 as 0x20 * Select jumper positions of AD4=0; AD5=0; AD6=1, to set the Address of U4 as 0x21 * Digital Pins 0 - 7 belongs to PORTA and 8 - 15 belongs to PORTB of U2 * Digital Pins 24 - 27 belongs to PORTA and 16 - 23 belongs to PORTB of U4 */ #include "Wire.h" void setup() { Wire.begin(); // Begin I2C bus /* * The following code starts I2C communication with MCP23017(U2).Please refer MCP23017 datasheet * for register addresses. Sets all GPIOs of this IC to OUTPUT */ Wire.beginTransmission(0x20); // Starts communication with MCP23017(U2) IC Wire.write(0x00); // Set MCP23017 memory pointer to IODIRA address Wire.write(0x00); // Set all pins of PORTA to outputs Wire.endTransmission(); // Ends I2C communication with MCP23017(U2) IC Wire.beginTransmission(0x20); // Starts communication with MCP23017(U2) IC Wire.write(0x01); // Set MCP23017 memory pointer to IODIRB address Wire.write(0x00); // Set all pins of PORTB to outputs Wire.endTransmission(); // Ends I2C communication with MCP23017(U2) IC /* * The following code starts I2C communication with MCP23017(U4).Please refer MCP23017 datasheet * for register addresses. Sets all GPIOs of this IC to OUTPUT */ Wire.beginTransmission(0x21); // Starts communication with MCP23017(U4) IC Wire.write(0x00); // Set MCP23017 memory pointer to IODIRA address Wire.write(0x00); // Set all PORTA pins to OUTPUT Wire.endTransmission(); // Ends I2C communication with MCP23017(U4) IC Wire.beginTransmission(0x21); // Starts communication with MCP23017(U4) IC Wire.write(0x01); // Set MCP23017 memory pointer to IODIRB address Wire.write(0x00); // Set all PORTB pins to OUTPUT Wire.endTransmission(); // Ends I2C communication with MCP23017(U4) IC } void DigitalIO(int i) { Wire.beginTransmission(0x20); Wire.write(0x12); Wire.write(i); // Set or clear PORTA pins of U2 Wire.endTransmission(); Wire.beginTransmission(0x20); Wire.write(0x13); Wire.write(i); // Set or clear PORTB pins of U2 Wire.endTransmission(); Wire.beginTransmission(0x21); Wire.write(0x12); Wire.write(i); // Set or clear PORTA pins of U4 Wire.endTransmission(); Wire.beginTransmission(0x21); Wire.write(0x13); Wire.write(i); // Set or clear PORTB pins of U4 Wire.endTransmission(); } void loop() { DigitalIO(255); // All digital pins High delay(100); DigitalIO(0); // All digital pins Low delay(100); } |
入出力方向設定
デジタル入力と同様、setup()内で各ポートの入出力方向を設定しています。
Wire.beginTransmission(0x20); // I2Cアドレス U2のアドレス
Wire.write(0x00); //内部アドレス 入出力方向方向設定レジスター
Wire.write(0x00); // 入出力方向方向設定
Wire.endTransmission();
1番目の0x00でPORT Aの入出力設定レジスターのアドレスを設定しています。PORT Bの場合は、0x01です。
2番目の0x00で入出力方向を設定しています。出力の場合は、0です。
デジタル出力を行う
loop() 内でデジタル出力を行いますが、サンプルスケッチでは、
DigitalIO(255);
と、関数を介して行っています。
サンプルスケッチでは、全ポート同じ状態を出力しています。
そのDigitalIOの定義を見ると、実際に出力する方法がわかります。
下記は、U2のPORT Aの例です。
Wire.beginTransmission(0x20);
Wire.write(0x12);
Wire.write(i);
Wire.endTransmission();
0x12 でPORT A のデジタル出力レジスターを指定しています。PORT Bの場合は、0x13 です。
Wire.write(i); が実際に出力するところです。8ビット分まとめて、出力します。8ビットのうち、1ビットのみ変化させたい場合は、前回の状態を保持しておき、その値との | (OR、論理和)、^ (ExOR、排他的論理和)を行います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
static int u2portAStatus; //出力状態保持用 //ビット2をONにする u2portAStatus |= 4; // ORでON Wire.beginTransmission(0x20); Wire.write(0x12); Wire.write(u2portAStatus); Wire.endTransmission(); //ビット3をOFFにする u2portAStatus |= 8; // ORで一旦ON u2portAStatus ^= 8; // ExORで反転 (上でONにしているので必ずOFFになる Wire.beginTransmission(0x20); Wire.write(0x12); Wire.write(u2portAStatus); Wire.endTransmission(); |
OFFにする場合、対象以外を1にして(上記の場合だと、0xf7)、ANDでマスクする方法もあります(この方が正当な方法)が、上記の方が計算が楽です。