「真夜中の工作室」さんの記事と「きむ茶工房ガレージハウス」さんの記事を参考にした。MCP23008/MCP23S08は8bitバージョンで日本語のDATASHEETがあるので、こちらも参考にした。
配線図
<追記:20170920> ブレッドボード図のArduinoの10番ピン~13番ピンがずれていたのをご指摘いただいたので修正しました。 </追記>
GPAとGPBで8bitずつ使うのが簡単そうなので、GPAをLED表示、GPBをタクトスイッチの入力として使った。また、INTA/INTBという割り込み線が用意されているので、これをArduinoの外部割込みのINT0(D2)につないで、タクトスイッチを押したときに割込みを発生させて、割込み処理ルーチン内でLEDを点灯させるようにした。
ソースコードは「真夜中の工作室」さんのものを元にした。ArduinoのSPIライブラリを使っていないので、素のAVRその他にも移植しやすいと思う。
Arduinoのスケッチ
/* EXIO-SPI Test MCP23S17 CS:LowActive MSB first SCK rising 2016.10.28 created */ //SPI IF #define DATAOUT 11//MOSI #define DATAIN 12//MISO #define SPICLOCK 13//sck #define SLAVESELECT 10//ss //Interrupt #define INTPIN 2 //MCP23S17 Device OPCODE #define HWADDW 0x40 #define HWADDR 0x41 volatile byte address = 0; volatile byte data = 0; //SPI 関数 byte spi_transfer(volatile byte 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 初期化 // IOCON(0x0A):0x20 // BANK :0 // MIRROR:0 // SEQOP :1 // DISSLW:0 // HAEN :0 // ODR :0 // INTPOL:0 // - :0 digitalWrite(SLAVESELECT, LOW); spi_transfer(HWADDW); //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 // GPA ----------------------------------------------- // Set as outputs // IODIRA(0x00): 1:input 0:output digitalWrite(SLAVESELECT, LOW); spi_transfer(HWADDW); //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 // GPB ----------------------------------------------- // Set as inputs // IODIRB(0x01): 1:input 0:output digitalWrite(SLAVESELECT, LOW); spi_transfer(HWADDW); //send MSByte address first address = 0x01; spi_transfer(address); //send MSByte address first data = 0xFF; spi_transfer(data); //send MSByte DATA digitalWrite(SLAVESELECT, HIGH); //release chip // Pull-up // GPPUB(0x0D): 1:pull-up enable 0:pull-up disable digitalWrite(SLAVESELECT, LOW); spi_transfer(HWADDW); //send MSByte address first address = 0x0D; spi_transfer(address); //send MSByte address first data = 0xFF; spi_transfer(data); //send MSByte DATA digitalWrite(SLAVESELECT, HIGH); //release chip // 入力ピンの論理値を反転 // IPOLB(0x03): 1:opposite 0:same digitalWrite(SLAVESELECT, LOW); spi_transfer(HWADDW); //send MSByte address first address = 0x03; spi_transfer(address); //send MSByte address first data = 0xFF; spi_transfer(data); //send MSByte DATA digitalWrite(SLAVESELECT, HIGH); //release chip // Interrupt-on-change enable // GPINTENB(0x05): 1:enable 0:disable digitalWrite(SLAVESELECT, LOW); spi_transfer(HWADDW); //send MSByte address first address = 0x05; spi_transfer(address); //send MSByte address first data = 0xFF; spi_transfer(data); //send MSByte DATA digitalWrite(SLAVESELECT, HIGH); //release chip //-------------------------------------------------------------- //外部割込み pinMode(INTPIN, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(INTPIN), dataChanged, FALLING); Serial.begin(9600); Serial.println("*** MCP23S17 Interrupt Test ***"); // LED Check for (int i = 0; i < 8; i++) { // Write data (LED) // GPIOA(0x12) digitalWrite(SLAVESELECT, LOW); spi_transfer(HWADDW); spi_transfer(0x12); spi_transfer(1 << i); digitalWrite(SLAVESELECT, HIGH); delay(100); } // Write data (LED) // GPIOA(0x12) digitalWrite(SLAVESELECT, LOW); spi_transfer(HWADDW); spi_transfer(0x12); spi_transfer(0x00); digitalWrite(SLAVESELECT, HIGH); } void dataChanged() { // Read data (SW) // INTCAPB(0x11) digitalWrite(SLAVESELECT, LOW); spi_transfer(HWADDR); spi_transfer(0x11); data = spi_transfer(0x00); digitalWrite(SLAVESELECT, HIGH); // Write data (LED) // GPIOA(0x12) digitalWrite(SLAVESELECT, LOW); spi_transfer(HWADDW); spi_transfer(0x12); spi_transfer(data); digitalWrite(SLAVESELECT, HIGH); } void loop() { }
メモ:
SPIの送受信のところが冗長だが、関数にまとめると動作しなくなったので深追いせずにそのままべた書き。
DATASHEETを読むと、冒頭から「BANK」とか「Sequencial mode」とか出てきてややこしい。BANKは8bit版のMCP23008/MCP23S08にはないのでMCP23S17の英文のDATASHEETを読まないとわからない。
Addressというのがデバイスごとのアドレス(15Pin~17PinのA0~A2で設定)と、レジスタ・アドレスがあるがBANKやSequencialの説明にあるアドレスは、レジスタ・アドレスのもの。
設定で大事なのはIOCONで8bit版とも違っているのでここをいじる時は注意が必要。
データの送受信は
1byte目 0b0100(固定) | デバイス・アドレス:3bit | R/W:1bitという手順で行う。受信の場合はMasterからダミーデータを送信して受信した。
2byte目 レジスタ・アドレス
3byte目 送受信データ
I2C版のMCP23017も慣れれば使えそうだが、さらにややこしそうな感じだ。通信速度も速いので線数が許すなら最初はSPI版の方が楽かも。
SPI/I2C経由でI/Oを16bit拡張するならATMega328Pやピンコンパチで値段の安いATMega88VをSlaveとして使うという手もある。
0 件のコメント:
コメントを投稿