Wednesday, December 14, 2016

Arduino and MCP23S17 Port Expander (Part 3)

Testing the MCP23S17 Port Expander — 
In the past two Parts of this series, the Microchip MCP23S17 Port Expander was installed either in a single chip breadboard prototype or as a quad-chip Arduino backpack. In the sketches, there was a lot of redundant code due to having multiple MCP Bank objects. In this Part 3, a sketch is provided that shows how to pass the instantiated Bankx objects to a function. As an example, the quad backpack has four (4) MCP23S17 chips, instantiated as Bank0, Bank1, Bank2, and Bank3. To process each of these objects, separate functions with identical internal code could be written for each. Alternatively, the object can be passed to a function, so that only one copy of the code has to be written. The SetChipPins() function used in Part 1 can be modified to pass the Bank object to another function setPin(&object). Each of the four Banks are processed using that same code.

// Set all Chip pins to desired mode
void setChipPins() {
// Example: pass the Bank object to the setPin function

    setPin(Bank0);
    setPin(Bank1);
    setPin(Bank2);
    setPin(Bank3);
}

void setPin(MCP23S17 &bank) {
    for (uint8_t i = 0; i <= 15; i++)
    bank.pinMode(i, INPUT_PULLUP);
}

The following sketch will print one line on Serial Monitor when one or more MCP23S17 input pins are pulled LOW (i.e., shorted with switch or wire to ground). The binary string that is printed will show which Bank digital pin [15,14,...,1,0] is LOW. For example, "Bank2: BIN: 1101111111111111 HEX: 0xDFFF DEC: 57343" shows that the Bank2 input pin 13 (INPUT_PULLUP, normally HIGH) was grounded (LOW). The binary display of the Port A and/or Port B pins provides a means of visually checking on the state of 8 or 16 pins. The functions "print8Bits(uint8_t)" and "print16Bits(uint16_t)" handle the binary print to Serial Monitor in place of the standard Serial.print function.

