Thursday, December 29, 2016

Arduino and MCP23S17 Port Expander (Part 4)

MCP23S17 Port Expander Interrupts— 
The MCP23S17 Port Expander chip has two built-in hardware interrupt pins. Pin 20 is the Port A interrupt (INTA) and pin 19 is the Port B interrupt (INTB). INTA and INTB terminology distinguishes the MCP interrupts from the Arduino Uno interrupts, INT0 and INT1.

The INTA and INTB pins can be used to sense when a pin on that particular port (A or B) has changed state. Alternatively, INTA and INTB pins can be mirrored so that any one of the 16 digital pins can be sensed when it has changed state.
The INTA interrupt pin can be wired to the Arduino INT0 pin so that the Arduino processor can be interrupted instantly if a pin changes state on an MCP Port A pin (any 1 or more of 8 pins). Similarly, the INTB interrupt pin can be wired to the Arduino INT1 pin so that the Arduino processor can be interrupted instantly if a pin changes state on an MCP Port B pin (any 1 or more of 8 pins).
The Majenko MCP23S17 library (.h and .cpp files) provides functions to handle MCP interrupts which use similar functions as the Arduino interrupt functions:
  • void 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);
I added Port interrupt functions to Majenko's code. The Port functions allow reading the 8 pin states of the port that was interrupted, as opposed to the functions provided by Majenko that read all 16 pin states when an interrupt occurs. Otherwise, the functions behave the same. The Port interrupt function code is provided later in this blog.
Function enableInterrupt(pin, type) is used on any MCP input pin on which an interrupt is desired. The type is CHANGE, RISING, or FALLING which matches interrupt types for the Arduino. CHANGE means trigger an interrupt on both rising or falling changes of state on this particular input pin.
Function setInterruptLevel(level), where level = HIGH means the interrupt pin will go HIGH when an interrupt occurs, level = LOW means it will go LOW.
Missing from the MCP interrupt functions is the Arduino attachInterrupt() function. For example, if MCP pin 4 changing state causes the MCP INTA pin to go LOW, and the MCP INTA pin is wired to the Arduino INT0 pin, then the INT0 pin will also go LOW triggering the INT0 code being immediately processed to take some action. The INTA and INT0 pins require the wire connection to trigger the Arduino attachInterrupt() function, otherwise the MCP interrupt pin INTA would have to be polled by the sketch to determine when INTA changed state. In cases where speed is not of concern, polling for an interrupt might work well. But if speed is a possible requirement, hardware interrupts will provide the best option.
Writing code for handling interrupts is a bit of a challenge, because MCP interrupts can occur on any input pin, but each triggers either INTA or INTB. The pin that caused the INTA or INTB interrupt must be determined so the proper action can be taken. The interrupt must be cleared so that another interrupt can be registered. And, what happens when more than one pin changes state at the same time? Can that happen?
Another issue surfaces for Arduino Uno processors that only have INT0 and INT1 hardware interrupt pins. What does one do if they want to use interrupts for more than one MCP23S17 chip? Where do the MCP23S17 INTA and INTB pins connect on the Arduino? The simple answer is to use a processor, like the Mega 2560, with more interrupts. A bit more difficult approach is to program other Arduino digital pins to have "pin change interrupts" and have code respond to those interrupts. A third option (yet untested) is to tie the MCP interrupts together using the setInterruptOD(boolean openDrain) function and attach them to one set of Arduino interrupts.
The getInterruptPins(), getInterruptAPins(), and getInterruptBPins() functions return either 16 or 8 pin values at the time of an MCP interrupt. These values need to be retained in uint16_t or uint8_t variables, because the registers on the chip will be reset when the interrupt is cleared. When that interrupt is cleared, the next interrupt can be triggered and new values will be written into the MCP interrupt registers. Meanwhile, the values saved in the variables do not change and can be used as needed in the sketch. Their values can then be updated for the new interrupt and the process can be repeated.
  • Note: use .getInterruptPins() to identify the pin that interrupted. Its value is retained until the interrupt is cleared. Further interrupts are blocked until the current interrupt is cleared.
  • Use .getInterruptValue() or .readPort(port) to clear the interrupt
  • Note: .getInterruptValue retains its value. It changes value when it reads the pins after a new pin interrupt. It then takes on the new pin values of .getInterruptPins.
