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 ¦

Next Post