Using a Freescale MPR121 touch controller with Arduino

01 Oct 2010

Freescale MPR121 on a Sparkfun breakout board

Below is an Arduino sketch that uses the Freescale MPR121 capacitive touch sensor controller on a Sparkfun breakout board. The MPR121 gives you a highly configurable touch sensor chip providing 12 individual electrodes and 8 LED driving pins that can be used when not configured as electrodes. The following code uses 6 pins of the MPR121 as electrodes and 6 pins to drive LEDs. It was originally written in AVR C, and I've simply adapted it to function and compile using the Arduino software.

Download the source code.

 MPR121 Test Code
 October 1, 2010
 by Rory Nugent
 Based on original code by Jim Lindblom

 This example code will both sense for touches, and drive LEDs
 ELE6-11 are used in GPIO mode, to drive 6 LEDs
 ELE0-5 are used as capacative touch sensors
 Triggering a touch sensor will cause a corresponding LED to illuminate

 For desired operation, you may need to play around with TOU_THRESH and REL_THRESH threshold values.
 With default settings, you can brush your fingers across the ELE0-5 pins to trigger the LEDs

 The 6 anodes of 2 RGB LEDs are tied to ELE6-11, cathodes tied to ground

 Tested on an Arduino Duemilanove
 A4 -> SDA
 A5 -> SCL
 D2 -> IRQ

#include <Wire.h>     // Wiring two-wire library for I2C
#include "mpr121.h"   // register definitions

#define irqPin 2      // interrupt pin

char touchStatus = 0;

void setup()
  pinMode(irqPin, INPUT);           // set the interrupt pin as a digital input


  // Setup the GPIO pins to drive LEDs
  mpr121Write(GPIO_EN, 0xFF);       // 0x77 is GPIO enable
  mpr121Write(GPIO_DIR, 0xFF);      // 0x76 is GPIO Dir
  mpr121Write(GPIO_CTRL0, 0xFF);    // Set to LED driver
  mpr121Write(GPIO_CTRL1, 0xFF);    // GPIO Control 1
  mpr121Write(GPIO_CLEAR, 0xFF);    // GPIO Data Clear

  // Blink LEDs to begin
  for (int i = 0; i < 2; i++)
    mpr121Write(GPIO_SET, 0xFF);
    mpr121Write(GPIO_CLEAR, 0xFF);

void loop()

  touchStatus = mpr121Read(0x00);      // read touch status
  mpr121Write(GPIO_CLEAR, 0xFF);       // clear all leds
  touchStatus = touchStatus << 2;      // shift two bits over
  mpr121Write(GPIO_SET, touchStatus);  // set LED

char mpr121Read(unsigned char address)
  char data;

  Wire.beginTransmission(0x5A);  // begin communication with the MPR121 on I2C address 0x5A
  Wire.send(address);            // sets the register pointer
  Wire.requestFrom(0x5A, 1);     // request for the MPR121 to send you a single byte

  // check to see if we've received the byte over I2C
  if(1 <= Wire.available())
    data = Wire.receive();

  Wire.endTransmission();        // ends communication

  return data;  // return the received data

void mpr121Write(unsigned char address, unsigned char data)
  Wire.beginTransmission(0x5A);  // begin communication with the MPR121 on I2C address 0x5A
  Wire.send(address);            // sets the register pointer
  Wire.send(data);               // sends data to be stored
  Wire.endTransmission();        // ends communication

// MPR121 Quick Config
// This will configure all registers as described in AN3944
// Input: none
// Output: none

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, 0x0C);    // Enables all 12 Electrodes
  mpr121Write(ELE_CFG, 0x06);       // Enable first 6 electrodes

  // Section F
  // Enable Auto Config and auto Reconfig
  /*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(digitalRead(irqPin) == 0)
    return 0;
    return 1;