The getInterruptValue(), getInterruptAValue(), getInterruptBValue() functions behave differently than the getInterruptPins() functions. The getInterruptValue() functions retain the values read for the 16 or 8 pins, until the values change. If pins are read over and over using getInterruptValue(), the values may or may not change. Using getInterruptValue() will clear the interrupt, but the getInterruptValue() pin values will not change until getInterruptPins() is called after a new interrupt occurs.
Test sketches with debugging indicate the following results:
Example 1. Ground MCP pin 9, followed by getInterruptBPins() indicates pin 9 was interrupted
     > Port B Interrupt Pins: 00000010
Then,  getInterruptBValues() captures port values
     > Port B Interrupt Values: 11111101
Then a second getInterruptBPins() indicate interrupts are cleared
     > Port B Interrupt Pins: 00000000
Again, getInterruptBValues() captures port values that have not changed
     > Port B Interrupt Values: 11111101
Example 2. Interrupt Port A pin, then interrupt Port B pin
- MCP INTA is wired to Arduino INT0.
- MCP INTB is wired to Arduino INT1.
     > INT0 is triggered by INTA and INT0 state changes from HIGH to LOW.
     > INTA is not cleared.
     > Independent of INTA, INT1 is triggered by INTB and INT1 state changes from HIGH to LOW.
     > INTB is not cleared.
- Neither INTA or INTB allow further interrupts on INT0 or INT1 until INTA and INTB  are cleared.
Example 3. Two or more MCP simultaneous interrupts can occur on a Port.
- Simultaneously ground 2 MCP pins 10 & 13 on Port B.
     > Port B Interrupt Pins: 00100100
     > Port B Interrupt Values: 11011011
- Determine which MCP interrupts (bits [7...0] , right to left) were set (LOW)
     > whichBit: 2
     > whichBit: 5
     > whichBitCount: 2
Example 3 demonstrates that one Arduino interrupt can indicate that one or more MCP port interrupts have occurred. Each MCP Port interrupt would need to be processed individually. Successive interrupts are therefore different than simultaneous interrupts, and would require different code to interpret the interrupts.
The INT0 and INT1 ISR code --

void interruptA() {
  // ISR to respond to INT0 interrupt
  // Respond to the INTA interrupt
  // which was triggered by one or more MCP inputs
  interruptMCP(Bank0, 0); // MCP ISR (bank, port)
}

When INT0 is triggered by INTA (due to an MCP pin going LOW), the function interruptA is executed and calls the interruptMCP(bank, port) function. Similar code is used for INT1 and INTB.

void interruptMCP(MCP23S17 &bank, uint8_t port) {
  // read the Port Expander Interrupt pins
  readBankPortIntPins(bank, port);

  // read the Port Expander Interrupt values
  readBankPortIntVal(bank, port);

  // take action on new pin interrupt values
  if (port < 1) {
    flagMCPA = 1; // set flag that Port A interrupt occurred
  } else {
    flagMCPB = 1; // set flag that Port B interrupt occurred
  }
  // act on Reading in loop()
}

The interruptMCP() function reads the Port getInterruptXPins(), where X=A or B. It then reads the Port getInterruptXValues() which saves the values to the mcpXReading variable. The mcpXReading variable (8 bits holding the Port X interrupt pin values) is then processed to take some action. In this case, a flag is set and passed to loop(). In loop(), the 8 bits are read to determine which pins caused the interrupt, and then that information is printed to Serial Monitor to show what just happened, and further processed as a demonstration of the code. 
Port Interrupt functions --
Add the following code at the end of public: in MCP23S17.h
/* Following functions added by Lowell Bahner, 2016-12-29, for Port interrupts */
        uint8_t getInterruptAPins();
        uint8_t getInterruptAValue();
        uint8_t getInterruptBPins();
        uint8_t getInterruptBValue();

And, add the following code to the end of MCP23S17.cpp:


/* Following functions added by Lowell Bahner, 2016-12-29, for Port interrupts */

/*! This function returns an 8-bit bitmap of the Port-A pin or pins that have caused an interrupt to fire.
 *
 *  Example:
 *
 *      unsigned int pins = myExpander.getInterruptAPins();
 */
