MCP2210 Library

After a couple of weeks’ coding and testing, I finally finished the initial version of the MCP2210 C++ library for Linux. This library utilizes functions from Signal 11‘s HID API (hidraw) to communicate over the USB HID interface.

You can get the latest source from here, or you can pull directly from github using the command below:

git clone git://github.com/kerrydwong/MCP2210-Library.git

This library exposes all the functionalities specified in the device datasheet.

The following code snippet shows how to generate a rectangular wave on the GP0 pin by rapidly toggling it on and off:

#include "mcp2210.h"

int main(int argc, char** argv) {
    int r = 0;

    hid_device *handle;

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

    /**
     * Configure GPIO0 direction to output
     */
    GPPinDef def = GetGPIOPinDirection(handle);    
    def.GP[0].GPIODirection = GPIO_DIRECTION_OUTPUT;

    r = SetGPIOPinDirection(handle, def);

    ///< Generate a rectangular wave by toggling GP0.
    while (1)  {
        def.GP[0].GPIOOutput = 1-def.GP[0].GPIOOutput;
        r = SetGPIOPinVal(handle, def);
    }
    
    /**
     * release the handle
     */
    ReleaseMCP2210(handle);

    return 0;
}

You should see a rectangular wave at GP0 (Pin 5) with a frequency of roughly 160 Hz.

The function calls are pretty straight forward. Most “Set” functions take the current device handle and the input data definition (e.g. SPITransferSettingsDef, GPPinDef, etc.) and returns a status code. A typical “Get” function call takes the current device handle and returns the corresponding data definition.

All SPI data transfer is handled via a single function call SPIDataTransfer, the function definition is shown below:

SPIDataTransferStatusDef SPIDataTransfer(hid_device *handle, byte* data, int length);

Basically, you supply the beginning address of the data to be transferred and the length of the data and when the call returns, the SPIDataTransferStatusDef contains the result along with the call status.

struct SPIDataTransferStatusDef {
    /**
     * Number of bytes received
     */
    unsigned int NumberOfBytesReceived;
    
    /**
     * SPI transfer engine status
     * 0x10: SPI transfer finished, no data to send
     * 0x20: SPI transfer started, no data to receive
     * 0x30: SPI data accepted, command completed successfully
     *
     */
    unsigned int SPIEngineStatus;
    
    /**
     * The buffer contains received data. 
     * Actual amount of data is determined by NumberOfBytesReceived.
     */
    unsigned char DataReceived[60];
    
    /**
     * The error code returned
     */    
    int ErrorCode;
};

For more detailed information, you can take a look at the header file (mcp2210.h) or the doxygen generated documentation located under the doc directory.

The project was created using Netbeans 7.2 and was compiled under Ubuntu 12.04 (64 bit). To compile the code, make sure that you have the build essentials installed and also you will need libudev.

sudo apt-get install libudev0

Note, the process must have write access to the USB device under Linux. This means that either you will need to use sudo to run your code or you will need to setup rules to allow read/write access to the device using instructions provided on signal11. I have provided a UDEV rule file (99-hid.rules), and you may need to add it to the /etc/udev/rules.d directory.

I will provide more sample code in the coming weeks. Please drop me a line if you run into any issues.

Be Sociable, Share!

