MPR121 Captouch sensor notes
The MPR121 is a capacitive sensor that was used in the Safecast Kickstarter Geiger counter (Onyx). I figured that the code could be of use to someone else adding MPR121 support to a STM32 (libmaple) project. The code could of course be adapted to other platforms with I2C support fairly easily. The complete codebase is of course of github here
From memory the fiddly bits were finding the right parameters to get the sensor working through the plastic enclosure (parameters hardcoded in cap_init). And working round issues were I’d randomly see all the sensors firing. There was a lot of fiddling to get the captouch to be responsive, but not to misfire…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 | #include "mpr121.h" #include "i2c.h" #define CAPTOUCH_ADDR 0x5A #define CAPTOUCH_I2C I2C1 #define CAPTOUCH_GPIO 30 GUI *system_gui; static struct i2c_dev *i2c; static uint16 touchList = 1 << 8 | 1 << 6 | 1 << 4 | 1 << 3 | 1 << 2 | 1 << 0; int last_key_state=0; bool captouch_disable_messages= false ; void cap_set_disable_messages( bool b) { captouch_disable_messages=b; } static void mpr121Write(uint8 addr, uint8 value) { struct i2c_msg msg; uint8 bytes[2]; int result; bytes[0] = addr; bytes[1] = value; msg.addr = CAPTOUCH_ADDR; msg.flags = 0; msg.length = sizeof (bytes); msg.xferred = 0; msg.data = bytes; result = i2c_master_xfer(i2c, &msg, 1, 100); return ; } static uint8 mpr121Read(uint8 addr) { struct i2c_msg msgs[2]; uint8 byte; byte = addr; msgs[0].addr = msgs[1].addr = CAPTOUCH_ADDR; msgs[0].length = msgs[1].length = sizeof (byte); msgs[0].data = msgs[1].data = &byte; msgs[0].flags = 0; msgs[1].flags = I2C_MSG_READ; int result = i2c_master_xfer(i2c, msgs, 2, 100); // retry reads for ( int n=0;(result == -1) && (n<5);n++) { result = i2c_master_xfer(i2c, msgs, 2, 100); } if (result == -1) return 255; return byte; } char c[100]; char *cap_diagdata( int e) { int elech=0; int elecl=0; if (e==0) elecl = mpr121Read(ELE0_DATAL); if (e==0) elech = mpr121Read(ELE0_DATAH); if (e==1) elecl = mpr121Read(ELE1_DATAL); if (e==1) elech = mpr121Read(ELE1_DATAH); if (e==2) elecl = mpr121Read(ELE2_DATAL); if (e==2) elech = mpr121Read(ELE2_DATAH); if (e==3) elecl = mpr121Read(ELE3_DATAL); if (e==3) elech = mpr121Read(ELE3_DATAH); if (e==4) elecl = mpr121Read(ELE4_DATAL); if (e==4) elech = mpr121Read(ELE4_DATAH); if (e==5) elecl = mpr121Read(ELE5_DATAL); if (e==5) elech = mpr121Read(ELE5_DATAH); if (e==6) elecl = mpr121Read(ELE6_DATAL); if (e==6) elech = mpr121Read(ELE6_DATAH); if (e==7) elecl = mpr121Read(ELE7_DATAL); if (e==7) elech = mpr121Read(ELE7_DATAH); if (e==8) elecl = mpr121Read(ELE8_DATAL); if (e==8) elech = mpr121Read(ELE8_DATAH); if (e==9) elecl = mpr121Read(ELE9_DATAL); if (e==9) elech = mpr121Read(ELE9_DATAH); if (e==10) elecl = mpr121Read(ELE10_DATAL); if (e==10) elech = mpr121Read(ELE10_DATAH); elech = elech & 0x3; uint32_t elecv = (( uint32_t )elech << 8) | ( uint32_t )elecl; uint32_t elec_base=0; if (e==0) elec_base = (( uint32_t )(( uint8_t )mpr121Read(ELE0_BASE))) << 2; if (e==1) elec_base = (( uint32_t )(( uint8_t )mpr121Read(ELE1_BASE))) << 2; if (e==2) elec_base = (( int )mpr121Read(ELE2_BASE)) << 2; if (e==3) elec_base = (( int )mpr121Read(ELE3_BASE)) << 2; if (e==4) elec_base = (( int )mpr121Read(ELE4_BASE)) << 2; if (e==5) elec_base = (( int )mpr121Read(ELE5_BASE)) << 2; if (e==6) elec_base = (( int )mpr121Read(ELE6_BASE)) << 2; if (e==7) elec_base = (( int )mpr121Read(ELE7_BASE)) << 2; if (e==8) elec_base = (( int )mpr121Read(ELE8_BASE)) << 2; if (e==9) elec_base = (( int )mpr121Read(ELE9_BASE)) << 2; if (e==10) elec_base = (( int )mpr121Read(ELE10_BASE)) << 2; uint32_t bs = mpr121Read(TCH_STATL); bs = bs | ((0x1F & mpr121Read(TCH_STATH)) << 8); uint32_t is_pressed=0; if (bs & (1 << e)) is_pressed=1; else is_pressed=0; sprintf (c, "%" PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32 "" ,elecv,elec_base,bs,is_pressed); return c; } bool cap_check() { uint8 d = mpr121Read(ELE0_DATAL); if (d == 255) return false ; return true ; } uint32_t press_time[16]; uint32_t release_time[16]; uint32_t press_time_any; uint32_t release_time_any; uint32_t cap_last_press( int key) { return press_time[key]; } uint32_t cap_last_release( int key) { return release_time[key]; } uint32_t cap_last_press_any() { return press_time_any; } uint32_t cap_last_release_any() { return release_time_any; } void cap_clear_press() { for ( int n=0;n<16;n++) press_time[n] = 0; for ( int n=0;n<16;n++) release_time[n] = 0; press_time_any=0; release_time_any=0; } /** * Interrupt handler for capacitive keyboard events */ static void cap_change( void ) { // first read int key_state=0; key_state = mpr121Read(TCH_STATL); key_state |= mpr121Read(TCH_STATH) << 8; // there appears to be a bug where it suddenly outputs a lot of bits set. unsigned int v=key_state; // count the number of bits set in v unsigned int c; // c accumulates the total bits set in v for (c = 0; v; c++) { v &= v - 1; // clear the least significant bit set } if (c > 3) return ; // clear unconnected electrodes key_state &= touchList; // detect keys pressed int keys_pressed = key_state & (~last_key_state); //TODO: ! bitwise NOT // detect keys released int keys_released = (~key_state) & last_key_state; //TODO: ! bitwise NOT bool readok= true ; if ((!captouch_disable_messages) && (readok == true )) { for ( int key=0; key<16; key++) { if (keys_pressed &(1<<key)) { system_gui->receive_key(key,KEY_PRESSED ); press_time[key] = realtime_get_unixtime(); press_time_any=realtime_get_unixtime(); } if (keys_released&(1<<key)) { system_gui->receive_key(key,KEY_RELEASED); release_time[key] = realtime_get_unixtime(); release_time_any = realtime_get_unixtime(); } } last_key_state = key_state; } } bool cap_ispressed( int key) { if (last_key_state & (1<<key)) { return true ; } else { return false ;} } int cap_lastkey() { return last_key_state; } uint8_t cap_mhd_r = 0x01; uint8_t cap_nhd_r = 0x01; uint8_t cap_ncl_r = 0xFF; uint8_t cap_fdl_r = 0x02; uint8_t cap_mhd_f = 0x01; uint8_t cap_nhd_f = 0x01; uint8_t cap_ncl_f = 0xFF; uint8_t cap_fdl_f = 0x02; uint8_t cap_dbr = 0x77; uint8_t cap_touch_threshold = 0x10; uint8_t cap_release_threshold = 0x0B; void cap_set_mhd_r( uint8_t v) { cap_mhd_r = v; } void cap_set_nhd_r( uint8_t v) { cap_nhd_r = v; } void cap_set_ncl_r( uint8_t v) { cap_ncl_r = v; } void cap_set_fdl_r( uint8_t v) { cap_fdl_r = v; } void cap_set_mhd_f( uint8_t v) { cap_mhd_f = v; } void cap_set_nhd_f( uint8_t v) { cap_nhd_f = v; } void cap_set_ncl_f( uint8_t v) { cap_ncl_f = v; } void cap_set_fdl_f( uint8_t v) { cap_fdl_f = v; } void cap_set_dbr ( uint8_t v) { cap_dbr = v; } void cap_set_touch_threshold ( uint8_t v) { cap_touch_threshold = v; } void cap_set_release_threshold( uint8_t v) { cap_release_threshold = v; } void cap_init( void ) { // 63 2 4 1 63 2 4 1 0 8 4 cap_set_mhd_r(63); cap_set_nhd_r(2); cap_set_ncl_r(4); cap_set_fdl_r(1); cap_set_mhd_f(63); cap_set_nhd_f(2); cap_set_ncl_r(4); cap_set_fdl_f(1); cap_set_dbr(0); cap_set_touch_threshold(8); cap_set_release_threshold(4); gpio_set_mode(PIN_MAP[9].gpio_device,PIN_MAP[9].gpio_bit,GPIO_OUTPUT_PP); gpio_set_mode(PIN_MAP[5].gpio_device,PIN_MAP[5].gpio_bit,GPIO_OUTPUT_PP); gpio_write_bit(PIN_MAP[9].gpio_device,PIN_MAP[9].gpio_bit,1); gpio_write_bit(PIN_MAP[5].gpio_device,PIN_MAP[5].gpio_bit,1); delay_us(1000); gpio_set_mode(PIN_MAP[9].gpio_device,PIN_MAP[9].gpio_bit,GPIO_INPUT_PD); // Can also be floating, but PD is safer if components misplaced. gpio_set_mode(PIN_MAP[5].gpio_device,PIN_MAP[5].gpio_bit,GPIO_INPUT_PD); i2c = CAPTOUCH_I2C; i2c_init(i2c); i2c_master_enable(i2c, 0); mpr121Write(0x80,0x63); // soft reset delay_us(1000); mpr121Write(ELE_CFG, 0x00); // disable electrodes for config delay_us(100); // Section A and B - R (rise) F (fall) T (touch) mpr121Write(MHD_R, cap_mhd_r); // (1 to 63) mpr121Write(NHD_R, cap_nhd_r); // (1 to 63) mpr121Write(NCL_R, cap_ncl_r); // (0 to 255) mpr121Write(FDL_R, cap_fdl_r); // (0 to 255) mpr121Write(MHD_F, cap_mhd_f); // (1 to 63) largest value to pass through filer mpr121Write(NHD_F, cap_nhd_f); // (1 to 63) maximum change allowed mpr121Write(NCL_F, cap_ncl_f); // (0 to 255) number of samples required to determine non-noise mpr121Write(FDL_F, cap_fdl_f); // (0 to 255) rate of filter operation, larger = slower. // Section D // Set the Filter Configuration // Set ESI2 // was 0x01, 0x25 mpr121Write(AFE_CONF, 0x01); //AFE_CONF 0x5C mpr121Write(FIL_CFG , 0x04); //FIL_CFG 0x5D // Section F mpr121Write(ATO_CFG0, 0x0B); // ATO_CFG0 0x7B // limits // was0xFF,0x00,0x0E mpr121Write(ATO_CFGU, 0x9C); // ATO_CFGU 0x7D mpr121Write(ATO_CFGL, 0x65); // ATO_CFGL 0x7E mpr121Write(ATO_CFGT, 0x8C); // ATO_CFGT 0x7F // enable debouncing mpr121Write(DBR , cap_dbr); // set debouncing, in this case 7 for both touch and release. // Section C // This group sets touch and release thresholds for each electrode mpr121Write(ELE0_T , cap_touch_threshold); mpr121Write(ELE0_R , cap_release_threshold); mpr121Write(ELE1_T , cap_touch_threshold); mpr121Write(ELE1_R , cap_release_threshold); mpr121Write(ELE2_T , cap_touch_threshold); mpr121Write(ELE2_R , cap_release_threshold); mpr121Write(ELE3_T , cap_touch_threshold); mpr121Write(ELE3_R , cap_release_threshold); mpr121Write(ELE4_T , cap_touch_threshold); mpr121Write(ELE4_R , cap_release_threshold); mpr121Write(ELE5_T , cap_touch_threshold); mpr121Write(ELE5_R , cap_release_threshold); mpr121Write(ELE6_T , cap_touch_threshold); mpr121Write(ELE6_R , cap_release_threshold); mpr121Write(ELE7_T , cap_touch_threshold); mpr121Write(ELE7_R , cap_release_threshold); mpr121Write(ELE8_T , cap_touch_threshold); mpr121Write(ELE8_R , cap_release_threshold); mpr121Write(ELE9_T , cap_touch_threshold); mpr121Write(ELE9_R , cap_release_threshold); mpr121Write(ELE10_T, cap_touch_threshold); mpr121Write(ELE10_R, cap_release_threshold); mpr121Write(ELE11_T, cap_touch_threshold); mpr121Write(ELE11_R, cap_release_threshold); delay_us(100); // 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 delay_us(100); // This can also be FLOATING, but PU is safer if components misplaced. gpio_set_mode(PIN_MAP[CAPTOUCH_GPIO].gpio_device,PIN_MAP[CAPTOUCH_GPIO].gpio_bit,GPIO_INPUT_PU); exti_attach_interrupt((afio_exti_num)(PIN_MAP[CAPTOUCH_GPIO].gpio_bit), gpio_exti_port(PIN_MAP[CAPTOUCH_GPIO].gpio_device), cap_change, EXTI_FALLING); // Clears the first interrupt for ( int n=0;n<16;n++) press_time[n] = realtime_get_unixtime(); for ( int n=0;n<16;n++) release_time[n] = realtime_get_unixtime(); press_time_any = realtime_get_unixtime(); release_time_any = realtime_get_unixtime(); return ; } void cap_deinit( void ) { exti_detach_interrupt((afio_exti_num)(PIN_MAP[CAPTOUCH_GPIO].gpio_bit)); // Disable MPR121 scanning, in case the chip is on mpr121Write(ELE_CFG, 0x00); return ; } |