grbl homing with NC switches can start out as something small but can become very…
Interfacing a touch keypad with an AVR
I’ve always admired touchpad technology and I recently got a chance to make my own 12-key touchpad using Freescale’s MPR121Q. This nifty little chip supports upto 12 debounced pads, configurable registers and works on the I2C protocol. All the electrodes can be combined into one electrode that acts as a proximity sensor. Also of the 12 electrodes, 8 can also be used as GPIO pins that can drive LEDs.
For a rundown on how to create your own touch keypad using this chip see this post. Gerbers, schematics are all included in the download here.
The basic task to get the MPR121Q working is by setting up a bunch of registers. These essentially control a variety of things including touch and release thresholds, baseline levels, touch filter levels (for debounced outputs) etc. More details on that are in the datasheet. For a quick guide to knowing what these registers do and how to set them up read the AN3944 application note from Freescale.
The Schematic
My test setup included interfacing the MPR121Q with an Atmega8A. I also included an LCD display that shows which of the keypads were touched. The code includes the I2C and MPR121 read, write and configuration routines from Sparkfun’s code. The LCD code is based on Peter Fluery’s excellent LCD tutorial. Please note that the code is in C. I compiled it using Atmel Studio 6.1. If you are using any Arduino flavors, check out Sparkfun’s code which gives the code in Arduino syntax.
Though folks at Sparkfun say that this can be hooked up to an Uno or Duemilanove or other 5V Arduino’s directly (only the SDA, SCL and IRQ pins. The VCC still needs to be 3.3V) I’ve not tried it. My entire setup is based on 3.3V (except for the LCD which is on 5V).
The Code
Before you upload the code, you may want to set the Atmega8A fuse bits to use an external oscillator. Also since the whole setup is on 3.3V the maximum oscillator frequency is 8MHz. The code also supports sending data through an UART. Hence to keep error rates to 0.00% a 7.3728MHz crystal is chosen.
lfuse: 0xFF
hfuse: 0xC9
Here is my code. Its quite long and you may want to break it up into libraries. The code is appropriately commented. If you have queries give us a shout in the comments.
/* This code demonstrates the functionality of a 12-key touchpad based on Freescale's MPR121Q. This code uses parts of Sparkfun's code (the I2C and MPR121 config routines) for their MPR121Q breakout board and their help is much appreciated. This code was compiled using Atmel Studio 6.1. The touchpad checks are based on the electrode wiring configuration mentioned below. If any other configuration is used, appropriate changes need to be made in the #defines in the code. This code also includes UART routines which can be used in tandem with mpr121ReadConfig() and mpr121ReadRegStatus() functions to check if the MPR121 is configured properly. In its unaltered form this code writes data read form the touchpad to an LCD module. Peter Fleury's LCD library is used here and his help is much appreciated. Connections to the LCD are also listed here. The LCD is a widely available 16x2 character HD44780-based module. Hardware: --------- # An ATmega8A powered with 3.3V (since the MPR121Q runs on 3.3V) and 5V (only for the LCD module). # 100n capacitors on every VCC and GND pair on the ATmega8A. # 100n and 10u capacitors on the main VCC and GND on the ATmega8A. # 10K resistor between RESET and VCC. # 27pF capacitors between the XTAL pins and GND. MPR121 connections ------------------ IRQ is PD2 SDA is PC4 SCL is PC5 Touchpad MPR121Q electrodes -------- ------------------ 1 ELE 03 (pin 11) 2 ELE 07 (pin 15) 3 ELE 11 (pin 19) 4 ELE 02 (pin 10) 5 ELE 06 (pin 14) 6 ELE 10 (pin 18) 7 ELE 01 (pin 09) 8 ELE 05 (pin 13) 9 ELE 09 (pin 17) 0 ELE 04 (pin 12) * ELE 00 (pin 08) # ELE 08 (pin 16) LCD connections --------------- Be sure to define XTAL in lcd.h to the same F_CPU as this file. RS - PD5 RS - PD6 EN - PD7 Data lines: D4 - PB4 D5 - PB3 D6 - PB2 D7 - PB1 */ #define F_CPU 7372800 #include <stdlib.h> #include <avr/io.h> #include <avr/pgmspace.h> #include <avr/interrupt.h> #include <util/delay.h> #include "types.h" #include "defs.h" #include "i2c.h" #include "mpr121.h" #include "lcd.h" //Define UART settings #define BAUD_RATE 9600 #define MYUBRR (((F_CPU / (BAUD_RATE * 16UL))) - 1) // Mapping the keypad with MPR121Q electrodes #define ONE 3 #define TWO 7 #define THREE 11 #define FOUR 2 #define FIVE 6 #define SIX 10 #define SEVEN 1 #define EIGHT 5 #define NINE 9 #define ZERO 4 #define STAR 0 #define HASH 8 unsigned int readData; //variable to read data from the MPR121's config //registers. Should be read in HEX or binary format. #define MPR121_R 0xB5 // ADD pin is grounded #define MPR121_W 0xB4 // So address is 0x5A #define sbi(var, mask) ((var) |= (uint8_t)(1 << mask)) #define cbi(var, mask) ((var) &= (uint8_t)~(1 << mask)) ///============Function Prototypes=========/// char mpr121Read(unsigned char address); void mpr121Write(unsigned char address, unsigned char data); void mpr121QuickConfig(void); void mpr121ReadConfig(void); void mpr121ReadRegStatus(void); unsigned char whichPad(void); void printPadLCD(void); void USART_Init(unsigned int ubrr); void USART_Transmit(unsigned char data); unsigned char USART_Receive(void); int checkInterrupt(void); void ioinit(void); int main (void) { USART_Init(MYUBRR); lcd_init(LCD_DISP_ON); ioinit(); i2cInit(); mpr121QuickConfig(); lcd_clrscr(); lcd_puts("System ON"); while(1) { while(checkInterrupt()) //do nothing till the interrupt is triggered ; printPadLCD(); } } char mpr121Read(unsigned char address) { char data; i2cSendStart(); i2cWaitForComplete(); i2cSendByte(MPR121_W); //write 0xB4 i2cWaitForComplete(); i2cSendByte(address); //write register address i2cWaitForComplete(); i2cSendStart(); i2cSendByte(MPR121_R); //write 0xB5 i2cWaitForComplete(); i2cReceiveByte(TRUE); i2cWaitForComplete(); data = i2cGetReceivedByte(); //get MSB result i2cWaitForComplete(); i2cSendStop(); cbi(TWCR, TWEN); // Disable TWI sbi(TWCR, TWEN); // Enable TWI return data; } void mpr121Write(unsigned char address, unsigned char data) { i2cSendStart(); i2cWaitForComplete(); i2cSendByte(MPR121_W); // write 0xB4 i2cWaitForComplete(); i2cSendByte(address); // write register address i2cWaitForComplete(); i2cSendByte(data); i2cWaitForComplete(); i2cSendStop(); } // MPR121 Quick Config // This will configure all registers as described in AN3944 void mpr121QuickConfig(void) { // Section A // This group controls filtering when data is > baseline. mpr121Write(MHD_R, 0x01); mpr121Write(NHD_R, 0x01); mpr121Write(NCL_R, 0x00); mpr121Write(FDL_R, 0x00); // Section B // This group controls filtering when data is < baseline. mpr121Write(MHD_F, 0x01); mpr121Write(NHD_F, 0x01); mpr121Write(NCL_F, 0xFF); mpr121Write(FDL_F, 0x02); // Section C // This group sets touch and release thresholds for each electrode mpr121Write(ELE0_T, TOU_THRESH); mpr121Write(ELE0_R, REL_THRESH); mpr121Write(ELE1_T, TOU_THRESH); mpr121Write(ELE1_R, REL_THRESH); mpr121Write(ELE2_T, TOU_THRESH); mpr121Write(ELE2_R, REL_THRESH); mpr121Write(ELE3_T, TOU_THRESH); mpr121Write(ELE3_R, REL_THRESH); mpr121Write(ELE4_T, TOU_THRESH); mpr121Write(ELE4_R, REL_THRESH); mpr121Write(ELE5_T, TOU_THRESH); mpr121Write(ELE5_R, REL_THRESH); mpr121Write(ELE6_T, TOU_THRESH); mpr121Write(ELE6_R, REL_THRESH); mpr121Write(ELE7_T, TOU_THRESH); mpr121Write(ELE7_R, REL_THRESH); mpr121Write(ELE8_T, TOU_THRESH); mpr121Write(ELE8_R, REL_THRESH); mpr121Write(ELE9_T, TOU_THRESH); mpr121Write(ELE9_R, REL_THRESH); mpr121Write(ELE10_T, TOU_THRESH); mpr121Write(ELE10_R, REL_THRESH); mpr121Write(ELE11_T, TOU_THRESH); mpr121Write(ELE11_R, REL_THRESH); // Section D // Set the Filter Configuration // Set ESI2 mpr121Write(FIL_CFG, 0x04); // Section E // Electrode Configuration // Enable 6 Electrodes and set to run mode // Set ELE_CFG to 0x00 to return to standby mode //mpr121Write(ELE_CFG, 0x06); // Enable first 6 electrodes mpr121Write(ELE_CFG, 0x0C); // Enables all 12 Electrodes // Section F // Enable Auto Config and auto Re-config //mpr121Write(ATO_CFG0, 0x0B); //mpr121Write(ATO_CFGU, 0xC9); // USL = (Vdd-0.7)/vdd*256 = 0xC9 @3.3V //mpr121Write(ATO_CFGL, 0x82); // LSL = 0.65*USL = 0x82 @3.3V //mpr121Write(ATO_CFGT, 0xB5); // Target = 0.9*USL = 0xB5 @3.3V } int checkInterrupt(void) { if ((PIND & (1<<2)) == 0) return 0; else return 1; } void ioinit (void) { //1 = output, 0 = input DDRB = 0b00111111; //PORTB1, B1 output DDRC = 0b00010111; //PORTC4 (SDA), PORTC5 (SCL), PORTC all others are inputs DDRD = 0b11111010; //PORTD (RX on PD0), IRQ on PD2 PORTC = 0b00110000; //pullups on the I2C bus } unsigned char whichPad(void) { unsigned char touchedPad; //Read all pads based on the electrode connections listed above // HEX values returned by mpr121Read() show which bits are set // and hence which pads are touched. if (mpr121Read(0x00) == 0x08 && mpr121Read(0x01) == 0x00) touchedPad = '1'; else if (mpr121Read(0x00) == 0x80 && mpr121Read(0x01) == 0x00) touchedPad = '2'; else if (mpr121Read(0x00) == 0x00 && mpr121Read(0x01) == 0x08) touchedPad = '3'; else if (mpr121Read(0x00) == 0x04 && mpr121Read(0x01) == 0x00) touchedPad = '4'; else if (mpr121Read(0x00) == 0x40 && mpr121Read(0x01) == 0x00) touchedPad = '5'; else if (mpr121Read(0x00) == 0x00 && mpr121Read(0x01) == 0x04) touchedPad = '6'; else if (mpr121Read(0x00) == 0x02 && mpr121Read(0x01) == 0x00) touchedPad = '7'; else if (mpr121Read(0x00) == 0x20 && mpr121Read(0x01) == 0x00) touchedPad = '8'; else if (mpr121Read(0x00) == 0x00 && mpr121Read(0x01) == 0x02) touchedPad = '9'; else if (mpr121Read(0x00) == 0x10 && mpr121Read(0x01) == 0x00) touchedPad = '0'; else if (mpr121Read(0x00) == 0x01 && mpr121Read(0x01) == 0x00) touchedPad = '*'; else if (mpr121Read(0x00) == 0x00 && mpr121Read(0x01) == 0x01) touchedPad = '#'; return touchedPad; } /* UART functions */ void USART_Init(unsigned int ubrr) { /* Set baud rate*/ UBRRH = (ubrr>>8); UBRRL = ubrr; /* Enable receiver and transmitter */ UCSRB = (1<<RXEN) | (1<<TXEN); /* Set frame format: 8data, 1 stop bit */ UCSRC = 0b10000110; //URSEL=1, UMSEL=0, UPM1=0, UPM0=0, UCSZ1=1, UCSZ0=1, UCPOL=0. } void USART_Transmit(unsigned char data) { /* Wait for empty transmit buffer */ while(!(UCSRA & (1<<UDRE))) ; /* Put data into buffer, sends the data */ UDR = data; } unsigned char USART_Receive(void) { /* Wait for data to be received */ while(!(UCSRA & (1<<RXC))) ; /* Get and return received data from buffer */ return UDR; } /* Output MPR121Q configuration registers contents on the UART */ void mpr121ReadConfig(void) { readData = mpr121Read(0x2B); //MHD rising USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x2C); //NHD rising USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x2D); //NCL rising USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x2E); //FDL rising USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x2F); //MHD falling USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x30); //NHD falling USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x31); //NCL falling USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x32); //FDL falling USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x41); //ELE0 touch threshold USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x42); //ELE0 release threshold USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x43); //ELE1 touch threshold USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x44); //ELE1 release threshold USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x45); //ELE2 touch threshold USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x46); //ELE2 release threshold USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x47); //ELE3 touch threshold USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x48); //ELE3 release threshold USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x49); //ELE4 touch threshold USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x4A); //ELE4 release threshold USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x4B); //ELE5 touch threshold USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x4C); //ELE5 release threshold USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x4D); //ELE6 touch threshold USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x4E); //ELE6 release threshold USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x4F); //ELE7 touch threshold USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x50); //ELE7 release threshold USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x51); //ELE8 touch threshold USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x52); //ELE8 release threshold USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x53); //ELE9 touch threshold USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x54); //ELE9 release threshold USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x55); //ELE10 touch threshold USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x56); //ELE10 release threshold USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x57); //ELE11 touch threshold USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x58); //ELE11 release threshold USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x5D); //Filter configuration USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x5E); //Number of electrodes enabled USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x7B); //AUTO-CONFIG control register 0 USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x7D); //AUTO-CONFIG USL register USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x7E); //AUTO-CONFIG LSL register USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x7F); //AUTO-CONFIG Target Level register USART_Transmit(readData); _delay_ms(1000); } /* Output MPR121Q touch status register contents on the UART */ void mpr121ReadRegStatus(void) { readData = mpr121Read(0x00); //ELE0-7 read status USART_Transmit(readData); _delay_ms(1000); readData = mpr121Read(0x01); //ELE8-11 read status USART_Transmit(readData); _delay_ms(1000); } void printPadLCD(void) { if (whichPad() == '1') { lcd_clrscr(); lcd_puts("Pad Touched: "); lcd_putc('1'); } else if (whichPad() == '2') { lcd_clrscr(); lcd_puts("Pad Touched: "); lcd_putc('2'); } else if (whichPad() == '3') { lcd_clrscr(); lcd_puts("Pad Touched: "); lcd_putc('3'); } else if (whichPad() == '4') { lcd_clrscr(); lcd_puts("Pad Touched: "); lcd_putc('4'); } else if (whichPad() == '5') { lcd_clrscr(); lcd_puts("Pad Touched: "); lcd_putc('5'); } else if (whichPad() == '6') { lcd_clrscr(); lcd_puts("Pad Touched: "); lcd_putc('6'); } else if (whichPad() == '7') { lcd_clrscr(); lcd_puts("Pad Touched: "); lcd_putc('7'); } else if (whichPad() == '8') { lcd_clrscr(); lcd_puts("Pad Touched: "); lcd_putc('8'); } else if (whichPad() == '9') { lcd_clrscr(); lcd_puts("Pad Touched: "); lcd_putc('9'); } else if (whichPad() == '0') { lcd_clrscr(); lcd_puts("Pad Touched: "); lcd_putc('0'); } else if (whichPad() == '*') { lcd_clrscr(); lcd_puts("Pad Touched: "); lcd_putc('*'); } else if (whichPad() == '#') { lcd_clrscr(); lcd_puts("Pad Touched: "); lcd_putc('#'); } }
So, if the ‘#’ is pressed, you end up reading either register 00 or 01 a total of 288 times before you determine what key was pressed ?
To be honest, this was a very long time ago and I don’t remember much of it. In the ‘printPadLCD’ function the two registers seems to be read once each for 11 times before it reaches the ‘#’ key (through the ‘whichPad’ function). It obviously could’ve been written to just read the register once and store its value in a variable and then compare against a known set of values for printing on the LCD. The ‘whichPad’ function, each register gets read once and that value is then compared against the set values of the keypad.
Thanks for the code.
hi dear
I have CVAVR how can I get a HEX file of your code, to program using CVAVR?
thanks
I’ve never used CVAVR and I do not have the hex file with me as of now. I worked on this project a long time ago… Sorry!
hello dear friend
I was tormented for a long time to launch your project in Avr Studio 6.1 but I always get error.
could you comile your project in avr studio 6.1 and send me a hex file?
I am ready to accept your commercial conditions for receiving a working hex file.
david