目次

I/OエキスパンダーMCP23017利用

 ArduinoのデジタルI/Oが不足するとき、IICバスで
 動作するI/Oエキスパンダーがあります。




 I/OエキスパンダーMCP23017を入手したので
 動作を確認します。

 データシートから、ピンアサインを拾うと
 次のようになっていました。



 IICバスを利用しているので、デバイスIDがあるはずと
 データシートを探すと、次の図が出てきます。



 この図から、デバイスIDは7ビット構成で8個を
 利用するとなれば、デバイスIDは次のどれかに
 なります。

 次の回路を利用した場合、デバイス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'を固定で接続。

 Teratermを利用して、MCP23017に接続して
 入力値を表示すると以下。



 GPAの値が、ブレッドボード上で固定した
 論理値になっていると確認できました。


目次

inserted by FC2 system