Notes About Working with Various Arduino & Netduino Microcontroller Boards

Thursday, June 4, 2015

C Program to Read an MPR121 Capacitive Touch Keypad Connected to an Arduino Galileo

This example presents C code that reads input from a Sparkfun MPR121 capacitive touch keypad. There are sample Arduino sketches around, but I wanted to use the keypad with a C program running at the Linux command prompt on the Galileo.

I have tried to keep this code as simple as possible.  This example does not use an interrupt but continuously polls the keypad to check for input and prints out the character pressed. The code doesn't make use of any bells and whistles to track the press and release of the keys. This program treats the keypad like a phone keypad.

For I2C access, this example uses the smbus library compiled as a shared library discussed in this post.

Sparkfun's MPR121 Hookup Guide has been very helpful. My understanding of the configuration parameters is limited, so I have taken the settings from the Sparkfun material. For understanding the Galileo's GPIO pins,  Sergey Kisilev's blog post is a critical piece of the puzzle.


MPR121  Arduino Galileo
VCC     3.3V 


Header File (mpr121.h)

April 8, 2010
by: Jim Lindblom

// MPR121 Register Defines
#define MHD_R 0x2B
#define NHD_R 0x2C
#define NCL_R 0x2D
#define FDL_R 0x2E
#define MHD_F 0x2F
#define NHD_F 0x30
#define NCL_F 0x31
#define FDL_F 0x32
#define ELE0_T 0x41
#define ELE0_R 0x42
#define ELE1_T 0x43
#define ELE1_R 0x44
#define ELE2_T 0x45
#define ELE2_R 0x46
#define ELE3_T 0x47
#define ELE3_R 0x48
#define ELE4_T 0x49
#define ELE4_R 0x4A
#define ELE5_T 0x4B
#define ELE5_R 0x4C
#define ELE6_T 0x4D
#define ELE6_R 0x4E
#define ELE7_T 0x4F
#define ELE7_R 0x50
#define ELE8_T 0x51
#define ELE8_R 0x52
#define ELE9_T 0x53
#define ELE9_R 0x54
#define ELE10_T 0x55
#define ELE10_R 0x56
#define ELE11_T 0x57
#define ELE11_R 0x58
#define FIL_CFG 0x5D
#define ELE_CFG 0x5E
#define GPIO_CTRL0 0x73
#define GPIO_CTRL1 0x74
#define GPIO_DATA 0x75
#define GPIO_DIR 0x76
#define GPIO_EN 0x77
#define GPIO_SET 0x78
#define GPIO_CLEAR 0x79
#define GPIO_TOGGLE 0x7A
#define ATO_CFG0 0x7B
#define ATO_CFGU 0x7D
#define ATO_CFGL 0x7E
#define ATO_CFGT 0x7F

// Global Constants
#define TOU_THRESH 0x06
#define REL_THRESH 0x0A

C Source Code (mpr121.c)

#include "mpr121.h"
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include <smbus.h>  // smbus.h copied to /usr/include
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdint.h>
#include <stdbool.h>

