Testing T4 and MCP23S17

Frukost

Well-known member
I am testing an MCP23S17 with one rotary encoder for a bigger project. My intention is to Serial.print the inputs of the rotary encoder as a first step of testing. Unfortunately I get nothing, and therefore I'd be grateful for feedback where I'm going wrong, in particular the configuration of the MCP... Thanks in advance!

The wiring looks like this:
Kopplingsschema.jpg

Code:
#include <Arduino.h>
#include <SPI.h>

// MCP23S17 registers
constexpr uint8_t IODIRA = 0x00;    // IO direction  (0 = output, 1 = input (Default))
constexpr uint8_t IPOLA = 0x02;     // IO polarity   (0 = normal, 1 = inverse)
constexpr uint8_t GPINTENA = 0x04;  // Interrupt on change (0 = disable, 1 = enable)
constexpr uint8_t DEFVALA = 0x06;   // Default comparison for interrupt on change (interrupts on opposite)
constexpr uint8_t INTCONA = 0x08;   // Interrupt control (0 = interrupt on change from previous, 1 = interrupt on change from DEFVAL)
constexpr uint8_t IOCON = 0x0A;     // IO Configuration: bank/mirror/seqop/disslw/haen/odr/intpol/notimp
constexpr uint8_t GPPUA = 0x0C;     // Pull-up resistor (0 = disabled, 1 = enabled)
constexpr uint8_t INFTFA = 0x0E;    // Interrupt flag (read only) : (0 = no interrupt, 1 = pin caused interrupt)
constexpr uint8_t INTCAPA = 0x10;   // Interrupt capture (read only) : value of GPIO at time of last interrupt
constexpr uint8_t GPIOA = 0x12;     // Port value. Write to change, read to obtain value
constexpr uint8_t OLLATA = 0x14;    // Output latch. Write to latch output.

constexpr uint8_t csPin = 10;
constexpr uint8_t ports = 0x20;
constexpr uint8_t interruptPin = 20;
volatile bool interrupted = false;

// ISR functions
void handleInterrupt() {
    interrupted = true;
}

// Write to expander
void expanderWrite(const uint8_t port, const uint8_t reg, const uint8_t data) {
    digitalWrite(csPin, LOW);
    SPI.transfer(port << 1);  // Write mode
    SPI.transfer(reg);
    SPI.transfer(data);
    digitalWrite(csPin, HIGH);
}

// Read from expander
uint8_t expanderRead(const uint8_t port, const uint8_t reg) {
    uint8_t data = 0;

    digitalWrite(csPin, LOW);
    SPI.transfer((port << 1) | 1);  // Read mode
    SPI.transfer(reg);
    data = SPI.transfer(0);
    digitalWrite(csPin, HIGH);

    return data;
}

void setup() {
    Serial.begin(115200);
    SPI.begin();

    pinMode(csPin, OUTPUT);
    digitalWrite(csPin, HIGH);

    pinMode(interruptPin, INPUT_PULLUP);
    attachInterrupt(digitalPinToInterrupt(interruptPin), handleInterrupt, FALLING);

    // Config MCP:
    //  Banks segregated = 1
    //  mirror interrupts = 1
    //  disable sequential mode = 1
    //  slew rate disabled = 0
    //  no hardware adressing = 0
    //  no interrupt pin open drain = 0
    //  interrupt active high = 1
    //  not used = 0

    expanderWrite(ports, IOCON, 0b11100010);

    // Set PORT A registers
    expanderWrite(ports, IODIRA, 0b11111111);    // IO direction, 1 = input
    expanderWrite(ports, GPPUA, 0b11111111);     // Pull-up resistor, 1 = enabled
    expanderWrite(ports, IPOLA, 0b11111111);     // IO polarity, 1 = inverse
    expanderWrite(ports, GPINTENA, 0b11111111);  // Interrupt on change, 1 = enable
    expanderWrite(ports, INTCONA, 0b11111111);   // Interrupt control, 1 = interrupt on change compared with DEFVAL bits
    expanderWrite(ports, DEFVALA, 0b11111111);   // Interrupt comparison value 1 = interrupt on != 1
}

void loop() {
    if (interrupted == true) {
        interrupted = false;
        uint8_t portA = expanderRead(ports, GPIOA);

        for (uint8_t bit = 0; bit < 8; ++bit) {
            Serial.print(String(bitRead(portA, bit)));
        }

        Serial.println();
    }
}
 
Thanks for your reply!

I've looked at several mcp libraries, and I don't doubt their usefulness. However, I'd still need to know how to configure the mcp23s17 - provided that my wiring is correct - so even though I use a library codewise it leads me back to my original question regarding configuration for my particular wiring.

Also, there may be errors in my circuit I'm still unaware of that could render any/all code useless.
 
I don't see any SPI.beginTransaction() in your program to configure the SPI settings.

Even if you intend to write all code from scratch, why not at least give a known-good library a run to check whether your hardware is connected properly?
 
Looking at : github.com/tonton81/MCP23S17/blob/master/mcp23s17.tpp#L49 this shows:
Code:
MCP23S17_FUNC void MCP23S17_OPT::initDefaults() {
  for ( uint8_t addr = 0; addr < 8; addr++ ) {
    [B]bus->beginTransaction(SPISettings(speed,MSBFIRST,SPI_MODE0));[/B]

@tonton81 writes awesome code for a variety of things. As noted, it might be good to follow to test function if not use as a likely well written and tested library solution.
 
I don't see any SPI.beginTransaction() in your program to configure the SPI settings.
Thanks for your feedback. I edited the code, but unfortunately no results:

Code:
SPISettings MCP(20000000, MSBFIRST, SPI_MODE0);

// Write to expander
void expanderWrite(const uint8_t port, const uint8_t reg, const uint8_t data) {
    SPI.beginTransaction(MCP);
    digitalWrite(csPin, LOW);

    SPI.transfer(port << 1);  // Write mode
    SPI.transfer(reg);
    SPI.transfer(data);

    digitalWrite(csPin, HIGH);
    SPI.endTransaction();
}

// Read from expander
uint8_t expanderRead(const uint8_t port, const uint8_t reg) {
    uint8_t data = 0;

    SPI.beginTransaction(MCP);
    digitalWrite(csPin, LOW);

    SPI.transfer((port << 1) | 1);  // Read mode
    SPI.transfer(reg);
    data = SPI.transfer(0);

    digitalWrite(csPin, HIGH);
    SPI.endTransaction();

    return data;
}

(On a side note, if you don't define the SPISettings object, does the SPI class have default settings? If so, what are the default values?)

Even if you intend to write all code from scratch, why not at least give a known-good library a run to check whether your hardware is connected properly?

I have tried alternatives to this code, for instance the adafruit MCP23xxx library. Regardless of using a well established library or using the code above, my conundrum is that i need guidance to configure the expanders IOCON and PORTA/B registries in either case. I understand that a library shows how to configure the hardware, my question regards what settings I need to make for my particular setup. And since there's a risk that my wiring isn't properly setup I wind up in a catch 22 situation. Currently there are too many combinations of variables for me to trial and error my way to a solution. This is why I ask about both code and wiring.
 
Last edited:
Another point regarding your schematic, I believe that the Reset pin should be tied to VDD, and not floating. this is how I run mine. The datasheet has "The on-chip POR circuit holds the device in reset until VDD has reached a high enough voltage to deactivate the POR circuit (i.e., release the device from reset)."
https://ww1.microchip.com/downloads/en/devicedoc/20001952c.pdf

You're absolutely right, good call! And thanks for your guidance for the settings, I'll look into it today!
 
Back
Top