/*
 * MCP23S17_MultiButtonTestInputs_01.ino
 * Library Ref: github.com/MajenkoLibraries/MCP23S17
 *
 * Lowell Bahner
 * 2016-12-11
 *
 * Hardware: Arduino UNO/Mega2560/Mega2560&CH340-Serial-USB
 * IDE: Arduino 1.6.5
 *
 * Useful SPI Ref: gammon.com.au/spi
 *
 * Wire the SPI Interface common lines (Mega2560 SPI pins shown in [ ]):
 * Arduino SPI_MOSI pin 11 [51] <->   SI  MCP23S17 pin 13
 * Arduino SPI_MISO pin 12 [50] <->   SO  MCP23S17 pin 14
 * Arduino SPI_CLOCK pin 13 [52]<->   SCK MCP23S17 pin 12
 * Arduino SPI_CS pin 10 [53]   <->   SS  MCP23S17 pin 11
 * MCP23S17 VDD pin 9 to +5V
 * MCP23S17 VSS pin 10 to Gnd
 * MCP23S17 Reset pin 18 to +5V
 * MCP23S17 Interrupt pins 19-20
 * MCP23S17 A0,A1,A2 pins connect to Gnd or +5V (000 = Address 0, 100 = Address 1, 010 = Address 2, etc)
 * MCP23S17 Port A pins are chip pins 21-28
 * MCP23S17 Port B pins are chip pins 1-8
 *
 * This sketch tests button inputs.
 * Temporarily short each MCP23S17 pin to ground to test for continuity.
 * If a pin is grounded the pin value will print to Serial Monitor.
 *
 * This sketch instantiates 4 MCP23S17 chips.
 * 
 * This code passes the Bank objects as parameters to a function
 *
 * This code was tested on UNO and Mega2560
 * using the ICSP pins for SPI addressing.
 *
 * On each chip, Port A and Port B are set to INPUT_PULLUP.
 * Buttons are connected to one or more Port A and B inputs.
 *
 * Port A & B INPUT_PULLUP pins are set HIGH.
 * When a switch sets the chip's Port A or B input pin to LOW
 * both Port A and Port B 0-7 pin values are read as uint8_t
 * (byte) values and printed on Serial Output. oneTime variable
 * limits printing of multiple values to Serial Monitor.
 *
 * Majenko MCP23S17 library functions (see .h,.cpp files for details):
 *
 *  bank.pinMode(uint8_t pin, uint8_t mode)
 *    where modes are:
 *      OUTPUT - sets pin HIGH
 *      INPUT  - sets pin LOW for sensing change to HIGH +V
 *      INPUT_PULLUP - sets pin HIGH for sensing change to LOW 0V
 *
 *  bank.digitalWrite(uint8_t pin, uint8_t value)
 *    if mode=OUTPUT, pin value = LOW (0) or HIGH (1)
 *    if mode=INPUT, value LOW disables pullup on pin, value HIGH enables pullup on pin
 *
 *  uint8_t value = bank.digitalRead(uint8_t pin)
 *    read the state of the pin
 *
 *  uint8_t value = bank.readPort(uint8_t port)
 *    read entire 8-bit port of all INPUT pins on Port A (port=0) or Port B (port>=1)
 *
 *  uint16_t longValue = bank.readPort()
 *    read both ports as 16-bit combined
 *
 *  bank.writePort(uint8_t port, uint8_t val)
 *    write val (0 or 1, hex example 0x55) to all OUTPUT pins on Port A (port=0) or Port B (port>=1)
 *
 *  bank.writePort(uint16_t val)
 *    write val (hex example 0x55AA) to all OUTPUT pins on Port A and Port B
 *
 *  Also, interrupt functions are defined:
 *      enableInterrupt(uint8_t pin, uint8_t type);
 *      void disableInterrupt(uint8_t pin);
 *      void setMirror(boolean m);
 *      uint16_t getInterruptPins();
 *      uint16_t getInterruptValue();
 *      void setInterruptLevel(uint8_t level);
 *      void setInterruptOD(boolean openDrain);
 *
 *
 *
 *
Run Sketch: Example Serial Monitor Output

-- MCP23S17_MultiButtonTestInputs_01 --

Bank0: BIN: 1111111111111110  HEX: 0xFFFE    DEC: 65534
Bank0: BIN: 1111011111111111  HEX: 0xF7FF    DEC: 63487
Bank1: BIN: 1111111111101111  HEX: 0xFFEF    DEC: 65519
Bank2: BIN: 1101111111111111  HEX: 0xDFFF    DEC: 57343
Bank3: BIN: 1111011111111111  HEX: 0xF7FF    DEC: 63487

 *
 *
 *
 */

// Majenko MCP23S17 Library (revised 2017-Jan-23)
#include <MCP23S17.h>

// Arduino Library SPI.h
#include <SPI.h>

// SPI CS/SS chipselect pin can be changed by user as desired
const uint8_t chipSelect = 10;

// Limit when printing occurs
uint8_t limitPrint = 1; // if 1, then limit printing to button push events

// Limit pin print value to one time
uint16_t oneTime0 = 0;

// example Arduino board pin
uint8_t pin7 = 7;

// Create an object for each chip
// Bank0 is address 0. Pins A0,A1,A2 grounded.
// Bank1 is address 1. Pin A0=+5V, A1,A2 grounded.
// Bank2 is address 2. Pin A1=+5V, A0,A2 grounded.
// Bank3 is address 3. Pin A0,A1=+5V, A2 grounded.

MCP23S17 Bank0(&SPI, chipSelect, 0);
MCP23S17 Bank1(&SPI, chipSelect, 1);
MCP23S17 Bank2(&SPI, chipSelect, 2);
MCP23S17 Bank3(&SPI, chipSelect, 3);

