From 4b722e35c713a2db86c3fa9c3f287a2e08470d28 Mon Sep 17 00:00:00 2001 From: nopnop2002 <> Date: Sat, 14 Jun 2025 16:50:49 +0900 Subject: [PATCH] Added support for new i2c drivers --- README.md | 30 +++++++--- components/mpr121/CMakeLists.txt | 13 +++++ components/mpr121/Kconfig.projbuild | 6 ++ components/mpr121/i2c_legacy.c | 74 +++++++++++++++++++++++++ components/mpr121/i2c_new.c | 68 +++++++++++++++++++++++ components/mpr121/mpr121.c | 86 +++++------------------------ components/mpr121/mpr121.h | 40 +++++++++++--- main/main.c | 11 ++-- 8 files changed, 236 insertions(+), 92 deletions(-) create mode 100644 components/mpr121/i2c_legacy.c create mode 100644 components/mpr121/i2c_new.c diff --git a/README.md b/README.md index 8d2670f..9a24a0e 100644 --- a/README.md +++ b/README.md @@ -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. -# Hardware requirements -MPR121 Capacitive Touch switch. +__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 diff --git a/components/mpr121/CMakeLists.txt b/components/mpr121/CMakeLists.txt index c23ffdc..29106cd 100644 --- a/components/mpr121/CMakeLists.txt +++ b/components/mpr121/CMakeLists.txt @@ -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 ".") diff --git a/components/mpr121/Kconfig.projbuild b/components/mpr121/Kconfig.projbuild index fb4a147..4ab4ecb 100644 --- a/components/mpr121/Kconfig.projbuild +++ b/components/mpr121/Kconfig.projbuild @@ -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 diff --git a/components/mpr121/i2c_legacy.c b/components/mpr121/i2c_legacy.c new file mode 100644 index 0000000..c57951a --- /dev/null +++ b/components/mpr121/i2c_legacy.c @@ -0,0 +1,74 @@ +#include +#include +#include +#include + +#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<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< +#include +#include +#include + +#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<error |= 1<_i2c_dev_handle, out_buf, 1, in_buf, 1, -1); + if (espRc == ESP_OK) { + dev->error &= ~(1<error |= 1<_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"); +} diff --git a/components/mpr121/mpr121.c b/components/mpr121/mpr121.c index 26c86ed..ec35570 100644 --- a/components/mpr121/mpr121.c +++ b/components/mpr121/mpr121.c @@ -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<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<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<=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<error &= ~(1<error |= 1<error &= ~(1<= 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 diff --git a/main/main.c b/main/main.c index 92b454f..31ee3e7 100644 --- a/main/main.c +++ b/main/main.c @@ -1,10 +1,10 @@ -/* MPR121 Example +/* MPR121 Example - This example code is in the Public Domain (or CC0 licensed, at your option.) + This example code is in the Public Domain (or CC0 licensed, at your option.) - Unless required by applicable law or agreed to in writing, this - software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, either express or implied. + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. */ #include #include @@ -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);