Hello, all.
I wanted to connect the keypad to arduino, but I run out of pins.
As the project was already using I2C communication, I decided to modify the connection and use one free GPIO set on MCP23017.
I used the interrupt ability of the MCP (that costed me 1 pin on Arduino) and the RESET function (another 1 pin) - I had a bad experience with power drops and MCP state.
The MCP address is set to 0x20 using 3 wires connected to GND.
Attached you can see the schematics, following is the code.
Should you have any improvements, please, do not hesitate to post it.
#include <Wire.h>
// basic I2C config (addresses)
#define MCP 0x20
// specific configuration for MCP23017
#define MCPenable 9 // reset pin
#define MCPint 10 // reading interrupt
// indicator led
#define LEDpin 13 // just to check if alive
// runtime variables
unsigned long mRunLast = 0UL; // Last time MCP read started
unsigned long mInt = 200UL; // Interval for MCP read (interrupt clear)
unsigned long currentMillis = 0UL; // main time-counting var
void setup() {
// setting serial
Serial.begin(115200);
Serial.println("Init...");
Wire.begin(); // setting I2C
setupMCP(); // setting up MCP and interrupt
pinMode(LEDpin, OUTPUT);
digitalWrite(LEDpin, LOW);
Serial.println("Init done. Starting...");
}
void loop() {
currentMillis = millis();
if (!digitalRead(MCPint) ) {
digitalWrite(LEDpin, HIGH);
if (currentMillis - mRunLast > mInt) getMCP();
}
else {
digitalWrite(LEDpin, LOW);
}
}
void setupMCP() {
// first we reset MCP
pinMode(MCPint, INPUT);
pinMode(MCPenable, OUTPUT);
digitalWrite(MCPenable, LOW);
delay(10);
digitalWrite(MCPenable, HIGH);
delay(10);
I2Csend(MCP, 0x00, B11100000); // set of bank A to INPUTS for first 3 pins, outputs for everything else
I2Csend(MCP, 0x02, B11111111); // invert inputs of bank A
I2Csend(MCP, 0x04, B11100000); // enable interrupt for input pins of bank A
I2Csend(MCP, 0x06, B00000000); // set the default values for INT comparision
I2Csend(MCP, 0x08, B11100000); // compare to default instead of previous value
I2Csend(MCP, 0x0C, B11100000); // set pullup ON for bank A for inputs
I2Csend(MCP, 0x12, B00000000); // set output pins to LOW
}
void getMCP() {
I2CsendSingle(MCP, 0x10); // Interrupt capture GPIO A
Wire.requestFrom(MCP, 1);
byte inputs = Wire.read();
I2CsendSingle(MCP, 0x12); // to avoid error, re-read current state of GPIO
Wire.requestFrom(MCP, 1);
inputs = Wire.read();
if (inputs != 0 && currentMillis - mRunLast >= mInt) {
byte column = inputs >> 5;
if (column == 4) column = 3;
I2Csend(MCP, 0x04, B00000000); // temporary disable interrupt for input pins of bank A
byte row = 0;
byte error = 0;
for (byte i = 0; i < 4; i++) {
I2Csend(MCP, 0x12, 1 << (i + 1)); // disabling one output line at a time
I2CsendSingle(MCP, 0x12);
Wire.requestFrom(MCP, 1);
byte portA = Wire.read();
if ((portA & B11100000) == 0) {
row = i;
error++;
}
}
if (error == 1) {
byte output = 255;
byte input = row * 10 + column;
if (input < 10) output = input;
if (input > 10 && input < 20) output = input - 10 + 3;
if (input > 20 && input < 30) output = input - 20 + 6;
if (input > 30) output = input - 30 + 9;
if (output == 11) output = 0;
Serial.print("Key: ");
Serial.println(output);
} else {
Serial.println("Error captured :(");
}
I2Csend(MCP, 0x12, B00000000); // disabling one output line at a time
I2Csend(MCP, 0x04, B11100000); // enable interrupt for input pins of bank A
// now we have to find out, which row is selected
}
mRunLast = millis();
}
void I2Csend(byte I2Caddress, byte memAddr, byte memValue) {
Wire.beginTransmission(I2Caddress);
Wire.write(memAddr); // IODIRA register
Wire.write(memValue); // set all of bank A to INPUTS
Wire.endTransmission();
}
void I2CsendSingle(byte I2Caddress, byte memValue) {
Wire.beginTransmission(I2Caddress);
Wire.write(memValue); // set all of bank A to INPUTS
Wire.endTransmission();
}