void setup() {

  Serial.begin(9600);

  Serial.print("\n -- MCP23S17_MultiButtonTestInputs_01 --"); Serial.println("");

  Bank0.begin();
  Bank1.begin();
  Bank2.begin();
  Bank3.begin();

  // LED connected to Arduino pin 7 to test inter-device communication
  pinMode(pin7, OUTPUT);

  //
  // Set MCP23S17 pin modes:
  // example set one pin: chip.pinMode(0, OUTPUT); // sets chip pin 0 (Port A 0) to OUTPUT mode
  //
  setChipPins(); // use loop to set individual pins

}

// Set all Chip pins to desired mode
void setChipPins() {
  // Example: pass the Bank object to the setPin function

  setPin(Bank0);
  setPin(Bank1);
  setPin(Bank2);
  setPin(Bank3);
}

void setPin(MCP23S17 &bank) {
  for (uint8_t i = 0; i <= 15; i++)
    bank.pinMode(i, INPUT_PULLUP);
}

//
// Read a button push on input pin
// 1) Switch an INPUT_PULLUP pin to ground
// 2) Read the pin value
// 3) Write the value to Serial Monitor
// Use functions digitalRead(pin) and digitalWrite(pin,value)
//

//
// Read Bank values
//
void RWinputs(MCP23S17 &bank, uint8_t id) {

  // read the Chip pin values
  // id is just passed so the bank is identified in Serial.print
  // id could also be used in functions that take different actions for
  // different objects
  //
  uint16_t value = bank.readPort();

  if (oneTime0 != value) {
    if ((limitPrint < 1) || (value < 65535)) {
      Serial.print("\nBank"); Serial.print(id);
      Serial.print(": BIN: "); print16Bits(value);
      Serial.print("  HEX: "); crPrintHEX(value, 4);
      Serial.print("  DEC: "); Serial.println(value, DEC);
      oneTime0 = value;
    }
  }
}


void loop() {

  // Read Switches on Port A & B and Write Switch settings
  // to Serial Monitor
  RWinputs(Bank0, 0);
  RWinputs(Bank1, 1);
  RWinputs(Bank2, 2);
  RWinputs(Bank3, 3);

  delay(100);

}

//---------------------------------------------------------------------------------
// print 8-bit byte as 8 bit binary string
//---------------------------------------------------------------------------------


void print8Bits(uint8_t myByte) {
  for (byte mask = 0x80; mask; mask >>= 1) {
    if (mask  & myByte)
      Serial.print('1');
    else
      Serial.print('0');
  }
}

//---------------------------------------------------------------------------------
// print 16-bit word as 16 bit binary string
//---------------------------------------------------------------------------------

void print16Bits(uint16_t myWord) {
  for (uint16_t mask = 0x8000; mask; mask >>= 1) {
    if (mask  & myWord)
      Serial.print('1');
    else
      Serial.print('0');
  }
}


//---------------------------------------------------------------------------------
// crPrintHEX print value as hex with specified number of digits
//---------------------------------------------------------------------------------

void crPrintHEX(unsigned long DATA, unsigned char numChars) {
  unsigned long mask  = 0x0000000F;
  mask = mask << 4 * (numChars - 1);
  Serial.print("0x");
  for (unsigned int i = numChars; i > 0; --i) {
    Serial.print(((DATA & mask) >> (i - 1) * 4), HEX);
    mask = mask >> 4;
  }

  Serial.print("  ");
}

  Checking on SPI -- Initially the quad MCP23S17 backpack did not work as expected due to some missing wires on the board. A Syscomp CGR-101 oscilloscope was used to take a look at the SPI pins to see if SPI was working. If you experience issues with your hardware, a small oScope can be useful. The oScope was almost essential trying to figure out how rotary encoders worked. Encoders will be discussed in future blogs.

The following image shows the CS pin (Channel B, blue) displayed against SPI pin SCK (Channel A, red). If a similar pattern is not displayed on an oScope when the sketch is running, you can be pretty sure that SPI is not functioning.



Useful references -


  1. SPI - Serial Peripheral Interface - for Arduino (gammon, 2011)


(Dec 14, 2016)

No comments:

Post a Comment