Arduino Serial I/O expansion, Serial Peripheral Interface (SPI) using MPC23S17

Hello to the group. I am new to Arduino. My electronics knowledge is rusty, I’ve been away from electronic design and programming for about 20 years.

I will be posting my progress with the SPI interface. The novice should be able to follow along, hence I will try to explain everything in full detail. I also would like the feedback from the experienced on this group as to the correctness and anything I post, or that is felt should be mentioned that I may have missed. I am an NOT AN EXPERT or experienced so your comments and questions will be welcome. Hoping to reduce my learning curve by input from others, and allow others inexperienced with SPI interface, to discover its’ power.
I was going to require more then the 14 Input/Output (I/O) pins available for my project. At first I was going to use the 74HC595 & 74HC165 shift registers for my I/O requirements and just bit bang away for my requirements, but after investigation, I discovered the Microchip MCP23S17 SPI chip. Upon realizing the power of this chip, I decided it was worth the extra time it would take it would take to learn how to use it. Because of its versatility, it will be my first preference for any future I/O expansion requirements. First thing I discovered, there are many postings on the internet with people having problems with implementation using the MCP23S17. Many aspects of using this chip are not so obvious. Many solutions provided code, but no explanation as to WHY the steps were required. Others skip relevant information; such as the defaults may have met their project needs. Hopefully this document with your help, will become an all encompassing “how to” document.

First recognize the MCP23S17 provides enhancements over the suggested SPI protocol. Therefore a little extra programming maybe required for SPI implementation into your project.

MCP23S17 Overview spec sheet
• SPI pin support
o SI - Serial In
o SO - Serial Out
o SCK – Clock – speeds up to 10 MHz
o CS (active low) – chip select (SS Slave Select from master)
• (2) - 8 bit General Purpose I/O (GPIO) registers, GPA0-7, GPB0-7
• (3) – hardware addressing pins A0, A1, A2 – allows addressing of up to 8 devices (0 through 7)
• (2) –Interrupts, INTA, INTB
• (1)-RESET (active low) - sets chip to a known state on power up
• (1) – VDD – supply voltage - range -0.3 V to 5.5V DC

Definition: active low means you take the pin to a LOW state, it normally sits at a HIGH, indicated with a solid line above the pin name

My reasons for choosing MCP23S17:

  1. Almost any I/O pin expansion requirement can be provided which is the power of using this chip
    a. (16) I/O ports is higher density then the 74C1595 or 74HC165 - 8 I/O, in about the same space
    b. One chip can support both Input and Output requirements at the same time
    c. Same chip family MCP23017 also provides I2C support
    d. full duplex operation
    e. synchronous data transfer, clock is provided by Master SPI device
    f. Using internal functionality of chip can reduce component count that would be required to implement the same functions outside of the chip
    g. 8 or 16 bit operation

  2. 16 I/O ports per chip
    a. Each port programmable as to behaviour, Input or Output
    b. Programmable Bit inversion on Input, this is useful when using Active Low to represent a 1
    c. Programmable biasing using an internal 100K on Outputs
    d. 25mA source/sink capability per I/O pin (limitation all 16 Output pins cannot simultaneously source 25mA)
    e. Programmable Open drain for each Output pin

  3. Hardware Address Pins
    a. Multiple SPI devices can share one buss (with hardware addressing enabled the MCP23S17 can support up to 8 devices)

  4. Interrupts
    a. Monitor input(s) on single port or multiple ports
    b. Interrupt on change from last state of pin
    c. Interrupt on change from reference state
    d. 2 interrupts available, monitoring A and B register ports independently
    e. Internal ORing of INTA and INTB
    f. INTA and INTB outputs can be set for
    i. active LOW\active HI
    ii. Open Drain – useful for “wired OR”, “implied AND”

  5. Speed (not sure yet how much faster then an I2C interface, but many sites mention this)

  6. Ease of use (once you get past learning the initial set-up of the chip)
    a. Arduino has a built in library
    b. Only program 3 steps required to write/read information with SPI

Definitions:
Full duplex - this allows the Ardunio to write to SPI device while simultaneously reading from the SPI device, half duplex you have to write, then read as two separate steps
Open Drain – allows you to connect several open drain pins together with one biasing resistor, forming a “wired OR” / “Implied AND”. Operation is active LOW. Any input that changes the output to LOW, creates a LOW on the shared Open Drain connection.
Synchronous: data is transferred using a Clock provided by the Master SPI device, this clock is used by the slave devices, (asynchronous – data is transferred by each device using its own internal clock, because the 2 devices clocks are not synchronized together, data could be transferred incorrectly because of clock edges drifting apart)

