// ***************************************************************************************************************** // * * // * SpikenzieLabs.com * // * * // * 64 Button Interface * // * * // * Demo Program * // * * // ***************************************************************************************************************** // // BY: MARK DEMERS // Sept 2008 // VERSION: 0.4 // // DESCRIPTION: // This sketch decodes an 8x8 button matrix, using a MCP23S17, where the 8 bits of Port A are the ROWS and Port B // bits are the columns. // The pins of Port B are all high normally and PORT A has the internal pull-ups active. This sketch cycles though // PORT B turning off (low) one pin at a time. PORT A is monitored for an interrupt. When an interrupt occurs it // means that a buttons is pressed. The column is know (Port B) and Port A is read to get the ROW that caused the // interrupt. The sketch then does a little decoding and the exact button pressed is know. // // // HOOK-UP // See spikenzielabs.com for "Project 64" for the wiring. // // USAGE: // 1. Hook it up. // 2. Copy the code into you sketch, or build your sketch into this one. // 3. Power it up and press some buttons. // // // LEGAL: // This code is provided as is. No guaranties or warranties are given in any form. It is up to you to determine // this codes suitability for your application. // //******************************************************************************************************************* // REQUIRED Define Statements //******************************************************************************************************************* // Port Expander #define MCP23S17 B01000000 // MCP23017 SPI Address (Not used in this sketch) #define IOCON 0x0A // MCP23017 Config Reg. #define IODIRA 0x00 // MCP23017 address of I/O direction #define IODIRB 0x01 // MCP23017 1=input #define IPOLA 0x02 // MCP23017 address of I/O Polarity #define IPOLB 0x03 // MCP23017 1= Inverted #define GPIOA 0x12 // MCP23017 address of GP Value #define GPIOB 0x13 // MCP23017 address of GP Value #define GPINTENA 0x04 // MCP23017 IOC Enable #define GPINTENB 0x05 // MCP23017 IOC Enable #define INTCONA 0x08 // MCP23017 Interrupt Cont #define INTCONB 0x09 // MCP23017 1= compair to DEFVAL(A or B) 0= change #define DEFVALA 0x06 // MCP23017 IOC Default value #define DEFVALB 0x07 // MCP23017 if INTCONA set then INT. if diff. #define GPPUA 0x0C // MCP23017 Weak Pull-Ups #define GPPUB 0x0D // MCP23017 1= Pulled HIgh via internal 100k #define OLATA 0x14 #define OLATB 0x15 #define INTFA 0x0E #define INTFB 0x0F #define INTCAPA 0x10 #define INTCAPB 0x11 // SPI #define SS 5 // Pin mapping to Arduino = SELECT #define MOSI 2 // Pin mapping to Arduino = Master Out Slave In #define SCLK 3 // Pin mapping to Arduino = Serial clock #define MISO 4 // Pin mapping to Arduino = Master IN slave OUT #define MCPINT 6 // Pin mapping to Arduino = Interrupt (Key Pressed) // The Arduino pins (above) may be changed to fit your project. //******************************************************************************************************************* // VARIABLE INITS //******************************************************************************************************************* int rx_data = 0; int buttonPress = 0; int error_flag = 0; int COLUMN = 0; //******************************************************************************************************************* // REQUIRED SET-UP //******************************************************************************************************************* void setup() { Serial.begin(9600); // Only used for debugging - not required pinMode(SS, OUTPUT); pinMode(MOSI, OUTPUT); pinMode(SCLK, OUTPUT); pinMode(MISO, INPUT); pinMode(MCPINT, INPUT); digitalWrite(SS, HIGH); digitalWrite(SCLK, LOW); digitalWrite(MOSI, LOW); Serial.println("Ready"); delay(1000); // This delay seems important for the MCP23S17 power-up SPI_portexpanderinit(); SPI_RX(MCP23S17,INTCAPA); // Used to clear initial interupt } //******************************************************************************************************************* // MAIN LOOP //******************************************************************************************************************* void loop() { CycleCols(); } //******************************************************************************************************************* // Functions and Subroutines //******************************************************************************************************************* void SPI_portexpanderinit() { // --- Set I/O Direction SPI_TX(MCP23S17,IODIRB,B00000000); // MCP23S17 port B = OUTPUT SPI_TX(MCP23S17,IODIRA,B11111111); // MCP23S17 port A = INPUT // --- Clear ALL Bits of GPIOB SPI_TX(MCP23S17,GPIOB,B00000000); // MCP23S17 Clear port B // --- Set Weak Pull-Up on of GPIOA SPI_TX(MCP23S17,GPPUA,B11111111); // MCP23S17 Use internal weak pull-ups on A // --- Set Default on Bit of GPIOA SPI_TX(MCP23S17,DEFVALA,B11111111); // MCP23S17 "Non" interrupt value on port A // --- Set Interrupt if value is different from DEFVALA SPI_TX(MCP23S17,INTCONA,B11111111); // MCP23S17 Interrupt when compared to port A // --- Set Interrupt ENABLED SPI_TX(MCP23S17,GPINTENA,B11111111); // MCP23S17 Enable interrupt on port A // --- Set active low of int pin SPI_TX(MCP23S17,IOCON,B01000010); // MCP23S17 Interrupt A goes HIGH on Int. } //******************************************************************************************************************* // SPI TX //******************************************************************************************************************* void SPI_TX(int device, int regadd, int tx_data) { digitalWrite(SS, LOW); // Select the Chip device = device & B11111110; // Clear last bit for a write SPI8BITTXLOOP(device); // SPI Device SPI8BITTXLOOP(regadd); // SPI REGISTER ADDRESS SPI8BITTXLOOP(tx_data); // Data digitalWrite(SS, HIGH); // Done UN-Select the Chip } //******************************************************************************************************************* // SPI RX //******************************************************************************************************************* void SPI_RX(byte device, byte regadd) { int i = 0; int val = 0; digitalWrite(SS, LOW); // Select the MCP23S17 Chip device = device | B00000001; // SET last bit for a READ SPI8BITTXLOOP(device); // SPI Device SPI8BITTXLOOP(regadd); // SPI REGISTER ADDRESS rx_data = 0; delay(1); // NEEDED ? Gives the MCP23S17 time to send data for(i=1; i < 9; i = i +1) // Read 8 bits of data { delay(1); digitalWrite(SCLK, HIGH); // SET SCLK delay(2); val = digitalRead(MISO); digitalWrite(SCLK, LOW); // Clear Clock rx_data = rx_data | val; if(i != 8) { rx_data = rx_data << 1; // Shift regadd LEFT by 1 } } digitalWrite(SS, HIGH); // Done UN-Select the Chip } //******************************************************************************************************************* // Transmit 8 bits over SPI //******************************************************************************************************************* void SPI8BITTXLOOP(int data) { int i = 0; int temp = 0; for(i=1; i < 9; i = i +1) { temp = (data >> 7); // Test bit 7 of DATA temp = temp & 1; if (temp == 0) // SET or CLEAR MOSI { digitalWrite(MOSI, LOW); } else { digitalWrite(MOSI, HIGH); } digitalWrite(SCLK, HIGH); // SET SCLK digitalWrite(SCLK, LOW); // Clear Clock data = data << 1; // Shift data LEFT by 1 } } //******************************************************************************************************************* // Check for a Button Press //******************************************************************************************************************* void CycleCols() { int actcol = 1; int BRS = 0; int i =0; // Local variable int invFlag =0; // Local variable invFlag = actcol ^ B11111111; // Invert the flag value for(i=1; i < 9; i = i +1) // LOOP: SET all rows but one { SPI_TX(MCP23S17, GPIOB, invFlag); delay(2); // This delay seems required to allow Int. to take effect actcol = actcol << 1; invFlag = actcol ^ B11111111; BRS = digitalRead(MCPINT); // Check if Interrupt due to button press if(BRS == HIGH) { COLUMN = i; DecodeButton(); if(error_flag == 0) // Check for an Error from bounce or multiple buttons { while(BRS == HIGH) // Wait for Button release, = no more interrupt { BRS = digitalRead(MCPINT); SPI_RX(MCP23S17,INTCAPA); } Serial.println("OK"); // JUST FOR TESTING } } } } //******************************************************************************************************************* // Decode the SENSED button press //******************************************************************************************************************* void DecodeButton() { int temp = 0; int ROW = 0; error_flag = 0; SPI_RX(MCP23S17,INTCAPA); // RE-TEST BOTH ROW AND COL HERE temp = rx_data; SPI_RX(MCP23S17,GPIOA); // If the same button is being pressed when checked if(rx_data == temp) // a second time it's OK. { rx_data = rx_data ^ B11111111; switch (rx_data) { case 128: ROW = 8; break; case 64: ROW = 7; break; case 32: ROW = 6; break; case 16: ROW = 5; break; case 8: ROW = 4; break; case 4: ROW = 3; break; case 2: ROW = 2; break; case 1: ROW = 1; break; default: ROW = 0; error_flag = 1; Serial.println("ROW-Error"); // JUST FOR TESTING ( > 1 button pressed in ROW) } if(error_flag == 0) { // Serial.println(ROW, DEC); // JUST FOR TESTING // Serial.println(COLUMN, DEC); // JUST FOR TESTING buttonPress = ((ROW-1)*8)+(COLUMN); // THIS IS THE BUTTON NUMBER ******************* Serial.print(buttonPress, DEC); // JUST FOR TESTING Serial.print(" "); // JUST FOR TESTING } } else { // Serial.println("Error"); // JUST FOR TESTING error_flag = 1; } }