真夜中の工作室 midnight craftcenter

電子工作とカメラ、写真の趣味のブログ

拡張IOでLEDMATRIX

 拡張IOの勉強のため、+LEDMATRIXでアニメをSketch。

送信者 工作

これは、ドークボットの神村工業ブースでも参考に展示したので先に書いておく。

 Arduinoのソフト開発はこれまでの組み込みマイコンに比べて格段に難易度を下げた。
オープンソースハードなので多少の知識があればハードしては、小型のクローンが用意に作成出来るし、特にソフトはポート設定も含め、ソースもそのまま流用出来るのでありがたい。
自分にとってはちょっとハードルが高いデバイスを制御しようと思った場合も、調べるてみると先人が既にArduinoで動作させてくれてたりして、参考になったり、そのまま使えたりする。
AVR328対応等プログラム容量も増えたものが出てきた。
でも、すぐに足りなくなるのはポート数ではないかと思う。
デフォルトのskecthにもあるように初回はまず、LEDを光らせところから始める方が多いのではないかと思うが、その延長で20個以上のLEDを光らせるとなると、プログラムは簡単でも、pinが足りなくなる。
例えば、秋月電子では100円で8*8のLEDMATRIXが売っていてこれを表示デバイスにしてみようと考えた場合、LEDMATRIXだけで16本pinが必要なので、SWで制御など考えてもかなり制限が出てしまう。
http://akizukidenshi.com/catalog/g/gI-00963/

そこで、IOpin拡張について考えてみた。
LEDMATRIXに限って言えば、標準ライブラリでMatrixがあるが、LEDドライバ高価だし、入力には使えない。通常のIOとして使いたい場合、、
Arduinoの亜流、Sanguinoを使えば32本IOがあるが、Pro mini並みに小型なものは自分は知らない(作ろうとは思っている)。
PLD等を組み合わせてSPIやI2Cで通信できれば、デバイス次第でいくらでもpinが増やせるが、PLDを使うとなるとVerilogなり、VHDLなりで開発する必要がありこれもちょっと敷居が高い
pin拡張のためだけのICで容易に入手できるところがないかと探したところ、マイクロファンで16本拡張できる専用ICを扱っていたので買ってみた。通信フォーマットはI2CとSPIの2種あったが、好みでSPIのにした。
MCP23S17
http://www.microfan.jp/shop/51_155.html
仕様書はこちら。
http://ww1.microchip.com/downloads/en/DeviceDoc/21952b.pdf

今回は、MCP23S17の16本は全てLEDMATRIXに割り当てた。
LEDMATRIXのpin配置は物理的制限から行列で綺麗に並んでいないので、MCP23S17との接続の際に、COL1-8はGPA[7:0]、ROW1-8はGPB[7:0]に順番に割り当てた。
ブロック図は下記。


この方が後の制御が楽になると思う。
ちなみにこのLEDMATRIXはCOL:L、ROW:Hに設定したドットが光る。
2進数で発光パターンを作った場合、発光したい部分を1としたら、COLがは~で反転した方が分かりやすいと思う。
LEDなので電流制限抵抗が必要。今回仮に360Ωとしたが、コネクタにして簡単に変更できるようにしておいた。


MCP23S17とArduino(今回は168 Pro mini 8MHz 3.3V)の接続はSPI。
拡張IOのSCK、SI、SOに繋がるpinはAVRの仕様で決まっているが、CSは任意のpinで可能、今回は10pinに割り当てた。
RESETはArduinoと共用とした。

SPIについては、本でもWEB上でもあちこちで説明されているのでここでは割愛。
自分が特に参考になったのは、
AVRマイコン・リファレンス・ブック
http://www.amazon.co.jp/AVR%E3%83%9E%E3%82%A4%E3%82%B3%E3%83%B3%E3%83%BB%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9%E3%83%BB%E3%83%96%E3%83%83%E3%82%AF%E2%80%95AVR%E3%81%AECPU%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E3%80%81%E8%B1%8A%E5%AF%8C%E3%81%AA%E5%86%85%E8%94%B5%E5%91%A8%E8%BE%BA%E6%A9%9F%E8%83%BD%E3%82%92%E8%A9%B3%E7%B4%B0%E8%A7%A3%E8%AA%AC-%E3%83%9E%E3%82%A4%E3%82%B3%E3%83%B3%E6%B4%BB%E7%94%A8%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA-%E5%B1%B1%E6%A0%B9-%E5%BD%B0/dp/4789837300/ref=pd_bxgy_b_img_a
のSPIインターフェイスについて書かれた160-161ページ
SPIは汎用的な規格だが、制限が緩いのでデバイスごとに設定を変えてやる必要がある。
特に、
 CSの極性。
 MSBかLSBか。
 クロック極性、クロック位相
