#75 Start resolving conflicts (updating component)
This commit is contained in:
parent
22893f0ae1
commit
2868e219fc
136
lvgl_helpers.c
136
lvgl_helpers.c
|
@ -14,9 +14,8 @@
|
|||
#include "lvgl_touch/tp_spi.h"
|
||||
|
||||
#include "lvgl_spi_conf.h"
|
||||
#include "lvgl_i2c_conf.h"
|
||||
|
||||
#include "driver/i2c.h"
|
||||
#include "lvgl_i2c/i2c_manager.h"
|
||||
|
||||
#ifdef LV_LVGL_H_INCLUDE_SIMPLE
|
||||
#include "lvgl.h"
|
||||
|
@ -68,7 +67,7 @@ void lvgl_driver_init(void)
|
|||
DISP_SPI_MISO, DISP_SPI_MOSI, DISP_SPI_CLK,
|
||||
SPI_BUS_MAX_TRANSFER_SZ, 1,
|
||||
DISP_SPI_IO2, DISP_SPI_IO3);
|
||||
|
||||
|
||||
disp_spi_add_device(TFT_SPI_HOST);
|
||||
disp_driver_init();
|
||||
|
||||
|
@ -86,48 +85,29 @@ void lvgl_driver_init(void)
|
|||
TP_SPI_MISO, DISP_SPI_MOSI, DISP_SPI_CLK,
|
||||
SPI_BUS_MAX_TRANSFER_SZ, 1,
|
||||
-1, -1);
|
||||
|
||||
|
||||
disp_spi_add_device(TFT_SPI_HOST);
|
||||
tp_spi_add_device(TOUCH_SPI_HOST);
|
||||
|
||||
|
||||
disp_driver_init();
|
||||
touch_driver_init();
|
||||
|
||||
return;
|
||||
#endif
|
||||
|
||||
#if defined (SHARED_I2C_BUS)
|
||||
ESP_LOGI(TAG, "Initializing shared I2C master");
|
||||
|
||||
lvgl_i2c_driver_init(DISP_I2C_PORT,
|
||||
DISP_I2C_SDA, DISP_I2C_SCL,
|
||||
DISP_I2C_SPEED_HZ);
|
||||
|
||||
disp_driver_init();
|
||||
touch_driver_init();
|
||||
|
||||
return;
|
||||
#endif
|
||||
|
||||
/* Display controller initialization */
|
||||
#if defined CONFIG_LV_TFT_DISPLAY_PROTOCOL_SPI
|
||||
ESP_LOGI(TAG, "Initializing SPI master for display");
|
||||
|
||||
|
||||
lvgl_spi_driver_init(TFT_SPI_HOST,
|
||||
DISP_SPI_MISO, DISP_SPI_MOSI, DISP_SPI_CLK,
|
||||
SPI_BUS_MAX_TRANSFER_SZ, 1,
|
||||
DISP_SPI_IO2, DISP_SPI_IO3);
|
||||
|
||||
|
||||
disp_spi_add_device(TFT_SPI_HOST);
|
||||
|
||||
|
||||
disp_driver_init();
|
||||
#elif defined (CONFIG_LV_TFT_DISPLAY_PROTOCOL_I2C)
|
||||
ESP_LOGI(TAG, "Initializing I2C master for display");
|
||||
/* Init the i2c master on the display driver code */
|
||||
lvgl_i2c_driver_init(DISP_I2C_PORT,
|
||||
DISP_I2C_SDA, DISP_I2C_SCL,
|
||||
DISP_I2C_SPEED_HZ);
|
||||
|
||||
#elif defined (CONFIG_LV_I2C_DISPLAY)
|
||||
disp_driver_init();
|
||||
#elif defined (CONFIG_LV_EPAPER_DISPLAY_PROTOCOL_PARALLEL)
|
||||
// Do not initialize SPI. Uses EPDiy
|
||||
|
@ -135,77 +115,35 @@ void lvgl_driver_init(void)
|
|||
// Check how not to initialize SPI. disp_driver_init() call is needed:
|
||||
disp_driver_init();
|
||||
#else
|
||||
#error "No protocol defined for display controller"
|
||||
#error "No protocol defined for display controller"
|
||||
#endif
|
||||
|
||||
/* Touch controller initialization */
|
||||
#if CONFIG_LV_TOUCH_CONTROLLER != TOUCH_CONTROLLER_NONE
|
||||
#if defined (CONFIG_LV_TOUCH_DRIVER_PROTOCOL_SPI)
|
||||
ESP_LOGI(TAG, "Initializing SPI master for touch");
|
||||
|
||||
|
||||
lvgl_spi_driver_init(TOUCH_SPI_HOST,
|
||||
TP_SPI_MISO, TP_SPI_MOSI, TP_SPI_CLK,
|
||||
0 /* Defaults to 4094 */, 2,
|
||||
-1, -1);
|
||||
|
||||
|
||||
tp_spi_add_device(TOUCH_SPI_HOST);
|
||||
|
||||
|
||||
touch_driver_init();
|
||||
#elif defined (CONFIG_LV_TOUCH_DRIVER_PROTOCOL_I2C)
|
||||
ESP_LOGI(TAG, "Initializing I2C master for touch");
|
||||
lvgl_i2c_driver_init(TOUCH_I2C_PORT,
|
||||
TOUCH_I2C_SDA, TOUCH_I2C_SCL,
|
||||
TOUCH_I2C_SPEED_HZ);
|
||||
|
||||
#elif defined (CONFIG_LV_I2C_TOUCH)
|
||||
touch_driver_init();
|
||||
#elif defined (CONFIG_LV_TOUCH_DRIVER_ADC)
|
||||
touch_driver_init();
|
||||
#elif defined (CONFIG_LV_TOUCH_DRIVER_DISPLAY) || defined (CONFIG_LV_TOUCH_CONTROLLER_L58)
|
||||
touch_driver_init();
|
||||
#else
|
||||
#error "No protocol defined for touch controller"
|
||||
|
||||
#error "No protocol defined for touch controller"
|
||||
#endif
|
||||
#else
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Config the i2c master
|
||||
*
|
||||
* This should init the i2c master to be used on display and touch controllers.
|
||||
* So we should be able to know if the display and touch controllers shares the
|
||||
* same i2c master.
|
||||
*/
|
||||
bool lvgl_i2c_driver_init(int port, int sda_pin, int scl_pin, int speed_hz)
|
||||
{
|
||||
esp_err_t err;
|
||||
|
||||
ESP_LOGI(TAG, "Initializing I2C master port %d...", port);
|
||||
ESP_LOGI(TAG, "SDA pin: %d, SCL pin: %d, Speed: %d (Hz)",
|
||||
sda_pin, scl_pin, speed_hz);
|
||||
|
||||
i2c_config_t conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = sda_pin,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_io_num = scl_pin,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master.clk_speed = speed_hz,
|
||||
};
|
||||
|
||||
ESP_LOGI(TAG, "Setting I2C master configuration...");
|
||||
err = i2c_param_config(port, &conf);
|
||||
assert(ESP_OK == err);
|
||||
|
||||
ESP_LOGI(TAG, "Installing I2C master driver...");
|
||||
err = i2c_driver_install(port,
|
||||
I2C_MODE_MASTER,
|
||||
0, 0 /*I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE */,
|
||||
0 /* intr_alloc_flags */);
|
||||
assert(ESP_OK == err);
|
||||
|
||||
return ESP_OK != err;
|
||||
}
|
||||
|
||||
/* Initialize spi bus master
|
||||
*
|
||||
|
@ -221,34 +159,16 @@ bool lvgl_spi_driver_init(int host,
|
|||
int dma_channel,
|
||||
int quadwp_pin, int quadhd_pin)
|
||||
{
|
||||
int dma_chan = 0 /* SPI_DMA_DISABLED */;
|
||||
|
||||
#if defined (CONFIG_IDF_TARGET_ESP32)
|
||||
assert((SPI_HOST <= host) && (VSPI_HOST >= host));
|
||||
const char *spi_names[] = {
|
||||
"SPI_HOST", "HSPI_HOST", "VSPI_HOST"
|
||||
};
|
||||
|
||||
dma_chan = dma_channel;
|
||||
#elif defined (CONFIG_IDF_TARGET_ESP32S2)
|
||||
assert((SPI_HOST <= host) && (HSPI_HOST >= host));
|
||||
const char *spi_names[] = {
|
||||
"SPI_HOST", "", ""
|
||||
};
|
||||
|
||||
dma_chan = dma_channel;
|
||||
#elif defined (CONFIG_IDF_TARGET_ESP32C3)
|
||||
assert((SPI1_HOST <= host) && (SPI3_HOST >= host));
|
||||
/**
|
||||
* @brief in idf 4.3 is giving me an error:
|
||||
* error: 'SPI_HOST_MAX' undeclared
|
||||
*/
|
||||
assert((0 <= host) && (SPI_HOST_MAX > host));
|
||||
const char *spi_names[] = {
|
||||
"SPI1_HOST", "SPI2_HOST", "SPI3_HOST"
|
||||
};
|
||||
|
||||
dma_chan = 3 /* SPI_DMA_CH_AUTO */;
|
||||
#else
|
||||
#error "Target chip not selected"
|
||||
#endif
|
||||
|
||||
ESP_LOGI(TAG, "Configuring SPI host %s (%d)", spi_names[host], host);
|
||||
ESP_LOGI(TAG, "Configuring SPI host %s", spi_names[host]);
|
||||
ESP_LOGI(TAG, "MISO pin: %d, MOSI pin: %d, SCLK pin: %d, IO2/WP pin: %d, IO3/HD pin: %d",
|
||||
miso_pin, mosi_pin, sclk_pin, quadwp_pin, quadhd_pin);
|
||||
|
||||
|
@ -256,17 +176,19 @@ bool lvgl_spi_driver_init(int host,
|
|||
|
||||
spi_bus_config_t buscfg = {
|
||||
.miso_io_num = miso_pin,
|
||||
.mosi_io_num = mosi_pin,
|
||||
.sclk_io_num = sclk_pin,
|
||||
.quadwp_io_num = quadwp_pin,
|
||||
.quadhd_io_num = quadhd_pin,
|
||||
.mosi_io_num = mosi_pin,
|
||||
.sclk_io_num = sclk_pin,
|
||||
.quadwp_io_num = quadwp_pin,
|
||||
.quadhd_io_num = quadhd_pin,
|
||||
.max_transfer_sz = max_transfer_sz
|
||||
};
|
||||
|
||||
ESP_LOGI(TAG, "Initializing SPI bus...");
|
||||
esp_err_t ret = spi_bus_initialize(host, &buscfg, dma_chan);
|
||||
#if defined (CONFIG_IDF_TARGET_ESP32C3)
|
||||
dma_channel = SPI_DMA_CH_AUTO;
|
||||
#endif
|
||||
esp_err_t ret = spi_bus_initialize(host, &buscfg, (spi_dma_chan_t)dma_channel);
|
||||
assert(ret == ESP_OK);
|
||||
|
||||
return ESP_OK != ret;
|
||||
}
|
||||
|
||||
}
|
98
lvgl_i2c/Kconfig
Normal file
98
lvgl_i2c/Kconfig
Normal file
|
@ -0,0 +1,98 @@
|
|||
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)"
|
||||
default 0
|
||||
config I2C_MANAGER_0_SCL
|
||||
int "SCL (GPIO pin)"
|
||||
default 0
|
||||
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)"
|
||||
config I2C_MANAGER_1_SCL
|
||||
int "SCL (GPIO pin)"
|
||||
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
|
90
lvgl_i2c/README.md
Normal file
90
lvgl_i2c/README.md
Normal file
|
@ -0,0 +1,90 @@
|
|||
# I2C in `lvgl_esp32_drivers`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Information for users
|
||||
|
||||
### I2C Manager support
|
||||
|
||||
`lvgl_esp32_drivers` integrates [I2C Manager](https://github.com/ropg/i2c_manager), which is used in case you select a touch sensor or screen that uses the I2C bus.
|
||||
|
||||
I2C Manager is also available as a separate ESP-IDF component and can help if you are in a situation where you want to avoid "bus conflicts" on the I2C bus. **If in your application nothing outside of LVGL needs to talk to the I2C bus, you can stop reading here.**
|
||||
|
||||
Suppose you use LVGL with a touch sensor that uses I2C, and your device also has another I2C device that needs to be read frequently, such as a 3D-accelerometer. ESP-IDF is not inherently "thread-safe". So if you read that from another task than the one LVGL uses to read the touch data, you need some kind of mechanism to keep these communications from interfering.
|
||||
|
||||
If you have (or write) a driver for that 3D-accelerometer that can use I2C Manager (or the I2C HAL and i2cdev abstraction layers 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_i2c_locking(i2c_manager_locking());
|
||||
lv_init();
|
||||
lvgl_driver_init();
|
||||
```
|
||||
|
||||
The `lvgl_i2c_locking` part will cause the LVGL I2C driver to play nice with anything else that uses the I2C port(s) through I2C Manager.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Information for LVGL driver developers
|
||||
|
||||
I2C support in the LVGL ESP drivers is provided exclusively by the files in this directory. Driver code that uses I2C communicates through the functions provided in `i2c_manager.h`.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### Using I2C in an LVGL driver, a multi-step guide
|
||||
|
||||
<dl>
|
||||
|
||||
<dt>Step 1</dt>
|
||||
|
||||
<dd>
|
||||
The Kconfig entries for your driver only need to specify that you will be using I2C. This is done by adding `select LV_I2C_DISPLAY` or `select LV_I2C_TOUCH`.
|
||||
</dd>
|
||||
|
||||
|
||||
<dt>Step 2</dt>
|
||||
|
||||
<dd>
|
||||
To use the I2C port in your code you would do something like:
|
||||
|
||||
|
||||
```c
|
||||
#include "lvgl_i2c/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 it writes the bytes from the buffer instead of reading them. _(It's ignored above but these functions return `esp_err_t` so you can check if the I2C communication worked.)_
|
||||
</dd>
|
||||
|
||||
<dt>Step 3</dt>
|
||||
|
||||
<dd>
|
||||
There is no step 3, you are already done.
|
||||
</dd>
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
### Meanwhile, behind the scenes ...
|
||||
|
||||
If any of the drivers selected by the user uses I2C, the menuconfig system will show an extra menu to select I2C port(s) for screen and/or touch sensor. An additional menu allows for setting of GPIO pins and bus speed of any port selected for use with LVGL. It's perfectly fine for a display and a touch sensor to be on 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. There are features not in the simple example above, such as reads and writes without specifying a register, 16-bit registers, 10-bit I2C addressing and more.
|
368
lvgl_i2c/i2c_manager.c
Normal file
368
lvgl_i2c/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 = I2C_MANAGER_0_TIMEOUT;
|
||||
}
|
||||
#endif
|
||||
#if defined (I2C_ONE)
|
||||
if (port == I2C_NUM_1) {
|
||||
timeout = I2C_MANAGER_1_TIMEOUT;
|
||||
}
|
||||
#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
lvgl_i2c/i2c_manager.h
Normal file
76
lvgl_i2c/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…
Reference in a new issue