34 Comments

  1. Pavel says:

    Hello,

    thank You for great job. Today I tried my MCP2210 with your library and test program, but no GPIO output activity I observed. With MCP2210 Utility under winXP it works fine. Are you sure, that all initialization procedures have been done in your test program? When i tried to read manufacturer i got empty string, the same fake activity I observed when I read some char from eeprom.

    Thanks in advance.

    Pavel

    • kwong says:

      Hi Pavel,

      Yes, I had tested the provided code using multiple chips. One thing I forgot to mention is that you will need to follow signall11’s directions to change device permissions on Linux, otherwise you will need to use sudo to run your code.

      Yes, without root access to the devices, you will not be able to manipulate the settings.

      Could you let me know if you are able to get it working by using sudo?

    • kwong says:

      One more thing, if you are still having problems, could you tell me what is the returned status code? I just added a note in the blog posting above about the permissions. Thanks.

  2. Russel Haynes says:

    I’ve been working on getting the MCP2210 to run with Java on a Mac. I’m using the javahidapi based on signal11’s work. It was really nice to come across your code for linux and use it as a model.

    Unfortunately, I’ve been having a difficult time getting SPI transfers to work reliably. After writing something like:

    42 03 00 00 13 40 00

    Part of the time, I get back this:

    42 00 00 20

    Meaning the write happened but nothing was read back. Part of the time I get this:

    42 f8

    Meaning the command was received but there was a SPI data transfer in progress. Also, part of the time I get this:

    42 00 03 10 00 00 00

    Meaning it finished and read correctly. The thing is, I think I have the connection configured synchronously so this sort of thing shouldn’t happen.

    Anyway, I know this is a different environment but I thought I would just take a shot at describing it and see if this sounds like a problem you solved.

    • kwong says:

      Hi Russel,

      I have not run into the problem you described. The SPI clock needs to be issued until the results are received. Did you try this? If you continue issuing the SPI transfer command you should see the engine status comes back as 0x10 eventually.

      Anyway, I am putting together a tutorial using MCP2210 SPI and should appear on this blog in the coming days.

  3. Russel Haynes says:

    Hi Kerry,

    I’ve got this working. In my particular case, the SPI results are read out during the second byte of a SPI transfer so sending out more clock cycles will not help. It turns out, if you receieve a 0x20 in byte 3, you need to initiate another SPI transfer but set the number of bytes (specified in byte 1) to zero. I got that from Microchip support and it seems to work well.

    In the case of an F8 in byte 1, you need to start the whole SPI transfer over as if it never happened. I placed a 25ms sleep before retrying a SPI transfer and this seems to minimize the number of re-trys.

    Thanks for looking over my question and giving me your thoughts on it.

    • kwong says:

      Hi Russel,

      Yep, that’s the exactly approach you would need. The SPI command needs to be issued continuously until all the data has been received as indicated by the engine status as I mentioned previously. I have found that the number of bytes field does not need to be 0 though (at least from my testing), it seems that that field is not used the subsequent SPI calls are made when retrieving the result.

    • Hi guys,

      I have a very similar issue. I’m getting lots of random f8 errors. In my application I basically have to put a SPICancelTransfer in the end, or I can often never run it again without resetting the MCP2210. My transfer is a single 3 byte transfer. I’m not reading anything back, its TX only… I’m really wondering whether this is a hidapi problem at this point. Also sometimes randomly my CS bit patterns are completely messed up i.e. don’t match at all what I set. This is so frustrating…..

  4. vchene says:

    Hi Kerry,

    Thank you for your work it was a great help.

    But there’s something not really clear about how the MCP2210 handles the data size and transmission size:

    If I configure the SPI to send 2 bytes per transmission and I want to send let’s say 4 bytes.
    Will the MCP trigger 2 transmissions or only one and give up on the remaining data ?
    So far it seems the data sent are truncated (not sent) when bigger than a transmission size.

    Is it actually how it works ? The only point of the message size is, in case it’s smaller than the transmission size, to allow the master keeping the CS and the clock active until the transmission is done ?

    Any information on this would be greatly appreciated !

    • kwong says:

      Yes, as you observed the buffer size needs to match the length of the data you are sending. As you concluded, if the buffer length is larger then the size of the data, 0’s will be clocked in as the buffer is initialized to 0 with the maximum length.

  5. Vic says:

    Hi Kerry,

    How should one handle the toggling of GPIO’s after SPI has been configured with one GPIO dedicated to CS for the downstream device (tilt sensor)?
    The tilt sensor will be periodically queried for its data, but GPIO changes will be sporadic. The issue is that the GPIO Levels are all set
    in two bytes in command 0x30, datasheet p.49, but what does one put for the CS? 0x00?

    Thanks! Great work, btw!

    • kwong says:

      Hi Vic,

      Thanks for your comments. The way MCP2210 works is that once you have designated a pin as CS, it will automatically select/deselect when you issue SPI command. You can see this in my example here (http://www.kerrywong.com/2012/10/22/mcp2210-library-mcp3204-spi-adc/)

      ….
      //chip select is GP1
      def.ActiveChipSelectValue = 0xfffd;
      def.IdleChipSelectValue = 0xffff;
      ….

      • Vic says:

        Hi Kerry,

        Once configured for CS, GPIO pins function extremely well for their intended purpose of serving SPI slaves. However, standalone GPIO control for individual pins seems not to work! I first read GET levels, assert a pin level, write SET levels, and read back GET levels for final confirmation and comparison. However, the MCP2210 never responds correctly, even if I configure the device with Microchip’s own utility. Is this chip not intended for general purpose GPIO’s, but specifically for SPI slaves or alternate functions? Microchip documentation points in that direction, and their demo kit seems to fit that model also, since they SPI slave an EEPROM, Temp Sensor, IO Expansion, and A/D.

        Thank you, and best regards,
        Vic

        • kwong says:

          Hi Vic,

          Could you give the example in this post a try? I used it to toggle GPIO0 and it worked fine. Did you try configure each individual pins separately? If you could post your code I can give it a try when I get back tonight.

          • Edgardo says:

            Kerry, In your example you are toogling the GPIO0, but you are keeping track of the status of the GPIO.
            When you do: def.GP[0].GPIOOutput = 1-def.GP[0].GPIOOutput; , def.GP[0].GPIOOutput is in the memory of your computer, not in the output register of the MCP2210.
            I think Vic is trying to use the GPIO as both input and output at the same time. If you set the levels of the GPIO, there is no need to read them back (you just set the level yourself). If the GPIO is defined as input, then setting levels have no meaning since the level is forced by an external voltage.
            So if the pin is Output, you can set the levels (to GND or VCC) but you probably wont be able to read the voltage (I should check the datasheet to see if it returns the flipflop value for the output bit).
            If the pin is Input, there is no way of setting the voltage.

            I wonder if the MCP2210 supports Open Colector output.

            Best Regards

  6. Rossi Eugenio says:

    Can I have all this information, with the operating system windows, xp, 7 ..?

  7. Prateek says:

    Hi kwong,
    Thanks for a good implementation of MCP2210 functionality.
    When i am compiling and running mcp2210test inside MCP2210-Library-master/dist/Debug/GNU-Linux-x86 it gives me Broken PIPE error that is -EPIPE.
    TestGPIO -> GetChipSettings -> SendUSBCmd -> hid_write -> write; write (return value -1), i have used udev rule to change the permission of device node.

    Please help.

    Thanks and Regards,
    Prateek

  8. E says:

    Hi Kerry,

    I see that the datasheet and your code set SPI transmissions to a maximum of 64 bytes (actually 60 bytes without command/response headers). What happens if I need to send more than 64 bytes or need a response that is greater than 64 bytes?

    thanks!

  9. E says:

    Thanks for the response, Kerry.

    In looking at SPIDataTransfer() it looks like a few response cases when the data is longer than 64 bytes. Something like…
    if (rsp[1] == 0xF7) { //SPI Data Not Accepted – SPI bus not available (the external owner has control over it)

    }
    else if (rsp[1] == 0xF8) { //SPI Data Not Accepted – SPI transfer in progress – cannot accept any data for the moment

    }
    else if (rsp[1] == 0) {
    if (rsp[3] == 0x20) { //0x20 – SPI transfer started – no data to receive
    }
    else if (rsp[3] == 0x30) { //SPI transfer not finished; received data available

    }
    else if (rsp[3] == 0x10) { //SPI transfer finished – no more data to send

    }
    }

    BTW, i’m not seeing why you’re incrementing by 4 on the line that does:
    cmd[i + 4] = data[i];

    Isn’t this a byte array being assigned byte values?

    Thanks!

    p.s. I don’t have my hardware fully setup yet so I’m just going by code…sorry if I’m missing something.

  10. sha says:

    Hi Kerry,

    is it possible to cross-compile the library with arm-none-linux-gnueabi-g++?

    Thanks!

  11. Prateek says:

    Hi kerry,
    I am using following function to set and reset pins of usb-spi expander.
    I am configuring GP[0] to GP[8] as output and able to change state (0 or 1) of GP[0] to GP[4] but unable to change state of GP[5] to GP[8].
    State of GP[5] to GP[8] always remains high- 3.2V.
    Any idea what could cause this? How can i change state of GP[5] to GP[8]?

    void TestGPIO(hid_device* handle, int gpio, int high_low) {
    ChipSettingsDef chipDef;

    /**
    * Set GPIO to be output
    */
    chipDef = GetChipSettings(handle);
    for (int i = 0; i < 9; i++) {
    chipDef.GP[i].PinDesignation = GP_PIN_DESIGNATION_GPIO;
    chipDef.GP[i].GPIODirection = GPIO_DIRECTION_OUTPUT;
    chipDef.GP[i].GPIOOutput = 0;
    }
    int r0 = SetChipSettings(handle, chipDef);
    if (r0) {
    printf("Error in setting chip setting\n");;
    }

    /**
    * Configure GPIO0 direction to output
    */
    GPPinDef def = GetGPIOPinDirection(handle);
    def.GP[gpio].GPIODirection = GPIO_DIRECTION_OUTPUT;
    def.GP[gpio].PinDesignation = GP_PIN_DESIGNATION_GPIO;
    int r = SetGPIOPinDirection(handle, def);
    if (r != 0){
    printf("\n\nError: Line:- %d errno:- %d\n\n", __LINE__, r);
    }
    def.GP[gpio].GPIOOutput = high_low;
    r = SetGPIOPinVal(handle, def);
    if (r != 0){
    printf("\n\nError: Line:- %d errno:- %d\n\n", __LINE__, r);
    }
    }

    Thanks.

  12. Cicoz says:

    Hi Kerry,

    I’m trying to WR/RD a 25AA128 eeprom via SPI using the MCP2210.
    When I try to WR/RD for example 10 bytes, all work well and after one try the spi engine status became 0x10, when I try with 12 bytes, after 3..4..5.. attemps it became 0x30, and then all data became corrupted… Any idea what could cause this? I’m trying also with a Sleep after each attemp…

    Thx

  13. Richard says:

    Hi,

    I am trying to use your library on Windows 7. All methods return -2, seems to be a problem with WriteFile in the
    HIDAPI library. I could not find a solution so far, would be nice if you could provide me any help.

    Thanks in advance

    Richard

  14. Bob Selby says:

    Hi, I’m trying to use your mcp2210 library but have run into a snag in that I consistently get a “pipe broken” error whenever the code does a write() to the device, from hid_write(). (in my case /dev/hidraw2 on my centos 6.6 system) when called form one of the test routines (eg TestGPIO()).

    Oddly, the IOCTL() calls seem to work OK
    I built the package as per the readme.

    I did have problems with the libudev library – it’s package was installed on my system but there was no libudev.h anywhere on the system so I searched the web and found a file that seems to work. I doubt if that’s the reason though since the problem seems to be rather fundamental to do with the write().

    Any suggestions most welcome.

    Bob

  15. Ken says:

    Nice work. Thanks for developing this. It works great on Ubuntu 17.04.
    -Ken

Leave a Reply