に注意。
MCP23S17はCS:lowactive、MSB first、CLKはH極性で立ち上がりでラッチ。通信速度はArduinoの最高速でも受け付けてくれるので4MHzにした。マスタかスレーブは当然Arduinoがマスタになる。
なので、設定は、
SPCR:0b01010000
SPSR:0b00000001
とした。
良くわからなくても、全く同じデバイスを使用するならこの設定で問題ない。
もっと興味がある人はオシロスコープでCSで同期を掛けてDATAとCLの波形を一度見るとわかりやすいと思う。

参考にもう少し詳しく書くと、
SPCR[7]:SPIE SPI割り込みイネーブル
SPCR[6]:SPE SPIイネーブル(0:SPI OFF/1:SPI ON)
SPCR[5]:DORD データ・オーダ(0:MSB/1:LSB)
SPCR[4]:MSTR マスタ/スレーブ(0:スレーブ/1:マスタ)
SPCR[3]:CPOL クロック極性(0:H/1:L)
SPCR[2]:CPHA クロック位相(0:Rising/1:falling)
SPCR[1]:SPR1 クロックレート選択ビット
SPCR[0]:SPR0 クロックレート選択ビット
(SPR[1:0]、00が最速、11が最遅)

SPSR[0]:SPI2X SPI2倍速ビット(0:*1/1:*2)

クロックを最速にしたいなら、SPI2X:1、SPR[1:0]:00とすればよい(fOSC/2となる)。
これはターゲットデバイスの通信速度によっては遅くする必要がある。

で、sketchはこちら。4つの画面を順番に光らせている。

/* 
  EXIO-SPI + LEDMATRIX
  MCP23S17
  CS:LowActive
  MSB first
  SCK rising
  Ver 0.02
*/


//SPI IF
#define DATAOUT 11//MOSI
#define DATAIN  12//MISO 
#define SPICLOCK  13//sck
#define SLAVESELECT 10//ss
#define HWADD 0x40

char address=0;
char data =0; 

#define WAIT 3000

char i=0,j=0,k=0;

//Pattern
char pat[4][8]={
{
0b01001100,
0b00101100,
0b00011101,
0b00001111,
0b00001100,
0b00010010,
0b00010010,
0b00100010
},
{
0b00000000,
0b00011001,
0b00011111,
0b00011100,
0b00101100,
0b01011110,
0b10010001,
0b00100010
},
{
0b10011000,
0b10011000,
0b01111110,
0b00011001,
0b00011001,
0b00100100,
0b01100100,
0b00000010
},
{
0b00011000,
0b00011001,
0b01111110,
0b10011000,
0b00011000,
0b00100100,
0b00100010,
0b01000000
}

};

//SPI 関数
char spi_transfer(volatile char data)
{
  SPDR = data;                    // Start the transmission 
  
  while (!(SPSR & (1<<SPIF)))     // Wait the end of the transmission
  {
  };
  
  return SPDR;                    // return the received byte
}
 