int main() {
   int deviceHandle = openI2C(0x5A);
   while(1) {
      unsigned char lsb = i2c_smbus_read_byte_data(deviceHandle, 0);
      unsigned char msb = i2c_smbus_read_byte_data(deviceHandle, 1);
      short touchStatus = (msb << 8) | lsb;
      int c;
      for(c = 0; c < 12; c++) {
         if(touchStatus & (1 << c)) {
            printf("%c\n", KEY[c]);
            break; // only 1 key even if more pressed at a time
      // Pause for 0.15 second before reading again
   return 0;

// Configure multiplexing of Galileo's GPIO pins for I2C
void configPins() {
   // Set multiplexing for i2c pins via gpio29. Start by exporting gpio29 
   // so it can be accessed via sysfs.
   int fd = open("/sys/class/gpio/export", O_WRONLY);
   write(fd, "29", 2);
   // Export I2C pins, too, so they can be set to pullup
   write(fd, "48", 2);
   write(fd, "49", 2);
   // Set gpio29's direction to out
   fd = open("/sys/class/gpio/gpio29/direction", O_WRONLY);
   write(fd, "out", 3);

   // Set gpio29's value to 0 to enable i2c via Quark chip
   fd = open("/sys/class/gpio/gpio29/value", O_WRONLY);
   // Note that 0 is passed as a string/char.
   write(fd, "0", 1);

   // Enable pullups on I2C pins. The default drive mode is pullup, 
   // so these lines may not be necessary. 
   fd = open("/sys/class/gpio/gpio48/drive", O_WRONLY);
   write(fd, "pullup", 6);
   fd = open("/sys/class/gpio/gpio49/drive", O_WRONLY);
   write(fd, "pullup", 6);

// Open connection to I2C device & return handle
int openI2C(unsigned char addr) {
   // On Galileo (Clanton), the I2C bus is /dev/i2c-0
   int deviceHandle = open("/dev/i2c-0", O_RDWR);
   // Connect to device at address
   if(ioctl(deviceHandle, I2C_SLAVE, addr) < 0) {
      fprintf(stderr, "Failed to open I2C device as slave.\n");
   return deviceHandle;

void mpr121QuickConfig(int dh) {
  i2c_smbus_write_byte_data(dh, ELE_CFG, 0x00); 

  // This group controls filtering when data is > baseline.
  i2c_smbus_write_byte_data(dh, MHD_R, 0x01);
  i2c_smbus_write_byte_data(dh, NHD_R, 0x01);
  i2c_smbus_write_byte_data(dh, NCL_R, 0x00);
  i2c_smbus_write_byte_data(dh, FDL_R, 0x00);

  // This group controls filtering when data is < baseline.
  i2c_smbus_write_byte_data(dh, MHD_F, 0x01);
  i2c_smbus_write_byte_data(dh, NHD_F, 0x01);
  i2c_smbus_write_byte_data(dh, NCL_F, 0xFF);
  i2c_smbus_write_byte_data(dh, FDL_F, 0x02);
  // This group sets touch and release thresholds for each electrode
  i2c_smbus_write_byte_data(dh, ELE0_T, TOU_THRESH);
  i2c_smbus_write_byte_data(dh, ELE0_R, REL_THRESH);
  i2c_smbus_write_byte_data(dh, ELE1_T, TOU_THRESH);
  i2c_smbus_write_byte_data(dh, ELE1_R, REL_THRESH);
  i2c_smbus_write_byte_data(dh, ELE2_T, TOU_THRESH);
  i2c_smbus_write_byte_data(dh, ELE2_R, REL_THRESH);
  i2c_smbus_write_byte_data(dh, ELE3_T, TOU_THRESH);
  i2c_smbus_write_byte_data(dh, ELE3_R, REL_THRESH);
  i2c_smbus_write_byte_data(dh, ELE4_T, TOU_THRESH);
  i2c_smbus_write_byte_data(dh, ELE4_R, REL_THRESH);
  i2c_smbus_write_byte_data(dh, ELE5_T, TOU_THRESH);
  i2c_smbus_write_byte_data(dh, ELE5_R, REL_THRESH);
  i2c_smbus_write_byte_data(dh, ELE6_T, TOU_THRESH);
  i2c_smbus_write_byte_data(dh, ELE6_R, REL_THRESH);
  i2c_smbus_write_byte_data(dh, ELE7_T, TOU_THRESH);
  i2c_smbus_write_byte_data(dh, ELE7_R, REL_THRESH);
  i2c_smbus_write_byte_data(dh, ELE8_T, TOU_THRESH);
  i2c_smbus_write_byte_data(dh, ELE8_R, REL_THRESH);
  i2c_smbus_write_byte_data(dh, ELE9_T, TOU_THRESH);
  i2c_smbus_write_byte_data(dh, ELE9_R, REL_THRESH);
  i2c_smbus_write_byte_data(dh, ELE10_T, TOU_THRESH);
  i2c_smbus_write_byte_data(dh, ELE10_R, REL_THRESH);
  i2c_smbus_write_byte_data(dh, ELE11_T, TOU_THRESH);
  i2c_smbus_write_byte_data(dh, ELE11_R, REL_THRESH);

  // Set the Filter Configuration
  i2c_smbus_write_byte_data(dh, FIL_CFG, 0x04);
  // Enable all 12 Electrodes
  i2c_smbus_write_byte_data(dh, ELE_CFG, 0x0C); 
  // Enable Auto Config and auto Reconfig
  i2c_smbus_write_byte_data(dh, ELE_CFG, 0x06);

To compile the program, run the gcc compiler like this -

gcc -lsmbus -o mpr121 mpr121.c

No comments:

Post a Comment