Tab5 power expander driver and devicetree parsing improvements (#507)

* **New Features**
  * PI4IOE5V6408 I2C I/O expander driver with public GPIO APIs
  * CLI tool to list devicetree dependencies

* **Device Tree Updates**
  * M5Stack Tab5 configured with two I2C IO expanders; PI4IOE5V6408 binding added

* **Build / Tooling**
  * Devicetree code generation integrated into build; generated artifacts and dynamic dependency resolution exposed

* **Refactor**
  * Kernel/run APIs updated to accept a null‑terminated devicetree modules array; many module symbols renamed

* **Documentation**
  * Added README and Apache‑2.0 license for new driver module
This commit is contained in:
Ken Van Hoeylandt 2026-02-17 22:59:30 +01:00 committed by GitHub
parent f0f764baff
commit d2048e01b6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
82 changed files with 749 additions and 253 deletions

View File

@ -0,0 +1,35 @@
import sys
import os
from source.printing import print_error
from source.config import parse_config
def print_help():
print("Usage: python dependencies.py [path]\n")
print("\t[in_file] the path where the root devicetree.yaml file is")
if __name__ == "__main__":
if "--help" in sys.argv:
print_help()
sys.exit()
args = [a for a in sys.argv[1:] if not a.startswith("--")]
if len(args) < 1:
print_error("Missing argument")
print_help()
sys.exit(1)
yaml_directory = args[0]
if not os.path.exists(yaml_directory):
print_error(f"Path not found: {yaml_directory}")
sys.exit(1)
config = parse_config(yaml_directory, os.getcwd())
# Device module is added first because it's started first:
# It creates the root device, so it must exist before its children.
device_dependency = os.path.basename(os.path.normpath(yaml_directory))
print(device_dependency)
for dependency in config.dependencies:
dependency_name = os.path.basename(os.path.normpath(dependency))
print(dependency_name)

View File

@ -212,7 +212,7 @@ def gather_devices(device: Device, output: list[Device]):
for child_device in device.devices:
gather_devices(child_device, output)
def generate_devicetree_c(filename: str, items: list[object], bindings: list[Binding], verbose: bool):
def generate_devicetree_c(filename: str, items: list[object], bindings: list[Binding], config, verbose: bool):
# Create a cache for looking up device names and aliases easily
# We still want to traverse it as a tree during code generation because of parent-setting
devices = list()
@ -225,6 +225,7 @@ def generate_devicetree_c(filename: str, items: list[object], bindings: list[Bin
// Default headers
#include <tactility/device.h>
#include <tactility/dts.h>
#include <tactility/module.h>
// DTS headers
'''))
@ -246,6 +247,25 @@ def generate_devicetree_c(filename: str, items: list[object], bindings: list[Bin
write_device_list_entry(file, item, bindings, verbose)
file.write("\tDTS_DEVICE_TERMINATOR\n")
file.write("};\n")
# Gather module symbols
module_symbol_names = []
for dependency in config.dependencies:
dependency_name = os.path.basename(os.path.normpath(dependency))
module_symbol_name = f"{dependency_name.replace('-', '_')}"
if not module_symbol_name.endswith("_module"):
module_symbol_name += "_module"
module_symbol_names.append(module_symbol_name)
file.write("\n")
# Forward declaration of symbol variables
for symbol in module_symbol_names:
file.write(f"extern struct Module {symbol};\n")
file.write("\n")
# Create array of symbol variables
file.write("struct Module* dts_modules[] = {\n")
for symbol in module_symbol_names:
file.write(f"\t&{symbol},\n")
file.write("\tNULL\n")
file.write("};\n")
def generate_devicetree_h(filename: str):
with open(filename, "w") as file:
@ -258,17 +278,21 @@ def generate_devicetree_h(filename: str):
extern "C" {
#endif
// Array of device tree modules terminated with DTS_MODULE_TERMINATOR
extern struct DtsDevice dts_devices[];
// Array of module symbols terminated with NULL
extern struct Module* dts_modules[];
#ifdef __cplusplus
}
#endif
'''))
def generate(output_path: str, items: list[object], bindings: list[Binding], verbose: bool):
def generate(output_path: str, items: list[object], bindings: list[Binding], config, verbose: bool):
if not os.path.exists(output_path):
os.makedirs(output_path)
devicetree_c_filename = os.path.join(output_path, "devicetree.c")
generate_devicetree_c(devicetree_c_filename, items, bindings, verbose)
generate_devicetree_c(devicetree_c_filename, items, bindings, config, verbose)
devicetree_h_filename = os.path.join(output_path, "devicetree.h")
generate_devicetree_h(devicetree_h_filename)

View File

@ -46,7 +46,7 @@ def main(config_path: str, output_path: str, verbose: bool) -> int:
if verbose:
for binding in bindings:
pprint(binding)
generate(output_path, transformed, bindings, verbose)
generate(output_path, transformed, bindings, config, verbose)
return 0
except DevicetreeException as caught:
print("\033[31mError: ", caught, "\033[0m")

View File

@ -99,9 +99,6 @@ if (NOT DEFINED ENV{ESP_IDF_VERSION})
set(FREERTOS_PORT GCC_POSIX CACHE STRING "")
add_subdirectory(Libraries/FreeRTOS-Kernel)
target_compile_definitions(freertos_kernel PUBLIC "projCOVERAGE_TEST=0")
target_include_directories(freertos_kernel
PUBLIC Devices/Simulator/Source # for FreeRTOSConfig.h
)
# EmbedTLS
set(ENABLE_TESTING OFF)

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module btt_panda_touch_module = {
.name = "btt-panda-touch",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module cyd_2432s024c_module = {
.name = "cyd-2432s024c",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module cyd_2432s028r_module = {
.name = "cyd-2432s028r",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module cyd_2432s028rv3_module = {
.name = "cyd-2432s028rv3",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module cyd_2432s032c_module = {
.name = "cyd-2432s032c",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module cyd_4848s040c_module = {
.name = "cyd-4848s040c",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module cyd_8048s043c_module = {
.name = "cyd-8048s043c",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module cyd_e32r28t_module = {
.name = "cyd-e32r28t",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module cyd_e32r32p_module = {
.name = "cyd-e32r32p",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module elecrow_crowpanel_advance_28_module = {
.name = "elecrow-crowpanel-advance-28",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module elecrow_crowpanel_advance_35_module = {
.name = "elecrow-crowpanel-advance-35",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module elecrow_crowpanel_advance_50_module = {
.name = "elecrow-crowpanel-advance-50",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module elecrow_crowpanel_basic_28_module = {
.name = "elecrow-crowpanel-basic-28",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module elecrow_crowpanel_basic_35_module = {
.name = "elecrow-crowpanel-basic-35",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module elecrow_crowpanel_basic_50_module = {
.name = "elecrow-crowpanel-basic-50",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module generic_esp32_module = {
.name = "generic-esp32",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module generic_esp32c6_module = {
.name = "generic-esp32c6",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module generic_esp32p4_module = {
.name = "generic-esp32p4",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module generic_esp32s3_module = {
.name = "generic-esp32s3",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module guition_jc1060p470ciwy_module = {
.name = "guition-jc1060p470ciwy",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module guition_jc2432w328c_module = {
.name = "guition-jc2432w328c",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module guition_jc3248w535c_module = {
.name = "guition-jc3248w535c",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module guition_jc8048w550c_module = {
.name = "guition-jc8048w550c",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module heltec_wifi_lora_32_v3_module = {
.name = "heltec-wifi-lora-32-v3",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module lilygo_tdeck_module = {
.name = "lilygo-tdeck",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module lilygo_tdisplay_s3_module = {
.name = "lilygo-tdisplay-s3",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module lilygo_tdisplay_module = {
.name = "lilygo-tdisplay",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module lilygo_tdongle_s3_module = {
.name = "lilygo-tdongle-s3",
.start = start,
.stop = stop,

View File

@ -20,8 +20,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module lilygo_tlora_pager_module = {
.name = "lilygo-tlora-pager",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module m5stack_cardputer_adv_module = {
.name = "m5stack-cardputer-adv",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module m5stack_cardputer_module = {
.name = "m5stack-cardputer",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module m5stack_core2_module = {
.name = "m5stack-core2",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module m5stack_cores3_module = {
.name = "m5stack-cores3",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module m5stack_papers3_module = {
.name = "m5stack-papers3",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module m5stack_stickc_plus_module = {
.name = "m5stack-stickc-plus",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module m5stack_stickc_plus2_module = {
.name = "m5stack-stickc-plus2",
.start = start,
.stop = stop,

View File

@ -3,5 +3,5 @@ file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
idf_component_register(
SRCS ${SOURCE_FILES}
INCLUDE_DIRS "Source"
REQUIRES Tactility esp_lvgl_port esp_lcd EspLcdCompat esp_lcd_ili9881c GT911 PwmBacklight driver vfs fatfs
REQUIRES Tactility esp_lvgl_port esp_lcd EspLcdCompat esp_lcd_ili9881c GT911 PwmBacklight driver vfs fatfs pi4ioe5v6408-module
)

View File

@ -6,6 +6,7 @@
#include <Tactility/hal/Configuration.h>
#include <Tactility/hal/i2c/I2c.h>
#include <drivers/pi4ioe5v6408.h>
using namespace tt::hal;
@ -18,9 +19,11 @@ static DeviceVector createDevices() {
};
}
static error_t initPower(::Device* i2c_controller) {
static error_t initPower(::Device* io_expander0, ::Device* io_expander1) {
constexpr TickType_t i2c_timeout = pdMS_TO_TICKS(10);
/*
PI4IOE5V6408-1 (0x43)
PI4IOE5V6408-0 (0x43)
- Bit 0: RF internal/external switch
- Bit 1: Speaker enable
- Bit 2: External 5V bus enable
@ -29,8 +32,18 @@ static error_t initPower(::Device* i2c_controller) {
- Bit 5: Touch reset
- Bit 6: Camera reset
- Bit 7: Headphone detect
*/
PI4IOE5V6408-2 (0x44)
check(pi4ioe5v6408_set_direction(io_expander0, 0b01111111, i2c_timeout) == ERROR_NONE);
check(pi4ioe5v6408_set_output_level(io_expander0, 0b01000110, i2c_timeout) == ERROR_NONE);
check(pi4ioe5v6408_set_output_high_impedance(io_expander0, 0b00000000, i2c_timeout) == ERROR_NONE);
check(pi4ioe5v6408_set_pull_select(io_expander0, 0b01111111, i2c_timeout) == ERROR_NONE);
check(pi4ioe5v6408_set_pull_enable(io_expander0, 0b01111111, i2c_timeout) == ERROR_NONE);
vTaskDelay(pdMS_TO_TICKS(10));
check(pi4ioe5v6408_set_output_level(io_expander0, 0b01110110, i2c_timeout) == ERROR_NONE);
/*
PI4IOE5V6408-1 (0x44)
- Bit 0: C6 WLAN enable
- Bit 1: /
- Bit 2: /
@ -41,57 +54,18 @@ static error_t initPower(::Device* i2c_controller) {
- Bit 7: IP2326: CHG_EN
*/
// Init byte arrays adapted from https://github.com/m5stack/M5GFX/blob/03565ccc96cb0b73c8b157f5ec3fbde439b034ad/src/M5GFX.cpp
static constexpr uint8_t reg_data_io1_1[] = {
0x03, 0b01111111, // PI4IO_REG_IO_DIR
0x05, 0b01000110, // PI4IO_REG_OUT_SET (bit4=LCD Reset, bit5=GT911 TouchReset -> LOW)
0x07, 0b00000000, // PI4IO_REG_OUT_H_IM
0x0D, 0b01111111, // PI4IO_REG_PULL_SEL
0x0B, 0b01111111, // PI4IO_REG_PULL_EN
};
static constexpr uint8_t reg_data_io1_2[] = {
0x05, 0b01110110, // PI4IO_REG_OUT_SET (bit4=LCD Reset, bit5=GT911 TouchReset -> HIGH)
};
static constexpr uint8_t reg_data_io2[] = {
0x03, 0b10111001, // PI4IO_REG_IO_DIR
0x07, 0b00000110, // PI4IO_REG_OUT_H_IM
0x0D, 0b10111001, // PI4IO_REG_PULL_SEL
0x0B, 0b11111001, // PI4IO_REG_PULL_EN
0x09, 0b01000000, // PI4IO_REG_IN_DEF_STA
0x11, 0b10111111, // PI4IO_REG_INT_MASK
0x05, 0b10001001, // PI4IO_REG_OUT_SET (enable WiFi, USB-A 5V and CHG_EN)
};
constexpr auto IO_EXPANDER1_ADDRESS = 0x43;
auto error = i2c_controller_write_register_array(i2c_controller, IO_EXPANDER1_ADDRESS, reg_data_io1_1, sizeof(reg_data_io1_1), pdMS_TO_TICKS(100));
if (error != ERROR_NONE) {
LOG_E(TAG, "IO expander 1 init failed in phase 1");
return ERROR_UNDEFINED;
}
constexpr auto IO_EXPANDER2_ADDRESS = 0x44;
error = i2c_controller_write_register_array(i2c_controller, IO_EXPANDER2_ADDRESS, reg_data_io2, sizeof(reg_data_io2), pdMS_TO_TICKS(100));
if (error != ERROR_NONE) {
LOG_E(TAG, "IO expander 2 init failed");
return ERROR_UNDEFINED;
}
// The M5Stack code applies this, but it's not known why
// TODO: Remove and test it extensively
tt::kernel::delayTicks(10);
error = i2c_controller_write_register_array(i2c_controller, IO_EXPANDER1_ADDRESS, reg_data_io1_2, sizeof(reg_data_io1_2), pdMS_TO_TICKS(100));
if (error != ERROR_NONE) {
LOG_E(TAG, "IO expander 1 init failed in phase 2");
return ERROR_UNDEFINED;
}
check(pi4ioe5v6408_set_direction(io_expander1, 0b10111001, i2c_timeout) == ERROR_NONE);
check(pi4ioe5v6408_set_output_high_impedance(io_expander1, 0b00000110, i2c_timeout) == ERROR_NONE);
check(pi4ioe5v6408_set_pull_select(io_expander1, 0b10111001, i2c_timeout) == ERROR_NONE);
check(pi4ioe5v6408_set_pull_enable(io_expander1, 0b11111001, i2c_timeout) == ERROR_NONE);
check(pi4ioe5v6408_set_input_default_level(io_expander1, 0b01000000, i2c_timeout) == ERROR_NONE);
check(pi4ioe5v6408_set_interrupt_mask(io_expander1, 0b10111111, i2c_timeout) == ERROR_NONE);
check(pi4ioe5v6408_set_output_level(io_expander1, 0b10001001, i2c_timeout) == ERROR_NONE);
return ERROR_NONE;
}
static error_t initSound(::Device* i2c_controller) {
static error_t initSound(::Device* i2c_controller, ::Device* io_expander0 = nullptr) {
// Init data from M5Unified:
// https://github.com/m5stack/M5Unified/blob/master/src/M5Unified.cpp
static constexpr uint8_t ES8388_I2C_ADDR = 0x10;
@ -139,13 +113,15 @@ static error_t initSound(::Device* i2c_controller) {
return error;
}
constexpr auto IO_EXPANDER1_ADDRESS = 0x43;
constexpr auto AMP_REGISTER = 0x05;
// Note: to disable the amplifier, reset the bits
error = i2c_controller_register8_set_bits(i2c_controller, IO_EXPANDER1_ADDRESS, AMP_REGISTER, 0b00000010, pdMS_TO_TICKS(100));
if (error != ERROR_NONE) {
uint8_t output_level = 0;
if (pi4ioe5v6408_get_output_level(io_expander0, &output_level, pdMS_TO_TICKS(100)) != ERROR_NONE) {
LOG_E(TAG, "Failed to read power level: %s", error_to_string(error));
return ERROR_RESOURCE;
}
if (pi4ioe5v6408_set_output_level(io_expander0, output_level | 0b00000010, pdMS_TO_TICKS(100)) != ERROR_NONE) {
LOG_E(TAG, "Failed to enable amplifier: %s", error_to_string(error));
return error;
return ERROR_RESOURCE;
}
return ERROR_NONE;
@ -155,12 +131,13 @@ static bool initBoot() {
auto* i2c0 = device_find_by_name("i2c0");
check(i2c0, "i2c0 not found");
auto error = initPower(i2c0);
if (error != ERROR_NONE) {
return false;
}
auto* io_expander0 = device_find_by_name("io_expander0");
auto* io_expander1 = device_find_by_name("io_expander1");
check(i2c0, "i2c0 not found");
error = initSound(i2c0);
initPower(io_expander0, io_expander1);
error_t error = initSound(i2c0, io_expander0);
if (error != ERROR_NONE) {
LOG_E(TAG, "Failed to enable ES8388");
}

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
Module m5stack_tab5_module = {
.name = "m5stack-tab5",
.start = start,
.stop = stop,

View File

@ -1,3 +1,4 @@
dependencies:
- Platforms/platform-esp32
- Drivers/pi4ioe5v6408-module
dts: m5stack,tab5.dts

View File

@ -5,6 +5,7 @@
#include <tactility/bindings/esp32_i2c.h>
#include <tactility/bindings/esp32_i2s.h>
#include <tactility/bindings/esp32_spi.h>
#include <bindings/pi4ioe5v6408.h>
/ {
compatible = "root";
@ -21,6 +22,36 @@
clock-frequency = <400000>;
pin-sda = <&gpio0 31 GPIO_FLAG_NONE>;
pin-scl = <&gpio0 32 GPIO_FLAG_NONE>;
/*
- Bit 0: RF internal/external switch
- Bit 1: Speaker enable
- Bit 2: External 5V bus enable
- Bit 3: /
- Bit 4: LCD reset
- Bit 5: Touch reset
- Bit 6: Camera reset
- Bit 7: Headphone detect
*/
io_expander0 {
compatible = "diodes,pi4ioe5v6408";
reg = <0x43>;
};
/*
- Bit 0: C6 WLAN enable
- Bit 1: /
- Bit 2: /
- Bit 3: USB-A 5V enable
- Bit 4: Device power: PWROFF_PLUSE
- Bit 5: IP2326: nCHG_QC_EN
- Bit 6: IP2326: CHG_STAT_LED
- Bit 7: IP2326: CHG_EN
*/
io_expander1 {
compatible = "diodes,pi4ioe5v6408";
reg = <0x44>;
};
};
i2c_port_a: i2c1 {

View File

@ -5,20 +5,20 @@ if (NOT DEFINED ENV{ESP_IDF_VERSION})
file(GLOB_RECURSE SOURCES "Source/*.c*")
file(GLOB_RECURSE HEADERS "Source/*.h*")
add_library(Simulator OBJECT)
add_library(simulator OBJECT)
target_sources(Simulator
target_sources(simulator
PRIVATE ${SOURCES}
PUBLIC ${HEADERS}
)
target_link_libraries(Simulator
target_link_libraries(simulator
PRIVATE Tactility
PRIVATE TactilityCore
PRIVATE lvgl
PRIVATE SDL2-static
)
target_link_libraries(Simulator PRIVATE ${SDL2_LIBRARIES})
target_link_libraries(simulator PRIVATE ${SDL2_LIBRARIES})
endif()

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module simulator_module = {
.name = "simulator",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module unphone_module = {
.name = "unphone",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module waveshare_esp32_s3_geek_module = {
.name = "waveshare-esp32-s3-geek",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module waveshare_s3_lcd_13_module = {
.name = "waveshare-s3-lcd-13",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module waveshare_s3_touch_lcd_128_module = {
.name = "waveshare-s3-touch-lcd-128",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module waveshare_s3_touch_lcd_147_module = {
.name = "waveshare-s3-touch-lcd-147",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module waveshare_s3_touch_lcd_43_module = {
.name = "waveshare-s3-touch-lcd-43",
.start = start,
.stop = stop,

View File

@ -12,8 +12,7 @@ static error_t stop() {
return ERROR_NONE;
}
/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
struct Module wireless_tag_wt32_sc01_plus_module = {
.name = "wireless-tag-wt32-sc01-plus",
.start = start,
.stop = stop,

View File

@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 3.20)
include("${CMAKE_CURRENT_LIST_DIR}/../../Buildscripts/module.cmake")
file(GLOB_RECURSE SOURCE_FILES "source/*.c*")
tactility_add_module(pi4ioe5v6408-module
SRCS ${SOURCE_FILES}
INCLUDE_DIRS include/
REQUIRES TactilityKernel
)

View File

@ -0,0 +1,195 @@
Apache License
==============
_Version 2.0, January 2004_
_&lt;<http://www.apache.org/licenses/>&gt;_
### Terms and Conditions for use, reproduction, and distribution
#### 1. Definitions
“License” shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.
“Licensor” shall mean the copyright owner or entity authorized by the copyright
owner that is granting the License.
“Legal Entity” shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, “control” means **(i)** the power, direct or
indirect, to cause the direction or management of such entity, whether by
contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the
outstanding shares, or **(iii)** beneficial ownership of such entity.
“You” (or “Your”) shall mean an individual or Legal Entity exercising
permissions granted by this License.
“Source” form shall mean the preferred form for making modifications, including
but not limited to software source code, documentation source, and configuration
files.
“Object” form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object code,
generated documentation, and conversions to other media types.
“Work” shall mean the work of authorship, whether in Source or Object form, made
available under the License, as indicated by a copyright notice that is included
in or attached to the work (an example is provided in the Appendix below).
“Derivative Works” shall mean any work, whether in Source or Object form, that
is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative Works
shall not include works that remain separable from, or merely link (or bind by
name) to the interfaces of, the Work and Derivative Works thereof.
“Contribution” shall mean any work of authorship, including the original version
of the Work and any modifications or additions to that Work or Derivative Works
thereof, that is intentionally submitted to Licensor for inclusion in the Work
by the copyright owner or by an individual or Legal Entity authorized to submit
on behalf of the copyright owner. For the purposes of this definition,
“submitted” means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, and
issue tracking systems that are managed by, or on behalf of, the Licensor for
the purpose of discussing and improving the Work, but excluding communication
that is conspicuously marked or otherwise designated in writing by the copyright
owner as “Not a Contribution.”
“Contributor” shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.
#### 2. Grant of Copyright License
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the Work and such
Derivative Works in Source or Object form.
#### 3. Grant of Patent License
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable (except as stated in this section) patent license to make, have
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
such license applies only to those patent claims licensable by such Contributor
that are necessarily infringed by their Contribution(s) alone or by combination
of their Contribution(s) with the Work to which such Contribution(s) was
submitted. If You institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
Contribution incorporated within the Work constitutes direct or contributory
patent infringement, then any patent licenses granted to You under this License
for that Work shall terminate as of the date such litigation is filed.
#### 4. Redistribution
You may reproduce and distribute copies of the Work or Derivative Works thereof
in any medium, with or without modifications, and in Source or Object form,
provided that You meet the following conditions:
* **(a)** You must give any other recipients of the Work or Derivative Works a copy of
this License; and
* **(b)** You must cause any modified files to carry prominent notices stating that You
changed the files; and
* **(c)** You must retain, in the Source form of any Derivative Works that You distribute,
all copyright, patent, trademark, and attribution notices from the Source form
of the Work, excluding those notices that do not pertain to any part of the
Derivative Works; and
* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any
Derivative Works that You distribute must include a readable copy of the
attribution notices contained within such NOTICE file, excluding those notices
that do not pertain to any part of the Derivative Works, in at least one of the
following places: within a NOTICE text file distributed as part of the
Derivative Works; within the Source form or documentation, if provided along
with the Derivative Works; or, within a display generated by the Derivative
Works, if and wherever such third-party notices normally appear. The contents of
the NOTICE file are for informational purposes only and do not modify the
License. You may add Your own attribution notices within Derivative Works that
You distribute, alongside or as an addendum to the NOTICE text from the Work,
provided that such additional attribution notices cannot be construed as
modifying the License.
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a whole,
provided Your use, reproduction, and distribution of the Work otherwise complies
with the conditions stated in this License.
#### 5. Submission of Contributions
Unless You explicitly state otherwise, any Contribution intentionally submitted
for inclusion in the Work by You to the Licensor shall be under the terms and
conditions of this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify the terms of
any separate license agreement you may have executed with Licensor regarding
such Contributions.
#### 6. Trademarks
This License does not grant permission to use the trade names, trademarks,
service marks, or product names of the Licensor, except as required for
reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
#### 7. Disclaimer of Warranty
Unless required by applicable law or agreed to in writing, Licensor provides the
Work (and each Contributor provides its Contributions) on an “AS IS” BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
including, without limitation, any warranties or conditions of TITLE,
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
solely responsible for determining the appropriateness of using or
redistributing the Work and assume any risks associated with Your exercise of
permissions under this License.
#### 8. Limitation of Liability
In no event and under no legal theory, whether in tort (including negligence),
contract, or otherwise, unless required by applicable law (such as deliberate
and grossly negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special, incidental,
or consequential damages of any character arising as a result of this License or
out of the use or inability to use the Work (including but not limited to
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
any and all other commercial damages or losses), even if such Contributor has
been advised of the possibility of such damages.
#### 9. Accepting Warranty or Additional Liability
While redistributing the Work or Derivative Works thereof, You may choose to
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
other liability obligations and/or rights consistent with this License. However,
in accepting such obligations, You may act only on Your own behalf and on Your
sole responsibility, not on behalf of any other Contributor, and only if You
agree to indemnify, defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason of your
accepting any such warranty or additional liability.
_END OF TERMS AND CONDITIONS_
### APPENDIX: How to apply the Apache License to your work
To apply the Apache License to your work, attach the following boilerplate
notice, with the fields enclosed by brackets `[]` replaced with your own
identifying information. (Don't include the brackets!) The text should be
enclosed in the appropriate comment syntax for the file format. We also
recommend that a file or class name and description of purpose be included on
the same “printed page” as the copyright notice for easier identification within
third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,8 @@
# PI4IOE5V6408 I/O expander
A driver for the `PI4IOE5V6408` low-voltage translating 8-bit I2C-bus I/O expander by Diodes Incorporated.
See https://www.diodes.com/part/view/PI4IOE5V6408
And: https://www.digikey.com/htmldatasheets/production/3031904/0/0/1/pi4ioe5v6408.html
License: [Apache v2.0](LICENSE-Apache-2.0.md)

View File

@ -0,0 +1,5 @@
description: Diodes Incorporated PI4IOE5V6408 low-voltage translating 8-bit I2C-bus I/O expander
include: ["i2c-device.yaml"]
compatible: "diodes,pi4ioe5v6408"

View File

@ -0,0 +1,3 @@
dependencies:
- TactilityKernel
bindings: bindings

View File

@ -0,0 +1,15 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <tactility/bindings/bindings.h>
#include <drivers/pi4ioe5v6408.h>
#ifdef __cplusplus
extern "C" {
#endif
DEFINE_DEVICETREE(pi4ioe5v6408, struct Pi4ioe5v6408Config)
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,41 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <stdint.h>
#include <tactility/error.h>
#include <tactility/freertos/freertos.h>
#ifdef __cplusplus
extern "C" {
#endif
struct Device;
struct Pi4ioe5v6408Config {
/** Address on bus */
uint8_t address;
};
error_t pi4ioe5v6408_set_direction(struct Device* device, uint8_t bits, TickType_t timeout);
error_t pi4ioe5v6408_set_output_level(struct Device* device, uint8_t bits, TickType_t timeout);
error_t pi4ioe5v6408_get_output_level(struct Device* device, uint8_t* bits, TickType_t timeout);
error_t pi4ioe5v6408_set_output_high_impedance(struct Device* device, uint8_t bits, TickType_t timeout);
error_t pi4ioe5v6408_set_input_default_level(struct Device* device, uint8_t bits, TickType_t timeout);
error_t pi4ioe5v6408_set_pull_enable(struct Device* device, uint8_t bits, TickType_t timeout);
error_t pi4ioe5v6408_set_pull_select(struct Device* device, uint8_t bits, TickType_t timeout);
error_t pi4ioe5v6408_get_input_level(struct Device* device, uint8_t* bits, TickType_t timeout);
error_t pi4ioe5v6408_set_interrupt_mask(struct Device* device, uint8_t bits, TickType_t timeout);
error_t pi4ioe5v6408_get_interrupt_level(struct Device* device, uint8_t* bits, TickType_t timeout);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,14 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <tactility/module.h>
#ifdef __cplusplus
extern "C" {
#endif
extern struct Module pi4ioe5v6408_module;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,33 @@
// SPDX-License-Identifier: Apache-2.0
#include <tactility/check.h>
#include <tactility/driver.h>
#include <tactility/module.h>
extern "C" {
extern Driver pi4ioe5v6408_driver;
extern const ModuleSymbol pi4ioe5v6408_module_symbols[];
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(&pi4ioe5v6408_driver) == ERROR_NONE);
return ERROR_NONE;
}
static error_t stop() {
/* We crash when destruct fails, because if a single driver fails to destruct,
* there is no guarantee that the previously destroyed drivers can be recovered */
check(driver_remove_destruct(&pi4ioe5v6408_driver) == ERROR_NONE);
return ERROR_NONE;
}
Module pi4ioe5v6408_module = {
.name = "pi4ioe5v6408",
.start = start,
.stop = stop,
.symbols = pi4ioe5v6408_module_symbols,
.internal = nullptr
};
}

View File

@ -0,0 +1,100 @@
// SPDX-License-Identifier: Apache-2.0
#include <drivers/pi4ioe5v6408.h>
#include <pi4ioe5v6408_module.h>
#include <tactility/device.h>
#include <tactility/driver.h>
#include <tactility/drivers/i2c_controller.h>
#include <tactility/log.h>
#define TAG "PI4IOE5V6408"
#define GET_CONFIG(device) (static_cast<const Pi4ioe5v6408Config*>((device)->config))
constexpr auto PI4_REGISTER_DIRECTION = 0x03;
constexpr auto PI4_REGISTER_OUTPUT_LEVEL = 0x05;
constexpr auto PI4_REGISTER_OUTPUT_HIGH_IMPEDANCE = 0x07;
constexpr auto PI4_REGISTER_INPUT_DEFAULT_LEVEL = 0x09;
constexpr auto PI4_REGISTER_PULL_ENABLE = 0x0B;
constexpr auto PI4_REGISTER_PULL_SELECT = 0x0D;
constexpr auto PI4_REGISTER_INPUT_LEVEL = 0x0F;
constexpr auto PI4_REGISTER_INTERRUPT_MASK = 0x11;
constexpr auto PI4_REGISTER_INTERRUPT_LEVEL = 0x13;
static error_t start(Device* device) {
auto* parent = device_get_parent(device);
if (device_get_type(parent) != &I2C_CONTROLLER_TYPE) {
LOG_E(TAG, "Parent device is not I2C");
return ERROR_RESOURCE;
}
LOG_I(TAG, "Started PI4IOE5V6408 device %s", device->name);
return ERROR_NONE;
}
static error_t stop(Device* device) {
return ERROR_NONE;
}
extern "C" {
error_t pi4ioe5v6408_set_direction(Device* device, uint8_t bits, TickType_t timeout) {
auto* parent = device_get_parent(device);
return i2c_controller_register8_set(parent, GET_CONFIG(device)->address, PI4_REGISTER_DIRECTION, bits, timeout);
}
error_t pi4ioe5v6408_set_output_level(Device* device, uint8_t bits, TickType_t timeout) {
auto* parent = device_get_parent(device);
return i2c_controller_register8_set(parent, GET_CONFIG(device)->address, PI4_REGISTER_OUTPUT_LEVEL, bits, timeout);
}
error_t pi4ioe5v6408_get_output_level(Device* device, uint8_t* bits, TickType_t timeout) {
auto* parent = device_get_parent(device);
return i2c_controller_register8_get(parent, GET_CONFIG(device)->address, PI4_REGISTER_OUTPUT_LEVEL, bits, timeout);
}
error_t pi4ioe5v6408_set_output_high_impedance(struct Device* device, uint8_t bits, TickType_t timeout) {
auto* parent = device_get_parent(device);
return i2c_controller_register8_set(parent, GET_CONFIG(device)->address, PI4_REGISTER_OUTPUT_HIGH_IMPEDANCE, bits, timeout);
}
error_t pi4ioe5v6408_set_input_default_level(struct Device* device, uint8_t bits, TickType_t timeout) {
auto* parent = device_get_parent(device);
return i2c_controller_register8_set(parent, GET_CONFIG(device)->address, PI4_REGISTER_INPUT_DEFAULT_LEVEL, bits, timeout);
}
error_t pi4ioe5v6408_set_pull_enable(struct Device* device, uint8_t bits, TickType_t timeout) {
auto* parent = device_get_parent(device);
return i2c_controller_register8_set(parent, GET_CONFIG(device)->address, PI4_REGISTER_PULL_ENABLE, bits, timeout);
}
error_t pi4ioe5v6408_set_pull_select(struct Device* device, uint8_t bits, TickType_t timeout) {
auto* parent = device_get_parent(device);
return i2c_controller_register8_set(parent, GET_CONFIG(device)->address, PI4_REGISTER_PULL_SELECT, bits, timeout);
}
error_t pi4ioe5v6408_get_input_level(struct Device* device, uint8_t* bits, TickType_t timeout) {
auto* parent = device_get_parent(device);
return i2c_controller_register8_get(parent, GET_CONFIG(device)->address, PI4_REGISTER_INPUT_LEVEL, bits, timeout);
}
error_t pi4ioe5v6408_set_interrupt_mask(struct Device* device, uint8_t bits, TickType_t timeout) {
auto* parent = device_get_parent(device);
return i2c_controller_register8_set(parent, GET_CONFIG(device)->address, PI4_REGISTER_INTERRUPT_MASK, bits, timeout);
}
error_t pi4ioe5v6408_get_interrupt_level(struct Device* device, uint8_t* bits, TickType_t timeout) {
auto* parent = device_get_parent(device);
return i2c_controller_register8_get(parent, GET_CONFIG(device)->address, PI4_REGISTER_INTERRUPT_LEVEL, bits, timeout);
}
Driver pi4ioe5v6408_driver = {
.name = "pi4ioe5v6408",
.compatible = (const char*[]) { "diodes,pi4ioe5v6408", nullptr},
.start_device = start,
.stop_device = stop,
.api = nullptr,
.device_type = nullptr,
.owner = &pi4ioe5v6408_module,
.internal = nullptr
};
}

View File

@ -0,0 +1,15 @@
#include <drivers/pi4ioe5v6408.h>
#include <tactility/module.h>
const struct ModuleSymbol pi4ioe5v6408_module_symbols[] = {
DEFINE_MODULE_SYMBOL(pi4ioe5v6408_set_direction),
DEFINE_MODULE_SYMBOL(pi4ioe5v6408_set_output_level),
DEFINE_MODULE_SYMBOL(pi4ioe5v6408_set_output_high_impedance),
DEFINE_MODULE_SYMBOL(pi4ioe5v6408_set_input_default_level),
DEFINE_MODULE_SYMBOL(pi4ioe5v6408_set_pull_enable),
DEFINE_MODULE_SYMBOL(pi4ioe5v6408_set_pull_select),
DEFINE_MODULE_SYMBOL(pi4ioe5v6408_get_input_level),
DEFINE_MODULE_SYMBOL(pi4ioe5v6408_set_interrupt_mask),
DEFINE_MODULE_SYMBOL(pi4ioe5v6408_get_interrupt_level),
MODULE_SYMBOL_TERMINATOR
};

View File

@ -2,17 +2,47 @@ cmake_minimum_required(VERSION 3.20)
file(GLOB_RECURSE SOURCE_FILES "Source/*.c*")
# For Generate target below
get_filename_component(PROJECT_ROOT "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE)
# Get the project and device id
if (DEFINED ENV{ESP_IDF_VERSION})
include("../Buildscripts/device.cmake")
init_tactility_globals("../sdkconfig")
get_property(TACTILITY_DEVICE_PROJECT GLOBAL PROPERTY TACTILITY_DEVICE_PROJECT)
get_property(TACTILITY_DEVICE_ID GLOBAL PROPERTY TACTILITY_DEVICE_ID)
else ()
set(TACTILITY_DEVICE_ID simulator)
set(COMPONENT_LIB FirmwareSim)
set(TACTILITY_DEVICE_PROJECT Simulator)
endif ()
set(DEVICETREE_LOCATION "${CMAKE_SOURCE_DIR}/Devices/${TACTILITY_DEVICE_ID}")
set(DEVICETREE_LOCATION "${PROJECT_ROOT}/Devices/${TACTILITY_DEVICE_ID}")
#
# DTS compiler python dependencies
#
execute_process(
COMMAND python -m pip install lark==1.3.1 pyyaml==6.0.3
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
)
#
# Devicetree dependency collection
#
execute_process(
COMMAND python "${PROJECT_ROOT}/Buildscripts/DevicetreeCompiler/dependencies.py" "${DEVICETREE_LOCATION}"
WORKING_DIRECTORY "${PROJECT_ROOT}"
OUTPUT_VARIABLE DEVICE_DEPENDENCIES
OUTPUT_STRIP_TRAILING_WHITESPACE
)
# Tokenize to array of lines
separate_arguments(DEVICE_DEPENDENCIES UNIX_COMMAND "${DEVICE_DEPENDENCIES}")
#
# "Generated/" directory creation
#
set(GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/Generated")
# Ensure the directory is built in the correct CMake build phase
@ -21,37 +51,53 @@ if (DEFINED CMAKE_CURRENT_BINARY_DIR)
file(MAKE_DIRECTORY "${GENERATED_DIR}")
endif ()
#
# Component
#
list(APPEND REQUIRES_LIST
Tactility
TactilityKernel
)
# Add devicetree dependencies
foreach(dts_dependency IN LISTS DEVICE_DEPENDENCIES)
message("Adding DTS dependency ${dts_dependency}")
list(APPEND REQUIRES_LIST ${dts_dependency})
endforeach()
if (DEFINED ENV{ESP_IDF_VERSION})
list(APPEND REQUIRES_LIST
TactilityC
)
idf_component_register(
SRCS ${SOURCE_FILES} "${GENERATED_DIR}/devicetree.c"
REQUIRES Tactility TactilityC TactilityKernel platform-esp32 ${TACTILITY_DEVICE_PROJECT}
REQUIRES ${REQUIRES_LIST}
)
else ()
add_executable(FirmwareSim ${SOURCE_FILES} "${GENERATED_DIR}/devicetree.c")
target_link_libraries(FirmwareSim PRIVATE
Tactility
list(APPEND REQUIRES_LIST
TactilityCore
TactilityFreeRtos
TactilityKernel
hal-device-module
lvgl-module
Simulator
platform-posix
SDL2::SDL2-static SDL2-static
SDL2::SDL2-static
SDL2-static
)
add_executable(FirmwareSim ${SOURCE_FILES} "${GENERATED_DIR}/devicetree.c")
target_link_libraries(FirmwareSim PRIVATE ${REQUIRES_LIST})
endif ()
#
# Devicetree code generation
#
add_custom_target(AlwaysRun
COMMAND ${CMAKE_COMMAND} -E rm -f "${GENERATED_DIR}/devicetree.c"
)
add_custom_command(
OUTPUT "${GENERATED_DIR}/devicetree.c"
"${GENERATED_DIR}/devicetree.h"
COMMAND pip install lark==1.3.1 pyyaml==6.0.3
COMMAND python "${CMAKE_SOURCE_DIR}/Buildscripts/DevicetreeCompiler/compile.py"
"${DEVICETREE_LOCATION}" "${GENERATED_DIR}"
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"

View File

@ -3,11 +3,6 @@
#include <tactility/driver.h>
#include <devicetree.h>
// From the relevant platform
extern struct Module platform_module;
// From the relevant device
extern struct Module device_module;
#ifdef ESP_PLATFORM
#include <tt_init.h>
#else
@ -32,7 +27,7 @@ void app_main() {
tt_init_tactility_c(); // ELF bindings for side-loading on ESP32
#endif
tt::run(config, &platform_module, &device_module, dts_devices);
tt::run(config, dts_modules, dts_devices);
}
} // extern

View File

@ -116,7 +116,7 @@ const static GpioControllerApi esp32_gpio_api = {
.get_native_pin_number = get_native_pin_number
};
extern struct Module platform_module;
extern struct Module platform_esp32_module;
Driver esp32_gpio_driver = {
.name = "esp32_gpio",
@ -125,7 +125,7 @@ Driver esp32_gpio_driver = {
.stop_device = stop,
.api = (void*)&esp32_gpio_api,
.device_type = &GPIO_CONTROLLER_TYPE,
.owner = &platform_module,
.owner = &platform_esp32_module,
.internal = nullptr
};

View File

@ -245,7 +245,7 @@ static constexpr I2cControllerApi ESP32_I2C_API = {
.write_register = write_register
};
extern Module platform_module;
extern Module platform_esp32_module;
Driver esp32_i2c_driver = {
.name = "esp32_i2c",
@ -254,7 +254,7 @@ Driver esp32_i2c_driver = {
.stop_device = stop,
.api = &ESP32_I2C_API,
.device_type = &I2C_CONTROLLER_TYPE,
.owner = &platform_module,
.owner = &platform_esp32_module,
.internal = nullptr
};

View File

@ -287,7 +287,7 @@ const static I2sControllerApi esp32_i2s_api = {
.reset = reset
};
extern struct Module platform_module;
extern struct Module platform_esp32_module;
Driver esp32_i2s_driver = {
.name = "esp32_i2s",
@ -296,7 +296,7 @@ Driver esp32_i2s_driver = {
.stop_device = stop,
.api = (void*)&esp32_i2s_api,
.device_type = &I2S_CONTROLLER_TYPE,
.owner = &platform_module,
.owner = &platform_esp32_module,
.internal = nullptr
};

View File

@ -142,7 +142,7 @@ const static struct SpiControllerApi esp32_spi_api = {
.unlock = unlock
};
extern struct Module platform_module;
extern struct Module platform_esp32_module;
Driver esp32_spi_driver = {
.name = "esp32_spi",
@ -151,7 +151,7 @@ Driver esp32_spi_driver = {
.stop_device = stop,
.api = (void*)&esp32_spi_api,
.device_type = &SPI_CONTROLLER_TYPE,
.owner = &platform_module,
.owner = &platform_esp32_module,
.internal = nullptr
};

View File

@ -402,7 +402,7 @@ const static UartControllerApi esp32_uart_api = {
.flush_input = flush_input
};
extern struct Module platform_module;
extern struct Module platform_esp32_module;
Driver esp32_uart_driver = {
.name = "esp32_uart",
@ -411,7 +411,7 @@ Driver esp32_uart_driver = {
.stop_device = stop,
.api = (void*)&esp32_uart_api,
.device_type = &UART_CONTROLLER_TYPE,
.owner = &platform_module,
.owner = &platform_esp32_module,
.internal = nullptr
};

View File

@ -32,8 +32,7 @@ static error_t stop() {
return ERROR_NONE;
}
// The name must be exactly "platform_module"
struct Module platform_module = {
struct Module platform_esp32_module = {
.name = "platform-esp32",
.start = start,
.stop = stop,

View File

@ -13,8 +13,7 @@ static error_t stop() {
return ERROR_NONE;
}
// The name must be exactly "platform_module"
struct Module platform_module = {
struct Module platform_posix_module = {
.name = "platform-posix",
.start = start,
.stop = stop,

View File

@ -23,11 +23,10 @@ struct Configuration {
/**
* @brief Main entry point for Tactility.
* @param platformModule Platform module to start (non-null).
* @param deviceModule Device module to start (non-null).
* @param dtsModules List of modules from devicetree, null-terminated, non-null parameter
* @param dtsDevices Array that is terminated with DTS_DEVICE_TERMINATOR
*/
void run(const Configuration& config, Module* platformModule, Module* deviceModule, struct DtsDevice dtsDevices[]);
void run(const Configuration& config, Module* dtsModules[], DtsDevice dtsDevices[]);
/**
* While technically nullable, this instance is always set if tt_init() succeeds.

View File

@ -323,23 +323,19 @@ void registerApps() {
registerInstalledAppsFromSdCards();
}
void run(const Configuration& config, Module* platformModule, Module* deviceModule, DtsDevice dtsDevices[]) {
void run(const Configuration& config, Module* dtsModules[], DtsDevice dtsDevices[]) {
LOGGER.info("Tactility v{} on {} ({})", TT_VERSION, CONFIG_TT_DEVICE_NAME, CONFIG_TT_DEVICE_ID);
assert(config.hardware);
LOGGER.info(R"(Calling kernel_init with modules: "{}" and "{}")", platformModule->name, deviceModule->name);
if (kernel_init(platformModule, deviceModule, dtsDevices) != ERROR_NONE) {
LOGGER.info("Initializing kernel");
if (kernel_init(dtsModules, dtsDevices) != ERROR_NONE) {
LOGGER.error("Failed to initialize kernel");
return;
}
// hal-device-module
check(module_construct(&hal_device_module) == ERROR_NONE);
check(module_add(&hal_device_module) == ERROR_NONE);
check(module_start(&hal_device_module) == ERROR_NONE);
const hal::Configuration& hardware = *config.hardware;
check(module_construct_add_start(&hal_device_module) == ERROR_NONE);
// Assign early so starting services can use it
config_instance = &config;

View File

@ -1,6 +1,6 @@
description: I2C device
properties:
register:
reg:
required: true
description: device address on the bus

View File

@ -1,21 +1,20 @@
#pragma once
#include <tactility/dts.h>
#include <tactility/error.h>
#include <tactility/module.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <tactility/device.h>
#include <tactility/error.h>
#include <tactility/module.h>
/**
* Initialize the kernel with platform and device modules, and a device tree.
* @param platform_module The platform module to start. This module should not be constructed yet.
* @param device_module The device module to start. This module should not be constructed yet. This parameter can be NULL.
* @param dts_devices The list of generated devices from the devicetree. The array must be terminated with DTS_DEVICE_TERMINATOR. This parameter can be NULL.
* Initialize the kernel with the provided modules from the device tree
* @param dts_modules List of modules from devicetree, null-terminated. Non-null parameter.
* @param dts_devices The list of generated devices from the devicetree. The array must be terminated with DTS_DEVICE_TERMINATOR. Non-null parameter.
* @return ERROR_NONE on success, otherwise an error code
*/
error_t kernel_init(struct Module* platform_module, struct Module* device_module, struct DtsDevice dts_devices[]);
error_t kernel_init(struct Module* dts_modules[], struct DtsDevice dts_devices[]);
#ifdef __cplusplus
}

View File

@ -3,6 +3,7 @@
#include "error.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus

View File

@ -1,5 +1,6 @@
#include "tactility/dts.h"
#include <tactility/kernel_init.h>
#include <tactility/device.h>
#include <tactility/log.h>
#ifdef __cplusplus
@ -8,7 +9,7 @@ extern "C" {
#define TAG "kernel"
extern const struct ModuleSymbol KERNEL_SYMBOLS[];
extern const ModuleSymbol KERNEL_SYMBOLS[];
static error_t start() {
extern Driver root_driver;
@ -20,7 +21,7 @@ static error_t stop() {
return ERROR_NONE;
}
struct Module root_module = {
Module root_module = {
.name = "kernel",
.start = start,
.stop = stop,
@ -28,7 +29,7 @@ struct Module root_module = {
.internal = nullptr
};
error_t kernel_init(struct Module* platform_module, struct Module* device_module, struct DtsDevice dts_devices[]) {
error_t kernel_init(Module* dts_modules[], DtsDevice dts_devices[]) {
LOG_I(TAG, "init");
if (module_construct_add_start(&root_module) != ERROR_NONE) {
@ -36,19 +37,15 @@ error_t kernel_init(struct Module* platform_module, struct Module* device_module
return ERROR_RESOURCE;
}
if (module_construct_add_start(platform_module) != ERROR_NONE) {
LOG_E(TAG, "platform module init failed");
return ERROR_RESOURCE;
}
if (device_module != nullptr) {
if (module_construct_add_start(device_module) != ERROR_NONE) {
LOG_E(TAG, "device module init failed");
Module** dts_module = dts_modules;
while (*dts_module != nullptr) {
if (module_construct_add_start(*dts_module) != ERROR_NONE) {
LOG_E(TAG, "dts module init failed: %s", (*dts_module)->name);
return ERROR_RESOURCE;
}
dts_module++;
}
if (dts_devices) {
DtsDevice* dts_device = dts_devices;
while (dts_device->device != nullptr) {
if (dts_device->status == DTS_DEVICE_STATUS_OKAY) {
@ -66,7 +63,6 @@ error_t kernel_init(struct Module* platform_module, struct Module* device_module
}
dts_device++;
}
}
LOG_I(TAG, "init done");
return ERROR_NONE;

View File

@ -5,8 +5,10 @@
#include "FreeRTOS.h"
#include "task.h"
#include <tactility/kernel_init.h>
#include <tactility/check.h>
#include <tactility/dts.h>
#include <tactility/hal_device_module.h>
#include <tactility/kernel_init.h>
typedef struct {
int argc;
@ -15,7 +17,7 @@ typedef struct {
} TestTaskData;
// From the relevant platform
extern "C" struct Module platform_module;
extern "C" struct Module platform_posix_module;
void test_task(void* parameter) {
auto* data = (TestTaskData*)parameter;
@ -27,7 +29,9 @@ void test_task(void* parameter) {
// overrides
context.setOption("no-breaks", true); // don't break in the debugger when assertions fail
check(kernel_init(&platform_module, &hal_device_module, nullptr) == ERROR_NONE);
Module* dts_modules[] = { &platform_posix_module, &hal_device_module, nullptr };
DtsDevice dts_devices[] = { DTS_DEVICE_TERMINATOR };
check(kernel_init(dts_modules, dts_devices) == ERROR_NONE);
data->result = context.run();

View File

@ -1,7 +1,9 @@
#define DOCTEST_CONFIG_IMPLEMENT
#include "doctest.h"
#include <cassert>
#include <tactility/check.h>
#include <tactility/dts.h>
#include <tactility/freertos/task.h>
#include <tactility/kernel_init.h>
@ -11,10 +13,8 @@ typedef struct {
int result;
} TestTaskData;
extern "C" {
// From the relevant platform
extern struct Module platform_module;
}
extern "C" struct Module platform_posix_module;
void test_task(void* parameter) {
auto* data = (TestTaskData*)parameter;
@ -26,7 +26,9 @@ void test_task(void* parameter) {
// overrides
context.setOption("no-breaks", true); // don't break in the debugger when assertions fail
check(kernel_init(&platform_module, nullptr, nullptr) == ERROR_NONE);
Module* dts_modules[] = { &platform_posix_module, nullptr };
DtsDevice dts_devices[] = { DTS_DEVICE_TERMINATOR };
check(kernel_init(dts_modules, dts_devices) == ERROR_NONE);
data->result = context.run();