To date, I have received my Arduino Uno, and I am in the process of learnign programming and ordering the electronic components required for the SPI setup. I should be ready to post in about 4 weeks, with my first experimentation. The first setup will be learning basic operation, just using the default settings were we can.:
• Arduino SPCR and SPSR register settings
• MCP23S17 Logical Address 0 (or 4) with Hardware Addressing disabled, i.e. HAEN=0
• setting I/O ports for Input or Output
• Read/Write operation

Definition
Logical Address – I differentiate hardware and logical addresses. Logical addresses are used by software to access a device. e.g. when HAEN is disabled, the MCP23S17 has one of 1 of 2 possible logical addresses, 0 or 4 (will discuss why later). When HAEN is enabled, the logical address of device becomes the hardware address set on pins A0-A1-A2.

My first ask is, does anyone have some good bookmarked sites explaining the use of the SPI MCP23S17 with Arduino? I have so far found the following sites to have the most useful information:
http://www.gammon.com.au/forum/?id=10892&page=999 Site just posted, good SPI overview
Using Serial Peripheral Interface (SPI) Master and Slave with Atmel AVR Microcontroller | ermicroblog Arduino control register explanation
http://www.myamicus.co.uk/content.php?246-Port-Expansion-using-SPI
http://jimsmindtank.com/atmega168-and-master-spi/ Arduino conrol register
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1279796536 Arduin Forum, relay shield
Reference documents
Arduino Atmel AVR
http://www.atmel.com/dyn/resources/prod_documents/doc8025.pdf Amtel AVR
SPI - Arduino Reference Aeduino SPI Library
Arduino Playground - Spi Arduino SPI library
http://arduino.cc/playground/uploads/Main/arduino_notebook_v1-1.pdf Notebook by B Evans
http://www.atmel.com/dyn/resources/prod_documents/doc2585.pdf Atmel App note : AVR151 Setup and Use of SPI
http://www.atmel.com/dyn/resources/prod_documents/doc2582.pdf Atmel App note: AVR319 SPI with USI module
www.atmel.com/dyn/resources/prod_documents/doc1108.pdf Atmel App Note: AVR320 SPI Master
Microchip MCP23X17
http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en023500 MCP2XS17 website
http://ww1.microchip.com/downloads/en/DeviceDoc/21952b.pdf MCP23X17 Data Sheet
http://ww1.microchip.com/downloads/en/DeviceDoc/80311a.pdf MCP23X17 Errata (A2 = 1 and HAEN = 0)
http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1824&appnote=en027237 MCP23S17 Unique Features

My project? A model railroad turntable self indexing drive system. By using an optical rotary encoder with 1024 steps and an index step, for absolute rotation control, when attached to the axle of turntable, any position can be programmed to within 0.3 degrees. There are simpler setups, but I wanted to be able to anticipate approaching the final destination, and start to slow the drive motor speed down in steps before reaching the destination. Also, the logic will determine if it is faster to go clockwise or counter-clockwise to get to the desired exit track. Why 1024 steps? I could not find an encoder with 256 or 512 steps that supported an index step. The 1024 steps required 10 bits to implement, and an11th bit for flipping the table 180 degrees. The MCP23S17 will interrupt based on one of the 11 bits changing from 1, and will also invert the input bits received. Three requirements met with 1 chip, handling 11 bits, interrupt and bit inversion. It doesn’t get much better then that. Turntables run infrequently on the layout, I’ll use an interrupt so I will not be constantly polling the registers for a change, I maybe will be able to use the same processor to run another planned railroad project. That project will require the MCP23017 I2C 2-wire operation.

I hope the forum is an appropriate spot to post for now. Once everything is checked out, I'll move the relevant information to the playground.

I maybe will be able to use the same processor to run another planned railroad project.

I would hope so, it doesn't sound like this one has much to do :slight_smile:

I’ll use an interrupt so I will not be constantly polling the registers for a change,

Reasonable, are you also driving a stepper motor at the same time? If so will you do that as an interrupt-driven function?

I ask becuase

a) if your not driving the motor with interrupts you may as well poll the encoder as well.
b) If you are you don't realy need the encoder, you know where the motor is (but sill have a sensor to locate a home position).


Rob

I’ll use an interrupt so I will not be constantly polling the registers for a change,

With a MCP23S17 there is an output pin called the in interrupt pin but this does not have to generate an interrupt. I use it to monitor if there has been any change in any one of the 16 inputs, only when I see this one pin change do I then go and read the registers. It saves a lot of time.

Hi Rob

I’ll respond here and if there is more interest in the railroad turntable project I’ll open a topic in the Projects section to move the discussion there, so this thread can remain about SPI.

Quote
I’ll use an interrupt so I will not be constantly polling the registers for a change,
Reasonable, are you also driving a stepper motor at the same time? If so will you do that as an interrupt-driven function?I ask because

a) if your not driving the motor with interrupts you may as well poll the encoder as well.
b) If you are you don't really need the encoder, you know where the motor is (but sill have a sensor to locate a home position).

