/******************************************************************************* Bare Conductive MPR121 library ------------------------------ MPR121.cpp - MPR121 class implementation file Based on code by Jim Lindblom and plenty of inspiration from the Freescale Semiconductor datasheets and application notes. Bare Conductive code written by Stefan Dzisiewski-Smith, Peter Krige and Szymon Kaliski. This work is licensed under a MIT license https://opensource.org/licenses/MIT Copyright (c) 2016, Bare Conductive Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *******************************************************************************/ #include #include #include #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_log.h" #include "driver/i2c.h" #include "driver/gpio.h" #include "mpr121.h" #define NOT_INITED_BIT 0 #define ADDRESS_UNKNOWN_BIT 1 #define READBACK_FAIL_BIT 2 #define OVERCURRENT_FLAG_BIT 3 #define OUT_OF_RANGE_BIT 4 #define I2C_NUM I2C_NUM_0 //#define I2C_NUM I2C_NUM_1 #define I2C_MASTER_FREQ_HZ 400000 /*!< I2C master clock frequency. no higher than 1MHz for now */ static const char *TAG = "MPR121"; void MPR121_type(MPR121_t * dev){ //dev->address = 0x5C; // default address is 0x5C, for use with Bare Conductive Touch Board dev->address = 0x5A; // default address is 0x5A, for use with Bare Conductive Touch Board dev->ECR_backup = 0x00; dev->running = false; dev->error = 1<touchData = 0; dev->lastTouchData = 0; dev->autoTouchStatusFlag = false; } void MPR121_setRegister(MPR121_t * dev, uint8_t reg, uint8_t value){ ESP_LOGD(TAG, "setRegister reg=0x%02x value=0x%02x", reg, value); bool wasRunning = false;; if(reg==MPR121_ECR){ // if we are modding MPR121_ECR, update our internal running status if(value&0x3F){ dev->running = true; } else { dev->running = false; } } else if(regrunning; if(wasRunning) MPR121_stop(dev); // we should ALWAYS be in stop mode for this // unless modding MPR121_ECR or GPIO / LED register } #if 0 Wire.beginTransmission(address); Wire.write(reg); Wire.write(value); if(Wire.endTransmission()!=0){ dev->error |= 1<error &= ~(1<address << 1) | I2C_MASTER_WRITE, true); i2c_master_write_byte(cmd, reg, true); i2c_master_write_byte(cmd, value, true); i2c_master_stop(cmd); esp_err_t espRc = i2c_master_cmd_begin(I2C_NUM, cmd, 10/portTICK_PERIOD_MS); if (espRc == ESP_OK) { ESP_LOGD(TAG, "setRegister reg=0x%02x value=0x%02x successfully", reg, value); dev->error &= ~(1<error |= 1<error &= ~(1<error |= 1<address << 1) | I2C_MASTER_WRITE, true); i2c_master_write_byte(cmd, reg, true); i2c_master_start(cmd); i2c_master_write_byte(cmd, (dev->address << 1) | I2C_MASTER_READ, true); i2c_master_read(cmd, buf, 1, I2C_MASTER_NACK); i2c_master_stop(cmd); esp_err_t espRc = i2c_master_cmd_begin(I2C_NUM, cmd, 10/portTICK_PERIOD_MS); if (espRc == ESP_OK) { dev->error &= ~(1<error |= 1<error |= 1<error &= ~(1<error |= 1<error &= ~(1< 0){ if( stuck_transaction ){ } else { } } // now we've released (if necessary) we can get on with things Wire.begin(); #endif i2c_config_t i2c_config = { .mode = I2C_MODE_MASTER, .sda_io_num = sda, .scl_io_num = scl, .sda_pullup_en = GPIO_PULLUP_ENABLE, .scl_pullup_en = GPIO_PULLUP_ENABLE, .master.clk_speed = I2C_MASTER_FREQ_HZ }; ESP_ERROR_CHECK(i2c_param_config(I2C_NUM, &i2c_config)); ESP_ERROR_CHECK(i2c_driver_install(I2C_NUM, I2C_MODE_MASTER, 0, 0, 0)); // addresses only valid 0x5A to 0x5D - if we don't change the address it stays at default if(address>=0x5A && address<=0x5D) { dev->address = address; // need to be specific here } dev->error &= ~(1<defaultSettings ); MPR121_settingsType(&(dev->defaultSettings)); MPR121_applySettings(dev); // only apply thresholds if they differ from existing defaults if( touchThreshold != dev->defaultSettings._TTHRESH ){ MPR121_setTouchThresholdAll(dev, touchThreshold ); } if( releaseThreshold != dev->defaultSettings._RTHRESH ){ MPR121_setReleaseThresholdAll(dev, releaseThreshold ); } if( interruptPin != dev->defaultSettings._INTERRUPT ){ MPR121_setInterruptPin(dev, interruptPin ); } return true; } else { return false; } } #if 0 void MPR121_clearSavedThresholds(MPR121_t * dev) { #ifdef ARDUINO_ARCH_AVR uint8_t maxElectrodes = 12; int len = E2END; for(uint8_t i=0; i<13; i++){ EEPROM.write(len - (i + 1), 255); EEPROM.write(len - (i + 1) - maxElectrodes, 255); } #endif } #endif #if 0 void MPR121_restoreSavedThresholds(MPR121_t * dev) { #ifdef ARDUINO_ARCH_AVR uint8_t maxElectrodes = 12; int len = E2END; for(uint8_t i=0; i<13; i++){ uint8_t releaseThreshold = EEPROM.read(len - (i + 1)); uint8_t touchThreshold = EEPROM.read(len - (i + 1) - maxElectrodes); if (touchThreshold < 255) { MPR121_setTouchThreshold(dev, i, touchThreshold + 1); // EEPROM values are saved off-by-one } else { MPR121_setTouchThreshold(dev, i, defaultSettings.TTHRESH); } if (releaseThreshold < 255) { MPR121_setReleaseThreshold(dev, i, releaseThreshold + 1); // EEPROM values are saved off-by-one } else { MPR121_setReleaseThreshold(dev, i, defaultSettings.RTHRESH); } } #endif } #endif #if 0 void MPR121_goSlow(MPR121_t * dev){ Wire.setClock(100000L); // set I2C clock to 100kHz } void MPR121_goFast(MPR121_t * dev){ Wire.setClock(400000L); // set I2C clock to 400kHz } #endif void MPR121_run(MPR121_t * dev){ if(!MPR121_isInited(dev)) return; MPR121_setRegister(dev, MPR121_ECR, dev->ECR_backup); // restore backup to return to run mode } void MPR121_stop(MPR121_t * dev){ if(!MPR121_isInited(dev)) return; dev->ECR_backup = MPR121_getRegister(dev, MPR121_ECR); // backup MPR121_ECR to restore when we enter run MPR121_setRegister(dev, MPR121_ECR, dev->ECR_backup & 0xC0); // turn off all electrodes to stop } bool MPR121_reset(MPR121_t * dev){ // return true if we successfully reset a device at the // address we are expecting // MPR121_AFE2 is one of the few registers that defaults to a non-zero value - // checking it is sensible as reading back an incorrect value implies // something went wrong - we also check MPR121_TS2 bit 7 to see if we have an // overcurrent flag set MPR121_setRegister(dev, MPR121_SRST, 0x63); // soft reset if(MPR121_getRegister(dev, MPR121_AFE2)!=0x24){ dev->error |= 1<error &= ~(1<error |= 1<error &= ~(1<_TTHRESH=40; defaultSettings->_RTHRESH=20; defaultSettings->_INTERRUPT=0; // note that this is not a hardware interrupt, just the digital // pin that the MPR121 ~INT pin is connected to defaultSettings->_MHDR=0x01; defaultSettings->_NHDR=0x01; defaultSettings->_NCLR=0x10; defaultSettings->_FDLR=0x20; defaultSettings->_MHDF=0x01; defaultSettings->_NHDF=0x01; defaultSettings->_NCLF=0x10; defaultSettings->_FDLF=0x20; defaultSettings->_NHDT=0x01; defaultSettings->_NCLT=0x10; defaultSettings->_FDLT=0xFF; defaultSettings->_MHDPROXR=0x0F; defaultSettings->_NHDPROXR=0x0F; defaultSettings->_NCLPROXR=0x00; defaultSettings->_FDLPROXR=0x00; defaultSettings->_MHDPROXF=0x01; defaultSettings->_NHDPROXF=0x01; defaultSettings->_NCLPROXF=0xFF; defaultSettings->_FDLPROXF=0xFF; defaultSettings->_NHDPROXT=0x00; defaultSettings->_NCLPROXT=0x00; defaultSettings->_FDLPROXT=0x00; defaultSettings->_DTR=0x11; defaultSettings->_AFE1=0xFF; defaultSettings->_AFE2=0x30; defaultSettings->_ECR=0xCC; // default to fast baseline startup and 12 electrodes enabled, no prox defaultSettings->_ACCR0=0x00; defaultSettings->_ACCR1=0x00; defaultSettings->_USL=0x00; defaultSettings->_LSL=0x00; defaultSettings->_TL=0x00; } //void MPR121_applySettings(MPR121_t * dev, MPR121_settings_type *settings){ void MPR121_applySettings(MPR121_t * dev){ bool wasRunning = dev->running; if(wasRunning) MPR121_stop(dev); // can't change most regs when running - checking // here avoids multiple stop() / run() calls MPR121_setRegister(dev, MPR121_MHDR, dev->defaultSettings._MHDR); MPR121_setRegister(dev, MPR121_NHDR, dev->defaultSettings._NHDR); MPR121_setRegister(dev, MPR121_NCLR, dev->defaultSettings._NCLR); MPR121_setRegister(dev, MPR121_FDLR, dev->defaultSettings._FDLR); MPR121_setRegister(dev, MPR121_MHDF, dev->defaultSettings._MHDF); MPR121_setRegister(dev, MPR121_NHDF, dev->defaultSettings._NHDF); MPR121_setRegister(dev, MPR121_NCLF, dev->defaultSettings._NCLF); MPR121_setRegister(dev, MPR121_FDLF, dev->defaultSettings._FDLF); MPR121_setRegister(dev, MPR121_NHDT, dev->defaultSettings._NHDT); MPR121_setRegister(dev, MPR121_NCLT, dev->defaultSettings._NCLT); MPR121_setRegister(dev, MPR121_FDLT, dev->defaultSettings._FDLT); MPR121_setRegister(dev, MPR121_MHDPROXR, dev->defaultSettings._MHDPROXR); MPR121_setRegister(dev, MPR121_NHDPROXR, dev->defaultSettings._NHDPROXR); MPR121_setRegister(dev, MPR121_NCLPROXR, dev->defaultSettings._NCLPROXR); MPR121_setRegister(dev, MPR121_FDLPROXR, dev->defaultSettings._FDLPROXR); MPR121_setRegister(dev, MPR121_MHDPROXF, dev->defaultSettings._MHDPROXF); MPR121_setRegister(dev, MPR121_NHDPROXF, dev->defaultSettings._NHDPROXF); MPR121_setRegister(dev, MPR121_NCLPROXF, dev->defaultSettings._NCLPROXF); MPR121_setRegister(dev, MPR121_FDLPROXF, dev->defaultSettings._FDLPROXF); MPR121_setRegister(dev, MPR121_NHDPROXT, dev->defaultSettings._NHDPROXT); MPR121_setRegister(dev, MPR121_NCLPROXT, dev->defaultSettings._NCLPROXT); MPR121_setRegister(dev, MPR121_FDLPROXT, dev->defaultSettings._FDLPROXT); MPR121_setRegister(dev, MPR121_DTR, dev->defaultSettings._DTR); MPR121_setRegister(dev, MPR121_AFE1, dev->defaultSettings._AFE1); MPR121_setRegister(dev, MPR121_AFE2, dev->defaultSettings._AFE2); MPR121_setRegister(dev, MPR121_ACCR0, dev->defaultSettings._ACCR0); MPR121_setRegister(dev, MPR121_ACCR1, dev->defaultSettings._ACCR1); MPR121_setRegister(dev, MPR121_USL, dev->defaultSettings._USL); MPR121_setRegister(dev, MPR121_LSL, dev->defaultSettings._LSL); MPR121_setRegister(dev, MPR121_TL, dev->defaultSettings._TL); MPR121_setRegister(dev, MPR121_ECR, dev->defaultSettings._ECR); dev->error &= ~(1<defaultSettings._TTHRESH); MPR121_setReleaseThresholdAll(dev, dev->defaultSettings._RTHRESH); //MPR121_setInterruptPin(dev, dev->defaultSettings._INTERRUPT); if(wasRunning) MPR121_run(dev); } //mpr121_error_type MPR121_getError(MPR121_t * dev){ uint8_t MPR121_getError(MPR121_t * dev){ // important - this resets the IRQ pin - as does any I2C comms MPR121_getRegister(dev, MPR121_OORS1); // OOR registers - we may not have read them yet, MPR121_getRegister(dev, MPR121_OORS2); // whereas the other errors should have been caught // order of error precedence is determined in this logic block if(!MPR121_isInited(dev)) return NOT_INITED; // this has its own checker function if((dev->error & (1<error & (1<error & (1<error & (1<error = 0; } bool MPR121_isRunning(MPR121_t * dev){ return dev->running; } bool MPR121_isInited(MPR121_t * dev){ return (dev->error & (1<autoTouchStatusFlag = false; dev->lastTouchData = dev->touchData; dev->touchData = (unsigned int)MPR121_getRegister(dev, MPR121_TS1) + ((unsigned int)MPR121_getRegister(dev, MPR121_TS2)<<8); } bool MPR121_getTouchData(MPR121_t * dev, uint8_t electrode){ if(electrode>12 || !MPR121_isInited(dev)) return false; // avoid out of bounds behaviour return((dev->touchData>>electrode)&1); } uint8_t MPR121_getNumTouches(MPR121_t * dev){ if(!MPR121_isInited(dev)) return(0xFF); uint8_t scratch = 0; for(uint8_t i=0; i<13; i++){ if(MPR121_getTouchData(dev, i)) scratch++; } return(scratch); } bool MPR121_getLastTouchData(MPR121_t * dev, uint8_t electrode){ if(electrode>12 || !MPR121_isInited(dev)) return false; // avoid out of bounds behaviour return((dev->lastTouchData>>electrode)&1); } bool MPR121_updateFilteredData(MPR121_t * dev){ if(!MPR121_isInited(dev)) return(false); uint8_t buf[26]; memset (buf, 0, 26); uint8_t LSB, MSB; if(MPR121_touchStatusChanged(dev)) { dev->autoTouchStatusFlag = true; } for(int i=0; i<26; i++){ // 13 filtered values buf[i] = MPR121_getRegister(dev, MPR121_E0FDL+i); } for(int i=0; i<13; i++){ // 13 filtered values if(MPR121_touchStatusChanged(dev)) { dev->autoTouchStatusFlag = true; } LSB = buf[i*2]; if(MPR121_touchStatusChanged(dev)) { dev->autoTouchStatusFlag = true; } MSB = buf[i*2+1]; dev->filteredData[i] = ((MSB << 8) | LSB); } return true; #if 0 uint8_t LSB, MSB; Wire.beginTransmission(address); Wire.write(MPR121_E0FDL); // set address register to read from the start of the //filtered data Wire.endTransmission(false); // repeated start if(MPR121_touchStatusChanged(dev)) { dev->autoTouchStatusFlag = true; } if(Wire.requestFrom(address,(uint8_t)26)==26){ for(int i=0; i<13; i++){ // 13 filtered values if(MPR121_touchStatusChanged(dev)) { dev->autoTouchStatusFlag = true; } LSB = Wire.read(); if(MPR121_touchStatusChanged(dev)) { dev->autoTouchStatusFlag = true; } MSB = Wire.read(); dev->filteredData[i] = ((MSB << 8) | LSB); } return(true); } else { // if we don't get back all 26 values we requested, don't update the FDAT values // and return false return(false); } #endif } int MPR121_getFilteredData(MPR121_t * dev, uint8_t electrode){ if(electrode>12 || !MPR121_isInited(dev)) return(0xFFFF); // avoid out of bounds behaviour return(dev->filteredData[electrode]); } bool MPR121_updateBaselineData(MPR121_t * dev){ if(!MPR121_isInited(dev)) return(false); uint8_t buf[13]; memset (buf, 0, 13); if(MPR121_touchStatusChanged(dev)) { dev->autoTouchStatusFlag = true; } for(int i=0; i<13; i++){ // 13 filtered values buf[i] = MPR121_getRegister(dev, MPR121_E0BV+i); if(MPR121_touchStatusChanged(dev)) { dev->autoTouchStatusFlag = true; } dev->baselineData[i] = buf[i]<<2; } return true; #if 0 Wire.beginTransmission(address); Wire.write(MPR121_E0BV); // set address register to read from the start of the // baseline data Wire.endTransmission(false); // repeated start if(MPR121_touchStatusChanged(dev)) { dev->autoTouchStatusFlag = true; } if(Wire.requestFrom(address,(uint8_t)13)==13){ for(int i=0; i<13; i++){ // 13 filtered values if(MPR121_touchStatusChanged(dev)) { dev->autoTouchStatusFlag = true; } dev->baselineData[i] = Wire.read()<<2; } return(true); } else { // if we don't get back all 26 values we requested, don't update the BVAL values // and return false return(false); } #endif } int MPR121_getBaselineData(MPR121_t * dev, uint8_t electrode){ if(electrode>12 || !MPR121_isInited(dev)) return(0xFFFF); // avoid out of bounds behaviour return(dev->baselineData[electrode]); } bool MPR121_isNewTouch(MPR121_t * dev, uint8_t electrode){ if(electrode>12 || !MPR121_isInited(dev)) return(false); // avoid out of bounds behaviour return((MPR121_getLastTouchData(dev, electrode) == false) && (MPR121_getTouchData(dev, electrode) == true)); } bool MPR121_isNewRelease(MPR121_t * dev, uint8_t electrode){ if(electrode>12 || !MPR121_isInited(dev)) return(false); // avoid out of bounds behaviour return((MPR121_getLastTouchData(dev, electrode) == true) && (MPR121_getTouchData(dev, electrode) == false)); } void MPR121_updateAll(MPR121_t * dev){ MPR121_updateTouchData(dev); MPR121_updateBaselineData(dev); MPR121_updateFilteredData(dev); } void MPR121_setTouchThresholdAll(MPR121_t * dev, uint8_t val){ if(!MPR121_isInited(dev)) return; bool wasRunning = dev->running; if(wasRunning) MPR121_stop(dev); // can only change thresholds when not running // checking here avoids multiple stop() / run() // calls for(uint8_t i=0; i<13; i++){ MPR121_setTouchThreshold(dev, i, val); } if(wasRunning) MPR121_run(dev); } #if 0 void MPR121_saveTouchThreshold(MPR121_t * dev, uint8_t electrode, uint8_t val){ #ifdef ARDUINO_ARCH_AVR if(electrode>12 || !MPR121_isInited(dev)) return; // avoid out of bounds behaviour MPR121_setTouchThreshold(dev, electrode, val); // store to EEPROM uint8_t maxElectrodes = 12; int len = E2END; int addr = len - maxElectrodes - (electrode + 1); EEPROM.write(addr, val - 1); // val - 1 so 255 stays as never-written-to #endif } #endif void MPR121_setTouchThreshold(MPR121_t * dev, uint8_t electrode, uint8_t val){ if(electrode>12 || !MPR121_isInited(dev)) return; // avoid out of bounds behaviour // this relies on the internal register map of the MPR121 MPR121_setRegister(dev, MPR121_E0TTH + (electrode<<1), val); } void MPR121_setReleaseThresholdAll(MPR121_t * dev, uint8_t val){ if(!MPR121_isInited(dev)) return; bool wasRunning = dev->running; if(wasRunning) MPR121_stop(dev); // can only change thresholds when not running // checking here avoids multiple stop / starts for(uint8_t i=0; i<13; i++){ MPR121_setReleaseThreshold(dev, i, val); } if(wasRunning) MPR121_run(dev); } void MPR121_setReleaseThreshold(MPR121_t * dev, uint8_t electrode, uint8_t val){ if(electrode>12 || !MPR121_isInited(dev)) return; // avoid out of bounds behaviour // this relies on the internal register map of the MPR121 MPR121_setRegister(dev, MPR121_E0RTH + (electrode<<1), val); } #if 0 void MPR121_saveReleaseThreshold(MPR121_t * dev, uint8_t electrode, uint8_t val){ #ifdef ARDUINO_ARCH_AVR if(electrode>12 || !MPR121_isInited(dev)) return; // avoid out of bounds behaviour MPR121_setReleaseThreshold(dev, electrode, val); // store to EEPROM int len = E2END; int addr = len - (electrode + 1); EEPROM.write(addr, val - 1); // val - 1 so 255 stays as never-written-to #endif } #endif uint8_t MPR121_getTouchThreshold(MPR121_t * dev, uint8_t electrode){ if(electrode>12 || !MPR121_isInited(dev)) return(0xFF); // avoid out of bounds behaviour return(MPR121_getRegister(dev, MPR121_E0TTH+(electrode<<1))); // "255" issue is in here somewhere //return(101); } uint8_t MPR121_getReleaseThreshold(MPR121_t * dev, uint8_t electrode){ if(electrode>12 || !MPR121_isInited(dev)) return(0xFF); // avoid out of bounds behaviour return(MPR121_getRegister(dev, MPR121_E0RTH+(electrode<<1))); // "255" issue is in here somewhere //return(51); } void MPR121_setInterruptPin(MPR121_t * dev, uint8_t pin){ // :: here forces the compiler to use Arduino's pinMode, not MPR121's if(!MPR121_isInited(dev)) return; //::pinMode(pin, INPUT_PULLUP); ESP_LOGD(TAG, "setInterruptPin pin=%d", pin); gpio_reset_pin(pin); gpio_set_direction(pin, GPIO_MODE_INPUT); dev->interruptPin = pin; } bool MPR121_touchStatusChanged(MPR121_t * dev){ // :: here forces the compiler to use Arduino's digitalRead, not MPR121's //return(dev->autoTouchStatusFlag || (!::digitalRead(dev->interruptPin))); return(dev->autoTouchStatusFlag || (!gpio_get_level(dev->interruptPin))); } //void MPR121_setProxMode(MPR121_t * dev, mpr121_proxmode_type mode){ void MPR121_setProxMode(MPR121_t * dev, uint8_t mode){ if(!MPR121_isInited(dev)) return; bool wasRunning = dev->running; if(wasRunning) MPR121_stop(dev); switch(mode){ case PROX_DISABLED: dev->ECR_backup &= ~(3<<4); // ELEPROX_EN[1:0] = 00 break; case PROX_0_1: dev->ECR_backup |= (1<<4); // ELEPROX_EN[1:0] = 01 dev->ECR_backup &= ~(1<<5); break; case PROX_0_3: dev->ECR_backup &= ~(1<<4); // ELEPROX_EN[1:0] = 10 dev->ECR_backup |= (1<<5); break; case PROX_0_11: dev->ECR_backup |= (3<<4); // ELEPROX_EN[1:0] = 11 break; } if(wasRunning) MPR121_run(dev); } //void MPR121_setCalibrationLock(MPR121_t * dev, mpr121_cal_lock_type lock){ void MPR121_setCalibrationLock(MPR121_t * dev, uint8_t lock){ if(!MPR121_isInited(dev)) return; bool wasRunning = dev->running; if(wasRunning) MPR121_stop(dev); switch(lock){ case CAL_LOCK_ENABLED: dev->ECR_backup &= ~(3<<6); // CL[1:0] = 00 break; case CAL_LOCK_DISABLED: dev->ECR_backup |= (1<<6); // CL[1:0] = 01 dev->ECR_backup &= ~(1<<7); break; case CAL_LOCK_ENABLED_5_BIT_COPY: dev->ECR_backup &= ~(1<<6); // CL[1:0] = 10 dev->ECR_backup |= (1<<7); break; case CAL_LOCK_ENABLED_10_BIT_COPY: dev->ECR_backup |= (3<<4); // CL[1:0] = 11 break; } if(wasRunning) MPR121_run(dev); } void MPR121_setGlobalCDC(MPR121_t * dev, uint8_t CDC){ if(CDC > 63) return; // current is only valid 0..63uA MPR121_setRegister(dev, MPR121_AFE1, (MPR121_getRegister(dev, MPR121_AFE1) & 0xC0) | CDC); } void MPR121_setElectrodeCDC(MPR121_t * dev, uint8_t electrode, uint8_t CDC){ if(CDC > 63 || electrode > 12) return; // current is only valid 0..63uA, electrode only valid 0..12 MPR121_setRegister(dev, MPR121_CDC0 + electrode, CDC); } //void MPR121_setGlobalCDT(MPR121_t * dev, mpr121_CDT_type CDT){ void MPR121_setGlobalCDT(MPR121_t * dev, uint8_t CDT){ MPR121_setRegister(dev, MPR121_AFE2, (MPR121_getRegister(dev, MPR121_AFE2) & 0x1F) | (CDT << 5)); } //void MPR121_setElectrodeCDT(MPR121_t * dev, uint8_t electrode, mpr121_CDT_type CDT){ void MPR121_setElectrodeCDT(MPR121_t * dev, uint8_t electrode, uint8_t CDT){ MPR121_setRegister(dev, MPR121_CDT01 + (electrode >> 1), (MPR121_getRegister(dev, MPR121_CDT01 + (electrode >> 1)) & (0x0F << (((electrode + 1) % 2)<<2))) | (CDT << ((electrode % 2)<<2))); } bool MPR121_autoSetElectrodeCDC(MPR121_t * dev, uint8_t electrode, uint16_t VCC_mV){ uint16_t upper_limit_FDAT = (uint16_t)((((uint32_t)VCC_mV - 700)*256)/VCC_mV) << 2; uint16_t target_FDAT = (uint16_t)(((uint32_t)upper_limit_FDAT * 90) / 100); uint16_t lower_limit_FDAT = (uint16_t)(((uint32_t)upper_limit_FDAT * 65) / 100); uint16_t this_value; int16_t last_distance = 0; int16_t this_distance = 0; const uint8_t max_num_delay_loops = 100; uint8_t num_delay_loops; bool scratch = false; // default to failure uint8_t saved_num_enabled_electrodes = MPR121_getNumEnabledElectrodes(dev); MPR121_setNumEnabledElectrodes(dev, electrode + 1); // reducing the number of running electrodes to a minimum speeds things up if(!dev->running) MPR121_run(dev); for(uint8_t CDC = 1; CDC < 63; CDC ++){ MPR121_setElectrodeCDC(dev, electrode, CDC); num_delay_loops = 0; do{ MPR121_updateFilteredData(dev); } while((MPR121_getFilteredData(dev, electrode) == 0) && (num_delay_loops++ < max_num_delay_loops)); this_value = MPR121_getFilteredData(dev, electrode); this_distance = (uint16_t)(abs((int16_t)this_value - (int16_t)target_FDAT)); // TODO: tidy up signed / unsigned types here if(CDC > 1){ // only need to see if we need to quit once we have at least two measurements to compare if(this_distance > last_distance){ // if we got further away from our target this setting should work (slightly prefer higher values) MPR121_setElectrodeCDC(dev, electrode, CDC); if((this_value >= lower_limit_FDAT) && (this_value <= upper_limit_FDAT)){ scratch = true; // success } break; } else if(CDC == 63){ // or if we're at the end of the available adjustment, see if we're close enough MPR121_setElectrodeCDC(dev, electrode, CDC); if((this_value >= lower_limit_FDAT) && (this_value <= upper_limit_FDAT)){ scratch = true; // success } break; } } last_distance = this_distance; } MPR121_setRegister(dev, MPR121_ECR, dev->ECR_backup); MPR121_setNumEnabledElectrodes(dev, saved_num_enabled_electrodes); // have to do this separately as ECR_backup gets invalidated by setNumEnabledElectrodes(electrode + 1); return(scratch); } bool MPR121_autoSetElectrodeCDCDefault(MPR121_t * dev, uint8_t electrode){ // default to 3.3V VCC if not explicitly stated return(MPR121_autoSetElectrodeCDC(dev, electrode, 3300)); } bool MPR121_autoSetElectrodeCDCAll(MPR121_t * dev){ bool scratch = true; for(uint8_t i=0; irunning; MPR121_stop(dev); MPR121_setRegister(dev, MPR121_USL, USL); MPR121_setRegister(dev, MPR121_TL, T_L); MPR121_setRegister(dev, MPR121_LSL, LSL); // don't enable retry, copy other settings from elsewhere MPR121_setRegister(dev, MPR121_ACCR0, 1 | ((dev->ECR_backup & 0xC0) >> 4) | (MPR121_getRegister(dev, MPR121_AFE1) & 0xC0)); // fixed charge time is useful for designs with higher lead-in resistance - e.g. using Bare Electric Paint MPR121_setRegister(dev, MPR121_ACCR1, fixedChargeTime ? 1 << 7 : 0); if(wasRunning){ MPR121_run(dev); } return(!(MPR121_getRegister(dev, MPR121_OORS2) & 0xC0)); } bool MPR121_autoSetElectrodesDefault(MPR121_t * dev, bool fixedChargeTime){ return(MPR121_autoSetElectrodes(dev, 3300, fixedChargeTime)); } void MPR121_setNumDigPins(MPR121_t * dev, uint8_t numPins){ if(!MPR121_isInited(dev)) return; bool wasRunning = dev->running; if(numPins>8) numPins = 8; // maximum number of GPIO pins is 8 out of 12 if(wasRunning){ MPR121_stop(dev); // have to stop to change MPR121_ECR } dev->ECR_backup = (0x0F&(12-numPins)) | (dev->ECR_backup&0xF0); if(wasRunning){ MPR121_run(dev); } } void MPR121_setNumEnabledElectrodes(MPR121_t * dev, uint8_t numElectrodes){ if(!MPR121_isInited(dev)) return; bool wasRunning = dev->running; if(numElectrodes>12) numElectrodes = 12; // avoid out-of-bounds behaviour if(wasRunning){ MPR121_stop(dev); // have to stop to change MPR121_ECR } dev->ECR_backup = (0x0F&numElectrodes) | (dev->ECR_backup&0xF0); if(wasRunning){ MPR121_run(dev); } } uint8_t MPR121_getNumEnabledElectrodes(MPR121_t * dev){ if(!MPR121_isInited(dev)) return(0xFF); return(MPR121_getRegister(dev, MPR121_ECR) & 0x0F); } #if 0 void MPR121_pinMode(MPR121_t * dev, uint8_t electrode, mpr121_pinf_type mode){ // only valid for ELE4..ELE11 if(electrode<4 || electrode >11 || !MPR121_isInited(dev)) return; // LED0..LED7 uint8_t bitmask = 1<<(electrode-4); switch(mode){ case INPUT_PULLDOWN: // MPR121_EN = 1 // MPR121_DIR = 0 // MPR121_CTL0 = 1 // MPR121_CTL1 = 0 MPR121_setRegister(dev, MPR121_EN, MPR121_getRegister(dev, MPR121_EN) | bitmask); MPR121_setRegister(dev, MPR121_DIR, MPR121_getRegister(dev, MPR121_DIR) & ~bitmask); MPR121_setRegister(dev, MPR121_CTL0, MPR121_getRegister(dev, MPR121_CTL0) | bitmask); MPR121_setRegister(dev, MPR121_CTL1, MPR121_getRegister(dev, MPR121_CTL1) & ~bitmask); break; case OUTPUT_HIGHSIDE: // MPR121_EN = 1 // MPR121_DIR = 1 // MPR121_CTL0 = 1 // MPR121_CTL1 = 1 MPR121_setRegister(dev, MPR121_EN, MPR121_getRegister(dev, MPR121_EN) | bitmask); MPR121_setRegister(dev, MPR121_DIR, MPR121_getRegister(dev, MPR121_DIR) | bitmask); MPR121_setRegister(dev, MPR121_CTL0, MPR121_getRegister(dev, MPR121_CTL0) | bitmask); MPR121_setRegister(dev, MPR121_CTL1, MPR121_getRegister(dev, MPR121_CTL1) | bitmask); break; case OUTPUT_LOWSIDE: // MPR121_EN = 1 // MPR121_DIR = 1 // MPR121_CTL0 = 1 // MPR121_CTL1 = 0 MPR121_setRegister(dev, MPR121_EN, MPR121_getRegister(dev, MPR121_EN) | bitmask); MPR121_setRegister(dev, MPR121_DIR, MPR121_getRegister(dev, MPR121_DIR) | bitmask); MPR121_setRegister(dev, MPR121_CTL0, MPR121_getRegister(dev, MPR121_CTL0) | bitmask); MPR121_setRegister(dev, MPR121_CTL1, MPR121_getRegister(dev, MPR121_CTL1) & ~bitmask); break; default: break; } } #endif void MPR121_pinMode(MPR121_t * dev, uint8_t electrode, int mode){ // this is to catch the fact that Arduino prefers its definitions of // INPUT, OUTPUT and INPUT_PULLUP to ours... // only valid for ELE4..ELE11 if(electrode<4 || electrode >11 || !MPR121_isInited(dev)) return; uint8_t bitmask = 1<<(electrode-4); switch(mode){ case OUTPUT: // MPR121_EN = 1 // MPR121_DIR = 1 // MPR121_CTL0 = 0 // MPR121_CTL1 = 0 MPR121_setRegister(dev, MPR121_EN, MPR121_getRegister(dev, MPR121_EN) | bitmask); MPR121_setRegister(dev, MPR121_DIR, MPR121_getRegister(dev, MPR121_DIR) | bitmask); MPR121_setRegister(dev, MPR121_CTL0, MPR121_getRegister(dev, MPR121_CTL0) & ~bitmask); MPR121_setRegister(dev, MPR121_CTL1, MPR121_getRegister(dev, MPR121_CTL1) & ~bitmask); break; case INPUT: // MPR121_EN = 1 // MPR121_DIR = 0 // MPR121_CTL0 = 0 // MPR121_CTL1 = 0 MPR121_setRegister(dev, MPR121_EN, MPR121_getRegister(dev, MPR121_EN) | bitmask); MPR121_setRegister(dev, MPR121_DIR, MPR121_getRegister(dev, MPR121_DIR) & ~bitmask); MPR121_setRegister(dev, MPR121_CTL0, MPR121_getRegister(dev, MPR121_CTL0) & ~bitmask); MPR121_setRegister(dev, MPR121_CTL1, MPR121_getRegister(dev, MPR121_CTL1) & ~bitmask); break; case INPUT_PULLUP: // MPR121_EN = 1 // MPR121_DIR = 0 // MPR121_CTL0 = 1 // MPR121_CTL1 = 1 MPR121_setRegister(dev, MPR121_EN, MPR121_getRegister(dev, MPR121_EN) | bitmask); MPR121_setRegister(dev, MPR121_DIR, MPR121_getRegister(dev, MPR121_DIR) & ~bitmask); MPR121_setRegister(dev, MPR121_CTL0, MPR121_getRegister(dev, MPR121_CTL0) | bitmask); MPR121_setRegister(dev, MPR121_CTL1, MPR121_getRegister(dev, MPR121_CTL1) | bitmask); break; default: break; } } void MPR121_digitalWrite(MPR121_t * dev, uint8_t electrode, uint8_t val){ // avoid out of bounds behaviour if(electrode<4 || electrode>11 || !MPR121_isInited(dev)) return; if(val){ MPR121_setRegister(dev, MPR121_SET, 1<<(electrode-4)); } else { MPR121_setRegister(dev, MPR121_CLR, 1<<(electrode-4)); } } void MPR121_digitalToggle(MPR121_t * dev, uint8_t electrode){ // avoid out of bounds behaviour if(electrode<4 || electrode>11 || !MPR121_isInited(dev)) return; MPR121_setRegister(dev, MPR121_TOG, 1<<(electrode-4)); } bool MPR121_digitalRead(MPR121_t * dev, uint8_t electrode){ // avoid out of bounds behaviour if(electrode<4 || electrode>11 || !MPR121_isInited(dev)) return false; return(((MPR121_getRegister(dev, MPR121_DAT)>>(electrode-4))&1)==1); } void MPR121_analogWrite(MPR121_t * dev, uint8_t electrode, uint8_t value){ // LED output 5 (ELE9) and output 6 (ELE10) have a PWM bug // https://community.nxp.com/thread/305474 // avoid out of bounds behaviour if(electrode<4 || electrode>11 || !MPR121_isInited(dev)) return; uint8_t shiftedVal = value>>4; if(shiftedVal > 0){ MPR121_setRegister(dev, MPR121_SET, 1<<(electrode-4)); // normal PWM operation } else { // this make a 0 PWM setting turn off the output MPR121_setRegister(dev, MPR121_CLR, 1<<(electrode-4)); } switch(electrode-4){ case 0: MPR121_setRegister(dev, MPR121_PWM0, (shiftedVal & 0x0F) | (MPR121_getRegister(dev, MPR121_PWM0) & 0xF0)); break; case 1: MPR121_setRegister(dev, MPR121_PWM0, ((shiftedVal & 0x0F)<<4) | (MPR121_getRegister(dev, MPR121_PWM0) & 0x0F)); break; case 2: MPR121_setRegister(dev, MPR121_PWM1, (shiftedVal & 0x0F) | (MPR121_getRegister(dev, MPR121_PWM1) & 0xF0)); break; case 3: MPR121_setRegister(dev, MPR121_PWM1, ((shiftedVal & 0x0F)<<4) | (MPR121_getRegister(dev, MPR121_PWM1) & 0x0F)); break; case 4: MPR121_setRegister(dev, MPR121_PWM2, (shiftedVal & 0x0F) | (MPR121_getRegister(dev, MPR121_PWM2) & 0xF0)); break; case 5: MPR121_setRegister(dev, MPR121_PWM2, ((shiftedVal & 0x0F)<<4) | (MPR121_getRegister(dev, MPR121_PWM2) & 0x0F)); break; case 6: MPR121_setRegister(dev, MPR121_PWM3, (shiftedVal & 0x0F) | (MPR121_getRegister(dev, MPR121_PWM3) & 0xF0)); break; case 7: MPR121_setRegister(dev, MPR121_PWM3, ((shiftedVal & 0x0F)<<4) | (MPR121_getRegister(dev, MPR121_PWM3) & 0x0F)); break; } } //void MPR121_setSamplePeriod(MPR121_t * dev, mpr121_sample_interval_type period){ void MPR121_setSamplePeriod(MPR121_t * dev, uint8_t period){ MPR121_setRegister(dev, MPR121_AFE2, (MPR121_getRegister(dev, MPR121_AFE2) & 0xF8) | (period & 0x07)); } //void MPR121_setFFI(MPR121_t * dev, mpr121_FFI_type FFI){ void MPR121_setFFI(MPR121_t * dev, uint8_t FFI){ MPR121_setRegister(dev, MPR121_AFE1, (MPR121_getRegister(dev, MPR121_AFE1) & 0x3F) | ((FFI & 0x03) << 6)); } //void MPR121_setSFI(MPR121_t * dev, mpr121_SFI_type SFI){ void MPR121_setSFI(MPR121_t * dev, uint8_t SFI){ MPR121_setRegister(dev, MPR121_AFE2, (MPR121_getRegister(dev, MPR121_AFE2) & 0xE7) | ((SFI & 0x03) << 3)); } //MPR121_type MPR121 = MPR121_type();