MCP23x17
MCP23017とMCP23S17、調べるとほとんどライブラリに頼っちゃってて。
いや、WireとかSPIもライブラリだけど。
そんなに難しいもんじゃないのでそれぞれこれだけあればつかえるよっていう最小限度のコードです。
MCP23017。
void mcp23017( uint8_t reg, uint8_t dat){
Wire.beginTransmission(0x20);
Wire.write(reg);
Wire.write(dat);
Wire.endTransmission();
}
ほんとにこれだけ。とても簡単。
例えばGPA0をHIGHにしたければ
mcp23017(0x00,0x00); // GPAを全てアウトプットに
mcp23017(0x14,0x01); // GPA0をHIGHに
みたいな。超簡単。
次、MCP23S17。SPIの方。
uint8_t buf[] = {0,0,0};
uint8_t ret;
void writeSPI(uint8_t addr, uint8_t value){
*buf = 0x40;
rwSPI( addr,value );
}
uint8_t readSPI(uint8_t addr, uint8_t value){
*buf = 0x41;
return rwSPI( addr,value );
}
uint8_t rwSPI(uint8_t addr, uint8_t value){
digitalWrite(10,LOW);
*(buf+1) = addr;
*(buf+2) = value;
for( int i=0; i<3; i++ ){
ret = SPI.transfer( *(buf+i) );
}
digitalWrite(10,HIGH);
return ret;
}
すこしコードが多い。
MCP23S17は3バイトのデータを送信することでコントロールする。
1バイトめ。デバイスアドレスと読み書きフラグ。
0100 A2 A1 A0 R/W R/Wは1でREAD、0でWRITE
A0~A2を全てGNDに落としてあって、書き込みしたければデバイスアドレスは0x40になるし、読み込みなら0x41になる。
2バイトめレジスタのアドレス。
3バイトめデータ。
1バイトめ以外はMCP23017と同じなのでそんなに難しくない。レジスタも一緒。(データシート参照のこと)
じゃあ、MCP23017と同じようにMCP23S17でGPA0をHIGHにするには。
writeSPI(0x00,0x00); // GPAを全てアウトプットに
writeSPI(0x14,0x01); // GPA0をHIGHに。
ほとんど一緒っすね。
ついでにソフトウェアSPI版。何がうれしいかというとハードウェアSPI使えない様なMCU使うときとか、ピンが確保し辛いとき好きにレイアウト出来る。 ちなみに速度は考慮してない。リファレンスとしてどうぞ。
(一説によるとArduinoの場合ハードウェアSPIのライブラリ使うよりソフトウェアSPIをチューニングしたほうが速いとかなんとか)
uint8_t buf[] = {0,0,0};
uint8_t ret;
/* Pinセットアップの内容。
#define MCPCS 10
#define MCPSCK 13
#define MCPSI 11
#define MCPSO 12
pinMode(MCPCS,OUTPUT);
pinMode(MCPSCK,OUTPUT);
pinMode(MCPSI,OUTPUT);
pinMode(MCPSO,INPUT);
*/
void writeSPI(uint8_t addr, uint8_t value){
*buf = 0x40;
*(buf+1) = addr;
*(buf+2) = value;
digitalWrite(MCPCS,LOW);
for(int i0 = 0; i0 < 3 ; i0 ++ ){
for(int i1 = 0; i1 < 8 ; i1 ++ ){
digitalWrite( MCPSCK, LOW );
digitalWrite( MCPSI , ((*(buf+i0) & (0x80 >> i1))!=0) ? HIGH : LOW );
digitalWrite( MCPSCK, HIGH );
}
}
digitalWrite(MCPCS,HIGH);
}
uint8_t readSPI(uint8_t addr){
*buf = 0x41;
*(buf+1) = addr;
digitalWrite(MCPCS,LOW);
for(int i0 = 0; i0 < 2 ; i0 ++ ){
for(int i1 = 0; i1 < 8 ; i1 ++ ){
digitalWrite( MCPSCK, LOW );
digitalWrite( MCPSI , ((*(buf+i0) & (0x80 >> i1))!=0) ? HIGH : LOW );
digitalWrite( MCPSCK, HIGH );
}
}
ret = 0;
for(int i1 = 0; i1 < 8 ; i1 ++ ){
digitalWrite( MCPSCK, LOW );
ret |= digitalRead( MCPSO ) << (7-i1);
digitalWrite( MCPSCK, HIGH );
}
digitalWrite(MCPCS,HIGH);
return ret;
}
ところで、MCP23x17の起動直後は全てのピンがインプット、プルアップなしになってます。
この状態のままデータを入力してみるとものすごいセンシティブで手を近づけただけでピンが反応します。
当然あちこち反応するのでこのままつかえはしないだろうけど。
何かおもしろい使い方があるかもしれないけどひょっとするとICが死ぬかもしれないので注意する必要はあり(笑
IODIRAとIODIRBで入出力を決めるのだけども、出力に設定してreadしてもなぜか読める。
これもいろいろあるので使いどころはアレなのだと思うけども、自分で設定したピンの内容をデバイスから得られるという使い方はありかもしれない。なんかよく解らないところが多いけど便利です。MCP23x17。いや、ほんとに。
真面目な話。
MCP23x17、同じような物にPCF8574というI2CのIOエキスパンダがあって、こっちの方が圧倒的に簡単に使えるのだけども、例えばタクトスイッチを16個ないしは8x8のマトリックスで使いたいみたいになったときMCP23x17は自前でプルアップ設定が出来るので回路がシンプルになる。 それに対してPCF8574の場合操作自体は簡単(設定がない)だけどプルアップも出来ない、出力もドレイン出力っぽいので吸い込みにするか、さもなくば周辺回路が山盛りになっちゃう。ついでにMCP23x17は16bitなのに対してPCF8574は8bit。PCF8574Aと合わせて16個使って(PCF8574x8 PCF8574Ax8)の128bitなのでデバイスアドレスの消費もなかなかなもの。
入手性も考えるとMCP23x17、秋月で買えるし。安いし。
今まではPCFひゃっほー とか言ってたけどどう考えてもMCP23x17、とくにMCP23S17が便利。安いし。