The motor is DC, the project will also be used by several friends as well as on my own layout. The idea is to be able to add the encoder to the existing motor shaft (axle), without having to change the drive mechanism. Operation today is to apply voltage from a momentary DPDT switch that controls polarity to the motor. Manual operation, hold switch until desired track is reached. If you overshoot/undershoot you toggle switch momentarily to go backwards/forwards. Because we use DC motors, rules out using stepping motor and just using an index step on the encoder. Thanks for the suggestion. I will be using the L293 H motor driver, so it could have been easily implemented. The other consideration I am concerned about, is that when the turntable was built, it may not be absolutely symmetrical. E.g. rotating the turntable 180 degrees, the track may not line up exactly. If it becomes a problem, I have a “nudge” concept, that will move the motor a partial step(s), to improve alignment. By logging the nudge(s) required, the next time that location is moved to, the nudges will occur automatically. Count would be kept in EPROM, so information would not be lost when powering down.
On first pass I believe using interrupts for the encoder will be best. I doubt most turntables would rotate the full 360 degrees in less than 20 seconds so 180 degree reversal would take 10 seconds. 180 degrees will generate 512 encoder steps in 10 seconds, that works out to a pulse to be counted and calculations to be performed maybe once every 20 msec. The chip I am using to read the encoder, also has the capability of doubling the pulse count (using quadrature encoding), so I could conceivably increase the resolution by doubling the step count. Right now, for a 1’ diameter turntable, the alignment works out to within 0.04 inch. Considering that my rail width is 0.07 in, there is still a possibility of a very slight misalignment. Two possibilities, move track, or compensate with electronics. Hmmm, now that I have gone from concept in my mind to paper, it makes sense to increase the resolution right from the beginning, as it is only a software count, and I have not constructed the encoder to date.

I am still keeping in the background that I may still get a second application running using the same processor, so while waiting for pulses from the encoder, I can still be updating the other application. The other application is not processor intensive. It only needs to push out a clock display to several displays distributed around a 30’ by 40’ room. I2C will be a good application, using line drivers to increase the distance and improve noise immunity.

There's at least one shield that already does this, although I suspect you'd rather cook up your own home-grown design: centipede_shield [macetech documentation]

By logging the nudge(s) required, the next time that location is moved to, the nudges will occur automatically. Count would be kept in EPROM, so information would not be lost when powering down.

This concept is good, however I'd be inclined to have a "learn" button that allows the Arduono to remember the current absolute location. When the system is first used you rotate the turn table by whatever means until it's is aligned with a track, press the button. Repeat until all done. Then sort and store the values in EEPROM.

Then a simple toggle or PB switch can be used to move to the next position or a more sophisticated interface can be used to go to position N.

it makes sense to increase the resolution right from the beginning

It sure does, if your maths is correct then maybe even a better encoder.

One problem with using a DC motor will be inertia, when you reach position X and turn off the motor it will keep going for a while and I think this "while" will vary according to the load, ie train on turntable or no train, and possibly other factors like varying friction in a home-made gadget.

I wanted to be able to anticipate approaching the final destination, and start to slow the drive motor speed down in steps before reaching the destination.

That probably covers what I just said, but the varying loads on the motor will still cause a few headaches I think and I'm not sure how slow you can go with a DC motor. High gearing would help here.


Rob

Hi Rob

I moved opened a thread under Projects called Turntable for anyone that was interested in the design.
http://arduino.cc/forum/index.php/topic,50230.0.html

Thanks again for your input.

Regards Bill

ccfreak2k:
There's at least one shield that already does this, although I suspect you'd rather cook up your own home-grown design: centipede_shield [macetech documentation]

Thanks for the link

I had reviewed the Centipede, it is using I2C for connection protocol. Since MCP23S17 can support up to 10Mhz clock with SPI, it is considerably quicker the the I2C protocol. For the moment, without having defined all my requirments, I am taking the path that quicker is better. The I2C possibly could meet my requiremnets, and I will definitely use if for another project that requires transmission over 20 feet (I'll add line drivers to get the required distance and noise immunity over the cabling length).

Again, thanks for the suggestion.

Regards Bill

P.S. What I initially thought would be easy, for me has turned into a major project. Makes me think I am missing something basic.

Can this chip handle Analog Inputs? Or only digital I/O through the 16 accessible pins?

Hi Bill,

I'm also investigating a model railroad turntable to update my fleischman 7286 with arduino and a stepper motor.

Did your project finish ?

I have a stepper working with examples using a v1 motershield, but i don't have much programmign experience and arduino is completely new.

My intention would be to make a base poitn using an optical switch (same 3d printers) and calculate then the steps to all the tracks.

Problem then is linking it to a DCC library and stearing it from the modelrailroad software.

DO you have any examples of your code or project ?

Cheers
Bart