Replace allLVGL driver I2C code with I2C Manager
For discussion see #70
This commit is contained in:
parent
8f1370d1c4
commit
e52112376f
24 changed files with 896 additions and 679 deletions
116
i2c_manager/Kconfig
Normal file
116
i2c_manager/Kconfig
Normal file
|
@ -0,0 +1,116 @@
|
|||
menu "I2C Port Settings"
|
||||
depends on LV_I2C && !HAVE_I2C_MANAGER
|
||||
|
||||
menu "I2C Port 0"
|
||||
|
||||
config I2C_MANAGER_0_ENABLED
|
||||
bool "Enable I2C port 0"
|
||||
|
||||
if I2C_MANAGER_0_ENABLED
|
||||
config I2C_MANAGER_0_SDA
|
||||
int "SDA (GPIO pin)"
|
||||
range 0 39 if IDF_TARGET_ESP32
|
||||
range 0 46 if IDF_TARGET_ESP32S2
|
||||
range 0 21 if IDF_TARGET_ESP32C3
|
||||
config I2C_MANAGER_0_SCL
|
||||
int "SCL (GPIO pin)"
|
||||
range 0 39 if IDF_TARGET_ESP32
|
||||
range 0 46 if IDF_TARGET_ESP32S2
|
||||
range 0 21 if IDF_TARGET_ESP32C3
|
||||
config I2C_MANAGER_0_FREQ_HZ
|
||||
int "Frequency (Hz)"
|
||||
default 400000
|
||||
range 100000 5000000
|
||||
help
|
||||
The clock speed in Hz. Ranges from 100000 (100 kHz) to
|
||||
5000000 (5 Mhz). I2C busses that involve external wires may
|
||||
have to be slower, and the real maximum speed the bus will
|
||||
support depends on the value of the pullup resistors and the
|
||||
design of the overall circuit.
|
||||
config I2C_MANAGER_0_TIMEOUT
|
||||
int "R/W timeout (ms)"
|
||||
default 20
|
||||
range 10 1000
|
||||
help
|
||||
Timeout for I2C read and write operations. This does not
|
||||
include the time waiting for a lock.
|
||||
config I2C_MANAGER_0_LOCK_TIMEOUT
|
||||
int "Stale lock override (ms)"
|
||||
default 50
|
||||
range 10 1000
|
||||
help
|
||||
Timeout at which point an operation waiting for its turn on
|
||||
the port will assume that whatever set the lock has died and
|
||||
overrides it. Set this somewhat larger than the previous
|
||||
timeout.
|
||||
config I2C_MANAGER_0_PULLUPS
|
||||
bool "Use ESP32 built-in bus pull-up resistors"
|
||||
help
|
||||
The I2C bus needs resistors to make sure it's in a defined
|
||||
state when nobody is talking. Many circuits have external
|
||||
pullup resistors already and turning these on will increase
|
||||
power consumption slightly and may limit the speed your bus
|
||||
can attain. Try with these off first if you don't know.
|
||||
endif
|
||||
|
||||
endmenu
|
||||
|
||||
|
||||
menu "I2C Port 1"
|
||||
|
||||
config I2C_MANAGER_1_ENABLED
|
||||
bool "Enable I2C port 1"
|
||||
|
||||
if I2C_MANAGER_1_ENABLED
|
||||
config I2C_MANAGER_1_SDA
|
||||
int "SDA (GPIO pin)"
|
||||
default 32
|
||||
range 0 39 if IDF_TARGET_ESP32
|
||||
range 0 46 if IDF_TARGET_ESP32S2
|
||||
range 0 21 if IDF_TARGET_ESP32C3
|
||||
config I2C_MANAGER_1_SCL
|
||||
int "SCL (GPIO pin)"
|
||||
default 33
|
||||
range 0 39 if IDF_TARGET_ESP32
|
||||
range 0 46 if IDF_TARGET_ESP32S2
|
||||
range 0 21 if IDF_TARGET_ESP32C3
|
||||
config I2C_MANAGER_1_FREQ_HZ
|
||||
int "Frequency (Hz)"
|
||||
default 1000000
|
||||
range 100000 5000000
|
||||
help
|
||||
The clock speed in Hz. Ranges from 100000 (100 kHz) to
|
||||
5000000 (5 Mhz). I2C busses that involve external wires may
|
||||
have to be slower, and the real maximum speed the bus will
|
||||
support depends on the value of the pullup resistors and the
|
||||
design of the overall circuit.
|
||||
config I2C_MANAGER_1_TIMEOUT
|
||||
int "R/W timeout (ms)"
|
||||
default 20
|
||||
range 10 1000
|
||||
help
|
||||
Timeout for I2C read and write operations. This does not
|
||||
include the time waiting for a lock. Default should be fine.
|
||||
config I2C_MANAGER_1_LOCK_TIMEOUT
|
||||
int "Stale lock override (ms)"
|
||||
default 50
|
||||
help
|
||||
Timeout at which point an operation waiting for its turn on
|
||||
the port will assume that whatever set the lock has died and
|
||||
overrides it. Set this somewhat larger than the previous
|
||||
timeout. Default should be fine.
|
||||
range 30 1000
|
||||
config I2C_MANAGER_1_PULLUPS
|
||||
bool "Use ESP32 built-in bus pull-up resistors"
|
||||
help
|
||||
The I2C bus needs resistors to make sure it's in a defined
|
||||
state when nobody is talking. Many circuits have external
|
||||
pullup resistors already and turning these on will increase
|
||||
power consumption slightly and may limit the speed your bus
|
||||
can attain. Try with these off first if you don't know.
|
||||
endif
|
||||
|
||||
endmenu
|
||||
|
||||
endmenu
|
||||
|
71
i2c_manager/README.md
Normal file
71
i2c_manager/README.md
Normal file
|
@ -0,0 +1,71 @@
|
|||
# I2C in `lvgl_esp32_drivers`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Information for users
|
||||
|
||||
### I2C Manager support
|
||||
|
||||
`lvgl_esp32_drivers` comes with built-in I2C support by integrating I2C Manager, which is used in case your touch interface or screen uses the I2C bus. The native I2C support offered by ESP-IDF is not thread-safe. Maybe you use LVGL with a touch sensor that has an i2c port, and maybe your device also has another i2c device that needs to be read frequently, such as a 3D-accelerometer. If you read that from another task than the lvgl uses to read the touch data, you need some kind of mechanism to keep these communications from interfering.
|
||||
|
||||
If you have other components that can use I2C Manager (or Mika Tuupola's I2C HAL abstraction that I2C Manager is compatible with) then put I2C Manager in your components directory by cloning the repository from below and in your main program do:
|
||||
|
||||
```c
|
||||
#include "i2c_manager.h"
|
||||
#include "lvgl_helpers.h"
|
||||
|
||||
[...]
|
||||
|
||||
lvgl_locking(i2c_manager_locking());
|
||||
lv_init();
|
||||
lvgl_driver_init();
|
||||
```
|
||||
|
||||
The `lvgl_locking` part will cause the LVGL I2C driver to play nice with anything else that uses the I2C port(s) through I2C Manager.
|
||||
|
||||
See the [I2C Manager GitHub repository](https://github.com/ropg/i2c_manager) for much more information.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Information for driver developers
|
||||
|
||||
I2C support in the LVGL ESP drivers is provided exclusively by the files in this directory. Code from all over the project that was talking to the I2C hardware directly has been replaced by code that communicates through the functions provided in `lvgl_i2c.h`. I2C is handled by the I2C Manager that was built into lvlg_esp32_drivers, but the code would be the same if it was routed through I2C Manager as a separate component. If you are providing a driver, you need not worry about any of this.
|
||||
|
||||
### Using I2C in a driver, a multi-step guide
|
||||
|
||||
#### Step 1
|
||||
|
||||
The Kconfig entries for your driver only need to specify that you will be using I2C. This is done by `select LV_I2C_DISPLAY` or `select LV_I2C_TOUCH`.
|
||||
|
||||
#### Step 2
|
||||
|
||||
To use the I2C port in your code you would do something like:
|
||||
|
||||
```c
|
||||
#include "i2c_manager/i2c_manager.h"
|
||||
|
||||
uint8_t data[2];
|
||||
lvgl_i2c_read(CONFIG_LV_I2C_TOUCH_PORT, 0x23, 0x42, &data, 2);
|
||||
```
|
||||
|
||||
This causes a touch driver to read two bytes at register `0x42` from the IC at address `0x23`. Replace `CONFIG_LV_I2C_TOUCH_PORT` by `CONFIG_LV_I2C_DISPLAY_PORT` when this is a display instead of a touch driver. `lvgl_i2c_write` works much the same way, except writing the bytes from the buffer instead of reading them.
|
||||
|
||||
> The example above ignores it but these functions return `esp_err_t` so you can check if the i2c communication worked.
|
||||
|
||||
#### Step 3
|
||||
|
||||
There is no step 3, you are already done.
|
||||
|
||||
### Behind the scenes
|
||||
|
||||
If anything in `lvgl_esp32_drivers` uses I2C, the config system will pop up an extra menu. This will allow you to select an I2C port for screen and one for the touch driver, if applicable. An extra menu allows you to set the GPIO pins and bus speed of any port you have selected for use. It's perfectly fine for a display and a touch driver to use the same I2C port or different ones.
|
||||
|
||||
|
||||
## More information
|
||||
|
||||
If you need more documentation, please refer to the [I2C Manager GitHub repository](https://github.com/ropg/i2c_manager) for more detailed information on how I2C manager works.
|
368
i2c_manager/i2c_manager.c
Normal file
368
i2c_manager/i2c_manager.c
Normal file
|
@ -0,0 +1,368 @@
|
|||
/*
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Rop Gonggrijp. Based on esp_i2c_helper by Mika Tuupola.
|
||||
|
||||
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 <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <esp_log.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/task.h"
|
||||
#include <driver/i2c.h>
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include "i2c_manager.h"
|
||||
|
||||
|
||||
#if defined __has_include
|
||||
#if __has_include ("esp_idf_version.h")
|
||||
#include "esp_idf_version.h"
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0)
|
||||
#define HAS_CLK_FLAGS
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
static const char* TAG = I2C_TAG;
|
||||
|
||||
static SemaphoreHandle_t I2C_FN(_local_mutex)[2] = { NULL, NULL };
|
||||
static SemaphoreHandle_t* I2C_FN(_mutex) = &I2C_FN(_local_mutex)[0];
|
||||
|
||||
static const uint8_t ACK_CHECK_EN = 1;
|
||||
|
||||
#if defined (I2C_NUM_0) && defined (CONFIG_I2C_MANAGER_0_ENABLED)
|
||||
#define I2C_ZERO I2C_NUM_0
|
||||
#if defined (CONFIG_I2C_MANAGER_0_PULLUPS)
|
||||
#define I2C_MANAGER_0_PULLUPS true
|
||||
#else
|
||||
#define I2C_MANAGER_0_PULLUPS false
|
||||
#endif
|
||||
|
||||
#define I2C_MANAGER_0_TIMEOUT CONFIG_I2C_MANAGER_0_TIMEOUT / portTICK_RATE_MS
|
||||
#define I2C_MANAGER_0_LOCK_TIMEOUT CONFIG_I2C_MANAGER_0_LOCK_TIMEOUT / portTICK_RATE_MS
|
||||
#endif
|
||||
|
||||
|
||||
#if defined (I2C_NUM_1) && defined (CONFIG_I2C_MANAGER_1_ENABLED)
|
||||
#define I2C_ONE I2C_NUM_1
|
||||
#if defined (CONFIG_I2C_MANAGER_1_PULLUPS)
|
||||
#define I2C_MANAGER_1_PULLUPS true
|
||||
#else
|
||||
#define I2C_MANAGER_1_PULLUPS false
|
||||
#endif
|
||||
|
||||
#define I2C_MANAGER_1_TIMEOUT CONFIG_I2C_MANAGER_1_TIMEOUT / portTICK_RATE_MS
|
||||
#define I2C_MANAGER_1_LOCK_TIMEOUT CONFIG_I2C_MANAGER_1_LOCK_TIMEOUT / portTICK_RATE_MS
|
||||
#endif
|
||||
|
||||
#define ERROR_PORT(port, fail) { \
|
||||
ESP_LOGE(TAG, "Invalid port or not configured for I2C Manager: %d", (int)port); \
|
||||
return fail; \
|
||||
}
|
||||
|
||||
#if defined(I2C_ZERO) && defined (I2C_ONE)
|
||||
#define I2C_PORT_CHECK(port, fail) \
|
||||
if (port != I2C_NUM_0 && port != I2C_NUM_1) ERROR_PORT(port, fail);
|
||||
#else
|
||||
#if defined(I2C_ZERO)
|
||||
#define I2C_PORT_CHECK(port, fail) \
|
||||
if (port != I2C_NUM_0) ERROR_PORT(port, fail);
|
||||
#elif defined(I2C_ONE)
|
||||
#define I2C_PORT_CHECK(port, fail) \
|
||||
if (port != I2C_NUM_1) ERROR_PORT(port, fail);
|
||||
#else
|
||||
#define I2C_PORT_CHECK(port, fail) \
|
||||
ERROR_PORT(port, fail);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static void i2c_send_address(i2c_cmd_handle_t cmd, uint16_t addr, i2c_rw_t rw) {
|
||||
if (addr & I2C_ADDR_10) {
|
||||
i2c_master_write_byte(cmd, 0xF0 | ((addr & 0x3FF) >> 7) | rw, ACK_CHECK_EN);
|
||||
i2c_master_write_byte(cmd, addr & 0xFF, ACK_CHECK_EN);
|
||||
} else {
|
||||
i2c_master_write_byte(cmd, (addr << 1) | rw, ACK_CHECK_EN);
|
||||
}
|
||||
}
|
||||
|
||||
static void i2c_send_register(i2c_cmd_handle_t cmd, uint32_t reg) {
|
||||
if (reg & I2C_REG_16) {
|
||||
i2c_master_write_byte(cmd, (reg & 0xFF00) >> 8, ACK_CHECK_EN);
|
||||
}
|
||||
i2c_master_write_byte(cmd, reg & 0xFF, ACK_CHECK_EN);
|
||||
}
|
||||
|
||||
esp_err_t I2C_FN(_init)(i2c_port_t port) {
|
||||
|
||||
I2C_PORT_CHECK(port, ESP_FAIL);
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
if (I2C_FN(_mutex)[port] == 0) {
|
||||
|
||||
ESP_LOGI(TAG, "Starting I2C master at port %d.", (int)port);
|
||||
|
||||
I2C_FN(_mutex)[port] = xSemaphoreCreateMutex();
|
||||
|
||||
i2c_config_t conf = {0};
|
||||
|
||||
#ifdef HAS_CLK_FLAGS
|
||||
conf.clk_flags = 0;
|
||||
#endif
|
||||
|
||||
#if defined (I2C_ZERO)
|
||||
if (port == I2C_NUM_0) {
|
||||
conf.sda_io_num = CONFIG_I2C_MANAGER_0_SDA;
|
||||
conf.scl_io_num = CONFIG_I2C_MANAGER_0_SCL;
|
||||
conf.sda_pullup_en = I2C_MANAGER_0_PULLUPS ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE;
|
||||
conf.scl_pullup_en = conf.sda_pullup_en;
|
||||
conf.master.clk_speed = CONFIG_I2C_MANAGER_0_FREQ_HZ;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined (I2C_ONE)
|
||||
if (port == I2C_NUM_1) {
|
||||
conf.sda_io_num = CONFIG_I2C_MANAGER_1_SDA;
|
||||
conf.scl_io_num = CONFIG_I2C_MANAGER_1_SCL;
|
||||
conf.sda_pullup_en = I2C_MANAGER_1_PULLUPS ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE;
|
||||
conf.scl_pullup_en = conf.sda_pullup_en;
|
||||
conf.master.clk_speed = CONFIG_I2C_MANAGER_1_FREQ_HZ;
|
||||
}
|
||||
#endif
|
||||
|
||||
conf.mode = I2C_MODE_MASTER;
|
||||
|
||||
ret = i2c_param_config(port, &conf);
|
||||
ret |= i2c_driver_install(port, conf.mode, 0, 0, 0);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to initialise I2C port %d.", (int)port);
|
||||
ESP_LOGW(TAG, "If it was already open, we'll use it with whatever settings were used "
|
||||
"to open it. See I2C Manager README for details.");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Initialised port %d (SDA: %d, SCL: %d, speed: %d Hz.)",
|
||||
port, conf.sda_io_num, conf.scl_io_num, conf.master.clk_speed);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t I2C_FN(_read)(i2c_port_t port, uint16_t addr, uint32_t reg, uint8_t *buffer, uint16_t size) {
|
||||
|
||||
I2C_PORT_CHECK(port, ESP_FAIL);
|
||||
|
||||
esp_err_t result;
|
||||
|
||||
// May seem weird, but init starts with a check if it's needed, no need for that check twice.
|
||||
I2C_FN(_init)(port);
|
||||
|
||||
ESP_LOGV(TAG, "Reading port %d, addr 0x%03x, reg 0x%04x", port, addr, reg);
|
||||
|
||||
TickType_t timeout = 0;
|
||||
#if defined (I2C_ZERO)
|
||||
if (port == I2C_NUM_0) {
|
||||
timeout = (CONFIG_I2C_MANAGER_0_TIMEOUT) / portTICK_RATE_MS;
|
||||
}
|
||||
#endif
|
||||
#if defined (I2C_ONE)
|
||||
if (port == I2C_NUM_1) {
|
||||
timeout = (CONFIG_I2C_MANAGER_1_TIMEOUT) / portTICK_RATE_MS;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (I2C_FN(_lock)((int)port) == ESP_OK) {
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
if (!(reg & I2C_NO_REG)) {
|
||||
/* When reading specific register set the addr pointer first. */
|
||||
i2c_master_start(cmd);
|
||||
i2c_send_address(cmd, addr, I2C_MASTER_WRITE);
|
||||
i2c_send_register(cmd, reg);
|
||||
}
|
||||
/* Read size bytes from the current pointer. */
|
||||
i2c_master_start(cmd);
|
||||
i2c_send_address(cmd, addr, I2C_MASTER_READ);
|
||||
i2c_master_read(cmd, buffer, size, I2C_MASTER_LAST_NACK);
|
||||
i2c_master_stop(cmd);
|
||||
result = i2c_master_cmd_begin(port, cmd, timeout);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
I2C_FN(_unlock)((int)port);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Lock could not be obtained for port %d.", (int)port);
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
if (result != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Error: %d", result);
|
||||
}
|
||||
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(TAG, buffer, size, ESP_LOG_VERBOSE);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
esp_err_t I2C_FN(_write)(i2c_port_t port, uint16_t addr, uint32_t reg, const uint8_t *buffer, uint16_t size) {
|
||||
|
||||
I2C_PORT_CHECK(port, ESP_FAIL);
|
||||
|
||||
esp_err_t result;
|
||||
|
||||
// May seem weird, but init starts with a check if it's needed, no need for that check twice.
|
||||
I2C_FN(_init)(port);
|
||||
|
||||
ESP_LOGV(TAG, "Writing port %d, addr 0x%03x, reg 0x%04x", port, addr, reg);
|
||||
|
||||
TickType_t timeout = 0;
|
||||
#if defined (I2C_ZERO)
|
||||
if (port == I2C_NUM_0) {
|
||||
timeout = (CONFIG_I2C_MANAGER_0_TIMEOUT) / portTICK_RATE_MS;
|
||||
}
|
||||
#endif
|
||||
#if defined (I2C_ONE)
|
||||
if (port == I2C_NUM_1) {
|
||||
timeout = (CONFIG_I2C_MANAGER_1_TIMEOUT) / portTICK_RATE_MS;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (I2C_FN(_lock)((int)port) == ESP_OK) {
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_send_address(cmd, addr, I2C_MASTER_WRITE);
|
||||
if (!(reg & I2C_NO_REG)) {
|
||||
i2c_send_register(cmd, reg);
|
||||
}
|
||||
i2c_master_write(cmd, (uint8_t *)buffer, size, ACK_CHECK_EN);
|
||||
i2c_master_stop(cmd);
|
||||
result = i2c_master_cmd_begin( port, cmd, timeout);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
I2C_FN(_unlock)((int)port);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Lock could not be obtained for port %d.", (int)port);
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
if (result != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Error: %d", result);
|
||||
}
|
||||
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(TAG, buffer, size, ESP_LOG_VERBOSE);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
esp_err_t I2C_FN(_close)(i2c_port_t port) {
|
||||
I2C_PORT_CHECK(port, ESP_FAIL);
|
||||
vSemaphoreDelete(I2C_FN(_mutex)[port]);
|
||||
I2C_FN(_mutex)[port] = NULL;
|
||||
ESP_LOGI(TAG, "Closing I2C master at port %d", port);
|
||||
return i2c_driver_delete(port);
|
||||
}
|
||||
|
||||
esp_err_t I2C_FN(_lock)(i2c_port_t port) {
|
||||
I2C_PORT_CHECK(port, ESP_FAIL);
|
||||
ESP_LOGV(TAG, "Mutex lock set for %d.", (int)port);
|
||||
|
||||
TickType_t timeout;
|
||||
#if defined (I2C_ZERO)
|
||||
if (port == I2C_NUM_0) {
|
||||
timeout = (CONFIG_I2C_MANAGER_0_LOCK_TIMEOUT) / portTICK_RATE_MS;
|
||||
}
|
||||
#endif
|
||||
#if defined (I2C_ONE)
|
||||
if (port == I2C_NUM_1) {
|
||||
timeout = (CONFIG_I2C_MANAGER_1_LOCK_TIMEOUT) / portTICK_RATE_MS;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (xSemaphoreTake(I2C_FN(_mutex)[port], timeout) == pdTRUE) {
|
||||
return ESP_OK;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Removing stale mutex lock from port %d.", (int)port);
|
||||
I2C_FN(_force_unlock)(port);
|
||||
return (xSemaphoreTake(I2C_FN(_mutex)[port], timeout) == pdTRUE ? ESP_OK : ESP_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t I2C_FN(_unlock)(i2c_port_t port) {
|
||||
I2C_PORT_CHECK(port, ESP_FAIL);
|
||||
ESP_LOGV(TAG, "Mutex lock removed for %d.", (int)port);
|
||||
return (xSemaphoreGive(I2C_FN(_mutex)[port]) == pdTRUE) ? ESP_OK : ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t I2C_FN(_force_unlock)(i2c_port_t port) {
|
||||
I2C_PORT_CHECK(port, ESP_FAIL);
|
||||
if (I2C_FN(_mutex)[port]) {
|
||||
vSemaphoreDelete(I2C_FN(_mutex)[port]);
|
||||
}
|
||||
I2C_FN(_mutex)[port] = xSemaphoreCreateMutex();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef I2C_OEM
|
||||
|
||||
void I2C_FN(_locking)(void* leader) {
|
||||
if (leader) {
|
||||
ESP_LOGI(TAG, "Now following I2C Manager for locking");
|
||||
I2C_FN(_mutex) = (SemaphoreHandle_t*)leader;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void* i2c_manager_locking() {
|
||||
return (void*)i2c_manager_mutex;
|
||||
}
|
||||
|
||||
int32_t i2c_hal_read(void *handle, uint8_t address, uint8_t reg, uint8_t *buffer, uint16_t size) {
|
||||
return i2c_manager_read(*(i2c_port_t*)handle, address, reg, buffer, size);
|
||||
}
|
||||
|
||||
int32_t i2c_hal_write(void *handle, uint8_t address, uint8_t reg, const uint8_t *buffer, uint16_t size) {
|
||||
return i2c_manager_write(*(i2c_port_t*)handle, address, reg, buffer, size);
|
||||
}
|
||||
|
||||
static i2c_port_t port_zero = (i2c_port_t)0;
|
||||
static i2c_port_t port_one = (i2c_port_t)1;
|
||||
|
||||
static i2c_hal_t _i2c_hal[2] = {
|
||||
{&i2c_hal_read, &i2c_hal_write, &port_zero},
|
||||
{&i2c_hal_read, &i2c_hal_write, &port_one}
|
||||
};
|
||||
|
||||
void* i2c_hal(i2c_port_t port) {
|
||||
I2C_PORT_CHECK(port, NULL);
|
||||
return (void*)&_i2c_hal[port];
|
||||
}
|
||||
|
||||
#endif
|
76
i2c_manager/i2c_manager.h
Normal file
76
i2c_manager/i2c_manager.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
#ifndef _I2C_MANAGER_H
|
||||
#define _I2C_MANAGER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
|
||||
If you copy the i2c_manager files to your own component instead of
|
||||
depending on i2c_manager, you MUST uncomment the define below
|
||||
and put in some short string that identifies your component (such
|
||||
as 'xyz'). This will cause i2c_manager to create functions named
|
||||
xyz_i2c_* instead of i2c_manager_*. See README.md for details.
|
||||
|
||||
*/
|
||||
|
||||
#define I2C_OEM lvgl
|
||||
|
||||
|
||||
// Only here to get the I2C_NUM_0 and I2C_NUM_1 defines.
|
||||
#include <driver/i2c.h>
|
||||
|
||||
#define CONCATX(A, B) A ## B
|
||||
#define CONCAT(A, B) CONCATX(A, B)
|
||||
#define STR_LITERAL(s) # s
|
||||
#define STR_EXPAND(s) STR_LITERAL(s)
|
||||
#define STR_QUOTE(s) STR_EXPAND(STR_EXPAND(s))
|
||||
|
||||
#ifdef I2C_OEM
|
||||
#define I2C_NAME_PREFIX CONCAT(I2C_OEM, _i2c)
|
||||
#else
|
||||
#define I2C_NAME_PREFIX i2c_manager
|
||||
#endif
|
||||
#define I2C_TAG STR_EXPAND(I2C_NAME_PREFIX)
|
||||
|
||||
#define I2C_FN(s) CONCAT(I2C_NAME_PREFIX, s)
|
||||
|
||||
|
||||
#define I2C_ADDR_10 ( 1 << 15 )
|
||||
#define I2C_REG_16 ( 1 << 31 )
|
||||
#define I2C_NO_REG ( 1 << 30 )
|
||||
|
||||
esp_err_t I2C_FN(_init)(i2c_port_t port);
|
||||
esp_err_t I2C_FN(_read)(i2c_port_t port, uint16_t addr, uint32_t reg, uint8_t *buffer, uint16_t size);
|
||||
esp_err_t I2C_FN(_write)(i2c_port_t port, uint16_t addr, uint32_t reg, const uint8_t *buffer, uint16_t size);
|
||||
esp_err_t I2C_FN(_close)(i2c_port_t port);
|
||||
esp_err_t I2C_FN(_lock)(i2c_port_t port);
|
||||
esp_err_t I2C_FN(_unlock)(i2c_port_t port);
|
||||
esp_err_t I2C_FN(_force_unlock)(i2c_port_t port);
|
||||
|
||||
|
||||
#ifdef I2C_OEM
|
||||
|
||||
void I2C_FN(_locking)(void* leader);
|
||||
|
||||
#else
|
||||
|
||||
void* i2c_manager_locking();
|
||||
|
||||
typedef struct {
|
||||
int32_t (* read)(void *handle, uint8_t address, uint8_t reg, uint8_t *buffer, uint16_t size);
|
||||
int32_t (* write)(void *handle, uint8_t address, uint8_t reg, const uint8_t *buffer, uint16_t size);
|
||||
void *handle;
|
||||
} i2c_hal_t;
|
||||
|
||||
void* i2c_hal(i2c_port_t port);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue