I designed a keypad board for the Freescale MPR121 Capacitive Touch Sensor Controller chip and received it in my latest batch of boards from dorkbotpdx.org. I'm really happy with this one. The MPR121 is really easy to use and, though it has tons of internal adjustments and controls to suit almost any sensing job, works perfectly with all default settings for my little board.
This board was pretty tough to design in Eagle. The touchpads and the traces to them have to be laid out just so or they might not work well. The autorouter completely refused to do anything useful with this board no matter what I tried, so I ended up hand routing everything, and even that was very difficult. But in the end it was worth it. It looks great, and works just as well.
Hand soldering that dinky little QFN-20 chip was a bit of a bear, but it went ok. You can see where I accidently soldered up some vias while doing the chip.
The chip is controlled with I2C. In the pics below I have the LCD displaying the hex value of the Touch Status bytes ($00 and $01). First eight keys are on byte $00 (keys1) and the last four are on byte $01 (keys2). Default chip settings give very clean readings. There's no jitter whatsoever. Smooth and nice - nice job Freescale.
The chip is a 3.3V piece, so don't go connecting it to 5V or you'll let the magic smoke out. It has an IRQ line that changes state whenever you touch or release a key. I connected that to RB4 and used the RB Port Change Interrupt to let my code know when a key is pressed.
If you want to make your own board just download my Eagle schematic and board layout. You'll also need my Eagle library. This file set is for the DorkbotPDX PCB service. You may possibly have to make changes for other PCB services - I don't know.
Schematic
Video of the board in action. Be sure to watch it in 720P so you can see the LCD:
Here's some rough BoostC demo code for 18F2550. Header file at bottom:
#include "mpr121.h"
bit keyflag = 0; //key press flag
unsigned char keys1,keys2;
char string[] = " ";
void interrupt(void){
if(intcon.RBIF){ //RB port change interrupt
keyflag = 1;
intcon.RBIF = 0;
}
}
void main()
{
unsigned char touch;
osccon = 0x72; //internal 8MHz clock
adcon1 = 0b00001111; //all digital
trisa = trisc = 0;
trisb = 0x13;
sspadd = 0x13;
sspcon1 = 0b00101000;
pir1.SSPIF = 0;
mpr_init();
lcd_init();
lcd_string("**** MPR121 ****",0);
intcon.RBIF = 0; //clear RB port change interrupt flag
intcon.RBIE = 1; //enable RBIF interrupt
intcon.GIE = 1; //enable global interrupt
while(1){
if(keyflag){
intcon.GIE = 0;
mpr_read(0);
/* sprintf(string,"keys1: $%2X",keys1);
lcd_string(string,2);
sprintf(string,"keys2: $%2X",keys2);
lcd_string(string,3);*/
switch(keys1){
case 0x01:
lcd_string("Key Pressed: 0",2);
break;
case 0x02:
lcd_string("Key Pressed: 1",2);
break;
case 0x04:
lcd_string("Key Pressed: 2",2);
break;
case 0x08:
lcd_string("Key Pressed: 3",2);
break;
case 0x10:
lcd_string("Key Pressed: 4",2);
break;
case 0x20:
lcd_string("Key Pressed: 5",2);
break;
case 0x40:
lcd_string("Key Pressed: 6",2);
break;
case 0x80:
lcd_string("Key Pressed: 7",2);
break;
}
switch(keys2){
case 0x01:
lcd_string("Key Pressed: 8",2);
break;
case 0x02:
lcd_string("Key Pressed: 9",2);
break;
case 0x04:
lcd_string("Key Pressed: A",2);
break;
case 0x08:
lcd_string("Key Pressed: B",2);
break;
}
keyflag = 0;
intcon.GIE = 1;
}
}
}
//*****************
//* I2C functions *
//*****************
unsigned char i2c_write(unsigned char x){
sspbuf = x;
busywait();
return(!sspcon2.ACKSTAT);
}
unsigned char i2c_read(void){
sspcon2.RCEN = 1; //receive enable
busywait();
return(sspbuf);
}
void ack(void){
sspcon2.ACKDT = 0; //send ack
sspcon2.ACKEN = 1;
// while(sspcon2.ACKEN);
busywait();
}
void nack(void){
sspcon2.ACKDT = 1; //send nack
sspcon2.ACKEN = 1;
busywait();
}
void busywait(void){
while(pir1.SSPIF == 0);
pir1.SSPIF = 0;
}
void start(void){
sspcon2.SEN = 1;
busywait();
// while(sspcon2.SEN);
}
void restart(void){
sspcon2.RSEN = 1;
busywait();
}
void stop(void){
sspcon2.PEN = 1;
busywait();
}
//******************
//MPR121 functions *
//******************
void mpr_read(unsigned char address){
unsigned char data;
start();
i2c_write(WADD); //slave address (write)
i2c_write(address); //register address
restart();
i2c_write(RADD); //slave address (read)
keys1 = i2c_read();
ack();
keys2 = i2c_read();
nack();
stop();
}
void mpr_init(void){
start();
i2c_write(WADD); //slave address
i2c_write(0x2b); //reg address
i2c_write(0x01); //reg 0x2b
i2c_write(0x01);
i2c_write(0x00);
i2c_write(0x00);
i2c_write(0x01); //reg 0x2f
i2c_write(0x01);
i2c_write(0xFF);
i2c_write(0x02);
stop();
start();
i2c_write(WADD);
i2c_write(0x41);
i2c_write(0x0f); //reg 0x41
i2c_write(0x0a);
i2c_write(0x0f);
i2c_write(0x0a);
i2c_write(0x0f);
i2c_write(0x0a);
i2c_write(0x0f);
i2c_write(0x0a);
i2c_write(0x0f);
i2c_write(0x0a);
i2c_write(0x0f);
i2c_write(0x0a);
i2c_write(0x0f);
i2c_write(0x0a);
i2c_write(0x0f);
i2c_write(0x0a);
i2c_write(0x0f);
i2c_write(0x0a);
i2c_write(0x0f);
i2c_write(0x0a);
i2c_write(0x0f);
i2c_write(0x0a);
i2c_write(0x0f);
i2c_write(0x0a);
stop();
start();
i2c_write(WADD);
i2c_write(0x5d);
i2c_write(0x04);
i2c_write(0x0C); // Enables all 12 Electrodes
stop();
}
//********************
//7110 LCD functions *
//********************
void lcd_string(const char *str,unsigned char row){
unsigned char x,page = 0xb0;
page = page + row;
lcd_send(page,0); //page address
lcd_send(0x11,0); //column address
lcd_send(0x02,0);
while(*str)
lcd_char(*str++);
}
void lcd_char(unsigned char letter){
unsigned char col;
if(letter < 0x52){
letter = letter - 0x20;
for(col=0;col<5;col++){
lcd_send(Alpha1[(letter*5)+col],1);
}
}
else{
letter = letter - 0x52;
for(col=0;col<5;col++){
lcd_send(Alpha2[(letter*5)+col],1);
}
}
lcd_send(0,1); //send space
}
void lcd_send(unsigned char cmd, unsigned char type){
char x;
if(type) //set for command or data
DATACHAR = 1;
else
DATACHAR = 0;
CS = 0;
for(x=0;x<9;x++){ //clock the byte out
SCLK = 0;
SDATA = 0;
if(cmd & 0x80)
SDATA = 1;
SCLK = 1;
cmd <<= 1;
}
CS = 1;
}
void lcd_cls(void){
unsigned char i,x,line;
line = 0xb0; //page address variable
for(x=0;x<9;x++){
lcd_send(line,0); //set page address
lcd_send(0x11,0); //set column address
lcd_send(0x02,0);
for(i=0;i<0x60;i++){ //write zeros to display RAM
lcd_send(0x00,1);
}
line++;
}
}
void lcd_row_cls(unsigned char row){
unsigned char x,page = 0xb0;
page = page + row;
for(x=0;x<0x60;x++)
lcd_send(0x00,1);
}
void lcd_init(void){
DATACHAR = 1;
delay_ms(2);
CS = 1;
delay_ms(2);
// RST = 1;
delay_ms(2);
lcd_send(0xa6,0); //Display: Normal
lcd_send(0xA3,0); //LCD Bias Settings: 1/7
lcd_send(0xA1,0); //ADC Selection: Reverse
lcd_send(0xC0,0); //Common Output: Normal Direction
// lcd_send(0xC8,0); //Common Output: Upside Down
lcd_send(0x22,0); //Set the V5 output Voltage
lcd_send(0x81,0); //set Electronic Volume - brightness
lcd_send(0x2f,0);
lcd_send(0x2E,0); //Power Controller Set:
lcd_send(0x2F,0); //Power Controller Set: Voltage follower circuit: ON
// lcd_send(0xE3,0); //Non-OPeration Command
// lcd_send(0x40,0); //Set the start line
lcd_send(0xAF,0); //LCD On
lcd_send(0xA4,0); //Display All Points: NORMAL
lcd_cls();
}
mpr121.h
#include <system.h>
#include <stdio.h>
#pragma CLOCK_FREQ 8000000
#pragma DATA _CONFIG1H, _FOSC_INTOSCIO_EC_1H
#pragma DATA _CONFIG2H, _WDT_OFF_2H
#pragma DATA _CONFIG3H, _MCLRE_ON_3H
#pragma DATA _CONFIG4L, _LVP_OFF_4L & _XINST_OFF_4L
void mpr_init(void);
void mpr_read(unsigned char);
void start(void);
void restart(void);
void stop(void);
void ack(void);
void nack(void);
void busywait(void);
void unstick(void);
unsigned char i2c_read(void);
unsigned char i2c_write(unsigned char);
void lcd_cls(void);
void lcd_row_cls(unsigned char);
void lcd_init(void);
void lcd_send(unsigned char, unsigned char);
void lcd_char(unsigned char);
void lcd_string(const char *,unsigned char);
//MPR121
#define WADD 0xb4
#define RADD 0xb5
//7110 LCD
#define CS portc.2
#define DATACHAR portc.1
#define SCLK portc.0
#define SDATA portc.6
rom char *Alpha1 = {
0x00,0x00,0x00,0x00,0x00, // 20 space
0x00,0x00,0x5f,0x00,0x00, // 21 !
0x00,0x07,0x00,0x07,0x00, // 22 "
0x14,0x7f,0x14,0x7f,0x14, // 23 #
0x24,0x2a,0x7f,0x2a,0x12, // 24 $
0x23,0x13,0x08,0x64,0x62, // 25 %
0x36,0x49,0x55,0x22,0x50, // 26 &
0x00,0x05,0x03,0x00,0x00, // 27 '
0x00,0x1c,0x22,0x41,0x00, // 28 (
0x00,0x41,0x22,0x1c,0x00, // 29 )
0x14,0x08,0x3e,0x08,0x14, // 2a *
0x08,0x08,0x3e,0x08,0x08, // 2b +
0x00,0x50,0x30,0x00,0x00, // 2c ,
0x08,0x08,0x08,0x08,0x08, // 2d -
0x00,0x60,0x60,0x00,0x00, // 2e .
0x20,0x10,0x08,0x04,0x02, // 2f /
0x3e,0x51,0x49,0x45,0x3e, // 30 0
0x00,0x42,0x7f,0x40,0x00, // 31 1
0x42,0x61,0x51,0x49,0x46, // 32 2
0x21,0x41,0x45,0x4b,0x31, // 33 3
0x18,0x14,0x12,0x7f,0x10, // 34 4
0x27,0x45,0x45,0x45,0x39, // 35 5
0x3c,0x4a,0x49,0x49,0x30, // 36 6
0x01,0x71,0x09,0x05,0x03, // 37 7
0x36,0x49,0x49,0x49,0x36, // 38 8
0x06,0x49,0x49,0x29,0x1e, // 39 9
0x00,0x36,0x36,0x00,0x00, // 3a :
0x00,0x56,0x36,0x00,0x00, // 3b ;
0x08,0x14,0x22,0x41,0x00, // 3c <
0x14,0x14,0x14,0x14,0x14, // 3d =
0x00,0x41,0x22,0x14,0x08, // 3e >
0x02,0x01,0x51,0x09,0x06, // 3f ?
0x32,0x49,0x79,0x41,0x3e, // 40 @
0x7e,0x11,0x11,0x11,0x7e, // 41 A
0x7f,0x49,0x49,0x49,0x36, // 42 B
0x3e,0x41,0x41,0x41,0x22, // 43 C
0x7f,0x41,0x41,0x22,0x1c, // 44 D
0x7f,0x49,0x49,0x49,0x41, // 45 E
0x7f,0x09,0x09,0x09,0x01, // 46 F
0x3e,0x41,0x49,0x49,0x7a, // 47 G
0x7f,0x08,0x08,0x08,0x7f, // 48 H
0x00,0x41,0x7f,0x41,0x00, // 49 I
0x20,0x40,0x41,0x3f,0x01, // 4a J
0x7f,0x08,0x14,0x22,0x41, // 4b K
0x7f,0x40,0x40,0x40,0x40, // 4c L
0x7f,0x02,0x0c,0x02,0x7f, // 4d M
0x7f,0x04,0x08,0x10,0x7f, // 4e N
0x3e,0x41,0x41,0x41,0x3e, // 4f O
0x7f,0x09,0x09,0x09,0x06, // 50 P
0x3e,0x41,0x51,0x21,0x5e}; // 51 Q
rom char *Alpha2 = {
0x7f,0x09,0x19,0x29,0x46, // 52 R
0x46,0x49,0x49,0x49,0x31, // 53 S
0x01,0x01,0x7f,0x01,0x01, // 54 T
0x3f,0x40,0x40,0x40,0x3f, // 55 U
0x1f,0x20,0x40,0x20,0x1f, // 56 V
0x3f,0x40,0x38,0x40,0x3f, // 57 W
0x63,0x14,0x08,0x14,0x63, // 58 X
0x07,0x08,0x70,0x08,0x07, // 59 Y
0x61,0x51,0x49,0x45,0x43, // 5a Z
0x00,0x7f,0x41,0x41,0x00, // 5b [
0x02,0x04,0x08,0x10,0x20, // 5c 55
0x00,0x41,0x41,0x7f,0x00, // 5d ]
0x04,0x02,0x01,0x02,0x04, // 5e ^
0x40,0x40,0x40,0x40,0x40, // 5f _
0x00,0x01,0x02,0x04,0x00, // 60 `
0x20,0x54,0x54,0x54,0x78, // 61 a
0x7f,0x48,0x44,0x44,0x38, // 62 b
0x38,0x44,0x44,0x44,0x20, // 63 c
0x38,0x44,0x44,0x48,0x7f, // 64 d
0x38,0x54,0x54,0x54,0x18, // 65 e
0x08,0x7e,0x09,0x01,0x02, // 66 f
0x0c,0x52,0x52,0x52,0x3e, // 67 g
0x7f,0x08,0x04,0x04,0x78, // 68 h
0x00,0x44,0x7d,0x40,0x00, // 69 i
0x20,0x40,0x44,0x3d,0x00, // 6a j
0x7f,0x10,0x28,0x44,0x00, // 6b k
0x00,0x41,0x7f,0x40,0x00, // 6c l
0x7c,0x04,0x18,0x04,0x78, // 6d m
0x7c,0x08,0x04,0x04,0x78, // 6e n
0x38,0x44,0x44,0x44,0x38, // 6f o
0x7c,0x14,0x14,0x14,0x08, // 70 p
0x08,0x14,0x14,0x18,0x7c, // 71 q
0x7c,0x08,0x04,0x04,0x08, // 72 r
0x48,0x54,0x54,0x54,0x20, // 73 s
0x04,0x3f,0x44,0x40,0x20, // 74 t
0x3c,0x40,0x40,0x20,0x7c, // 75 u
0x1c,0x20,0x40,0x20,0x1c, // 76 v
0x3c,0x40,0x30,0x40,0x3c, // 77 w
0x44,0x28,0x10,0x28,0x44, // 78 x
0x0c,0x50,0x50,0x50,0x3c, // 79 y
0x44,0x64,0x54,0x4c,0x44, // 7a z
0x00,0x08,0x36,0x41,0x00, // 7b {
0x00,0x00,0x7f,0x00,0x00, // 7c |
0x00,0x41,0x36,0x08,0x00, // 7d }
0x10,0x08,0x08,0x10,0x08, // 7e ~
0x78,0x46,0x41,0x46,0x78}; // 7f ¦