I made a few minor tweaks to the open source MCP2210 library during the past few days, and had also updated the documentation. In my previous post, I illustrated how to manipulate the GPIO pins using this library and today I will show you an example of communicating with a MCP23S08 8 bit IO port expander using SPI.

First, I would like to thank Bogdan Bolocan at Microchip for sending me a couple of MCP2210 evaluation kits for testing purposes. If you recall, I was having some issues with the crystal oscillator circuit and had to resort to an external clock.

Bogdan approached me on this matter and we exchanged a few emails trying to figure out what might have caused this oscillator issue. While no conclusive findings yet, I am leaning towards the stray capacitance in my dev-board wiring as the main culprit which might have been exacerbated by the stringent component requirements of the chip oscillator circuit components. Bodgan suggested that it could also be an issue (i.e. manufacturing defect) with the specific batch of the chip.

Interesting enough though, the evaluation board uses a ceramic resonator rather than a crystal. Many people have suggested that the problem would go away with a resonator in the situation where the circuit wouldn’t start with a crystal. Anyway, I will write back if and when I hear back from Microchip on this issue. In the mean time, you can always use an active clock source like I illustrated in my previous post if you are having similar problems, or you may try using a ceramic resonator instead.

Anyway, in this post I will show you how to use the MCP2210 library to drive the MCP23S08 8 bit port expander chip on the evaluation board. The evaluation board uses GP4 to drive the chip select pin of MCP223S08 and the chip address pins are both tied to the ground. If your setup is different, you will need to change ActiveChipSelectValue, IdleChipSelectValue and the chip address accordingly. More on this a little bit later.

The code below creates a “bouncing ball” effect by lighting up each individual LED connected to MCP23S08 on the evaluation board. The light would run back and forth between LED0 and LED7 for 10 times.

void TestMCP23s08(hid_device* handle) {
    ChipSettingsDef chipDef;

    //set GPIO pins to be CS
    chipDef = GetChipSettings(handle);

    for (int i = 0; i < 9; i++) {
        chipDef.GP[i].PinDesignation = GP_PIN_DESIGNATION_CS;
        chipDef.GP[i].GPIODirection = GPIO_DIRECTION_OUTPUT;
        chipDef.GP[i].GPIOOutput = 1;
    }
    int r = SetChipSettings(handle, chipDef);

    //configure SPI
    SPITransferSettingsDef def;
    def = GetSPITransferSettings(handle);

    def.ActiveChipSelectValue = 0xffef;
    def.IdleChipSelectValue = 0xffff;
    def.BitRate = 6000000l;
    def.BytesPerSPITransfer = 3;

    r = SetSPITransferSettings(handle, def);

    if (r != 0) {
        printf("Errror setting SPI parameters.\n");
        return;
    }

    byte spiCmdBuffer[3];

    spiCmdBuffer[0] = 0x40; //device address is 01000A1A0, write
    spiCmdBuffer[1] = 0x00; //write to IODIR register,
    spiCmdBuffer[2] = 0x00; //set all outputs to low

    SPIDataTransferStatusDef def1;

    def1 = SPISendReceive(handle, spiCmdBuffer, 3);

    spiCmdBuffer[0] = 0x40;
    spiCmdBuffer[1] = 0x0a;

    for (int k = 0; k < 10; k++) {
        //lights up LED0 through LED7 one by one
        for (int i = 0; i < 8; i++) {
            spiCmdBuffer[2] = 1 << i;
            SPIDataTransferStatusDef def2 = SPISendReceive(handle, spiCmdBuffer, 3);
            usleep(20000ul);
        }
        //lights up LED7 through LED0 one by one
        for (int i = 0; i < 8; i++) {
            spiCmdBuffer[2] = 0x80 >> i;
            SPIDataTransferStatusDef def2 = SPISendReceive(handle, spiCmdBuffer, 3);
            usleep(20000ul);
        }
    }
}

In order for the CS pin on MCP23S08 to be automatically selected during an SPI call, the GPIO pin used to control the chip select must be configured correctly (on the evaluation board, GP4 is used to control MCP23S08’s chip select).

ActiveChipSelectValue is set to 0xffef so that the fourth bit (GP4) is 0 when active (i.e. chip is selected) and IdleChipSelectValue is set to 0xffff so that the fourth bit is 1 when the CS line is deselected.

The SPI function call is performed using SPISendReceive. It basically is just a wrapper over SPIDataTransfer. The SPIDataTransfer function returns right away after the SPI call, but the results may not have been received. The status of the SPI transfer can be checked using the SPIEngineStatus field in the returned SPIDataTransferStatusDef structure. So depending on the engine status, further SPI calls are needed to clock the result data out, until there is no more data to receive.

Since most of the SPI calls do anticipate returning data, I created a separate function SPISendReceive to simplify the process a little bit. You can always use SPIDataTransfer directly if you need finer control of the process.

In the code above, you can also see that prior to setting the SPI transfer parameters, I first called GetSPITransferSettings to retrieve the current settings. This is necessary so that the parameters not affected by the set operation need not to be specified explicitly in the SPITransferSettingsDef as they will retain the same values as fetched. This technique is used throughout the examples in this code library.

The main function is listed below. All the code mentioned here can be found in mcp2210test.cpp.

#include "mcp2210.h"

int main(int argc, char** argv) {
    hid_device *handle;

    /**
     * initializing the MCP2210 device.
     */
    handle = InitMCP2210();

    if (handle == 0) {
        printf("ERROR opening device. Try using sudo.\n");
        exit(-1);
    }

    TestMCP23S08(handle);

    /**
     * release the handle
     */
    ReleaseMCP2210(handle);

    return 0;
}

Again if you have any questions or find a bug in the library, please let me know. You can always find the latest code at github.

Be Sociable, Share!