uint8_t MCP23S17::getInterruptAPins() {
    readRegister(INTFA);
    return  _reg[INTFA];
}

/*! This returns a snapshot of the Port-A IO pin states at the moment the last interrupt occured.  Reading
 *  this value clears the interrupt status (and hence the INT pins) for the port.
 *  Until this value is read (or the current live port value is read) no further interrupts can
 *  be indicated.
 *
 *  Example:
 *
 *      unsigned int pinValues = myExpander.getInterruptAValue();
 */
uint8_t MCP23S17::getInterruptAValue() {
    readRegister(INTCAPA);
    return _reg[INTCAPA];
} 

/*! This function returns an 8-bit bitmap of the Port-B pin or pins that have caused an interrupt to fire.
 *
 *  Example:
 *
 *      unsigned int pins = myExpander.getInterruptBPins();
 */
uint8_t MCP23S17::getInterruptBPins() {
    readRegister(INTFB);
    return _reg[INTFB];
}

/*! This returns a snapshot of the Port-B IO pin states at the moment the last interrupt occured.  Reading
 *  this value clears the interrupt status (and hence the INT pins) for the port.
 *  Until this value is read (or the current live port value is read) no further interrupts can
 *  be indicated.
 *
 *  Example:
 *
 *      unsigned int pinValues = myExpander.getInterruptBValue();
 */
uint8_t MCP23S17::getInterruptBValue() {
    readRegister(INTCAPB);
    return _reg[INTCAPB];
} 

Sketch Results and Code --

Following is an example of the Serial Monitor report from the sketch. Each time one or more MCP23S17 input pins are grounded (Switch or wire), the Port pins that caused the interrupt are identified and counted, then those pins are further processed, as an example of processing each pin that had changed state.

 Starting MCP23S17 Interrupt Test

 > Wire MCP23S17 Pin INTA to Arduino interrupt pin pinA (D2). 
 > Wire MCP23S17 Pin INTB to Arduino interrupt pin pinB (D3). 
 > Run Sketch with Serial Monitor window ON at 115200 baud.
 > Take MCP23S17 input pin(s) LOW (Switch or wire to Ground). 

 >> Ready >> 

 --- Interrupts Port A Values: 00100100
 Port A whichBit: 2
 Port A whichBit: 5
 byteACount: 2

 Process Port A pin: 2
 Process Port A pin: 5

 --- Interrupts Port B Values: 10000000
 Port B whichBit: 7
 byteBCount: 1

 Process Port B pin: 7


Sketch to demonstrate MCP23S17 Interrupts --

/*
 * MCP23S17_Interrupt_Counter_01
 * 2016-12-29
 * Lowell Bahner
 *
 * This code counts interrupts on an MCP23S17 Port Expander
 *
 * A push button or wire takes one or more MCP23S17 input pins LOW to trigger interrupts
 * which are processed and the action is printed to Serial Monitor.
 *
 * The MCP23S17 has an interrupt on each digital input pin which can set
 * a PORT interrupt pin. The Port A interrupt (INTA) is chip pin 20, and Port B
 * interrupt (INTB) is chip pin 19.
 *
 * The MCP23S17 INTA and INTB can be wired to interrupt pins on the Arduino
 * which can use hardware interrupts to interrupt the sketch code to take
 * immediate action.
 *
 * UNO hardware interrupt pins INT0 (D2), INT1 (D3)
 * Mega 2560 interrupt pins D2, D3, D18, D19, D20, D21
 *
 * ======================================================

  Sketch Interrupt Tests:
  Test 1. Demonstrate MCP port interrupt process.
   - Ground MCP pin 9 and getInterruptBPins() indicate pin 9 was interrupted
      >  Port B Interrupt Pins: 00000010
   - then getInterruptBValues() captures port values
      >  Port B Interrupt Values: 11111101
   - then a second getInterruptBPins() indicate interrupts are cleared
      >  Port B Interrupt Pins: 00000000
   - again, getInterruptBValues() captures port values that have not changed
      >  Port B Interrupt Values: 11111101

  Test 2. Interrupt A and B are separate events.
          INTA ==> INT0 state change is captured.
          INTA is not cleared and does not capture another interrupt.
          INTB ==> INT1 state changes.
          INTB is not cleared and does not capture another interrupt.
      Index  Int Pin     IntA     IntB     Time
  -------  -------  -------  -------  -------
        0        x        1        1  0
        1        A        0        1  4

    Index  Int Pin     IntA     IntB     Time
  -------  -------  -------  -------  -------
        0        x        0        1  0
        1        B        0        0  4

  Test 3. Ground 2 MCP pins 10 & 13 on Port B.
    - One Arduino interrupt can indicate one or more simultaneous MCP interrupts.
    >  Port B Interrupt Pins: 00100100
    >  Port B Interrupt Values: 11011011
    - Determine which MCP interrupts (bits [7...0]) were set (LOW)
    >  whichBit: 2
    >  whichBit: 5
    >  whichBitCount: 2


 * ======================================================
 *
 *
   Sketch Output to Serial Monitor

   Starting MCP23S17 Interrupt Test


 > Wire MCP23S17 Pin INTA to Arduino interrupt pin pinA (D2).
 > Wire MCP23S17 Pin INTB to Arduino interrupt pin pinB (D3).
 > Run Sketch with Serial Monitor window ON at 115200 baud.
 > Take MCP23S17 input pin(s) LOW (Switch or wire to Ground).

 >> Ready >>


 --- Interrupts Port A Values: 00100100
 Port A whichBit: 2
 Port A whichBit: 5
 byteACount: 2

 Process Port A pin: 2
 Process Port A pin: 5

 --- Interrupts Port B Values: 10000000
 Port B whichBit: 7
 byteBCount: 1

 Process Port B pin: 7

 *
 *
 */