void setup()
{

//pinmode 設定  
  pinMode(DATAOUT, OUTPUT);
  pinMode(DATAIN, INPUT);
  pinMode(SPICLOCK,OUTPUT);
  pinMode(SLAVESELECT,OUTPUT);
  digitalWrite(SLAVESELECT,HIGH); //disable device

//SPI設定
  // SPCR = 01010000
  //interrupt disabled,spi enabled,msb 1st,master,clk low when idle,
  //sample on rising edge of clk,system clock/2 rate (fastest)
  SPCR = 0x50;
  SPSR = 0x01; // SPI2X:1
 
 //MCP23S17 初期化
	digitalWrite(SLAVESELECT,LOW);
  		spi_transfer(HWADD);   //send MSByte address first
  		address=0x0A;
  		spi_transfer(address);   //send MSByte address first
		data = 0x20;
  		spi_transfer(data);      //send MSByte DATA
  	digitalWrite(SLAVESELECT,HIGH); //release chip

	digitalWrite(SLAVESELECT,LOW);
  		spi_transfer(HWADD);   //send MSByte address first
  		address=0x0B;
  		spi_transfer(address);   //send MSByte address first
		data = 0x20;
  		spi_transfer(data);      //send MSByte DATA
  	digitalWrite(SLAVESELECT,HIGH); //release chip
  
	digitalWrite(SLAVESELECT,LOW);
  		spi_transfer(HWADD);   //send MSByte address first
  		address=0x00;
  		spi_transfer(address);   //send MSByte address first
		data = 0x00;
  		spi_transfer(data);      //send MSByte DATA
  	digitalWrite(SLAVESELECT,HIGH); //release chip

	digitalWrite(SLAVESELECT,LOW);
  		spi_transfer(HWADD);   //send MSByte address first
  		address=0x01;
  		spi_transfer(address);   //send MSByte address first
		data = 0x00;
  		spi_transfer(data);      //send MSByte DATA
  	digitalWrite(SLAVESELECT,HIGH); //release chip

 
 	digitalWrite(SLAVESELECT,LOW);
  		spi_transfer(HWADD);   //send MSByte address first
  		address=0x12;
  		spi_transfer(address);   //send MSByte address first
		data = 0xFF;
  		spi_transfer(data);      //send MSByte DATA
  	digitalWrite(SLAVESELECT,HIGH); //release chip

 	digitalWrite(SLAVESELECT,LOW);
  		spi_transfer(HWADD);   //send MSByte address first
  		address=0x13;
  		spi_transfer(address);   //send MSByte address first
		data = 0x00;
  		spi_transfer(data);      //send MSByte DATA
  	digitalWrite(SLAVESELECT,HIGH); //release chip
  
}
  
void loop(){
  for(j=0;j<4;j++){
     for(k=0;k<32;k++){
      data = 0x01;
        for(i=0;i<8;i++){ 
    
 	digitalWrite(SLAVESELECT,LOW);
  		spi_transfer(HWADD);   //send MSByte address first;
  		spi_transfer(0x12);   //send MSByte address first
  		spi_transfer(~pat[j][i]);      //send MSByte DATA
  	digitalWrite(SLAVESELECT,HIGH); //release chip 

   	digitalWrite(SLAVESELECT,LOW);
  		spi_transfer(HWADD);   //send MSByte address first
  		spi_transfer(0x13);   //send MSByte address first
  		spi_transfer(data);      //send MSByte DATA
  	digitalWrite(SLAVESELECT,HIGH); //release chip  
  
        data = data<<1;
        delayMicroseconds(WAIT);
        }//i 
     }//k
  }//j

}//loop

実際の通信は、spi_transfarで送っている。
8bit毎にデータを送る。
今回はLEDを光らせるのみなので、SPIは送信のみ。
SOは繋がなくても良い。
仕様書をみてちょっと戸惑ったのが、デバイスごとに固有のアドレスがあること。
1アドレス分送る場合の例だと、
”デバイスのアドレス”、”アドレス”、”データ”の3byte単位で送る必要がある。デバイスのアドレスはA[2:0]がALL0なら、0x40となる。
理解すると便利なことが分かる。つまり、デバイスアドレスを変えてやれば、一本のCSでもデバイスを8個まで区別して通信できるのだから。
複数個使わないなら設定によりハードアドレスを無視することもできるので今回は無視する設定。
後はアドレスとデータの値を任意に設定すれば仕様書通り動作させられるので簡単。
ただ、このデバイス、入力割り込みや、アドレス自動インクリメントや、極性反転機能など思ったより多機能。
今回出力のH/L切り替えだけなので、最低限の設定だけ使っている。
アドレスも分かりやすく1アドレス単位での通信とした。BANKも0扱いである。
オープンドレインは使わず、Hだけ出している。
個人的にはなかなか使い勝手が良く、価格も262円と個人で買う分にはコストパフォーマンスが高いと思う。
このICの入力を使って、キーボードも作った。