目次
前
次
I/OエキスパンダーMCP23017利用
ArduinoのデジタルI/Oが不足するとき、IICバスで
動作するI/Oエキスパンダーがあります。
I/OエキスパンダーMCP23017を入手したので
動作を確認します。
データシートから、ピンアサインを拾うと
次のようになっていました。
IICバスを利用しているので、デバイスIDがあるはずと
データシートを探すと、次の図が出てきます。
この図から、デバイスIDは7ビット構成で8個を
利用するとなれば、デバイスIDは次のどれかに
なります。
- 0x20
- 0x21
- 0x22
- 0x23
- 0x24
- 0x25
- 0x26
- 0x27
次の回路を利用した場合、デバイスIDは0x20になります。
ArduinoMega2560互換基板で、I/Oエキスパンダーの出力
動作を確認してみます。
ブレッドボードにMCP23017、抵抗、LED等の部品を
挿して、動作を確認できるようにします。
MCP23017内部には、どんなレジスタがあるのかを
データシートから拾い出します。
レジスタのアドレスは、2通りありました。
右に記されたアドレスは、パワーオンリセットで
使えるとなっています。左に示されたアドレスを
利用するときは、IOCONレジスタのBANKビットに
'1'を書き込んで使う仕様。
連続しているアドレスを使う方が、関数の定義は
楽なので、右に記されたアドレスを使います。
ArduinoAPIには、IICバスを扱う関数が用意されているので
これらを使って、IICバスの初期化とレジスタへの書き込み
を担当する関数を定義。
#define DEVICE0 0x20
#define DEVICE1 0x21
#define OREG_ENTRY 0x14
void init_iic_device()
{
byte ii ;
byte jj ;
byte xx ;
/* enable */
Wire.begin();
/* all output */
for ( ii = 0 ; ii < 2 ; ii++ ) {
/* select device */
xx = DEVICE0 ;
if ( ii == 1 ) { xx = DEVICE1 ; }
/* START */
Wire.beginTransmission( xx );
/*
entry address
lower DDR (output)
upper DDR (output)
*/
for ( jj = 0 ; jj < 3 ; jj++ ) { Wire.write(0x00); }
/* STOP */
Wire.endTransmission();
}
}
void gpio_send_data(byte cx,word dx)
{
byte da ;
byte db ;
byte xx ;
/* separate */
da = (byte)(dx & 255);
db = (byte)((dx >> 8) & 255);
/* select device */
xx = DEVICE0 ;
if ( cx == 1 ) { xx = DEVICE1 ; }
/* START */
Wire.beginTransmission( xx );
/* entry address */
Wire.write(OREG_ENTRY);
/* lower pattern */
Wire.write( da );
/* upper pattern */
Wire.write( db );
/* STOP */
Wire.endTransmission();
}
void gpio_0_send_data(word x)
{
gpio_send_data(0,x);
}
void gpio_1_send_data(word x)
{
gpio_send_data(1,x);
}
IICバスを初期化し、2個のMCP23017を使えるように
関数を定義。
16ビット分の情報を、ひとつのI/Oエキスパンダーから
出力できるように、関数を定義しています。
2個のMCP23017を使いたいとして、それぞれのデバイス
専用のラッパー関数を定義しました。
これらの関数を利用するArduinoMega2560のスケッチは
次のようになります。
#include <Wire.h>
#define OFF 0
#define ON OFF+1
#define LED_BIT 5
#define DEVICE0 0x20
#define DEVICE1 0x21
#define OREG_ENTRY 0x14
/* function prototype */
void show_help();
void rs_putchar(char x);
void rs_puts(char *ptr);
void crlf();
void gpio_send_data(byte cx,word dx);
void gpio_0_send_data(word x);
void gpio_1_send_data(word x);
void init_iic_device();
void blink_led();
/* variables */
boolean tflag ;
boolean uflag ;
boolean aflag ;
char sbuf[8] ;
byte sindex ;
char cmd ;
unsigned long prev ;
unsigned long curv ;
word sdiff ;
byte state ;
byte bcnt ;
#define SDIFFMAX 1000
void setup()
{
/* initialize serial */
Serial.begin(9600);
sindex = 0 ;
rs_puts("Hello !");
crlf();
show_help();
/* clear flags */
tflag = OFF ;
uflag = OFF ;
aflag = OFF ;
/* initialize port values */
PORTB = 0x00 ;
PORTC = 0x00 ;
PORTD = 0x00 ;
/* initialize port direction */
DDRB = 0xff ;
DDRC = 0xff ;
DDRD = 0xfe ;
/* timer handling */
prev = millis() ;
/* enable IIC bus */
init_iic_device();
/* others */
state = 0 ;
bcnt = 0 ;
tflag = OFF ;
uflag = OFF ;
aflag = OFF ;
pinMode(LED_BUILTIN, OUTPUT);
}
void loop()
{
/* timer handling */
curv = millis() ;
sdiff = curv - prev ;
if ( sdiff >= SDIFFMAX ) {
prev = curv ;
tflag = ON ;
}
/* command interpreter */
if ( uflag == ON ) {
/* clear flag */
uflag = OFF ;
/* get command */
cmd = *(sbuf+0) ;
/* help */
if ( cmd == '?' ) { show_help(); }
/* timer start */
if ( cmd == 'A' ) {
aflag = ON ;
state = 0 ;
}
/* timer stop */
if ( cmd == 'a' ) {
aflag = OFF ;
gpio_0_send_data(0x0000);
}
/* new line */
crlf() ;
}
/* timer handling */
if ( tflag == ON ) {
/* clear flag */
tflag = OFF ;
/* blink LED */
blink_led();
/* execute */
if ( aflag == ON ) {
switch ( state ) {
case 1 :
gpio_0_send_data(0x4002);
rs_puts("0x4002");
break ;
case 2 :
gpio_0_send_data(0x2004);
rs_puts("0x2004");
break ;
case 3 :
gpio_0_send_data(0x1008);
rs_puts("0x1008");
break ;
case 4 :
gpio_0_send_data(0x0810);
rs_puts("0x0810");
break ;
case 5 :
gpio_0_send_data(0x0420);
rs_puts("0x0420");
break ;
case 6 :
gpio_0_send_data(0x0240);
rs_puts("0x0240");
break ;
case 7 :
gpio_0_send_data(0x0180);
rs_puts("0x0180");
break ;
default :
gpio_0_send_data(0x8001);
rs_puts("0x8001");
break ;
}
crlf();
/* update */
state++ ;
state &= 7 ;
}
}
}
void show_help()
{
rs_puts("? help") ; crlf();
rs_puts("A begin test"); crlf();
rs_puts("a exit test") ; crlf();
}
void rs_putchar(char x)
{
Serial.write(x);
}
void rs_puts(char *ptr)
{
while ( *ptr ) {
rs_putchar( *ptr );
ptr++;
}
}
void crlf()
{
rs_putchar('\r');
rs_putchar('\n');
}
void gpio_send_data(byte cx,word dx)
{
byte da ;
byte db ;
byte xx ;
/* separate */
da = (byte)(dx & 255);
db = (byte)((dx >> 8) & 255);
/* select device */
xx = DEVICE0 ;
if ( cx == 1 ) { xx = DEVICE1 ; }
/* START */
Wire.beginTransmission( xx );
/* entry address */
Wire.write(OREG_ENTRY);
/* lower pattern */
Wire.write( da );
/* upper pattern */
Wire.write( db );
/* STOP */
Wire.endTransmission();
}
void gpio_0_send_data(word x)
{
gpio_send_data(0,x);
}
void gpio_1_send_data(word x)
{
gpio_send_data(1,x);
}
void init_iic_device()
{
byte ii ;
byte jj ;
byte xx ;
/* enable */
Wire.begin();
/* all output */
for ( ii = 0 ; ii < 2 ; ii++ ) {
/* select device */
xx = DEVICE0 ;
if ( ii == 1 ) { xx = DEVICE1 ; }
/* START */
Wire.beginTransmission( xx );
/*
entry address
lower DDR (output)
upper DDR (output)
*/
for ( jj = 0 ; jj < 3 ; jj++ ) { Wire.write(0x00); }
/* STOP */
Wire.endTransmission();
}
}
void blink_led()
{
digitalWrite(LED_BUILTIN, bcnt & ON );
bcnt++ ;
}
/* receive interrupt */
void serialEvent()
{
char ch;
if ( Serial.available() > 0 ) {
/* get 1 character */
ch = Serial.read();
/* store */
*(sbuf+sindex) = ch ;
/* increment */
sindex++ ;
/* judge */
if ( ch == '\r' ) {
sindex = 0 ;
uflag = ON ;
}
}
}
Teratermを利用し、端末からI/Oエキスパンダーの各ビット
から、論理値を出力すると、次のようになります。
コマンド'A'は、タイマーによる動作開始
コマンド'a'は、タイマーによる動作終了
としてあります。
0x0180を出力すると、2個のLEDが点灯することを
確認できています。
ここまでで、I/Oエキスパンダーによる出力ビット数
を拡張できました。
次に、入力ビット数を拡張することを確かめてみます。
ひとつのMCP23017には、GPA、GPBの2ポートがあり
それぞれ8ビットで、各ビットを入出力のどちらか
に設定できます。
GPAを入力、GPBを出力にするには、レジスタ番号の
0x00、0x01に論理値を書き込めばおしまい。
次のように初期化の関数を定義するとよいでしょう。
void init_iic_device()
{
byte ii ;
byte xx ;
/* enable */
Wire.begin();
/* Data Direction */
for ( ii = 0 ; ii < 2 ; ii++ ) {
/* select device */
xx = DEVICE0 ;
if ( ii == 1 ) { xx = DEVICE1 ; }
/* START */
Wire.beginTransmission( xx );
/* entry address */
Wire.write(0x00);
/* GPA DDR (input) */
Wire.write(0xFF);
/* GPB DDR (output) */
Wire.write(0x00);
/* STOP */
Wire.endTransmission();
}
}
入力に指定したポートは、プルアップ抵抗を入れて
接続なしでも'H'に保てるようにできます。
レジスタ番号は、次の図で確認できます。
GPAを入力に設定し、プルアップ抵抗を入れると
初期化関数に追加。
void init_iic_device()
{
byte ii ;
byte xx ;
/* enable */
Wire.begin();
/* Data Direction */
for ( ii = 0 ; ii < 2 ; ii++ ) {
/* select device */
xx = DEVICE0 ;
if ( ii == 1 ) { xx = DEVICE1 ; }
/* START */
Wire.beginTransmission( xx );
/* entry address */
Wire.write(0x00);
/* GPA DDR (input) */
Wire.write(0xFF);
/* GPB DDR (output) */
Wire.write(0x00);
/* STOP */
Wire.endTransmission();
}
/* pull up */
for ( ii = 0 ; ii < 2 ; ii++ ) {
/* select device */
xx = DEVICE0 ;
if ( ii == 1 ) { xx = DEVICE1 ; }
/* START */
Wire.beginTransmission( xx );
/* entry address */
Wire.write(0x0C);
/* pull up */
Wire.write(0xFF);
/* STOP */
Wire.endTransmission();
}
}
GPAから状態を読み込む処理が必要なので
専用の関数を定義します。
レジスタ番号は、次の図で確認。
データシートでは、どう扱うのか示されてます。
ArduinoのAPI関数を利用して、データを取得する
関数を定義すると、以下。
byte get_iic_data()
{
byte result ;
/* default */
result = 0 ;
/* START */
Wire.beginTransmission( DEVICE0 );
/* entry address */
Wire.write(0x12);
/* STOP */
Wire.endTransmission();
/* RESTART */
Wire.requestFrom(xx,1);
/* read 1 byte */
if ( Wire.available() ) { result = Wire.read(); }
return result ;
}
データ入力を定義したので、データ出力も記述します。
GPBのための出力ラッチのアドレスは、次の図で確認。
レジスタ番号が分かれば、関数を定義。
void put_iic_data(byte x)
{
/* START */
Wire.beginTransmission( DEVICE0 );
/* entry address */
Wire.write(0x15);
/* data */
Wire.write( x );
/* STOP */
Wire.endTransmission();
}
2個のMCP23017を利用したいときは、プリミティブ関数を
用意して、対応すればよいでしょう。
void init_iic_device()
{
byte ii ;
byte xx ;
/* */
Wire.begin();
/* GPA : input / GPB : output */
for ( ii = 0 ; ii < 2 ; ii++ ) {
/* select device */
xx = DEVICE0 ;
if ( ii == 1 ) { xx = DEVICE1 ; }
/* Data Direction */
/* START */
Wire.beginTransmission( xx );
/* entry address */
Wire.write(0x00);
/* lower DDR (output) */
Wire.write(0xFF);
/* upper DDR (output) */
Wire.write(0x00);
/* STOP */
Wire.endTransmission();
/* pull up */
/* START */
Wire.beginTransmission( xx );
/* entry address */
Wire.write(0x0C);
/* pull up */
Wire.write(0xFF);
/* STOP */
Wire.endTransmission();
}
}
byte get_iic_data(byte cx)
{
byte result ;
byte xx ;
/* default */
result = 0 ;
/* select device */
xx = DEVICE0 ;
if ( cx == 1 ) { xx = DEVICE1 ; }
/* START */
Wire.beginTransmission( xx );
/* entry address */
Wire.write(OREG_ENTRY-2);
/* STOP */
Wire.endTransmission();
/* RESTART */
Wire.requestFrom(xx,1);
/* read 1 byte */
if ( Wire.available() ) { result = Wire.read(); }
return result ;
}
byte get_iic0_data()
{
return get_iic_data(0);
}
byte get_iic1_data()
{
return get_iic_data(1);
}
void put_iic_data(byte cx,byte dx)
{
byte xx ;
/* select device */
xx = DEVICE0 ;
if ( cx == 1 ) { xx = DEVICE1 ; }
/* START */
Wire.beginTransmission( xx );
/* entry address */
Wire.write(OREG_ENTRY+1);
/* put data */
Wire.write( dx );
/* STOP */
Wire.endTransmission();
}
void put_iic0_data(byte x)
{
put_iic_data(0,x);
}
void put_iic1_data(byte x)
{
put_iic_data(1,x);
}
動作確認のために、Arduinoスケッチを定義。
#include <Wire.h>
#define OFF 0
#define ON OFF+1
#define LED_BIT 5
#define DEVICE0 0x20
#define DEVICE1 0x21
#define OREG_ENTRY 0x14
/* function prototype */
void show_help();
void rs_putchar(char x);
void rs_puts(char *ptr);
void crlf();
void put_iic_data(byte cx,byte dx);
void put_iic0_data(byte x);
void put_iic1_data(byte x);
byte get_iic_data(byte cx);
byte get_iic0_data();
byte get_iic1_data();
void init_iic_device();
void show_binary(byte x);
void blink_led();
/* variables */
boolean tflag ;
boolean uflag ;
boolean aflag ;
char sbuf[8] ;
byte sindex ;
char cmd ;
unsigned long prev ;
unsigned long curv ;
word sdiff ;
byte state ;
byte bcnt ;
#define SDIFFMAX 1000
void setup()
{
/* initialize serial */
Serial.begin(9600);
sindex = 0 ;
rs_puts("Hello !");
crlf();
show_help();
/* clear flags */
tflag = OFF ;
uflag = OFF ;
aflag = OFF ;
/* initialize port values */
PORTB = 0x00 ;
PORTC = 0x00 ;
PORTD = 0x00 ;
/* initialize port direction */
DDRB = 0xff ;
DDRC = 0xff ;
DDRD = 0xfe ;
/* timer handling */
prev = millis() ;
/* enable IIC bus */
init_iic_device();
/* others */
state = 0 ;
bcnt = 0 ;
tflag = OFF ;
uflag = OFF ;
aflag = OFF ;
pinMode(LED_BUILTIN, OUTPUT);
}
void loop()
{
byte tmp;
/* timer handling */
curv = millis() ;
sdiff = curv - prev ;
if ( sdiff >= SDIFFMAX ) {
prev = curv ;
tflag = ON ;
}
/* command interpreter */
if ( uflag == ON ) {
/* clear flag */
uflag = OFF ;
/* get command */
cmd = *(sbuf+0) ;
/* help */
if ( cmd == '?' ) { show_help(); }
/* timer start */
if ( cmd == 'A' ) {
aflag = ON ;
state = 0 ;
}
/* timer stop */
if ( cmd == 'a' ) {
aflag = OFF ;
put_iic0_data(0x00);
}
/* get data */
if ( cmd == 'G' ) {
tmp = get_iic0_data() ;
show_binary( tmp );
}
/* new line */
crlf() ;
}
/* timer handling */
if ( tflag == ON ) {
/* clear flag */
tflag = OFF ;
/* blink LED */
blink_led();
/* execute */
if ( aflag == ON ) {
switch ( state ) {
case 1 :
put_iic0_data(0x02);
rs_puts("0x02");
break ;
case 2 :
put_iic0_data(0x04);
rs_puts("0x04");
break ;
case 3 :
put_iic0_data(0x08);
rs_puts("0x08");
break ;
case 4 :
put_iic0_data(0x10);
rs_puts("0x10");
break ;
case 5 :
put_iic0_data(0x20);
rs_puts("0x20");
break ;
case 6 :
put_iic0_data(0x40);
rs_puts("0x40");
break ;
case 7 :
put_iic0_data(0x80);
rs_puts("0x80");
break ;
default :
put_iic0_data(0x01);
rs_puts("0x01");
break ;
}
crlf();
/* update */
state++ ;
state &= 7 ;
}
}
}
void show_help()
{
rs_puts("? help") ; crlf();
rs_puts("A begin test"); crlf();
rs_puts("a exit test") ; crlf();
rs_puts("G get data") ; crlf();
}
void rs_putchar(char x)
{
Serial.write(x);
}
void rs_puts(char *ptr)
{
while ( *ptr ) {
rs_putchar( *ptr );
ptr++;
}
}
void crlf()
{
rs_putchar('\r');
rs_putchar('\n');
}
void gpio_send_data(byte cx,word dx)
{
byte da ;
byte db ;
byte xx ;
/* separate */
da = (byte)(dx & 255);
db = (byte)((dx >> 8) & 255);
/* select device */
xx = DEVICE0 ;
if ( cx == 1 ) { xx = DEVICE1 ; }
/* START */
Wire.beginTransmission( xx );
/* entry address */
Wire.write(OREG_ENTRY);
/* lower pattern */
Wire.write( da );
/* upper pattern */
Wire.write( db );
/* STOP */
Wire.endTransmission();
}
void put_iic_data(byte cx,byte dx)
{
byte xx ;
/* select device */
xx = DEVICE0 ;
if ( cx == 1 ) { xx = DEVICE1 ; }
/* START */
Wire.beginTransmission( xx );
/* entry address */
Wire.write(OREG_ENTRY+1);
/* put data */
Wire.write( dx );
/* STOP */
Wire.endTransmission();
}
void put_iic0_data(byte x)
{
put_iic_data(0,x);
}
void put_iic1_data(byte x)
{
put_iic_data(1,x);
}
byte get_iic_data(byte cx)
{
byte result ;
byte xx ;
/* default */
result = 0 ;
/* select device */
xx = DEVICE0 ;
if ( cx == 1 ) { xx = DEVICE1 ; }
/* START */
Wire.beginTransmission( xx );
/* entry address */
Wire.write(OREG_ENTRY-2);
/* STOP */
Wire.endTransmission();
/* RESTART */
Wire.requestFrom(xx,1);
/* read 1 byte */
if ( Wire.available() ) { result = Wire.read(); }
return result ;
}
byte get_iic0_data()
{
return get_iic_data(0);
}
byte get_iic1_data()
{
return get_iic_data(1);
}
void init_iic_device()
{
byte ii ;
byte xx ;
/* */
Wire.begin();
/* GPA : input / GPB : output */
for ( ii = 0 ; ii < 2 ; ii++ ) {
/* select device */
xx = DEVICE0 ;
if ( ii == 1 ) { xx = DEVICE1 ; }
/* Data Direction */
/* START */
Wire.beginTransmission( xx );
/* entry address */
Wire.write(0x00);
/* lower DDR (output) */
Wire.write(0xFF);
/* upper DDR (output) */
Wire.write(0x00);
/* STOP */
Wire.endTransmission();
/* pull up */
/* START */
Wire.beginTransmission( xx );
/* entry address */
Wire.write(0x0C);
/* pull up */
Wire.write(0xFF);
/* STOP */
Wire.endTransmission();
}
}
void show_binary(byte x)
{
int ii ;
char tmp ;
for ( ii = 7 ; ii > -1 ; ii-- ) {
/* shift */
tmp = (x >> ii) & ON ;
/* value -> digit */
tmp += '0' ;
/* send to terminal */
rs_putchar( tmp ) ;
}
/* new line */
crlf();
}
void blink_led()
{
PORTB &= ~(1 << LED_BIT);
if ( bcnt & ON ) { PORTB |= (1 << LED_BIT) ; }
bcnt++ ;
}
/* receive interrupt */
void serialEvent()
{
char ch;
if ( Serial.available() > 0 ) {
/* get 1 character */
ch = Serial.read();
/* store */
*(sbuf+sindex) = ch ;
/* increment */
sindex++ ;
/* judge */
if ( ch == '\r' ) {
sindex = 0 ;
uflag = ON ;
}
}
}
ブレッドボードに、ICを実装してみると以下。
GPAの8ビットをすべて入力として、'H'、'L'を固定で接続。
- GPA7 'L'
- GPA6 'L'
- GPA5 'H'
- GPA4 'H'
- GPA3 'H'
- GPA2 'H'
- GPA1 'H'
- GPA0 'H'
Teratermを利用して、MCP23017に接続して
入力値を表示すると以下。
GPAの値が、ブレッドボード上で固定した
論理値になっていると確認できました。
目次
前
次