パラレルI/O(MCP23017編)


RaspberryでのパラレルI/Oのやり方はいくつかあります。
今回はI2C接続のIOエクスパンダであるMCP23017を使ったパラレル I/Oを紹介します。
Raspberryとの結線は以下の様になります。



初めてi2cを使う場合、こちらな どを参考にi2cを有効にする必要があります。
最初はwiringPiライブラリを使った8BitのパラレルI/Oの例です。

//
// test program for MCP23017 with wiringPi
// cc -o mcp23017w mcp23017w.c -l wiringPi
//
#include <stdio.h>
#include <time.h>
#include <wiringPi.h>
#include <wiringPiI2C.h>

#define IODIRA          0x00            // MCP23017 address of I/O direction
#define IODIRB          0x01            // MCP23017 address of I/O direction
#define GPIOA           0x12            // MCP23017 address of GP Value
#define GPIOB           0x13            // MCP23017 address of GP Value
#define ADDRESS         0x20            // MCP23017 I2C address

int main(int argc, char **argv) {
 int i;
 int loop,loopm;
 int fd;
 time_t      start_time;
 time_t      end_time;
 int byte;

 loopm=30000;
 if (argc == 2) loopm=1;

 if ((fd=wiringPiI2CSetup(ADDRESS)) == -1){
   printf("Setup Fail\n");
   return 1;
 }

 wiringPiI2CWriteReg16(fd,IODIRA,0x00);
 wiringPiI2CWriteReg16(fd,IODIRB,0x00);
 wiringPiI2CWriteReg16(fd,GPIOA,0x00);
 wiringPiI2CWriteReg16(fd,GPIOB,0x00);

 start_time = time(NULL);
 for(loop=0;loop<loopm;loop++) {
  byte=0;
  for(i=0;i<8;i++) {
   byte=(byte<<1) + 1;
   wiringPiI2CWriteReg16(fd,GPIOA,byte);
   if (argc == 2) delay(500);
  }

  byte=0xff;
  for(i=0;i<8;i++) {
   byte=byte-(1<<i);
   wiringPiI2CWriteReg16(fd,GPIOA,byte);
   if (argc == 2) delay(500);
  }
 }
 end_time = time(NULL);
 printf("time:%.1fSec\n",difftime(end_time,start_time));
}

適当な引数を指定して実行すると1回だけ処理を行います。
MCP23017のGPA0からGPA7にLEDを付けると動きが良くわかります。
引数無しで実行すると30000回の処理を行います。

$ sudo ./mcp23017w
time:209.0Sec

次にbcm2835ライブラリを使った8BitのパラレルI/Oの例です。
bcm2835ライブラリのインストールは以下の手順で行います。

$ wget http://www.open.com.au/mikem/bcm2835/bcm2835-1.49.tar.gz
$ tar xvfz bcm2835-1.49.tar.gz
$ cd bcm2835-1.49
$ ./configure
$ make
$ sudo make install

//
// test program for MCP23017 with bcm2835
// cc -o mcp23017b mcp23017b.c -l bcm2835
//
#include <stdio.h>
#include <time.h>
#include <bcm2835.h>

#define IODIRA          0x00            // MCP23017 address of I/O direction
#define IODIRB          0x01            // MCP23017 address of I/O direction
#define GPIOA           0x12            // MCP23017 address of GP Value
#define GPIOB           0x13            // MCP23017 address of GP Value
#define ADDRESS         0x20            // MCP23017 I2C address


// MCP23017 I2C Data Transfer
void I2C_TX(unsigned char regadd, unsigned char tx_data)
{
 char wbuf[2];
 wbuf[0]=regadd;
 wbuf[1]=tx_data;
 bcm2835_i2c_write(wbuf, sizeof(wbuf));
}


int main(int argc, char **argv) {
 int i;
 int loop,loopm;
 int fd;
 time_t      start_time;
 time_t      end_time;
 int byte;

 loopm=30000;
 if (argc == 2) loopm=1;

 if (bcm2835_init() == -1) {
   printf("bmc2835_init Error\n");
   return 1;
 }
 bcm2835_i2c_begin();
 bcm2835_i2c_setSlaveAddress(ADDRESS);
 I2C_TX(IODIRA,0x00);
 I2C_TX(IODIRB,0x00);
 I2C_TX(GPIOA,0x00);
 I2C_TX(GPIOB,0x00);

 start_time = time(NULL);
 for(loop=0;loop<loopm;loop++) {
  byte=0;
  for(i=0;i<8;i++) {
   byte=(byte<<1) + 1;
   I2C_TX(GPIOA,byte);
   if (argc == 2) delay(500);
  }

  byte=0xff;
  for(i=0;i<8;i++) {
   byte=byte-(1<<i);
   I2C_TX(GPIOA,byte);
   if (argc == 2) delay(500);
  }
 }
 end_time = time(NULL);
 printf("time:%.1fSec\n",difftime(end_time,start_time));

//  bcm2835_i2c_end();
  bcm2835_close();
}

適当な引数を指定して実行すると1回だけ処理を行います。
引数無しで実行すると30000回の処理を行います。

$ sudo ./mcp23017b
time:145.0Sec

wirinPiライブラリよりも30%程度、効率がいいようです。

今回は入力電圧として3.3Vを使いましたが、MCP23017のデータシートを見ると5.5Vまでいけそうなので
入力電圧として5Vを使うと、3.3V→5Vのレベルシフターとして使うこともできます。
入力電圧として5Vを使ったときの出力は4.1Vぐらいでした。
74HC595と比べると少し電圧のDROPが大きいです。

続く...