Added support for new i2c drivers

This commit is contained in:
nopnop2002
2025-06-14 16:50:49 +09:00
parent c4d8e72e8e
commit 4b722e35c7
8 changed files with 236 additions and 92 deletions

View File

@@ -4,18 +4,19 @@ MPR121 Capacitive Touch Driver for esp-idf.
I ported from [here](https://github.com/BareConductive/mpr121).
# Software requirements
ESP-IDF V4.4/V5.x.
ESP-IDF V5.0 is required when using ESP32-C2.
ESP-IDF V5.0 or later.
ESP-IDF V4.4 release branch reached EOL in July 2024.
ESP-IDF V5.1 is required when using ESP32-C6.
__Note for ESP-IDF V5.2.__
A new i2c driver is now available in ESP-IDF V5.2.
Under ESP-IDF V5.2 or later, this project uses a new i2c driver.
# Hardware requirements
MPR121 Capacitive Touch switch.
![mpr121-1](https://user-images.githubusercontent.com/6020549/147515909-cd50a16a-5c60-4bd0-bc32-c288f5d8ee88.JPG)
# Installation
```Shell
git clone https://github.com/nopnop2002/esp-idf-mpr121
cd esp-idf-mpr121
@@ -26,12 +27,25 @@ idf.py flash
# Configuration
![Image](https://github.com/user-attachments/assets/66e7e417-3a55-49aa-ab57-8e9ff0f848a3)
![Image](https://github.com/user-attachments/assets/40d3a5ac-43cd-4fc7-a80d-4f330d677d94)
![Image](https://github.com/user-attachments/assets/0629e4ae-c980-4dc8-b18a-07b98681a1a3)
## I2C Port selection
# I2C Clock speed
According to the MPR121 datasheet, the maximum i2c clock frequency is 400KHz.
The i2c clock frequency used by this project is 400KHz.
# I2C Port selection
XTENSA's ESP32 has two i2c port: I2C_NUM_0/1.
You can use these two ports freely.
If you use this module at the same time as another I2C device using I2C port 0, you must change it to I2C port 1.
![Image](https://github.com/user-attachments/assets/797d89e9-3d6f-45b5-b5a1-791ce00a8340)
# Force legacy i2c driver
XTENSA's ESP32 has two i2c port: I2C_NUM_0/1.
I2C_NUM_0/1 are initialized independently, but legacy i2c drivers and new i2c drivers cannot be mixed.
If I2C_NUM_0 is initialized with the legacy i2c driver, I2C_NUM_1 must also be initialized with the legacy i2c driver.
If you use the MPR121 at the same time as other I2C devices that use legacy I2C drivers, the MPR121 must also be initialized with the legacy I2C driver.
Enabling this will use the legacy i2c driver even after ESP-IDF V5.2.
![Image](https://github.com/user-attachments/assets/d40cdd42-3a03-4ea8-a0ea-ebdfe2cdb8e2)
# Wirering

View File

@@ -1,3 +1,16 @@
set(component_srcs "mpr121.c")
# get IDF version for comparison
set(idf_version "${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}")
if(idf_version VERSION_GREATER_EQUAL "5.2")
if(CONFIG_LEGACY_DRIVER)
list(APPEND component_srcs "i2c_legacy.c")
else()
list(APPEND component_srcs "i2c_new.c")
endif()
else()
list(APPEND component_srcs "i2c_legacy.c")
endif()
idf_component_register(SRCS "${component_srcs}" PRIV_REQUIRES driver INCLUDE_DIRS ".")

View File

@@ -25,6 +25,12 @@ menu "MPR121 Configuration"
Use I2C_PORT_1.
endchoice
config LEGACY_DRIVER
bool "Force legacy i2c driver"
default false
help
Force legacy i2c driver.
config I2C_ADDRESS
hex "I2C address"
default 0x5A

View File

@@ -0,0 +1,74 @@
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <stdbool.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/i2c.h"
#include "mpr121.h"
void i2c_setRegister(MPR121_t * dev, uint8_t reg, uint8_t value) {
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (dev->_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(__FUNCTION__, "setRegister reg=0x%02x value=0x%02x successfully", reg, value);
dev->error &= ~(1<<ADDRESS_UNKNOWN_BIT);
} else {
ESP_LOGE(__FUNCTION__, "setRegister reg=0x%02x value=0x%02x failed. code: 0x%02x", reg, value, espRc);
dev->error |= 1<<ADDRESS_UNKNOWN_BIT; // set address unknown bit
}
i2c_cmd_link_delete(cmd);
}
uint8_t i2c_getRegister(MPR121_t * dev, uint8_t reg) {
ESP_LOGD(__FUNCTION__, "getRegister reg=0x%02x", reg);
uint8_t scratch = 0;
uint8_t buf[2];
memset (buf, 0, 2);
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (dev->_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<<ADDRESS_UNKNOWN_BIT);
scratch = buf[0];
ESP_LOGD(__FUNCTION__, "getRegister reg=0x%02x successfully scratch=0x%02x", reg, scratch);
} else {
ESP_LOGE(__FUNCTION__, "getRegister reg=0x%02x failed. code: 0x%02x", reg, espRc);
dev->error |= 1<<ADDRESS_UNKNOWN_BIT; // set address unknown bit
}
i2c_cmd_link_delete(cmd);
return scratch;
}
void i2c_register(MPR121_t * dev, int16_t sda, int16_t scl){
ESP_LOGI(__FUNCTION__, "Legacy i2c driver is used");
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));
ESP_LOGI(__FUNCTION__, "Legacy i2c driver installed");
}

View File

@@ -0,0 +1,68 @@
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <stdbool.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/i2c_master.h"
#include "mpr121.h"
void i2c_setRegister(MPR121_t * dev, uint8_t reg, uint8_t value) {
uint8_t out_buf[2];
out_buf[0] = reg;
out_buf[1] = value;
esp_err_t espRc = i2c_master_transmit(dev->_i2c_dev_handle, out_buf, 2, I2C_TICKS_TO_WAIT);
if (espRc == ESP_OK) {
ESP_LOGD(__FUNCTION__, "setRegister reg=0x%02x value=0x%02x successfully", reg, value);
dev->error &= ~(1<<ADDRESS_UNKNOWN_BIT);
} else {
ESP_LOGE(__FUNCTION__, "setRegister reg=0x%02x value=0x%02x failed. code: 0x%02x", reg, value, espRc);
dev->error |= 1<<ADDRESS_UNKNOWN_BIT; // set address unknown bit
}
}
uint8_t i2c_getRegister(MPR121_t * dev, uint8_t reg) {
uint8_t scratch = 0;
uint8_t out_buf[1];
out_buf[0] = reg;
uint8_t in_buf[1];
esp_err_t espRc = i2c_master_transmit_receive(dev->_i2c_dev_handle, out_buf, 1, in_buf, 1, -1);
if (espRc == ESP_OK) {
dev->error &= ~(1<<ADDRESS_UNKNOWN_BIT);
scratch = in_buf[0];
ESP_LOGD(__FUNCTION__, "getRegister reg=0x%02x successfully scratch=0x%02x", reg, scratch);
} else {
ESP_LOGE(__FUNCTION__, "getRegister reg=0x%02x failed. code: 0x%02x", reg, espRc);
dev->error |= 1<<ADDRESS_UNKNOWN_BIT; // set address unknown bit
}
return scratch;
}
void i2c_register(MPR121_t * dev, int16_t sda, int16_t scl){
ESP_LOGI(__FUNCTION__, "New i2c driver is used");
i2c_master_bus_config_t i2c_mst_config = {
.clk_source = I2C_CLK_SRC_DEFAULT,
.glitch_ignore_cnt = 7,
.i2c_port = I2C_NUM,
.scl_io_num = scl,
.sda_io_num = sda,
.flags.enable_internal_pullup = true,
};
i2c_master_bus_handle_t i2c_bus_handle;
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_mst_config, &i2c_bus_handle));
i2c_device_config_t dev_cfg = {
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
.device_address = dev->_address,
.scl_speed_hz = I2C_MASTER_FREQ_HZ,
};
i2c_master_dev_handle_t i2c_dev_handle;
ESP_ERROR_CHECK(i2c_master_bus_add_device(i2c_bus_handle, &dev_cfg, &i2c_dev_handle));
dev->_i2c_bus_handle = i2c_bus_handle;
dev->_i2c_dev_handle = i2c_dev_handle;
ESP_LOGI(__FUNCTION__, "New i2c driver installed");
}

View File

@@ -48,25 +48,11 @@
#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
#if CONFIG_I2C_PORT_0
#define I2C_NUM I2C_NUM_0
#else
#define I2C_NUM I2C_NUM_1
#endif
#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->_address = 0x5A; // default address is 0x5A, for use with Bare Conductive Touch Board
dev->ECR_backup = 0x00;
dev->running = false;
dev->error = 1<<NOT_INITED_BIT; // initially, we're not initialised
@@ -93,53 +79,18 @@ void MPR121_setRegister(MPR121_t * dev, uint8_t reg, uint8_t value){
// unless modding MPR121_ECR or GPIO / LED register
}
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (dev->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<<ADDRESS_UNKNOWN_BIT);
} else {
ESP_LOGE(TAG, "setRegister reg=0x%02x value=0x%02x failed. code: 0x%02x", reg, value, espRc);
dev->error |= 1<<ADDRESS_UNKNOWN_BIT; // set address unknown bit
}
i2c_cmd_link_delete(cmd);
// i2c write register
ESP_LOGD(TAG, "setRegister reg=0x%02x", reg);
i2c_setRegister(dev, reg, value);
if(wasRunning) MPR121_run(dev); // restore run mode if necessary
}
uint8_t MPR121_getRegister(MPR121_t * dev, uint8_t reg){
// i2c read register
ESP_LOGD(TAG, "getRegister reg=0x%02x", reg);
uint8_t scratch = 0;
uint8_t buf[2];
memset (buf, 0, 2);
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (dev->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<<ADDRESS_UNKNOWN_BIT);
scratch = buf[0];
ESP_LOGD(TAG, "getRegister reg=0x%02x successfully scratch=0x%02x", reg, scratch);
} else {
ESP_LOGE(TAG, "getRegister reg=0x%02x failed. code: 0x%02x", reg, espRc);
dev->error |= 1<<ADDRESS_UNKNOWN_BIT; // set address unknown bit
}
i2c_cmd_link_delete(cmd);
uint8_t scratch = i2c_getRegister(dev, reg);
// auto update errors for registers with error data
if(reg == MPR121_TS2 && ((scratch&0x80)!=0)){
@@ -157,25 +108,14 @@ uint8_t MPR121_getRegister(MPR121_t * dev, uint8_t reg){
bool MPR121_begin(MPR121_t * dev, int16_t address, int16_t touchThreshold, int16_t releaseThreshold, int16_t interruptPin, int16_t sda, int16_t scl){
// SDA and SCL should idle high, but MPR121 can get stuck waiting to complete a transaction
// this code detects this state and releases us from it
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->_address = address;
}
// i2c driver register
i2c_register(dev, sda, scl);
dev->error &= ~(1<<NOT_INITED_BIT); // clear NOT_INITED error flag
if( MPR121_reset(dev) ){
@@ -284,11 +224,15 @@ bool MPR121_reset(MPR121_t * dev){
dev->error &= ~(1<<READBACK_FAIL_BIT);
}
vTaskDelay(10);
ESP_LOGD(__FUNCTION__, "start getRegister MPR121_TS2");
if((MPR121_getRegister(dev, MPR121_TS2)&0x80)!=0){
dev->error |= 1<<OVERCURRENT_FLAG_BIT;
} else {
dev->error &= ~(1<<OVERCURRENT_FLAG_BIT);
}
vTaskDelay(10);
ESP_LOGD(__FUNCTION__, "done getRegister MPR121_TS2");
if(MPR121_getError(dev)==NOT_INITED || MPR121_getError(dev)==NO_ERROR){ // if our only error is that we are not inited...
return true;

View File

@@ -40,6 +40,26 @@
#include "mpr121_defs.h"
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0))
#include "driver/i2c_master.h"
#else
#include "driver/i2c.h"
#endif
#define I2C_MASTER_FREQ_HZ 400000 /*!< I2C master clock frequency. no higher than 1MHz for now */
#define I2C_TICKS_TO_WAIT 100 // Maximum ticks to wait before issuing a timeout.
#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
#if CONFIG_I2C_PORT_0
#define I2C_NUM I2C_NUM_0
#else
#define I2C_NUM I2C_NUM_1
#endif
// idea behind this is to create a settings structure that we can use to store
// all the setup variables for a particular setup - comes pre-instantiated with
@@ -107,10 +127,14 @@ typedef struct
} MPR121_settings_type;
typedef struct {
uint8_t address;
uint8_t _address;
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0))
i2c_master_bus_handle_t _i2c_bus_handle;
i2c_master_dev_handle_t _i2c_dev_handle;
#endif
MPR121_settings_type defaultSettings;
uint8_t ECR_backup; // so we can re-enable the correct number of electrodes
// when recovering from stop mode
uint8_t ECR_backup; // so we can re-enable the correct number of electrodes when recovering from stop mode
uint8_t error;
bool running;
uint8_t interruptPin;
@@ -118,9 +142,8 @@ typedef struct {
int16_t filteredData[13];
int16_t baselineData[13];
uint16_t touchData;
uint16_t lastTouchData;
bool autoTouchStatusFlag; // we use this to catch touch / release events that happen
// during other update calls
uint16_t lastTouchData; // we use this to catch touch / release events that happen during other update calls
bool autoTouchStatusFlag;
} MPR121_t;
@@ -226,6 +249,10 @@ enum mpr121_SFI_type
// -------------------- BASIC FUNCTIONS --------------------
void i2c_setRegister(MPR121_t * dev, uint8_t reg, uint8_t value);
uint8_t i2c_getRegister(MPR121_t * dev, uint8_t reg);
void i2c_register(MPR121_t * dev, int16_t sda, int16_t scl);
void MPR121_type(MPR121_t * dev);
// begin() must be called before using any other function
@@ -428,6 +455,5 @@ void MPR121_setSFI(MPR121_t * dev, uint8_t SFI);
//extern MPR121_type MPR121;
#endif // MPR121_H

View File

@@ -27,7 +27,6 @@ void app_main(void)
uint16_t touchThreshold = 40;
uint16_t releaseThreshold = 20;
//uint16_t interruptPin = 4;
bool ret = MPR121_begin(&dev, CONFIG_I2C_ADDRESS, touchThreshold, releaseThreshold, CONFIG_IRQ_GPIO, CONFIG_SDA_GPIO, CONFIG_SCL_GPIO);
ESP_LOGI(TAG, "MPR121_begin=%d", ret);