// ======================================================
// Sketch Code

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

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

const byte NUM_ENCODERS = 4;

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

uint8_t pinA = 2;   // INT0 Connected to MCP INTA
uint8_t pinB = 3;   // INT1 Connected to MCP INTB

volatile uint16_t mcpReading = 0; // 16-bit interrupt reading
volatile uint8_t mcpAReading = 0; // 8-bit Port-A interrupt reading
volatile uint8_t mcpBReading = 0; // 8-bit Port-B interrupt reading
uint8_t whichBit = 0;    // one bit [7...0] that is set in byte
uint8_t byteACount = 0; // number of bits set in byte
uint8_t byteBCount = 0; // number of bits set in byte
uint8_t byteABits[8] = {0}; // set bit array
uint8_t byteBBits[8] = {0}; // set bit array
volatile uint8_t flagMCPA = 0;
volatile uint8_t flagINTA = 0;
volatile uint8_t flagMCPB = 0;
volatile uint8_t flagINTB = 0;

// 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);

// ++++++++++++++++++++++++++++++++++++++++++++++++++++
// function setup
// ++++++++++++++++++++++++++++++++++++++++++++++++++++

void setup() {

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

  pinMode (pinA, INPUT_PULLUP);
  pinMode (pinB, INPUT_PULLUP);

  attachInterrupt(digitalPinToInterrupt(pinA), interruptA, CHANGE);
  attachInterrupt(digitalPinToInterrupt(pinB), interruptB, CHANGE);

  Serial.begin (115200);
  Serial.println(F("\n Starting MCP23S17 Interrupt Test"));

  displayInstructions();
  displayReady();

  //----------------------------------------------------------
  // Port Expander pin and interrupts configuration
  //----------------------------------------------------------
  //
  // Input port code
  // pins 0-15 are on device:port
  // device==1 Bank0
  // port==0 Port A, 1 Port B
  //
  // Set MCP23S17 pin modes and Interrupt configurations
  // example set one pin: chip.pinMode(0, INPUT_PULLUP);
  //
  setChipPins(); // use loop to set individual pins

  // clear the interrupt values variables
  mcpAReading = 0;
  mcpBReading = 0;
  mcpReading = 0;

}  // end of setup



// ++++++++++++++++++++++++++++++++++++++++++++++++++++
// function setChipPins()
// ++++++++++++++++++++++++++++++++++++++++++++++++++++

// 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);
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++
// function setPin()
// ++++++++++++++++++++++++++++++++++++++++++++++++++++

