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

  Wire.begin();
  mpr121QuickConfig();

  // 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);
    delay(500);
    mpr121Write(GPIO_CLEAR, 0xFF);
    delay(500);
  }
}

void loop()
{
  while(checkInterrupt());

  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;
  else
    return 1;
}