New kernel drivers and more (#560)

- Added kernel base drivers for: display, pointer (touch, mouse, etc.), keyboard, adc, power supply and backlight
- Implement new kernel driver modules: `st7789-module` and `gt911-module`
- Implement ESP32 ADC "oneshot" kernel driver
- Implement ESP32  backlight kernel driver with ledc API
- Implemented `battery-sense` driver that allows for voltage measurement and creates a power supply child device
- Updated github actions
- Updated flash scripts
- Fix for esp32 legacy driver conflict with ADC
- Created separate `lilygo-tdeck` and `lilygo-tdeck-plus` devices
- Created `lilygo-module` with LilyGO kernel drivers
- Fix for intermittent errors in build related to code generation of `devicetree.c`
- `lvgl-module` can now map kernel drivers onto LVGL devices
- Created `KernelDisplayApp` as a mirror of `HalDisplayApp` (formerly `DisplayApp`)
- Removed `struct` and `enum` prefix in a lot of kernel driver cpp source files
- `lilygo-tdeck` and `lilygo-tdeck-plus` are now fully relying on kernel drivers and don't use any of the old HAL
This commit is contained in:
Ken Van Hoeylandt
2026-07-12 00:29:12 +02:00
committed by GitHub
parent c4406b24ba
commit 50c0a14a93
149 changed files with 5274 additions and 1266 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ idf_component_register(
SRCS ${SOURCES}
INCLUDE_DIRS "include/"
PRIV_INCLUDE_DIRS "private/"
REQUIRES TactilityKernel driver esp_driver_i2c vfs fatfs esp_wifi esp_netif esp_event
REQUIRES TactilityKernel driver esp_adc esp_driver_i2c vfs fatfs esp_wifi esp_netif esp_event
)
idf_component_optional_requires(PRIVATE bt usb espressif__usb_host_hid espressif__usb_host_msc)
@@ -0,0 +1,30 @@
description: ESP32 ADC Controller (oneshot mode)
include: ["adc-controller.yaml"]
compatible: "espressif,esp32-adc-oneshot"
properties:
unit-id:
type: int
required: true
description: |
The ADC unit, defined by adc_unit_t (e.g. ADC_UNIT_1, ADC_UNIT_2).
clk-src:
type: int
default: 0
description: |
Clock source, defined by adc_oneshot_clk_src_t. 0 selects the default clock source.
ulp-mode:
type: int
default: 0
description: |
ULP control mode, defined by adc_ulp_mode_t. 0 means ADC_ULP_MODE_DISABLE.
channels:
type: phandle-array
element-type: "struct Esp32AdcOneshotChannelConfig"
default: "{ }"
description: |
Per-channel configuration. Each entry is written as <channel atten bitwidth>,
e.g. <ADC_CHANNEL_3 ADC_ATTEN_DB_12 ADC_BITWIDTH_DEFAULT>.
A consumer device refers to a channel with a phandle and channel number, e.g. `<&adc0 3>`.
@@ -0,0 +1,29 @@
description: ESP32 LEDC-backed PWM backlight
compatible: "espressif,esp32-ledc-backlight"
properties:
pin-backlight:
type: phandles
required: true
description: Backlight PWM output pin
frequency-hz:
type: int
default: 30000
description: PWM frequency in Hz
brightness-level-range:
type: values
default: [0, 255]
description: Inclusive [min,max] brightness range. The minimum value turns the backlight off.
brightness-default:
type: int
default: 200
description: Default brightness level, applied by set_brightness_default(). Should fall within brightness-level-range.
ledc-timer:
type: int
default: 0
description: LEDC timer index, defined by ledc_timer_t
ledc-channel:
type: int
default: 0
description: LEDC channel index, defined by ledc_channel_t
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <tactility/bindings/bindings.h>
#include <tactility/drivers/esp32_adc_oneshot.h>
#ifdef __cplusplus
extern "C" {
#endif
DEFINE_DEVICETREE(esp32_adc_oneshot, struct Esp32AdcOneshotConfig)
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <tactility/bindings/bindings.h>
#include <tactility/drivers/esp32_ledc_backlight.h>
#ifdef __cplusplus
extern "C" {
#endif
DEFINE_DEVICETREE(esp32_ledc_backlight, struct Esp32LedcBacklightConfig)
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <tactility/device.h>
#include <esp_adc/adc_oneshot.h>
#include <hal/adc_types.h>
#ifdef __cplusplus
extern "C" {
#endif
struct Esp32AdcOneshotChannelConfig {
adc_channel_t channel;
adc_atten_t atten;
adc_bitwidth_t bitwidth;
};
struct Esp32AdcOneshotConfig {
adc_unit_t unit_id;
adc_oneshot_clk_src_t clk_src;
adc_ulp_mode_t ulp_mode;
/** Per-channel configuration */
const struct Esp32AdcOneshotChannelConfig* channels;
/** The item count of channels */
size_t channel_count;
};
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <driver/ledc.h>
#include <tactility/drivers/backlight.h>
#include <tactility/drivers/gpio.h>
#ifdef __cplusplus
extern "C" {
#endif
struct Esp32LedcBacklightConfig {
struct GpioPinSpec pin_backlight;
uint32_t frequency_hz;
struct BrightnessLevelRange brightness_range;
uint8_t brightness_default;
ledc_timer_t ledc_timer;
ledc_channel_t ledc_channel;
};
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,102 @@
// SPDX-License-Identifier: Apache-2.0
#include <esp_adc/adc_oneshot.h>
#include <tactility/error_esp32.h>
#include <tactility/driver.h>
#include <tactility/drivers/adc_controller.h>
#include <tactility/drivers/esp32_adc_oneshot.h>
#include <tactility/log.h>
#include <tactility/time.h>
#define TAG "esp32_adc_oneshot"
#define GET_CONFIG(device) ((Esp32AdcOneshotConfig*)device->config)
#define GET_HANDLE(device) ((adc_oneshot_unit_handle_t)device_get_driver_data(device))
static const Esp32AdcOneshotChannelConfig* find_channel_config(const Esp32AdcOneshotConfig* dts_config, uint8_t channel_index) {
if (channel_index >= dts_config->channel_count) {
return nullptr;
}
return &dts_config->channels[channel_index];
}
extern "C" {
static error_t read_raw(Device* device, uint8_t channel, int* out_raw, TickType_t timeout) {
if (xPortInIsrContext()) return ERROR_ISR_STATUS;
auto* dts_config = GET_CONFIG(device);
auto* channel_config = find_channel_config(dts_config, channel);
if (channel_config == nullptr) {
return ERROR_OUT_OF_RANGE;
}
esp_err_t esp_error = adc_oneshot_read(GET_HANDLE(device), channel_config->channel, out_raw);
if (esp_error != ESP_OK) {
LOG_E(TAG, "read(channel=%u) failed: %s", channel, esp_err_to_name(esp_error));
}
return esp_err_to_error(esp_error);
}
static error_t start(Device* device) {
LOG_I(TAG, "start %s", device->name);
auto* dts_config = GET_CONFIG(device);
adc_oneshot_unit_init_cfg_t init_config = {
.unit_id = dts_config->unit_id,
.clk_src = dts_config->clk_src,
.ulp_mode = dts_config->ulp_mode,
};
adc_oneshot_unit_handle_t handle;
esp_err_t esp_error = adc_oneshot_new_unit(&init_config, &handle);
if (esp_error != ESP_OK) {
LOG_E(TAG, "Failed to create ADC unit %d: %s", (int)dts_config->unit_id, esp_err_to_name(esp_error));
return ERROR_RESOURCE;
}
for (size_t i = 0; i < dts_config->channel_count; i++) {
const auto& channel_config = dts_config->channels[i];
adc_oneshot_chan_cfg_t chan_cfg = {
.atten = channel_config.atten,
.bitwidth = channel_config.bitwidth,
};
esp_error = adc_oneshot_config_channel(handle, channel_config.channel, &chan_cfg);
if (esp_error != ESP_OK) {
LOG_E(TAG, "Failed to configure channel %d: %s", (int)channel_config.channel, esp_err_to_name(esp_error));
adc_oneshot_del_unit(handle);
return ERROR_RESOURCE;
}
}
device_set_driver_data(device, handle);
return ERROR_NONE;
}
static error_t stop(Device* device) {
LOG_I(TAG, "stop %s", device->name);
esp_err_t esp_error = adc_oneshot_del_unit(GET_HANDLE(device));
if (esp_error != ESP_OK) {
LOG_E(TAG, "Deleting ADC unit failed: %s", esp_err_to_name(esp_error));
}
device_set_driver_data(device, nullptr);
return ERROR_NONE;
}
static constexpr AdcControllerApi ESP32_ADC_ONESHOT_API = {
.read_raw = read_raw
};
extern Module platform_esp32_module;
Driver esp32_adc_oneshot_driver = {
.name = "esp32_adc_oneshot",
.compatible = (const char*[]) { "espressif,esp32-adc-oneshot", nullptr },
.start_device = start,
.stop_device = stop,
.api = &ESP32_ADC_ONESHOT_API,
.device_type = &ADC_CONTROLLER_TYPE,
.owner = &platform_esp32_module,
.internal = nullptr
};
} // extern "C"
@@ -0,0 +1,139 @@
// SPDX-License-Identifier: Apache-2.0
#include <tactility/drivers/esp32_ledc_backlight.h>
#include <tactility/device.h>
#include <tactility/driver.h>
#include <tactility/drivers/backlight.h>
#include <tactility/log.h>
#include <driver/ledc.h>
#include <esp_err.h>
#include <cstdlib>
#define TAG "Esp32LedcBacklight"
#define GET_CONFIG(device) (static_cast<const Esp32LedcBacklightConfig*>((device)->config))
struct Esp32LedcBacklightInternal {
uint8_t brightness;
};
// region Driver lifecycle
static error_t start(Device* device) {
const auto* config = GET_CONFIG(device);
ledc_timer_config_t timer_config = {
.speed_mode = LEDC_LOW_SPEED_MODE,
.duty_resolution = LEDC_TIMER_8_BIT,
.timer_num = config->ledc_timer,
.freq_hz = config->frequency_hz,
.clk_cfg = LEDC_AUTO_CLK,
.deconfigure = false,
};
if (ledc_timer_config(&timer_config) != ESP_OK) {
LOG_E(TAG, "Failed to configure LEDC timer");
return ERROR_RESOURCE;
}
ledc_channel_config_t channel_config = {
.gpio_num = (int)config->pin_backlight.pin,
.speed_mode = LEDC_LOW_SPEED_MODE,
.channel = config->ledc_channel,
.intr_type = LEDC_INTR_DISABLE,
.timer_sel = config->ledc_timer,
.duty = config->brightness_range.min,
.hpoint = 0,
.sleep_mode = LEDC_SLEEP_MODE_NO_ALIVE_NO_PD,
.flags = {
.output_invert = 0,
},
};
if (ledc_channel_config(&channel_config) != ESP_OK) {
LOG_E(TAG, "Failed to configure LEDC channel");
return ERROR_RESOURCE;
}
auto* internal = static_cast<Esp32LedcBacklightInternal*>(malloc(sizeof(Esp32LedcBacklightInternal)));
if (internal == nullptr) {
return ERROR_OUT_OF_MEMORY;
}
internal->brightness = config->brightness_range.min;
device_set_driver_data(device, internal);
backlight_set_brightness_default(device); // Allowed to fail, we don't care about the result
return ERROR_NONE;
}
static error_t stop(Device* device) {
backlight_set_brightness(device, 0); // Allowed to fail, we don't care about the result
auto* internal = static_cast<Esp32LedcBacklightInternal*>(device_get_driver_data(device));
free(internal);
return ERROR_NONE;
}
// endregion
// region BacklightApi
static error_t esp32_ledc_backlight_set_brightness(Device* device, uint8_t brightness) {
const auto* config = GET_CONFIG(device);
auto* internal = static_cast<Esp32LedcBacklightInternal*>(device_get_driver_data(device));
esp_err_t ret = ledc_set_duty(LEDC_LOW_SPEED_MODE, config->ledc_channel, brightness);
if (ret == ESP_OK) {
ret = ledc_update_duty(LEDC_LOW_SPEED_MODE, config->ledc_channel);
}
if (ret != ESP_OK) {
LOG_E(TAG, "Failed to set brightness: %s", esp_err_to_name(ret));
return ERROR_RESOURCE;
}
internal->brightness = brightness;
return ERROR_NONE;
}
static error_t esp32_ledc_backlight_set_brightness_default(Device* device) {
return esp32_ledc_backlight_set_brightness(device, GET_CONFIG(device)->brightness_default);
}
static error_t esp32_ledc_backlight_get_brightness(Device* device, uint8_t* out_brightness) {
auto* internal = static_cast<Esp32LedcBacklightInternal*>(device_get_driver_data(device));
*out_brightness = internal->brightness;
return ERROR_NONE;
}
static uint8_t esp32_ledc_backlight_get_min_brightness(Device* device) {
return GET_CONFIG(device)->brightness_range.min;
}
static uint8_t esp32_ledc_backlight_get_max_brightness(Device* device) {
return GET_CONFIG(device)->brightness_range.max;
}
// endregion
static const BacklightApi esp32_ledc_backlight_api = {
.set_brightness = esp32_ledc_backlight_set_brightness,
.set_brightness_default = esp32_ledc_backlight_set_brightness_default,
.get_brightness = esp32_ledc_backlight_get_brightness,
.get_min_brightness = esp32_ledc_backlight_get_min_brightness,
.get_max_brightness = esp32_ledc_backlight_get_max_brightness,
};
extern Module platform_esp32_module;
Driver esp32_ledc_backlight_driver = {
.name = "esp32_ledc_backlight",
.compatible = (const char*[]) { "espressif,esp32-ledc-backlight", nullptr },
.start_device = start,
.stop_device = stop,
.api = &esp32_ledc_backlight_api,
.device_type = &BACKLIGHT_TYPE,
.owner = &platform_esp32_module,
.internal = nullptr
};
@@ -11,10 +11,12 @@
extern "C" {
extern Driver esp32_adc_oneshot_driver;
extern Driver esp32_gpio_driver;
extern Driver esp32_i2c_driver;
extern Driver esp32_i2c_master_driver;
extern Driver esp32_i2s_driver;
extern Driver esp32_ledc_backlight_driver;
#if SOC_SDMMC_HOST_SUPPORTED
extern Driver esp32_sdmmc_driver;
#endif
@@ -42,10 +44,12 @@ extern Driver esp32_usbhost_msc_driver;
static error_t start() {
/* We crash when construct fails, because if a single driver fails to construct,
* there is no guarantee that the previously constructed drivers can be destroyed */
check(driver_construct_add(&esp32_adc_oneshot_driver) == ERROR_NONE);
check(driver_construct_add(&esp32_gpio_driver) == ERROR_NONE);
check(driver_construct_add(&esp32_i2c_driver) == ERROR_NONE);
check(driver_construct_add(&esp32_i2c_master_driver) == ERROR_NONE);
check(driver_construct_add(&esp32_i2s_driver) == ERROR_NONE);
check(driver_construct_add(&esp32_ledc_backlight_driver) == ERROR_NONE);
#if SOC_SDMMC_HOST_SUPPORTED
check(driver_construct_add(&esp32_sdmmc_driver) == ERROR_NONE);
#endif
@@ -91,10 +95,12 @@ static error_t stop() {
check(driver_remove_destruct(&esp32_ble_serial_driver) == ERROR_NONE);
check(driver_remove_destruct(&esp32_bluetooth_driver) == ERROR_NONE);
#endif
check(driver_remove_destruct(&esp32_adc_oneshot_driver) == ERROR_NONE);
check(driver_remove_destruct(&esp32_gpio_driver) == ERROR_NONE);
check(driver_remove_destruct(&esp32_i2c_driver) == ERROR_NONE);
check(driver_remove_destruct(&esp32_i2c_master_driver) == ERROR_NONE);
check(driver_remove_destruct(&esp32_i2s_driver) == ERROR_NONE);
check(driver_remove_destruct(&esp32_ledc_backlight_driver) == ERROR_NONE);
#if SOC_SDMMC_HOST_SUPPORTED
check(driver_remove_destruct(&esp32_sdmmc_driver) == ERROR_NONE);
#endif