// Set all Port Expander pins to desired mode INPUT_PULLUP
// Set all Port Expander input pins interrupts
void setPin(MCP23S17 &bank) {
  for (uint8_t ind = 0; ind <= 15; ind++) {
    bank.pinMode(ind, INPUT_PULLUP);
    bank.enableInterrupt(ind, FALLING);
  }

  // set Port Expander Interrupt configuratons
  bank.setMirror(false);
  bank.setInterruptOD(false);
  bank.setInterruptLevel(LOW);
  // setInterruptLevel(LOW) requires the revised library (2017-Jan-23)
  // clear all interrupts on this Port Expander
  mcpReading = bank.getInterruptValue();
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++
// function readBankPortIntVal()
// ++++++++++++++++++++++++++++++++++++++++++++++++++++

// Read all Port Expander port interrupt values
void readBankPortIntVal(MCP23S17 &bank, uint8_t port) {
  if (port < 1) { // Port A
    mcpAReading = bank.getInterruptAValue();
    //Serial.print ("\n Port A Interrupt Values: "); print8Bits(mcpAReading);
  } else {        // Port B
    mcpBReading = bank.getInterruptBValue();
    //Serial.print ("\n Port B Interrupt Values: "); print8Bits(mcpBReading);
  }
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++
// function readBankPortIntPins()
// ++++++++++++++++++++++++++++++++++++++++++++++++++++

// Read all Port Expander port interrupt pins
void readBankPortIntPins(MCP23S17 &bank, uint8_t port) {
  if (port < 1) { // Port A
    mcpAReading = bank.getInterruptAPins();
    //Serial.print ("\n Port A Interrupt Pins: "); print8Bits(mcpAReading);
  } else {        // Port B
    mcpBReading = bank.getInterruptBPins();
    //Serial.print ("\n Port B Interrupt Pins: "); print8Bits(mcpBReading);
  }
}


// ++++++++++++++++++++++++++++++++++++++++++++++++++++
// function readByteBits()
// ++++++++++++++++++++++++++++++++++++++++++++++++++++

// Read all Port Expander port interrupt pins
// save set bit to byteBits arrays
// byteBits array can be sequentially processed later
void readByteBits(MCP23S17 &bank, uint8_t port) {
  if (port < 1) { // Port A
    byteACount = 0;
    for (uint8_t ind = 0; ind < 8; ind++) {
      if (bitRead(~mcpAReading, ind)) { // ~ inverts bit values
        byteABits[byteACount] = ind; // save the bit number to sequential array
        byteACount = byteACount + 1;
        Serial.print ("\n Port A whichBit: "); Serial.print (ind);
      }
    }
    Serial.print ("\n byteACount: "); Serial.print (byteACount);
  } else { // Port B
    byteBCount = 0;
    for (uint8_t ind = 0; ind < 8; ind++) {
      if (bitRead(~mcpBReading, ind)) { // ~ inverts bit values
        byteBBits[byteBCount] = ind; // save the bit number to sequential array
        byteBCount = byteBCount + 1;
        Serial.print ("\n Port B whichBit: "); Serial.print (ind);
      }
    }
    Serial.print ("\n byteBCount: "); Serial.print (byteBCount);
  }
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++
// function processPins()
// ++++++++++++++++++++++++++++++++++++++++++++++++++++

// Process the port byteBits array of interrupted MCP pins
void processPins(MCP23S17 &bank, uint8_t port) {
  if (port < 1) { // Port A
    // byteACount = number of pins to process
    for (uint8_t ind = 0; ind < byteACount; ind++) {
      uint8_t pin = byteABits[ind]; // recover the bit number from sequential array
      Serial.print ("\n Process Port A pin: "); Serial.print (pin);
    }
  } else { // Port B
    // byteACount = number of pins to process
    for (uint8_t ind = 0; ind < byteBCount; ind++) {
      uint8_t pin = byteBBits[ind]; // recover the bit number from sequential array
      Serial.print ("\n Process Port B pin: "); Serial.print (pin);
    }
  }
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++
// function interruptA()
// ++++++++++++++++++++++++++++++++++++++++++++++++++++

void interruptA() {
  // ISR to respond to INT0 interrupt
  // Respond to the INTA interrupt
  // which was triggered by one or more MCP inputs
  interruptMCP(Bank0, 0); // MCP ISR (bank, port)
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++
// function interruptB()
// ++++++++++++++++++++++++++++++++++++++++++++++++++++

void interruptB() {
  // ISR to respond to INT1 interrupt
  // Respond to the INTB interrupt
  // which was triggered by one or more MCP inputs
  interruptMCP(Bank0, 1); // MCP ISR (bank, port)
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++
// function interruptMCP()
// ++++++++++++++++++++++++++++++++++++++++++++++++++++

void interruptMCP(MCP23S17 &bank, uint8_t port) {
  // read the Port Expander Interrupt pins
  readBankPortIntPins(bank, port);

  // read the Port Expander Interrupt values
  readBankPortIntVal(bank, port);

  // take action on new pin interrupt values
  if (port < 1) {
    flagMCPA = 1; // set flag that Port A interrupt occurred
  } else {
    flagMCPB = 1; // set flag that Port B interrupt occurred
  }
  // act on Reading in loop()
}


// ++++++++++++++++++++++++++++++++++++++++++++++++++++
// function loop
// ++++++++++++++++++++++++++++++++++++++++++++++++++++

void loop ()
{

  // This code runs the MCP Interrupt processing from the Arduino ISR
  // and then takes action (prints) on the MCP pins that went LOW

  if (flagMCPA > 0) {
    //Serial.println ("\n loop flagMCPA");
    printInterruptHeader(Bank0, 0);
    processPins(Bank0, 0); // do some processing of each interrupted pin
    flagMCPA = 0;
  }

  if (flagMCPB > 0) {
    //Serial.println ("\n loop flagMCPB");
    printInterruptHeader(Bank0, 1);
    processPins(Bank0, 1); // do some processing of each interrupted pin
    flagMCPB = 0;
  }

  delay (10); // give loop something to do while idle

}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++
// function printInterruptHeader
// ++++++++++++++++++++++++++++++++++++++++++++++++++++

void printInterruptHeader(MCP23S17 bank, uint8_t port) {
  // inReading is either mcpAReading or mcbBReading 8-pin byte values
  Serial.print ("\n\n --- Interrupts Port ");
  if (port < 1) {
    Serial.print (F("A "));
    Serial.print ("Values: "); print8Bits(~mcpAReading);
  } else {
    Serial.print (F("B "));
    Serial.print ("Values: "); print8Bits(~mcpBReading);

  }
  // get the bit count and write the bits to byteBit[] array
  readByteBits(bank, port);
  Serial.println ();
} // end of printInterruptHeader


// ++++++++++++++++++++++++++++++++++++++++++++++++++++
// function displayInstructions()
// ++++++++++++++++++++++++++++++++++++++++++++++++++++

void displayInstructions() {
  Serial.println (F("\n"));
  Serial.println (F(" > Wire MCP23S17 Pin INTA to Arduino interrupt pin pinA (D2). "));
  Serial.println (F(" > Wire MCP23S17 Pin INTB to Arduino interrupt pin pinB (D3). "));
  Serial.println (F(" > Run Sketch with Serial Monitor window ON at 115200 baud."));
  Serial.println (F(" > Take MCP23S17 input pin(s) LOW (Switch or wire to Ground). "));
}

void displayReady() {
  Serial.println (F("\n >> Ready >> "));
}


// ++++++++++++++++++++++++++++++++++++++++++++++++++++
// print binary8 binary16 and hex functions
// ++++++++++++++++++++++++++++++++++++++++++++++++++++

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

void print8Bits(uint8_t myByte) {
  for (uint8_t 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 eID = numChars; eID > 0;  --eID) {
    Serial.print(((DATA & mask) >> (eID - 1) * 4), HEX);
    mask = mask >> 4;
  }
  Serial.print("  ");
}

Useful references -
  1. SPI - Serial Peripheral Interface - for Arduino (gammon, 2011)
  2. MCP23017 Interrupts (gammon, 2013)
(Dec 29, 2016)

2 comments:

  1. Hello, I just wanted to say thanks for your blog. I'm using Majenko's MCP23S17 breakout board and library with P3D and found your examples very useful although I'm still getting my head around the more complex examples with my limited knowledge.

    Regards

    Paul

    ReplyDelete
    Replies
    1. Thank you, Paul. Best of luck. I am pleased that you found this useful.

      Delete