From 3f0de346c8f6757cae24dd4d5fac1f128c7bb64e Mon Sep 17 00:00:00 2001 From: Manuel <71137295+mverch67@users.noreply.github.com> Date: Sun, 31 May 2026 01:05:42 +0200 Subject: [PATCH 1/4] initial support for wio tracker l2 --- drivers/lp5814.c | 352 ++++++++++++++++++ drivers/lp5814.h | 294 +++++++++++++++ include/graphics/LGFX/LGFX_WIO_TRACKER_L2.h | 213 +++++++++++ include/graphics/driver/DisplayDriverConfig.h | 3 +- .../graphics/driver/DisplayDriverFactory.cpp | 7 + 5 files changed, 868 insertions(+), 1 deletion(-) create mode 100644 drivers/lp5814.c create mode 100644 drivers/lp5814.h create mode 100644 include/graphics/LGFX/LGFX_WIO_TRACKER_L2.h diff --git a/drivers/lp5814.c b/drivers/lp5814.c new file mode 100644 index 00000000..bf1a3241 --- /dev/null +++ b/drivers/lp5814.c @@ -0,0 +1,352 @@ +/* + * SPDX-FileCopyrightText: 2026 + * + * SPDX-License-Identifier: MIT + */ + +#include "lp5814.h" +#include + +// -------------------------------------------- +// PRIVATE FUNCTIONS +// -------------------------------------------- + +/** + * @brief Write a single byte to a LP5814 register. + * + * @param handle Pointer to the LP5814 handle. + * @param reg Register address. + * @param data Data byte to write. + * + * @return + * - ESP_OK on success. + * - ESP_ERR_INVALID_ARG if handle is NULL. + * - ESP_FAIL if I2C communication fails. + */ +static esp_err_t lp5814_write_register(lp5814_handle_t *handle, uint8_t reg, uint8_t data) +{ + if (handle == NULL || !handle->initialized) { + return ESP_ERR_INVALID_ARG; + } + + uint8_t buffer[2]; + buffer[0] = reg; + buffer[1] = data; + + return i2c_master_transmit(handle->i2c_dev, buffer, sizeof(buffer), -1); +} + +/** + * @brief Read a single byte from a LP5814 register. + * + * @param handle Pointer to the LP5814 handle. + * @param reg Register address. + * @param data Pointer to store the read data. + * + * @return + * - ESP_OK on success. + * - ESP_ERR_INVALID_ARG if handle or data is NULL. + * - ESP_FAIL if I2C communication fails. + */ +static esp_err_t lp5814_read_register(lp5814_handle_t *handle, uint8_t reg, uint8_t *data) +{ + if (handle == NULL || data == NULL || !handle->initialized) { + return ESP_ERR_INVALID_ARG; + } + + esp_err_t ret = i2c_master_transmit_receive(handle->i2c_dev, ®, 1, data, 1, -1); + return ret; +} + +/** + * @brief Modify bits in a register while preserving other bits. + * + * @param handle Pointer to the LP5814 handle. + * @param reg Register address. + * @param mask Bit mask for bits to modify. + * @param data Data to write to masked bits. + * + * @return + * - ESP_OK on success. + * - ESP_ERR_INVALID_ARG if handle is NULL. + * - ESP_FAIL if I2C communication fails. + */ +static esp_err_t lp5814_modify_register(lp5814_handle_t *handle, uint8_t reg, uint8_t mask, uint8_t data) +{ + uint8_t current_value; + esp_err_t ret = lp5814_read_register(handle, reg, ¤t_value); + if (ret != ESP_OK) { + return ret; + } + + uint8_t new_value = (current_value & ~mask) | (data & mask); + return lp5814_write_register(handle, reg, new_value); +} + +/** + * @brief Get DC register address for a specific LED. + * + * @param led_mask LED channel mask (bit position). + * + * @return Register address. + */ +static inline uint8_t lp5814_get_dc_register(uint32_t led_mask) +{ + // Extract bit position (0-3) from mask + uint8_t led_num = 0; + switch (led_mask) { + case LP5814_LED0: + led_num = 0; + break; + case LP5814_LED1: + led_num = 1; + break; + case LP5814_LED2: + led_num = 2; + break; + case LP5814_LED3: + led_num = 3; + break; + default: + led_num = 0; + break; + } + return LP5814_REG_LED0_DC + led_num; +} + +/** + * @brief Get PWM register address for a specific LED. + * + * @param led_mask LED channel mask (bit position). + * + * @return Register address. + */ +static inline uint8_t lp5814_get_pwm_register(uint32_t led_mask) +{ + // Extract bit position (0-3) from mask + uint8_t led_num = 0; + switch (led_mask) { + case LP5814_LED0: + led_num = 0; + break; + case LP5814_LED1: + led_num = 1; + break; + case LP5814_LED2: + led_num = 2; + break; + case LP5814_LED3: + led_num = 3; + break; + default: + led_num = 0; + break; + } + return LP5814_REG_LED0_PWM + led_num; +} + +// -------------------------------------------- +// PUBLIC API IMPLEMENTATION +// -------------------------------------------- + +esp_err_t lp5814_init(i2c_master_bus_handle_t *i2c_bus, uint8_t dev_addr, lp5814_handle_t *handle) +{ + if (i2c_bus == NULL || handle == NULL) { + return ESP_ERR_INVALID_ARG; + } + + // Configure I2C device + i2c_device_config_t dev_cfg = { + .dev_addr_length = I2C_ADDR_BIT_LEN_7, + .device_address = dev_addr, + .scl_speed_hz = 100000, // I2C max speed is 100kHz + }; + + esp_err_t ret = i2c_master_bus_add_device(*i2c_bus, &dev_cfg, &handle->i2c_dev); + if (ret != ESP_OK) { + return ret; + } + + handle->dev_addr = dev_addr; + handle->initialized = true; + + // Initialize device: enable chip and set default values + ret = lp5814_write_register(handle, LP5814_REG_DEVICE_CONFIG0, LP5814_CHIP_EN_ENABLE); + if (ret != ESP_OK) { + handle->initialized = false; + i2c_master_bus_rm_device(handle->i2c_dev); + return ret; + } + + // Set default max current to 25.5mA + ret = lp5814_set_max_current(handle, LP5814_MAX_CURRENT_51MA); + if (ret != ESP_OK) { + handle->initialized = false; + i2c_master_bus_rm_device(handle->i2c_dev); + return ret; + } + + // Disable all LED outputs initially + ret = lp5814_write_register(handle, LP5814_REG_ENABLE_CONTROL, 0x00); + if (ret != ESP_OK) { + handle->initialized = false; + i2c_master_bus_rm_device(handle->i2c_dev); + return ret; + } + + lp5814_set_led_dim_mode(handle, 0x4E); + lp5814_set_led_engine_mode(handle, 0xF0); + lp5814_set_led_dc(handle, LP5814_LED_ALL, 200); + lp5814_set_led_enable(handle, LP5814_LED_ALL, true); + lp5814_set_led_update_param(handle); + lp5814_set_led_pwm(handle, LP5814_LED_ALL, 0); + + return ESP_OK; +} + +esp_err_t lp5814_deinit(lp5814_handle_t *handle) +{ + if (handle == NULL || !handle->initialized) { + return ESP_ERR_INVALID_ARG; + } + + // Disable all LEDs and chip + lp5814_write_register(handle, LP5814_REG_ENABLE_CONTROL, 0x00); + lp5814_write_register(handle, LP5814_REG_DEVICE_CONFIG0, LP5814_CHIP_EN_DISABLE); + + // Remove I2C device + esp_err_t ret = i2c_master_bus_rm_device(handle->i2c_dev); + + handle->initialized = false; + return ret; +} + +esp_err_t lp5814_set_chip_enable(lp5814_handle_t *handle, bool enable) +{ + if (handle == NULL || !handle->initialized) { + return ESP_ERR_INVALID_ARG; + } + + uint8_t value = enable ? LP5814_CHIP_EN_ENABLE : LP5814_CHIP_EN_DISABLE; + return lp5814_write_register(handle, LP5814_REG_DEVICE_CONFIG0, value); +} + +esp_err_t lp5814_set_max_current(lp5814_handle_t *handle, lp5814_max_current_t mode) +{ + if (handle == NULL || !handle->initialized) { + return ESP_ERR_INVALID_ARG; + } + + return lp5814_modify_register(handle, LP5814_REG_MAX_CURRENT, LP5814_MAX_CURRENT_MASK, (uint8_t)mode); +} + +esp_err_t lp5814_set_led_enable(lp5814_handle_t *handle, uint32_t led_mask, bool enable) +{ + if (handle == NULL || !handle->initialized || led_mask == 0 || (led_mask & ~0x0F)) { + return ESP_ERR_INVALID_ARG; + } + + // led_mask contains bits 0-3 for LEDs 0-3, which can be used directly + uint8_t mask = (uint8_t)led_mask; + uint8_t value = enable ? mask : 0; + + return lp5814_modify_register(handle, LP5814_REG_ENABLE_CONTROL, mask, value); +} + +esp_err_t lp5814_set_led_dim_mode(lp5814_handle_t *handle, uint8_t mode) +{ + if (handle == NULL || !handle->initialized) { + return ESP_ERR_INVALID_ARG; + } + + return lp5814_write_register(handle, LP5814_REG_DIM_MODE, mode); +} + +esp_err_t lp5814_set_led_engine_mode(lp5814_handle_t *handle, uint8_t mode) +{ + if (handle == NULL || !handle->initialized) { + return ESP_ERR_INVALID_ARG; + } + + return lp5814_write_register(handle, LP5814_REG_ENGINE_MODE, mode); +} + +esp_err_t lp5814_set_led_update_param(lp5814_handle_t *handle) +{ + if (handle == NULL || !handle->initialized) { + return ESP_ERR_INVALID_ARG; + } + + return lp5814_write_register(handle, LP5814_REG_UPDATE, 0x55); +} + +esp_err_t lp5814_set_led_dc(lp5814_handle_t *handle, uint32_t led_mask, uint8_t dc) +{ + if (handle == NULL || !handle->initialized || led_mask == 0 || (led_mask & ~0x0F)) { + return ESP_ERR_INVALID_ARG; + } + + esp_err_t ret = ESP_OK; + + // Iterate through each LED bit in the mask + for (uint8_t led = 0; led < 4; led++) { + if (led_mask & (1 << led)) { + uint8_t reg = LP5814_REG_LED0_DC + led; + ret = lp5814_write_register(handle, reg, dc); + if (ret != ESP_OK) { + return ret; + } + } + } + + return ret; +} + +esp_err_t lp5814_set_led_pwm(lp5814_handle_t *handle, uint32_t led_mask, uint8_t pwm) +{ + if (handle == NULL || !handle->initialized || led_mask == 0 || (led_mask & ~0x0F)) { + return ESP_ERR_INVALID_ARG; + } + + esp_err_t ret = ESP_OK; + + // Iterate through each LED bit in the mask + for (uint8_t led = 0; led < 4; led++) { + if (led_mask & (1 << led)) { + uint8_t reg = LP5814_REG_LED0_PWM + led; + ret = lp5814_write_register(handle, reg, pwm); + if (ret != ESP_OK) { + return ret; + } + } + } + + return ret; +} + +esp_err_t lp5814_set_led_dc_pwm(lp5814_handle_t *handle, uint32_t led_mask, uint8_t dc, uint8_t pwm) +{ + if (handle == NULL || !handle->initialized || led_mask == 0 || (led_mask & ~0x0F)) { + return ESP_ERR_INVALID_ARG; + } + + esp_err_t ret = lp5814_set_led_dc(handle, led_mask, dc); + if (ret != ESP_OK) { + return ret; + } + + return lp5814_set_led_pwm(handle, led_mask, pwm); +} + +float lp5814_calculate_current(lp5814_max_current_t mode, uint8_t dc) +{ + if (mode == LP5814_MAX_CURRENT_51_MA) { + // 51mA max: 0.2mA to 51mA in 256 steps + // Current = 0.2 + (dc * 0.2) + return 0.2f + (dc * 0.2f); + } else { + // 25.5mA max: 0.1mA to 25.5mA in 256 steps + // Current = 0.1 + (dc * 0.1) + return 0.1f + (dc * 0.1f); + } +} \ No newline at end of file diff --git a/drivers/lp5814.h b/drivers/lp5814.h new file mode 100644 index 00000000..1ea1309e --- /dev/null +++ b/drivers/lp5814.h @@ -0,0 +1,294 @@ +/* + * SPDX-FileCopyrightText: 2026 + * + * SPDX-License-Identifier: MIT + */ + +#ifndef LP5814_H +#define LP5814_H + +#include "driver/i2c_master.h" +#include "esp_err.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// -------------------------------------------- +// I2C ADDRESS +// -------------------------------------------- + +#define LP5814_I2C_ADDR_DEFAULT (0x2C) + +// -------------------------------------------- +// REGISTER ADDRESS +// -------------------------------------------- + +#define LP5814_REG_DEVICE_CONFIG0 (0x00) +#define LP5814_REG_MAX_CURRENT (0x01) +#define LP5814_REG_ENABLE_CONTROL (0x02) +#define LP5814_REG_DIM_MODE (0x04) +#define LP5814_REG_ENGINE_MODE (0x05) +#define LP5814_REG_UPDATE (0x0F) + +#define LP5814_REG_LED0_DC (0x14) +#define LP5814_REG_LED1_DC (0x15) +#define LP5814_REG_LED2_DC (0x16) +#define LP5814_REG_LED3_DC (0x17) + +#define LP5814_REG_LED0_PWM (0x18) +#define LP5814_REG_LED1_PWM (0x19) +#define LP5814_REG_LED2_PWM (0x1A) +#define LP5814_REG_LED3_PWM (0x1B) + +// -------------------------------------------- +// DEVICE CONFIG0 REGISTER +// -------------------------------------------- + +#define LP5814_CHIP_EN_MASK (0x01) +#define LP5814_CHIP_EN_ENABLE (0x01) +#define LP5814_CHIP_EN_DISABLE (0x00) + +// -------------------------------------------- +// MAX CURRENT REGISTER +// -------------------------------------------- + +#define LP5814_MAX_CURRENT_MASK (0x01) +#define LP5814_MAX_CURRENT_25_5MA (0x00) +#define LP5814_MAX_CURRENT_51MA (0x01) + +// -------------------------------------------- +// ENABLE CONTROL REGISTER +// -------------------------------------------- + +#define LP5814_OUT0_EN_MASK (0x01) +#define LP5814_OUT1_EN_MASK (0x02) +#define LP5814_OUT2_EN_MASK (0x04) +#define LP5814_OUT3_EN_MASK (0x08) +#define LP5814_OUTX_EN_SHIFT(ch) (ch) + +// -------------------------------------------- +// LED CHANNEL BIT MASK +// -------------------------------------------- + +#define LP5814_LED0 (1 << 0) // Bit 0: LED0 channel +#define LP5814_LED1 (1 << 1) // Bit 1: LED1 channel +#define LP5814_LED2 (1 << 2) // Bit 2: LED2 channel +#define LP5814_LED3 (1 << 3) // Bit 3: LED3 channel +#define LP5814_LED_ALL (LP5814_LED0 | LP5814_LED1 | LP5814_LED2 | LP5814_LED3) // All channels + +// -------------------------------------------- +// MAX CURRENT MODE +// -------------------------------------------- + +typedef enum { + LP5814_MAX_CURRENT_25_5_MA = LP5814_MAX_CURRENT_25_5MA, + LP5814_MAX_CURRENT_51_MA = LP5814_MAX_CURRENT_51MA +} lp5814_max_current_t; + +// -------------------------------------------- +// DEVICE HANDLE STRUCTURE +// -------------------------------------------- + +typedef struct lp5814_handle_t { + i2c_master_dev_handle_t i2c_dev; + uint8_t dev_addr; + bool initialized; +} lp5814_handle_t; + +// -------------------------------------------- +// PUBLIC API FUNCTIONS +// -------------------------------------------- + +/** + * @brief Initialize the LP5814 LED driver. + * + * This function initializes the LP5814 device by setting up the I2C device + * handle, configuring the device address, and performing initial chip enable. + * + * @param i2c_bus Pointer to the I2C master bus handle. + * @param dev_addr I2C device address (7-bit, typically 0x2C). + * @param handle Pointer to store the LP5814 handle. + * + * @return + * - ESP_OK on success. + * - ESP_ERR_INVALID_ARG if any parameter is NULL. + * - ESP_FAIL if I2C communication fails. + */ +esp_err_t lp5814_init(i2c_master_bus_handle_t *i2c_bus, uint8_t dev_addr, lp5814_handle_t *handle); + +/** + * @brief Deinitialize the LP5814 LED driver. + * + * This function disables the chip and frees resources. + * + * @param handle Pointer to the LP5814 handle. + * + * @return + * - ESP_OK on success. + * - ESP_ERR_INVALID_ARG if handle is NULL. + */ +esp_err_t lp5814_deinit(lp5814_handle_t *handle); + +/** + * @brief Enable or disable the LP5814 chip. + * + * When disabled, the chip enters low-power standby mode. + * + * @param handle Pointer to the LP5814 handle. + * @param enable true to enable, false to disable. + * + * @return + * - ESP_OK on success. + * - ESP_ERR_INVALID_ARG if handle is NULL. + * - ESP_FAIL if I2C communication fails. + */ +esp_err_t lp5814_set_chip_enable(lp5814_handle_t *handle, bool enable); + +/** + * @brief Set the maximum current mode for all LEDs. + * + * The LP5814 supports two current ranges: + * - 25.5mA max: 0.1mA to 25.5mA in 256 steps + * - 51mA max: 0.2mA to 51mA in 256 steps + * + * @param handle Pointer to the LP5814 handle. + * @param mode Maximum current mode. + * + * @return + * - ESP_OK on success. + * - ESP_ERR_INVALID_ARG if handle is NULL. + * - ESP_FAIL if I2C communication fails. + */ +esp_err_t lp5814_set_max_current(lp5814_handle_t *handle, lp5814_max_current_t mode); + +/** + * @brief Enable or disable LED output channels. + * + * Each LED channel can be individually enabled or disabled. + * Multiple channels can be specified using bitwise OR (e.g., LP5814_LED0 | LP5814_LED2). + * + * @param handle Pointer to the LP5814 handle. + * @param led_mask LED channel mask (LP5814_LED0 | LP5814_LED1 | LP5814_LED2 | LP5814_LED3). + * @param enable true to enable, false to disable. + * + * @return + * - ESP_OK on success. + * - ESP_ERR_INVALID_ARG if handle is NULL or led_mask is invalid. + * - ESP_FAIL if I2C communication fails. + */ +esp_err_t lp5814_set_led_enable(lp5814_handle_t *handle, uint32_t led_mask, bool enable); + +/** + * @brief + * + * @param handle + * @param mode + * + * @return + * - ESP_OK on success. + * - ESP_ERR_INVALID_ARG if handle is NULL or led_mask is invalid. + * - ESP_FAIL if I2C communication fails. + */ +esp_err_t lp5814_set_led_dim_mode(lp5814_handle_t *handle, uint8_t mode); + +/** + * @brief + * + * @param handle + * @param mode + * + * @return + * - ESP_OK on success. + * - ESP_ERR_INVALID_ARG if handle is NULL or led_mask is invalid. + * - ESP_FAIL if I2C communication fails. + */ +esp_err_t lp5814_set_led_engine_mode(lp5814_handle_t *handle, uint8_t mode); + +/** + * @brief + * + * @param handle + * @param mode + * + * @return + * - ESP_OK on success. + * - ESP_ERR_INVALID_ARG if handle is NULL or led_mask is invalid. + * - ESP_FAIL if I2C communication fails. + */ +esp_err_t lp5814_set_led_update_param(lp5814_handle_t *handle); + +/** + * @brief Set dot current (analog brightness) for specific LEDs. + * + * The dot current is an 8-bit value (0-255) that controls LED current + * in combination with the max current mode. This provides analog dimming. + * Multiple channels can be specified using bitwise OR. + * + * @param handle Pointer to the LP5814 handle. + * @param led_mask LED channel mask (LP5814_LED0 | LP5814_LED1 | LP5814_LED2 | LP5814_LED3). + * @param dc Dot current value (0-255). + * + * @return + * - ESP_OK on success. + * - ESP_ERR_INVALID_ARG if handle is NULL, led_mask is invalid, or dc > 255. + * - ESP_FAIL if I2C communication fails. + */ +esp_err_t lp5814_set_led_dc(lp5814_handle_t *handle, uint32_t led_mask, uint8_t dc); + +/** + * @brief Set PWM duty cycle for specific LEDs. + * + * The PWM is an 8-bit value (0-255) that controls LED brightness + * through pulse-width modulation. 0 = 0% duty cycle, 255 = 100% duty cycle. + * Multiple channels can be specified using bitwise OR. + * + * @param handle Pointer to the LP5814 handle. + * @param led_mask LED channel mask (LP5814_LED0 | LP5814_LED1 | LP5814_LED2 | LP5814_LED3). + * @param pwm PWM value (0-255). + * + * @return + * - ESP_OK on success. + * - ESP_ERR_INVALID_ARG if handle is NULL, led_mask is invalid, or pwm > 255. + * - ESP_FAIL if I2C communication fails. + */ +esp_err_t lp5814_set_led_pwm(lp5814_handle_t *handle, uint32_t led_mask, uint8_t pwm); + +/** + * @brief Set both DC and PWM for specific LEDs in one call. + * + * This is a convenience function that sets both the dot current and + * PWM value for LED channels. Multiple channels can be specified using bitwise OR. + * + * @param handle Pointer to the LP5814 handle. + * @param led_mask LED channel mask (LP5814_LED0 | LP5814_LED1 | LP5814_LED2 | LP5814_LED3). + * @param dc Dot current value (0-255). + * @param pwm PWM value (0-255). + * + * @return + * - ESP_OK on success. + * - ESP_ERR_INVALID_ARG if handle is NULL, led_mask is invalid, or values > 255. + * - ESP_FAIL if I2C communication fails. + */ +esp_err_t lp5814_set_led_dc_pwm(lp5814_handle_t *handle, uint32_t led_mask, uint8_t dc, uint8_t pwm); + +/** + * @brief Calculate the actual LED current in mA. + * + * This helper function calculates the actual LED current based on + * the max current mode and dot current value. + * + * @param mode Maximum current mode. + * @param dc Dot current value (0-255). + * + * @return LED current in mA. + */ +float lp5814_calculate_current(lp5814_max_current_t mode, uint8_t dc); + +#ifdef __cplusplus +} +#endif + +#endif // LP5814_H \ No newline at end of file diff --git a/include/graphics/LGFX/LGFX_WIO_TRACKER_L2.h b/include/graphics/LGFX/LGFX_WIO_TRACKER_L2.h new file mode 100644 index 00000000..3192be28 --- /dev/null +++ b/include/graphics/LGFX/LGFX_WIO_TRACKER_L2.h @@ -0,0 +1,213 @@ +#ifndef LGFX_PICO_H +#define LGFX_PICO_H + +#define LGFX_USE_V1 +#include "Wire.h" +#include "util/ILog.h" +#include + +#ifndef SPI_FREQUENCY +#define SPI_FREQUENCY 75000000 +#endif + +#define LP5814_I2C_ADDR_DEFAULT 0x2c +#define TCA9535_I2C_ADDR_DEFAULT 0x21 + +class Wio_Tracker_Light : public lgfx::v1::ILight +{ + // LP5814 register addresses + static constexpr uint8_t REG_DEVICE_CONFIG0 = 0x00; + static constexpr uint8_t REG_MAX_CURRENT = 0x01; + static constexpr uint8_t REG_ENABLE_CONTROL = 0x02; + static constexpr uint8_t REG_DIM_MODE = 0x04; + static constexpr uint8_t REG_ENGINE_MODE = 0x05; + static constexpr uint8_t REG_UPDATE = 0x0F; + static constexpr uint8_t REG_LED0_DC = 0x14; + static constexpr uint8_t REG_LED0_PWM = 0x18; + + public: + struct config_t { + uint8_t brightness = 153; // 60% + }; + + const config_t &config(void) const { return _cfg; } + void config(const config_t &cfg) { _cfg = cfg; } + + bool init(uint8_t brightness) override + { + // Probe device + Wire.beginTransmission(LP5814_I2C_ADDR_DEFAULT); + if (Wire.endTransmission() != 0) { + ILOG_ERROR("LP5814 not found at 0x%02x", LP5814_I2C_ADDR_DEFAULT); + return false; + } + + writeReg(REG_DEVICE_CONFIG0, 0x01); // chip enable + writeReg(REG_MAX_CURRENT, 0x01); // 51 mA max current + writeReg(REG_ENABLE_CONTROL, 0x00); // disable outputs while configuring + writeReg(REG_DIM_MODE, 0x4E); // dim mode config + writeReg(REG_ENGINE_MODE, 0xF0); // engine mode config + // Set DC current for all 4 channels (registers 0x14..0x17) + for (uint8_t i = 0; i < 4; i++) { + writeReg(REG_LED0_DC + i, 200); + } + writeReg(REG_ENABLE_CONTROL, 0x0F); // enable all 4 channels + writeReg(REG_UPDATE, 0x55); // latch parameters (LP5814 requires 0x55) + delay(5); // LP5814 engine startup settling time + + setBrightness(brightness); + return true; + } + + void setBrightness(uint8_t brightness) override + { + // Write PWM to all 4 channels (registers 0x18..0x1B). + // No UPDATE write needed: after the 0x55 latch in init(), PWM writes take effect immediately. + for (uint8_t i = 0; i < 4; i++) { + writeReg(REG_LED0_PWM + i, brightness); + } + _cfg.brightness = brightness; + } + + uint8_t getBrightness(void) const { return _cfg.brightness; } + + virtual ~Wio_Tracker_Light(void) = default; + + private: + void writeReg(uint8_t reg, uint8_t value) + { + Wire.beginTransmission(LP5814_I2C_ADDR_DEFAULT); + Wire.write(reg); + Wire.write(value); + uint8_t error = Wire.endTransmission(); + if (error != 0) + ILOG_ERROR("LP5814 write reg 0x%02x failed: %d", reg, error); + } + + config_t _cfg; +}; + +class LGFX_WIO_TRACKER_L2 : public lgfx::LGFX_Device +{ + lgfx::Panel_NV3031B _panel_instance; + lgfx::Bus_SPI _bus_instance; + lgfx::Touch_GT911 _touch_instance; + Wio_Tracker_Light _light_instance; + + public: + const uint32_t screenWidth = 320; + const uint32_t screenHeight = 240; + + bool hasButton(void) { return true; } + + bool init_impl(bool use_reset, bool use_clear) override + { + // Initialize LP5814 before GT911 touch driver runs (bus is clean here). + _light_instance.init(_light_instance.config().brightness); + + bool result = LGFX_Device::init_impl(use_reset, use_clear); + + // GT911::init() may leave the ESP32 I2C peripheral with BUSY flag stuck + // (NACK during GT911 probe clears the TX but not the hardware BUSY state). + // Wire.end() + Wire.begin() resets the peripheral so LP5814 setBrightness() + // called by init_lgfx() immediately after us doesn't timeout. + Wire.end(); + Wire.begin(47, 48); + + return result; + } + + lgfx::ILight *light(void) const + { + // pointer is used by LGFXDriver to check for hasLight() + return (lgfx::ILight *)&_light_instance; + } + + LGFX_WIO_TRACKER_L2(void) + { + { + auto cfg = _bus_instance.config(); + + // SPI + cfg.spi_host = SPI3_HOST; + cfg.spi_mode = 3; + cfg.freq_write = SPI_FREQUENCY; // SPI clock for transmission (up to 80MHz, rounded to + // the value obtained by dividing 80MHz by an integer) + cfg.freq_read = 16000000; // SPI clock when receiving + cfg.pin_sclk = 42; // Set SPI SCLK pin number + // quad spi pins + cfg.pin_io0 = 41; + cfg.pin_io1 = 40; + cfg.pin_io2 = 39; + cfg.pin_io3 = 38; + + _bus_instance.config(cfg); // applies the set value to the bus. + _panel_instance.setBus(&_bus_instance); // set the bus on the panel. + } + + { // Set the display panel control. + auto cfg = _panel_instance.config(); // Gets a structure for display panel settings. + + cfg.pin_cs = 46; // Pin number where CS is connected (-1 = disable) + cfg.pin_rst = -1; // Pin number where RST is connected (-1 = disable) + cfg.pin_busy = -1; // Pin number where BUSY is connected (-1 = disable) + + cfg.panel_width = screenHeight; // actual displayable width + cfg.panel_height = screenWidth; // actual displayable height + cfg.memory_width = screenHeight; + cfg.memory_height = screenWidth; + cfg.offset_x = 0; // Panel offset amount in X direction + cfg.offset_y = 0; // Panel offset amount in Y direction + cfg.offset_rotation = 1; // Rotation direction value offset 0~7 (4~7 is + // upside down) + cfg.invert = true; // Set to true if the light/darkness of the panel is reversed + cfg.rgb_order = true; // Set to true if the panel's red and blue are swapped + cfg.dlen_16bit = false; // Set to true for panels that transmit data length in 16-bit + // units with 16-bit parallel or SPI + cfg.bus_shared = true; // If the bus is shared with the SD card, set to + // true (bus control with drawJpgFile etc.) + + _panel_instance.config(cfg); + } + + // Configure settings for touch screen control. + { + auto cfg = _touch_instance.config(); + + cfg.pin_cs = -1; + cfg.x_min = 0; + cfg.x_max = screenHeight - 1; + cfg.y_min = 0; + cfg.y_max = screenWidth - 1; + cfg.pin_int = -1; + cfg.offset_rotation = 2; + + cfg.i2c_port = 0; + cfg.i2c_addr = 0x5D; + cfg.pin_sda = 47; + cfg.pin_scl = 48; + cfg.bus_shared = true; + cfg.freq = 400000; + + _touch_instance.config(cfg); + _panel_instance.setTouch(&_touch_instance); + } + + _panel_instance.setLight(&_light_instance); // Attach LP5814 backlight driver + setPanel(&_panel_instance); // Sets the panel to use. + } + + void sleep(void) + { + _panel->setSleep(true); + _light_instance.setBrightness(0); + } + + void wakeup(void) + { + _panel->setSleep(false); + _light_instance.setBrightness(_light_instance.config().brightness); + } +}; + +#endif \ No newline at end of file diff --git a/include/graphics/driver/DisplayDriverConfig.h b/include/graphics/driver/DisplayDriverConfig.h index 4af5d3eb..8af39d46 100644 --- a/include/graphics/driver/DisplayDriverConfig.h +++ b/include/graphics/driver/DisplayDriverConfig.h @@ -40,7 +40,8 @@ class DisplayDriverConfig ESPJC4827W543C, ESP4848S040, MAKERFABS480X480, - HELTECV4_TFT + HELTECV4_TFT, + WIO_TRACKER_L2 }; struct panel_config_t { diff --git a/source/graphics/driver/DisplayDriverFactory.cpp b/source/graphics/driver/DisplayDriverFactory.cpp index cdf5f33c..0675fb15 100644 --- a/source/graphics/driver/DisplayDriverFactory.cpp +++ b/source/graphics/driver/DisplayDriverFactory.cpp @@ -75,6 +75,9 @@ #ifdef HELTEC_VISION_MASTER_T190 #include "graphics/LGFX/LGFX_VISION_MASTER_T190.h" #endif +#ifdef SEEED_WIO_TRACKER_L2 +#include "graphics/LGFX/LGFX_WIO_TRACKER_L2.h" +#endif #ifdef NODEMCU_32S #include "graphics/LGFX/LGFX_ESPILI9341XPT2046.h" #endif @@ -205,6 +208,10 @@ DisplayDriver *DisplayDriverFactory::create(const DisplayDriverConfig &cfg) case DisplayDriverConfig::device_t::HELTECV4_TFT: return new LGFXDriver(cfg.width(), cfg.height()); break; +#elif defined(SEEED_WIO_TRACKER_L2) + case DisplayDriverConfig::device_t::WIO_TRACKER_L2: + return new LGFXDriver(cfg.width(), cfg.height()); + break; #endif #elif defined(USE_FRAMEBUFFER) case DisplayDriverConfig::device_t::FB: From 8ac49ddc6564d27c4b607b74550cbb2c52e3d7d5 Mon Sep 17 00:00:00 2001 From: Manuel <71137295+mverch67@users.noreply.github.com> Date: Sun, 31 May 2026 01:05:56 +0200 Subject: [PATCH 2/4] generic qspi support --- include/graphics/LGFX/LGFX_GENERIC.h | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/include/graphics/LGFX/LGFX_GENERIC.h b/include/graphics/LGFX/LGFX_GENERIC.h index a9b5e942..d80c84b3 100644 --- a/include/graphics/LGFX/LGFX_GENERIC.h +++ b/include/graphics/LGFX/LGFX_GENERIC.h @@ -44,6 +44,10 @@ #define LGFX_SPI_3WIRE false #endif +#ifndef LGFX_SPI_MODE +#define LGFX_SPI_MODE 0 +#endif + #ifndef LGFX_PIN_SCK #define LGFX_PIN_SCK -1 #endif @@ -60,6 +64,22 @@ #define LGFX_PIN_DC -1 #endif +#ifndef LGFX_PIN_IO0 +#define LGFX_PIN_IO0 -1 +#endif + +#ifndef LGFX_PIN_IO1 +#define LGFX_PIN_IO1 -1 +#endif + +#ifndef LGFX_PIN_IO2 +#define LGFX_PIN_IO2 -1 +#endif + +#ifndef LGFX_PIN_IO3 +#define LGFX_PIN_IO3 -1 +#endif + #ifndef LGFX_PIN_CS #define LGFX_PIN_CS -1 #endif @@ -217,7 +237,7 @@ class LGFX_GENERIC : public lgfx::LGFX_Device // SPI cfg.spi_host = LGFX_CFG_HOST; - cfg.spi_mode = 0; + cfg.spi_mode = LGFX_SPI_MODE; cfg.freq_write = SPI_FREQUENCY; // SPI clock for transmission (up to 80MHz, rounded to // the value obtained by dividing 80MHz by an integer) cfg.freq_read = 16000000; // SPI clock when receiving @@ -231,6 +251,12 @@ class LGFX_GENERIC : public lgfx::LGFX_Device cfg.pin_miso = LGFX_PIN_MISO; // Set SPI MISO pin number (-1 = disable) cfg.pin_dc = LGFX_PIN_DC; // Set SPI DC pin number (-1 = disable) + // quad spi pins (only used if all 4 are defined) + cfg.pin_io0 = LGFX_PIN_IO0; // Set Quad SPI IO0 pin number + cfg.pin_io1 = LGFX_PIN_IO1; // Set Quad SPI IO1 pin number + cfg.pin_io2 = LGFX_PIN_IO2; // Set Quad SPI IO2 pin number + cfg.pin_io3 = LGFX_PIN_IO3; // Set Quad SPI IO3 pin number + _bus_instance.config(cfg); // applies the set value to the bus. _panel_instance.setBus(&_bus_instance); // set the bus on the panel. } From 109cec2dc868b3866b99d219653982b4af51fe20 Mon Sep 17 00:00:00 2001 From: Manuel <71137295+mverch67@users.noreply.github.com> Date: Sun, 31 May 2026 01:06:10 +0200 Subject: [PATCH 3/4] support esp_log --- include/util/ILog.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/include/util/ILog.h b/include/util/ILog.h index 4efb109b..ba78125d 100644 --- a/include/util/ILog.h +++ b/include/util/ILog.h @@ -31,6 +31,17 @@ class ILog static ILog *_logger; }; +#elif defined(USE_ESP_LOG) + +// alternative approach to use esp_log directly +#include "esp_log.h" +#define ILOG_DEBUG(...) ESP_LOGD("DeviceUI", __VA_ARGS__) +#define ILOG_INFO(...) ESP_LOGI("DeviceUI", __VA_ARGS__) +#define ILOG_WARN(...) ESP_LOGW("DeviceUI", __VA_ARGS__) +#define ILOG_ERROR(...) ESP_LOGE("DeviceUI", __VA_ARGS__) +#define ILOG_CRIT(...) ESP_LOGE("DeviceUI", __VA_ARGS__) +#define ILOG_TRACE(...) ESP_LOGD("DeviceUI", __VA_ARGS__) + #elif defined(USE_LOG_DEBUG) // alternative approach to directly use LOG_DEBUG macros From c23fdc22f2ba949829661fdf851846fcf7433d89 Mon Sep 17 00:00:00 2001 From: Manuel <71137295+mverch67@users.noreply.github.com> Date: Sun, 31 May 2026 22:53:50 +0200 Subject: [PATCH 4/4] fix I2C errors by not relying on low level mutex but SPILock --- include/graphics/LGFX/LGFX_WIO_TRACKER_L2.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/graphics/LGFX/LGFX_WIO_TRACKER_L2.h b/include/graphics/LGFX/LGFX_WIO_TRACKER_L2.h index 3192be28..77614bae 100644 --- a/include/graphics/LGFX/LGFX_WIO_TRACKER_L2.h +++ b/include/graphics/LGFX/LGFX_WIO_TRACKER_L2.h @@ -164,7 +164,7 @@ class LGFX_WIO_TRACKER_L2 : public lgfx::LGFX_Device cfg.rgb_order = true; // Set to true if the panel's red and blue are swapped cfg.dlen_16bit = false; // Set to true for panels that transmit data length in 16-bit // units with 16-bit parallel or SPI - cfg.bus_shared = true; // If the bus is shared with the SD card, set to + cfg.bus_shared = false; // If the bus is shared with the SD card, set to // true (bus control with drawJpgFile etc.) _panel_instance.config(cfg); @@ -186,7 +186,7 @@ class LGFX_WIO_TRACKER_L2 : public lgfx::LGFX_Device cfg.i2c_addr = 0x5D; cfg.pin_sda = 47; cfg.pin_scl = 48; - cfg.bus_shared = true; + cfg.bus_shared = false; cfg.freq = 400000; _touch_instance.config(cfg);