製作モジュール一覧

ラベル MCP23S17 の投稿を表示しています。 すべての投稿を表示
ラベル MCP23S17 の投稿を表示しています。 すべての投稿を表示

2017年1月6日金曜日

ベースマシン I/Oエクスパンダ―基板 でけた

Exio Board


I/OエキスパンダーのMCP23S17とEEPROMのAT24C1024を載せた基板。基板の入出力は、電源、I2C(AT24C1024用)、SPI(MCP23S17用)、GPIO×16本。

回路図

基板図


MISOをPull Downする抵抗を追加。MISOだけ無信号時にハイ・インピーダンスになるようなので1kΩのRでブルダウンした。ハイ・インピーダンスになるのはMCP23S17の出力の使用なのかな?
(参考:「I/OエキスパンダーのMCP23S17をNucleo F401RE(mbed)で使ってみる。 その2」

テスト用にGNDのテストポイント(JP6)を追加。

部品面


ハンダ面



今回も部品面で被覆付きの単線でジャンパを配線した。SMDとうで高密度配線をするのでなければこっちの方がよさそう。慣れればハンダ面でジャンパを這わすよりはんだ付けも楽だ。うっかりハンダ面を引っ掛けてジャンパ線を傷つける心配も少ないと思う。

Exio UI Board


ロータリーエンコーダ1個とタクトスイッチ6個。MCP23S17のGPIOに接続する。

回路図


基板図


部品面


ハンダ面


こちらはジャンパなしで配線できた。

ユニットテスト


今回制作した基板をつないでユニットテスト。はんだ付け作業より、ケーブルを作ったりテストする方が時間がかかった。

MCP23S17用テストプログラム
https://developer.mbed.org/users/ryood/code/ExioBufferdController_Test/ Revision:13

AT24C1024用テストプログラム

https://developer.mbed.org/users/ryood/code/AT24C1024_Test/ Revision:4

ベースマシンのUIパネルに固定


mbed repository
https://developer.mbed.org/users/ryood/code/BaseMachine/ Revision:19

メモ:


mbedのオンライン・コンパイラをWidows10のEdgeで使うとCtrl-Cでコピー出来ない。Chrome、FirefoxならOK。

考えていたハードウェアはだいたい出来たので、次回はまとめ(弱め)の予定です。

2016年12月30日金曜日

ベースマシン I/Oエクスパンダ―基板


下側のスイッチx8はパターン切替用。aitendoで購入。キートップはついていなかったが、艶消しの基板でなかなかかっちょいい。

上側のスイッチ類はパターンのロードセーブ、コピー&ペースト用。

真ん中はI/Oエクスパンダ―のMCP23S17とEEPROMのAT24C1024。

回路図



基板図(配線まだ)


基板は真ん中に電源ラインがあらかじめ配線されているユニバーサル基板を使ってみるつもり。

ずいぶん前に買ったはいいものの使いどころが難しい。

スイッチ類回路図


スイッチ類基板図(配線まだ)

2016年11月14日月曜日

ベースマシンでMCP23S17を使う&SPIバス・バッファ(TC74HC541)基板を使う。

I/OエキスパンダーのMCP23S17を使ってIOピンを増やしてみた。

配線図

ファームウェア
BaseMachineUIControler Classのテスト
https://developer.mbed.org/users/ryood/code/BaseMachine_UIController_Test/ Revision:7

BaseMachine本体
https://developer.mbed.org/users/ryood/code/BaseMachine/ Revision:4

UI系ボード


ブレッドボード上のMCP23S17につなげたのはタクトスイッチを8個並べたもので、再生中のシーケンスパターンを切り替える予定。まだパターン切替の機能は実装していないが、MCP23S17から値を読み取ってLCDに表示するところまでは動作確認した(^q^/

MCP23S17の仕様としては単体でもあと8bit増やせるし、MCP23S17を何個か並べてデバイスのアドレスを指定すれば16bit×8まで信号線を増やさずに拡張できるようだ。 

Synth系ボード


NucleoのSPI出力はSPIバスバッファに入れてDCO、DCF、DCAに分配する。SPIバスバッファから信号が出力されているのと、DCOが制御できているのを確認した。


2016年11月5日土曜日

Nucleo F401RE(mbed)でMCP23S17を使う。 バッファリングしてみる。

使っているライブラリがTickerで一定間隔で状態を問い合わせる仕組みになっているので毎回MCP23S17にSPIで問い合わせると通信コストが高そうなので、バッファリングしてみることを考えてみた。

mbedのソースの中までは見ていないが、SPI通信中は値が取得できるまで待ち状態になってそうな気がする。必要な値を毎回MCP23S17にぶら下がっているデバイスに問い合わせているとその間他の処理ができなくなる可能性が高いと思う。

MCP23S17のGPIOを全部読み取りとして使えば定期的にSPI経由でポーリングして2Byte(MCP23S17はGPIOがPortAx8bit、PortBx8bit)を本体のNucleoにバッファリングしてやれば、SPI通信のコストは下げられそうだ。

4×4のキーパッドを使うとGPIOのうち4本は書き込みに使わないといけないので、バッファリングの仕組みが複雑になる。キーパッドはあきらめてタクトスイッチとロータリーエンコーダーだけにしてテストしてみた。


mbed repogitory:
https://developer.mbed.org/users/ryood/code/ExioBufferdController_Test/

SPI通信のようす


PortBにつなげているタクトスイッチx8を適当に押しながら測定


ch1:sck ch2:MISO

拡大


Nucleo F401RE(mbed)でMCP23S17経由でキーパッド(4×4)を使えるようにする。

Classのコンストラクタでハードウェアに結びついているオブジェクトをポインタ渡しに変更した。

他のクラスと一緒にキーパッドが使えなかった原因は、クラスの内部で使っているTicker(タイマー)が原因だったようだ。キーパッド用のクラスではMCP23S17とのSPI通信を普通のメンバ関数で行っていたが、他のクラスはチャタリング対策としてTickerの割込み処理ハンドラ内で行っていた。

キーパッド用のクラスのメンバ関数内でSPI通信を行っている途中で、他のクラスからタイマー割り込みがかかってSPI通信がおかしくなっていたのかも?

また、Tickerのスタートのタイミングを初期化後にまとめて行えるように変更した。内部PullUpの指定等(これもSPI経由で行う)を行う前に、Tickerがスタートしてしまうと動作がおかしくなる。

ちょこちょこ直して、ひとまず、DebouncerIn(DigitalInのチャタリング対策)、PinDetect(ピンの状態の変化検出)、RotaryEncoder、Keypad4x4をMCP23S17経由で同時に動かすことができた。

ベースマシンのシーケンサー本体はRTOS::Timerでタイミングをとっているので、コンフリクトした場合はUI系のタイマーもTickerではなくRTOS::Timerを使うように修正した方がいいかもしれない。(参考:「mbedのRtosTimerをClassの中で使う。」)なんか自信ないなあ(@@;


mbed repogitory:
https://developer.mbed.org/users/ryood/code/ExioController_Test/ Revision:15

SPI通信のようす



ch1:MOSI ch2:SCK

拡大


2016年11月2日水曜日

Nucleo F401RE(mbed)でMCP23S17経由でロータリーエンコーダーを使えるようにする。

配線図



MCP23S17のPA0..PA4はロータリーエンコーダー、PA6、PA7はタクトスイッチ。PortBは4×4のキーパッドをつないだ。重なって見づらいが、MCP23S17のPin5..8(GPB4..GPP7)は10kΩのRでPullDownしている。

mbed repogitry:
https://developer.mbed.org/users/ryood/code/ExioController_Test/ Revision:10

main.cpp

#include "mbed.h"
#include "rtos.h"
 
#include "ExioMcp23s17PinDetect.h"
#include "ExioMcp23s17RotaryEncoder.h"
#include "ExioMcp23s17Keypad4x4.h"
 
SPI Spi(PC_12, PC_11, PC_10); // SPI3: mosi, miso, sclk
//SPI Spi(PA_7, PA_6, PA_5); // SPI1:  mosi, miso, sclk
 
// MExioMcp23s17(int hardwareaddress, SPI& spi, PinName nCs, PinName nReset);
ExioMcp23s17 Exio(0x00, Spi, PD_2, PA_13);
 
bool state = false;
uint8_t count = 0;
 
// トグル動作
void pa6Falled() {
    state = !state;
}
 
// インクリメント
void pa7Falled() {
    count++;
}
 
int main()
{
    //ExioMcp23s17Keypad4x4Test();
    
    printf("\r\n\n*** ExioMcp23s17Controller test ***\r\n");
    
    Exio.reset();
 
    // set PORTA[6:7] as inputs
    ExioMcp23s17PinDetect PinA6(Exio, ExioPortA, 6);
    ExioMcp23s17PinDetect PinA7(Exio, ExioPortA, 7);
    
    // assign callback functions
    PinA6.attach_asserted(&pa6Falled);
    PinA6.setAssertValue(0);
    PinA6.setSampleFrequency();
    
    PinA7.attach_asserted(&pa7Falled);
    PinA7.setAssertValue(0);
    PinA7.setSampleFrequency();
    
    // set PORTA[0:1], PORTA[2:3] as a Rotary Encoder
    //    ExioMcp23s17RotaryEncoder(
    //    ExioMcp23s17& device, ExioPort port, int pin1, int pin2,
    //    int min = 0, int max = 100, int val = 50
    //);
    ExioMcp23s17RotaryEncoder RotEnc1(Exio, ExioPortA, 0, 1, 0, 100, 50);
    RotEnc1.setInterval(500);
    ExioMcp23s17RotaryEncoder RotEnc2(Exio, ExioPortA, 2, 3, 0, 100, 50);
    RotEnc2.setInterval(500);
   
    // set PORTB as Keypad 4x4
    //ExioMcp23s17Keypad4x4 keypad(Exio, ExioPortB);
  
    while (true) {
        int reVal1 = RotEnc1.getVal();
        int reVal2 = RotEnc2.getVal();
        printf("state:%d count:%d RE1:%d RE2:%d \r\n", state, count, reVal1, reVal2);
        //Thread::wait(10);
    }
}

メモ:


4x4のキーパッドも単体では動作できたが、他の機能と同時に動作させるとうまく動かない。Classを適当に作ったのでRTOS等のタイミングがらみか。

ハードウェアに結びついているオブジェクトをコンストラクタで参照渡ししているのも問題かも(^q^; ポインタ渡しに直しておくか。

今日は疲れたので一旦キーパッド入力はあきらめて、ベースマシンのパターン切替えにはタクトスイッチを8個並べる方向で考えてベースマシン本体と結合してみる。

2016年10月31日月曜日

mbedのPinDetectをHackしてMCP23S17で使えるようにする。

mbedのチャタリング対策用のライブラリのPinDetectのソースをよく見てみると割込みではなくてポーリングで処理していた。

中で使っているのはDigitalInなのでこれをMCP23S17経由で読み取れるようにしてみた。

MCP23S17のPortB:0だけトグル動作。

mbed repository:
https://developer.mbed.org/users/ryood/code/ExioController_Test/ Revision:4

ExioController_Test/main.cpp
#include "mbed.h"
#include "rtos.h"
#include "ExioMcp23s17.h"
#include "ExioMcp23s17DigitalIn.h"
#include "ExioMcp23s17PinDetect.h"

SPI Spi(PC_12, PC_11, PC_10); // SPI3: mosi, miso, sclk
//SPI Spi(PA_7, PA_6, PA_5); // SPI1:  mosi, miso, sclk

// MExioMcp23s17(int hardwareaddress, SPI& spi, PinName nCs, PinName nReset);
ExioMcp23s17 Exio(0x00, Spi, PD_2, PA_13);

// トグル動作
bool pb0state = false;
void pb0Falled() {
    uint8_t data = Exio.readPort(ExioPortA);
    pb0state = !pb0state;
    if (pb0state) {
        data |= (1 << 0);
    } else {
        data &= ~(1 << 0);
    } 
    Exio.writePort(ExioPortA, data);
}

void pb1Falled() {
    Exio.writePort(ExioPortA, (1 << 1));
}

void pb2Falled() {
    Exio.writePort(ExioPortA, (1 << 2));
}

void pb3Falled() {
    Exio.writePort(ExioPortA, (1 << 3));
}

void pb4Falled() {
    Exio.writePort(ExioPortA, (1 << 4));
}

void pb5Falled() {
    Exio.writePort(ExioPortA, (1 << 5));
}

void pb6Falled() {
    Exio.writePort(ExioPortA, (1 << 6));
}

void pb7Falled() {
    Exio.writePort(ExioPortA, (1 << 7));
}

void ExioMcp23s17PinDetectTest()
{
    printf("\r\n\n*** ExioMcp23s17PinDetect test ***\r\n");
    
    Exio.reset();
    
    // set PORTA as output
    Exio.ioDirection(ExioPortA, 0x00);
    
    // create PORTB pins as inputs via ExioMcp23s17PinDetect
    ExioMcp23s17PinDetect PinB0(Exio, ExioPortB, 0);
    ExioMcp23s17PinDetect PinB1(Exio, ExioPortB, 1);
    ExioMcp23s17PinDetect PinB2(Exio, ExioPortB, 2);
    ExioMcp23s17PinDetect PinB3(Exio, ExioPortB, 3);
    ExioMcp23s17PinDetect PinB4(Exio, ExioPortB, 4);
    ExioMcp23s17PinDetect PinB5(Exio, ExioPortB, 5);
    ExioMcp23s17PinDetect PinB6(Exio, ExioPortB, 6);
    ExioMcp23s17PinDetect PinB7(Exio, ExioPortB, 7);

    // assign callback functions
    PinB0.attach_asserted(&pb0Falled);
    PinB0.setAssertValue(0);
    PinB0.setSampleFrequency();
    
    PinB1.attach_asserted(&pb1Falled);
    PinB1.setAssertValue(0);
    PinB1.setSampleFrequency();
    
    PinB2.attach_asserted(&pb2Falled);
    PinB2.setAssertValue(0);
    PinB2.setSampleFrequency();
    
    PinB3.attach_asserted(&pb3Falled);
    PinB3.setAssertValue(0);
    PinB3.setSampleFrequency();
    
    PinB4.attach_asserted(&pb4Falled);
    PinB4.setAssertValue(0);
    PinB4.setSampleFrequency();
    
    PinB5.attach_asserted(&pb5Falled);
    PinB5.setAssertValue(0);
    PinB5.setSampleFrequency();
    
    PinB6.attach_asserted(&pb6Falled);
    PinB6.setAssertValue(0);
    PinB6.setSampleFrequency();
    
    PinB7.attach_asserted(&pb7Falled);
    PinB7.setAssertValue(0);
    PinB7.setSampleFrequency();
    

    // LED Check (PortA)
    for (int i = 0; i < 8; i++) {
        Exio.writePort(ExioPortA, 1 << i);
        wait(0.1);
    }
    Exio.writePort(ExioPortA, 0x00);
    
    while (true) {
    }
}

int main()
{
    ExioMcp23s17PinDetectTest();
}

2016年10月30日日曜日

I/OエキスパンダーのMCP23S17をNucleo F401RE(mbed)で使ってみる。 その2

MCP23S17からの割込みが効かないとプログラミングに制約が出るのでもう少し。

前回割り込みが効かなかったのはプログラミングのミスだったと思う。mbedのMCP23S17用のライブラリもhttps://developer.mbed.org/users/stjo2809/code/MCP23S17/に変更して割込みのテストをしてみた。こちらのLibraryはhttps://developer.mbed.org/users/romilly/code/MCP23S17/より低レベルというかレジスタ操作のラッパーという感じだ。

配線図


割込み線(MCP23S17 -> Nucreo)をPA_14に割り当て、ResetをPA_13に割り当てた。Resetは別に使わなくてもいい。

(追記:2016.11.09)

起動時にResetを掛けておかないとMCP23S17がうまく動作しないようです。

(/追記)

mbed repository:
https://developer.mbed.org/users/ryood/code/Nucleo_MCP23S17_Test/ Revision:3

/*
 * MCP23S17 Test
 *
 * MCP23S17 Library:
 * https://developer.mbed.org/users/stjo2809/code/MCP23S17/
 *
 * mbed:  rev 121
 * mbed-rtos: rev 117
 *
 * Created: 2016.10.28
 *
 */

#include "mbed.h"
#include "rtos.h"
#include "MCP23S17.h"

SPI Spi(PC_12, PC_11, PC_10); // SPI3: mosi, miso, sclk
//SPI Spi(PA_7, PA_6, PA_5); // SPI1:  mosi, miso, sclk

InterruptIn Mcp23s17Int(PA_14);

// MCP23S17(int hardwareaddress, SPI& spi, PinName nCs, PinName nReset);
MCP23S17 Mcp23s17(0x00, Spi, PD_2, PA_13);

void dataChanged()
{
    char data = Mcp23s17.intcapb();
    wait_us(1);
    Mcp23s17.gpioa(data);
}

int main()
{
    printf("\r\n\n*** MCP23S17 Test ***\r\n");

    // Reset MCP23S17
    Mcp23s17.reset();
    
    // PORTA output
    Mcp23s17.iodira(0x00);

    // PORTB input
    Mcp23s17.iodirb(0xFF);
    // PORTB pull-up
    Mcp23s17.gppub(0xFF);
    // PORTB invert polarity
    Mcp23s17.ipolb(0xFF);
    // PORTB enable on change interrupt
    Mcp23s17.gpintenb(0xFF);
    
    // Attach the callback function
    Mcp23s17Int.rise(&dataChanged);
    
    // LED Check
    for (int i = 0; i < 8; i++) {
        Mcp23s17.gpioa(1 << i);
        wait(0.1);
    }
    Mcp23s17.gpioa(0x00);
    
    while (true) {
    }
}

SPI信号


MOSI

ch1:sck ch2:mosi

拡大

2byte目と3byte目の間で一瞬MOSIがLになっている。SPI Mode 0の場合SCKの立ち上がりで評価するので問題ないが、高速な場合誤動作するかも?

MISO

ch1:sck ch2:miso

拡大


MISOが無出力のとき、Lレベルがふらふらしているので、Pull-Downしておいた方が良さそう。

MISO 10kΩでPull-down

MISO 3.3kΩでPull-down

3.3kΩの方が多少きれい。3.3V駆動でロジック・レベルがHの時3.3V/3.3kΩ=1mA消費することになる。

CS

ch1:sck ch2:cs
char data = Mcp23s17.intcapb();
Mcp23s17.gpioa(data);
と読み込み、書き込みを連続した状態で測定。(Read:3byte、Write:3byte)誤動作の可能性があるので間にウェイトを入れた方が良さそう。

Interrupt

ch1:sck ch2:intb

Active-highに設定しているのでPinの状態に変化があった時INTBがHになり、INTCAPBを読み込んだ時Lに戻るのでこれで良さそう。

メモ:


ベースマシンはNucleo2台使いをやめてI/Oエクスパンダ―でI/Oピンを増やす方向で。SPI通信の割込みが効かなくなる原因追及に疲れたし(^q^; 細かい実験用にNucleoを1台空けたい。

mbedのクラスが使えなくなるので、タクトスイッチ(トグル動作)、ロータリーエンコーダー、キーパッドの読み取りを自前で書かないとダメか(@@;

2016年10月28日金曜日

I/OエキスパンダーのMCP23S17をNucleo F401RE(mbed)で使ってみる。

I/OエキスパンダーのMCP23S17をNucleo F401RE(mbed)で使ってみる。

ライブラリはRomilly CockingさんのMCP23S17 https://developer.mbed.org/users/romilly/code/MCP23S17/を使った。

配線図


配線はArduinoの場合とほぼ同じ。Nucleo F401REにはSPIが3つあるのでSPI3を使ってみた。

mbed:
https://developer.mbed.org/users/ryood/code/Nucleo_MCP23S17_Test/ Revision:2

/*
 * MCP23S17 Test
 *
 * https://developer.mbed.org/users/romilly/code/MCP23S17/
 *
 * mbed:  rev 121
 * mbed-rtos: rev 117
 *
 * Created: 2016.10.28
 *
 */

#include "mbed.h"
#include "rtos.h"
#include "MCP23S17.h"

#define OPCODE (0x40)

SPI Spi(PC_12, PC_11, PC_10); // SPI3: mosi, miso, sclk
//SPI Spi(PA_7, PA_6, PA_5); // SPI1:  mosi, miso, sclk

// MCP23S17(SPI& spi, PinName ncs, char writeOpcode);
MCP23S17 Mcp23s17(Spi, PD_2, OPCODE);

int main()
{
    printf("\r\n\n*** MCP23S17 Test ***\r\n");

    // PORTA output
    Mcp23s17.direction(PORT_A, 0x00);

    // PORTB input
    Mcp23s17.direction(PORT_B, 0xFF);
    // PORTB pull-up
    Mcp23s17.configurePullUps(PORT_B, 0xFF);
    
    // LED Check
    for (int i = 0; i < 8; i++) {
        Mcp23s17.write(PORT_A, (1 << i));
        wait(0.2);
    }
    Mcp23s17.write(PORT_A, 0x00);
    
    while (true) {
        char data = ~Mcp23s17.read(PORT_B);
        Mcp23s17.write(PORT_A, data);
        
        //printf("%02x\r\n", data);
    }
}

メモ:

ポーリングだとSPI3を使っても動作できたが、割り込みは試してみたがうまく動かせなかった。MCP23S17から割込み信号が出力されない。

ポーリングで動作させてもオシロのプローブをSCK/MOSIあたりにあてるとLEDがふんわりと明るくなって点滅する。外せば正常。


I/OエキスパンダーのMCP23S17をArduinoで使ってみる。

Nucleo64を使っても入出力ピンが足りなくなりそうなので、IOエキスパンダーのMCP23S17を仕入れてみた。まずはArduinoでテスト。

「真夜中の工作室」さんの記事と「きむ茶工房ガレージハウス」さんの記事を参考にした。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
2byte目 レジスタ・アドレス
3byte目 送受信データ
という手順で行う。受信の場合はMasterからダミーデータを送信して受信した。

I2C版のMCP23017も慣れれば使えそうだが、さらにややこしそうな感じだ。通信速度も速いので線数が許すなら最初はSPI版の方が楽かも。

SPI/I2C経由でI/Oを16bit拡張するならATMega328Pやピンコンパチで値段の安いATMega88VをSlaveとして使うという手もある。