The Freescale/NXP MPR121 serves as both the capacitive touch controller and as a GPIO expander on the badge. It is connected to the ESP32 through I2C and an interrupt line.
- The MPR121 is connected to the ESP32 through I2C on pins IO26 (SDA) and IO27 (SCL).
- Software pullups are not necessary, as there are two pullup resistors on the board.
- The MPR's interrupt pin is connected to IO25 on the ESP.
- Its I2C slave address is 0x5A.
The MPR121 has twelve electrode connections (ELE0-11), of which eight can be used as GPIO. We are using the last four electrode connections as I/O.
|Electrode||GPIO||Function / direction||Connection|
|ELE8||GPIO4||Push/pull output||Vibration motor|
|ELE9||GPIO5||Input||TP4056 Charge status|
|ELE10||GPIO6||Push/pull output||WS2812 / SD Card power enable|
|ELE11||GPIO7||Input||SD Card detect|
The most important function of the MPR121: capacitive touch. I (Kartoffel) will describe how I was able to get it to work, though it might not be ideal and definitely needs tweaking. I left a lot of registers unexplored, and did not implement the over current detection which can halt the IC.
The basic setup steps:
- Initialize global baseline filter (registers 0x2B to 0x40) - see AN3891 for information about the baseline system.
- Set the touch and release thresholds for each electrode (registers 0x41 to 0x5A).
- Set electrode sample interval (register 0x5D) - this directly influences the current consumption.
Finally, to get the MPR121 into run mode:
- Enable the electrodes for touch detection (register 0x5E) - set this to 0x08 to enable just ELE0-ELE7 to make sure we can use the rest as GPIO.
Now the MPR is in run mode and scanning the touch electrodes.
When the state of an electrode changes the interrupt pin will go low, and the state should be read by the ESP. Register 0x0 holds the touch status of ELE0 to ELE7.
We are using ELE8-11 (GPIO4-7) as GPIO. The MPR uses eight registers to control its GPIO pins:
|0x73||GPIO Control 0|
|0x74||GPIO Control 1|
In order to use the GPIO pins, we first have to initialize them:
- Set the GPIO direction of IO4 and IO6 as output, IO5 and IO7 as input. (adress 0x76, data 0x50)
- Set the control registers. For CMOS outputs and inputs without pullups, both of these should be set to 0 for GPIO4-7. (adress 0x73, data 0x00 and adress 0x74, data 0x00)
- Enable GPIO4-7 by writing 0xF0 to the GPIO Enable register. (adress 0x77, data 0xF0)
Next, the two output pins can be set to HIGH, LOW, or their state can be toggled with the Data Set, Data Clear, and Data Toggle registers. The state of the input pins can be read in register 0x01.
The GPIO5 and GPIO7 inputs have external pullup resistors, so they do not need internal bias.
The IRQ-pin is connected to the ESP32 on IO25. It is an active-low pin that triggers on a touch-event (being touched or no longer being touched) and resets upon reading the registers via I2c. That way you can easily do an interrupt in your code or choose to ignore inputs until you have time to handle them.
No hacks necessary :)