Compare commits

...

14 Commits

Author SHA1 Message Date
Ken Van Hoeylandt
139d35cb69 Merge branch 'main' into develop 2026-07-14 08:35:47 +02:00
Ken Van Hoeylandt
9f92c6daaf Add support for min/max property range in dts 2026-07-14 01:30:44 +02:00
Ken Van Hoeylandt
7fef42426f Fix for display orientation 2026-07-14 01:16:55 +02:00
Ken Van Hoeylandt
4d41d8497a unphone display and touch migration 2026-07-14 01:07:08 +02:00
Ken Van Hoeylandt
44f3eec365 Fix for ADC channel 2026-07-14 00:33:54 +02:00
Ken Van Hoeylandt
ceed6f1d27 Migrate crowpanel-basic-28 2026-07-14 00:27:20 +02:00
Ken Van Hoeylandt
daded517aa Migrate cyd-e32r32p to kernel drivers 2026-07-14 00:09:22 +02:00
Ken Van Hoeylandt
f1e96d2ef5 Fix for build 2026-07-14 00:07:06 +02:00
Ken Van Hoeylandt
b02663b4bf Remove unused driver 2026-07-14 00:03:50 +02:00
Ken Van Hoeylandt
1069032148 Improve gpio-hog driver 2026-07-14 00:02:40 +02:00
Ken Van Hoeylandt
49af1d42f8 Fixes and improvements 2026-07-13 23:58:14 +02:00
Ken Van Hoeylandt
d2889b2d3d Support for gamma curves 2026-07-13 23:42:37 +02:00
Ken Van Hoeylandt
f31103882d Device and driver migrations 2026-07-13 23:18:37 +02:00
Ken Van Hoeylandt
8f5e92d6e4 Device implementations and touch calibration updates 2026-07-13 21:38:28 +02:00
156 changed files with 3706 additions and 2696 deletions

View File

@ -46,6 +46,8 @@ def parse_binding(file_path: str, binding_dirs: list[str]) -> Binding:
description=details.get('description', '').strip(),
default=details.get('default', None),
element_type=details.get('element-type', None),
min=details.get('min', None),
max=details.get('max', None),
)
properties_dict[name] = prop
filename = os.path.basename(file_path)

View File

@ -118,6 +118,25 @@ def resolve_phandle_array_entries(device_property, devices):
entries.append(str(item))
return entries
def validate_property_range(device: Device, binding_property, value) -> None:
"""Enforces a binding's optional min/max on int-typed properties. Silently skips
values that aren't parseable as a plain integer literal (e.g. a passed-through
symbolic #define) - those can't be range-checked at compile time."""
if binding_property.min is None and binding_property.max is None:
return
try:
numeric_value = int(value, 0) if isinstance(value, str) else int(value)
except (TypeError, ValueError):
return
if binding_property.min is not None and numeric_value < binding_property.min:
raise DevicetreeException(
f"Device '{device.node_name}' property '{binding_property.name}' value {numeric_value} is below minimum {binding_property.min}"
)
if binding_property.max is not None and numeric_value > binding_property.max:
raise DevicetreeException(
f"Device '{device.node_name}' property '{binding_property.name}' value {numeric_value} is above maximum {binding_property.max}"
)
def resolve_parameters_from_bindings(device: Device, bindings: list[Binding], devices: list[Device]) -> list:
compatible_property = find_device_property(device, "compatible")
if compatible_property is None:
@ -168,6 +187,7 @@ def resolve_parameters_from_bindings(device: Device, bindings: list[Binding], de
if device_property is None:
if binding_property.default is not None:
validate_property_range(device, binding_property, binding_property.default)
temp_prop = DeviceProperty(
name=binding_property.name,
type=binding_property.type,
@ -184,6 +204,7 @@ def resolve_parameters_from_bindings(device: Device, bindings: list[Binding], de
else:
raise DevicetreeException(f"Device {device.node_name} doesn't have property '{binding_property.name}' and no default value is set")
else:
validate_property_range(device, binding_property, device_property.value)
result.append(property_to_string(device_property, devices))
return result, phandle_arrays

View File

@ -40,6 +40,8 @@ class BindingProperty:
description: str
default: object = None
element_type: str = None
min: object = None
max: object = None
@dataclass
class Binding:

View File

@ -76,6 +76,139 @@ def test_compile_invalid_dts():
print("PASSED")
return True
def write_minmax_config(tmp_dir, device_property_line, binding_min=0, binding_max=3, binding_default=1):
config_dir = os.path.join(tmp_dir, "minmax_data")
bindings_dir = os.path.join(config_dir, "bindings")
os.makedirs(bindings_dir)
with open(os.path.join(config_dir, "devicetree.yaml"), "w") as f:
f.write("dts: test.dts\nbindings: bindings")
with open(os.path.join(config_dir, "test.dts"), "w") as f:
f.write(f"""/dts-v1/;
/ {{
compatible = "test,root";
model = "Test Model";
test-device@0 {{
compatible = "test,minmax-device";
{device_property_line}
}};
}};
""")
with open(os.path.join(bindings_dir, "test,root.yaml"), "w") as f:
f.write("description: Test root binding\ncompatible: \"test,root\"\nproperties:\n model:\n type: string\n")
with open(os.path.join(bindings_dir, "test,minmax-device.yaml"), "w") as f:
f.write(f"""description: Test min/max binding
compatible: "test,minmax-device"
properties:
ranged-prop:
type: int
default: {binding_default}
min: {binding_min}
max: {binding_max}
""")
return config_dir
def test_minmax_within_range_succeeds():
print("Running test_minmax_within_range_succeeds...")
with tempfile.TemporaryDirectory() as tmp_dir:
config_dir = write_minmax_config(tmp_dir, "ranged-prop = <2>;")
output_dir = os.path.join(tmp_dir, "output")
os.makedirs(output_dir)
result = run_compiler(config_dir, output_dir)
if result.returncode != 0:
print(f"FAILED: Compilation should have succeeded: {result.stderr} {result.stdout}")
return False
print("PASSED")
return True
def test_minmax_below_minimum_fails():
print("Running test_minmax_below_minimum_fails...")
with tempfile.TemporaryDirectory() as tmp_dir:
config_dir = write_minmax_config(tmp_dir, "ranged-prop = <-1>;")
output_dir = os.path.join(tmp_dir, "output")
os.makedirs(output_dir)
result = run_compiler(config_dir, output_dir)
if result.returncode == 0:
print("FAILED: Compilation should have failed for a below-minimum value")
return False
if "below minimum" not in result.stdout:
print(f"FAILED: Expected 'below minimum' error message, got: {result.stdout}")
return False
print("PASSED")
return True
def test_minmax_above_maximum_fails():
print("Running test_minmax_above_maximum_fails...")
with tempfile.TemporaryDirectory() as tmp_dir:
config_dir = write_minmax_config(tmp_dir, "ranged-prop = <7>;")
output_dir = os.path.join(tmp_dir, "output")
os.makedirs(output_dir)
result = run_compiler(config_dir, output_dir)
if result.returncode == 0:
print("FAILED: Compilation should have failed for an above-maximum value")
return False
if "above maximum" not in result.stdout:
print(f"FAILED: Expected 'above maximum' error message, got: {result.stdout}")
return False
print("PASSED")
return True
def test_minmax_out_of_range_default_fails():
print("Running test_minmax_out_of_range_default_fails...")
with tempfile.TemporaryDirectory() as tmp_dir:
# Property omitted from the .dts entirely, so the (invalid) binding default is used.
config_dir = write_minmax_config(tmp_dir, "", binding_default=9)
output_dir = os.path.join(tmp_dir, "output")
os.makedirs(output_dir)
result = run_compiler(config_dir, output_dir)
if result.returncode == 0:
print("FAILED: Compilation should have failed for an out-of-range default value")
return False
if "above maximum" not in result.stdout:
print(f"FAILED: Expected 'above maximum' error message, got: {result.stdout}")
return False
print("PASSED")
return True
def test_minmax_symbolic_value_skips_validation():
print("Running test_minmax_symbolic_value_skips_validation...")
with tempfile.TemporaryDirectory() as tmp_dir:
# A passed-through symbolic constant can't be range-checked at compile time and must
# not be rejected just because a min/max is declared.
config_dir = write_minmax_config(tmp_dir, "ranged-prop = <SOME_DEFINE>;")
output_dir = os.path.join(tmp_dir, "output")
os.makedirs(output_dir)
result = run_compiler(config_dir, output_dir)
if result.returncode != 0:
print(f"FAILED: Compilation should have succeeded for a symbolic value: {result.stderr} {result.stdout}")
return False
print("PASSED")
return True
def test_compile_missing_config():
print("Running test_compile_missing_config...")
with tempfile.TemporaryDirectory() as output_dir:
@ -96,7 +229,12 @@ if __name__ == "__main__":
tests = [
test_compile_success,
test_compile_invalid_dts,
test_compile_missing_config
test_compile_missing_config,
test_minmax_within_range_succeeds,
test_minmax_below_minimum_fails,
test_minmax_above_maximum_fails,
test_minmax_out_of_range_default_fails,
test_minmax_symbolic_value_skips_validation
]
failed = 0

View File

@ -1,7 +1,6 @@
file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
file(GLOB_RECURSE SOURCE_FILES source/*.c*)
idf_component_register(
SRCS ${SOURCE_FILES}
INCLUDE_DIRS "Source"
REQUIRES Tactility esp_lvgl_port ILI934x XPT2046 PwmBacklight driver vfs fatfs
REQUIRES TactilityKernel driver
)

View File

@ -1,21 +0,0 @@
#include "devices/Display.h"
#include <Tactility/hal/Configuration.h>
#include <PwmBacklight.h>
using namespace tt::hal;
static bool initBoot() {
return driver::pwmbacklight::init(LCD_PIN_BACKLIGHT);
}
static DeviceVector createDevices() {
return {
createDisplay()
};
}
extern const Configuration hardwareConfiguration = {
.initBoot = initBoot,
.createDevices = createDevices
};

View File

@ -1,46 +0,0 @@
#include "Display.h"
#include "Xpt2046Touch.h"
#include <Ili934xDisplay.h>
#include <PwmBacklight.h>
static std::shared_ptr<tt::hal::touch::TouchDevice> createTouch(esp_lcd_spi_bus_handle_t spiDevice) {
auto configuration = std::make_unique<Xpt2046Touch::Configuration>(
spiDevice,
TOUCH_CS_PIN,
LCD_HORIZONTAL_RESOLUTION,
LCD_VERTICAL_RESOLUTION,
true, // swapXY
false, // mirrorX
true // mirrorY
);
return std::make_shared<Xpt2046Touch>(std::move(configuration));
}
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
auto spi_configuration = std::make_shared<Ili934xDisplay::SpiConfiguration>(Ili934xDisplay::SpiConfiguration {
.spiHostDevice = LCD_SPI_HOST,
.csPin = LCD_PIN_CS,
.dcPin = LCD_PIN_DC,
.pixelClockFrequency = 40'000'000,
.transactionQueueDepth = 10
});
Ili934xDisplay::Configuration panel_configuration = {
.horizontalResolution = LCD_HORIZONTAL_RESOLUTION,
.verticalResolution = LCD_VERTICAL_RESOLUTION,
.gapX = 0,
.gapY = 0,
.swapXY = true,
.mirrorX = true,
.mirrorY = true,
.invertColor = false,
.swapBytes = true,
.bufferSize = LCD_BUFFER_SIZE,
.touch = createTouch(spi_configuration->spiHostDevice),
.backlightDutyFunction = driver::pwmbacklight::setBacklightDuty,
.resetPin = LCD_PIN_RST,
.rgbElementOrder = LCD_RGB_ELEMENT_ORDER_RGB
};
return std::make_shared<Ili934xDisplay>(panel_configuration, spi_configuration, true);
}

View File

@ -1,28 +0,0 @@
#pragma once
#include <Tactility/hal/display/DisplayDevice.h>
#include <driver/gpio.h>
#include <driver/spi_common.h>
#include <memory>
// Display
constexpr auto LCD_SPI_HOST = SPI2_HOST;
constexpr auto LCD_PIN_CS = GPIO_NUM_15;
constexpr auto LCD_PIN_DC = GPIO_NUM_2;
constexpr auto LCD_PIN_RST = GPIO_NUM_NC; // tied to ESP32 RST
constexpr auto LCD_PIN_CLK = GPIO_NUM_14;
constexpr auto LCD_PIN_MOSI = GPIO_NUM_13;
constexpr auto LCD_PIN_MISO = GPIO_NUM_12;
constexpr auto LCD_HORIZONTAL_RESOLUTION = 240;
constexpr auto LCD_VERTICAL_RESOLUTION = 320;
constexpr auto LCD_BUFFER_HEIGHT = LCD_VERTICAL_RESOLUTION / 10;
constexpr auto LCD_BUFFER_SIZE = LCD_HORIZONTAL_RESOLUTION * LCD_BUFFER_HEIGHT;
// Backlight
constexpr auto LCD_PIN_BACKLIGHT = GPIO_NUM_27;
// Touch
constexpr auto TOUCH_CS_PIN = GPIO_NUM_33;
constexpr auto TOUCH_IRQ_PIN = GPIO_NUM_36;
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay();

View File

@ -5,9 +5,10 @@
#include <tactility/bindings/esp32_gpio.h>
#include <tactility/bindings/esp32_spi.h>
#include <tactility/bindings/esp32_sdspi.h>
#include <tactility/bindings/display_placeholder.h>
#include <tactility/bindings/pointer_placeholder.h>
#include <tactility/bindings/esp32_uart.h>
#include <tactility/bindings/esp32_ledc_backlight.h>
#include <bindings/ili9341.h>
#include <bindings/xpt2046.h>
/ {
compatible = "root";
@ -23,6 +24,15 @@
gpio-count = <40>;
};
display_backlight {
compatible = "espressif,esp32-ledc-backlight";
// Off by default so display power-on won't show the screen from before the last power loss.
// The display backlight is turned on during the boot process.
status = "disabled";
pin-backlight = <&gpio0 27 GPIO_FLAG_NONE>;
frequency-hz = <512>;
};
spi0 {
compatible = "espressif,esp32-spi";
host = <SPI2_HOST>;
@ -33,11 +43,23 @@
<&gpio0 33 GPIO_FLAG_NONE>; // Touch
display@0 {
compatible = "display-placeholder";
compatible = "ilitek,ili9341";
horizontal-resolution = <240>;
vertical-resolution = <320>;
swap-xy;
mirror-x;
mirror-y;
pixel-clock-hz = <40000000>;
pin-dc = <&gpio0 2 GPIO_FLAG_NONE>;
backlight = <&display_backlight>;
};
touch@1 {
compatible = "pointer-placeholder";
compatible = "xptek,xpt2046";
x-max = <240>;
y-max = <320>;
swap-xy;
mirror-y;
};
};
@ -61,4 +83,4 @@
pin-tx = <&gpio0 1 GPIO_FLAG_NONE>;
pin-rx = <&gpio0 3 GPIO_FLAG_NONE>;
};
};
};

View File

@ -7,6 +7,8 @@ hardware.target=ESP32
hardware.flashSize=4MB
hardware.spiRam=false
dependencies.useDeprecatedHal=false
storage.userDataLocation=SD
display.size=2.4"

View File

@ -1,3 +1,5 @@
dependencies:
- Platforms/platform-esp32
- Drivers/ili9341-module
- Drivers/xpt2046-module
dts: cyd,2432s024r.dts

View File

@ -1,7 +1,6 @@
file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
file(GLOB_RECURSE SOURCE_FILES source/*.c*)
idf_component_register(
SRCS ${SOURCE_FILES}
INCLUDE_DIRS "Source"
REQUIRES Tactility esp_lvgl_port ILI934x XPT2046SoftSPI PwmBacklight driver vfs fatfs
REQUIRES TactilityKernel driver
)

View File

@ -1,32 +0,0 @@
#include "devices/Display.h"
#include <driver/gpio.h>
#include <Tactility/hal/Configuration.h>
#include <PwmBacklight.h>
using namespace tt::hal;
static bool initBoot() {
// Set the RGB LED Pins to output and turn them off
ESP_ERROR_CHECK(gpio_set_direction(GPIO_NUM_4, GPIO_MODE_OUTPUT)); // Red
ESP_ERROR_CHECK(gpio_set_direction(GPIO_NUM_16, GPIO_MODE_OUTPUT)); // Green
ESP_ERROR_CHECK(gpio_set_direction(GPIO_NUM_17, GPIO_MODE_OUTPUT)); // Blue
// 0 on, 1 off
ESP_ERROR_CHECK(gpio_set_level(GPIO_NUM_4, 1)); // Red
ESP_ERROR_CHECK(gpio_set_level(GPIO_NUM_16, 1)); // Green
ESP_ERROR_CHECK(gpio_set_level(GPIO_NUM_17, 1)); // Blue
return driver::pwmbacklight::init(LCD_PIN_BACKLIGHT);
}
static DeviceVector createDevices() {
return {
createDisplay(),
};
}
extern const Configuration hardwareConfiguration = {
.initBoot = initBoot,
.createDevices = createDevices
};

View File

@ -1,49 +0,0 @@
#include "Display.h"
#include "Xpt2046SoftSpi.h"
#include <Ili934xDisplay.h>
#include <PwmBacklight.h>
static std::shared_ptr<tt::hal::touch::TouchDevice> createTouch() {
auto configuration = std::make_unique<Xpt2046SoftSpi::Configuration>(
TOUCH_MOSI_PIN,
TOUCH_MISO_PIN,
TOUCH_SCK_PIN,
TOUCH_CS_PIN,
LCD_HORIZONTAL_RESOLUTION,
LCD_VERTICAL_RESOLUTION,
false, // swapXY
true, // mirrorX
false // mirrorY
);
return std::make_shared<Xpt2046SoftSpi>(std::move(configuration));
}
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
Ili934xDisplay::Configuration panel_configuration = {
.horizontalResolution = LCD_HORIZONTAL_RESOLUTION,
.verticalResolution = LCD_VERTICAL_RESOLUTION,
.gapX = 0,
.gapY = 0,
.swapXY = false,
.mirrorX = true,
.mirrorY = false,
.invertColor = false,
.swapBytes = true,
.bufferSize = LCD_BUFFER_SIZE,
.touch = createTouch(),
.backlightDutyFunction = driver::pwmbacklight::setBacklightDuty,
.resetPin = GPIO_NUM_NC,
.rgbElementOrder = LCD_RGB_ELEMENT_ORDER_BGR
};
auto spi_configuration = std::make_shared<Ili934xDisplay::SpiConfiguration>(Ili934xDisplay::SpiConfiguration {
.spiHostDevice = LCD_SPI_HOST,
.csPin = LCD_PIN_CS,
.dcPin = LCD_PIN_DC,
.pixelClockFrequency = 40'000'000,
.transactionQueueDepth = 10
});
return std::make_shared<Ili934xDisplay>(panel_configuration, spi_configuration, true);
}

View File

@ -1,27 +0,0 @@
#pragma once
#include <Tactility/hal/display/DisplayDevice.h>
#include <driver/gpio.h>
#include <driver/spi_common.h>
#include <memory>
// Display
constexpr auto LCD_SPI_HOST = SPI2_HOST;
constexpr auto LCD_PIN_CS = GPIO_NUM_15;
constexpr auto LCD_PIN_DC = GPIO_NUM_2;
constexpr auto LCD_HORIZONTAL_RESOLUTION = 240;
constexpr auto LCD_VERTICAL_RESOLUTION = 320;
constexpr auto LCD_BUFFER_HEIGHT = LCD_VERTICAL_RESOLUTION / 10;
constexpr auto LCD_BUFFER_SIZE = LCD_HORIZONTAL_RESOLUTION * LCD_BUFFER_HEIGHT;
// Display backlight (PWM)
constexpr auto LCD_PIN_BACKLIGHT = GPIO_NUM_21;
// Touch (Software SPI)
constexpr auto TOUCH_MISO_PIN = GPIO_NUM_39;
constexpr auto TOUCH_MOSI_PIN = GPIO_NUM_32;
constexpr auto TOUCH_SCK_PIN = GPIO_NUM_25;
constexpr auto TOUCH_CS_PIN = GPIO_NUM_33;
constexpr auto TOUCH_IRQ_PIN = GPIO_NUM_36;
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay();

View File

@ -1,23 +0,0 @@
#include <tactility/module.h>
extern "C" {
static error_t start() {
// Empty for now
return ERROR_NONE;
}
static error_t stop() {
// Empty for now
return ERROR_NONE;
}
struct Module cyd_2432s028r_module = {
.name = "cyd-2432s028r",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
};
}

View File

@ -7,8 +7,9 @@
#include <tactility/bindings/esp32_spi.h>
#include <tactility/bindings/esp32_uart.h>
#include <tactility/bindings/esp32_sdspi.h>
#include <tactility/bindings/display_placeholder.h>
#include <tactility/bindings/pointer_placeholder.h>
#include <tactility/bindings/esp32_ledc_backlight.h>
#include <bindings/ili9341.h>
#include <bindings/xpt2046_softspi.h>
/ {
compatible = "root";
@ -32,21 +33,43 @@
pin-scl = <&gpio0 22 GPIO_FLAG_NONE>;
};
display_backlight {
compatible = "espressif,esp32-ledc-backlight";
// Off by default so display power-on won't show the screen from before the last power loss.
// The display backlight is turned on during the boot process.
status = "disabled";
pin-backlight = <&gpio0 21 GPIO_FLAG_NONE>;
frequency-hz = <512>;
};
touch {
compatible = "xptek,xpt2046-softspi";
pin-mosi = <&gpio0 32 GPIO_FLAG_NONE>;
pin-miso = <&gpio0 39 GPIO_FLAG_NONE>;
pin-sck = <&gpio0 25 GPIO_FLAG_NONE>;
pin-cs = <&gpio0 33 GPIO_FLAG_NONE>;
x-max = <240>;
y-max = <320>;
mirror-x;
};
spi0 {
compatible = "espressif,esp32-spi";
host = <SPI2_HOST>;
cs-gpios = <&gpio0 15 GPIO_FLAG_NONE>, // Display
<&gpio0 33 GPIO_FLAG_NONE>; // Touch
cs-gpios = <&gpio0 15 GPIO_FLAG_NONE>;
pin-mosi = <&gpio0 13 GPIO_FLAG_NONE>;
pin-miso = <&gpio0 12 GPIO_FLAG_NONE>;
pin-sclk = <&gpio0 14 GPIO_FLAG_NONE>;
display@0 {
compatible = "display-placeholder";
};
touch@1 {
compatible = "pointer-placeholder";
display@0 {
compatible = "ilitek,ili9341";
horizontal-resolution = <240>;
vertical-resolution = <320>;
mirror-x;
bgr-order;
pixel-clock-hz = <40000000>;
pin-dc = <&gpio0 2 GPIO_FLAG_NONE>;
backlight = <&display_backlight>;
};
};

View File

@ -7,12 +7,17 @@ hardware.target=ESP32
hardware.flashSize=4MB
hardware.spiRam=false
dependencies.useDeprecatedHal=false
storage.userDataLocation=SD
display.size=2.8"
display.shape=rectangle
display.dpi=143
touch.calibrationSupported=true
touch.calibrationRequired=false
cdn.warningMessage=There are 3 hardware variants of this board. This build works on the original variant only ("v1").
lvgl.colorDepth=16

View File

@ -1,3 +1,5 @@
dependencies:
- Platforms/platform-esp32
- Drivers/ili9341-module
- Drivers/xpt2046-softspi-module
dts: cyd,2432s028r.dts

View File

@ -0,0 +1,34 @@
#include <tactility/module.h>
#include <tactility/error.h>
#include <driver/gpio.h>
extern "C" {
static error_t start() {
// Set the RGB LED pins to output and turn them off (0 on, 1 off)
gpio_set_direction(GPIO_NUM_4, GPIO_MODE_OUTPUT); // Red
gpio_set_direction(GPIO_NUM_16, GPIO_MODE_OUTPUT); // Green
gpio_set_direction(GPIO_NUM_17, GPIO_MODE_OUTPUT); // Blue
gpio_set_level(GPIO_NUM_4, 1); // Red
gpio_set_level(GPIO_NUM_16, 1); // Green
gpio_set_level(GPIO_NUM_17, 1); // Blue
return ERROR_NONE;
}
static error_t stop() {
// Empty for now
return ERROR_NONE;
}
struct Module cyd_2432s028r_module = {
.name = "cyd-2432s028r",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
};
}

View File

@ -1,7 +1,6 @@
file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
file(GLOB_RECURSE SOURCE_FILES source/*.c*)
idf_component_register(
SRCS ${SOURCE_FILES}
INCLUDE_DIRS "Source"
REQUIRES Tactility esp_lvgl_port ST7789 XPT2046SoftSPI PwmBacklight driver vfs fatfs
REQUIRES TactilityKernel driver
)

View File

@ -1,32 +0,0 @@
#include "devices/Display.h"
#include <driver/gpio.h>
#include <Tactility/hal/Configuration.h>
#include <PwmBacklight.h>
using namespace tt::hal;
static bool initBoot() {
// Set the RGB LED Pins to output and turn them off
ESP_ERROR_CHECK(gpio_set_direction(GPIO_NUM_4, GPIO_MODE_OUTPUT)); // Red
ESP_ERROR_CHECK(gpio_set_direction(GPIO_NUM_16, GPIO_MODE_OUTPUT)); // Green
ESP_ERROR_CHECK(gpio_set_direction(GPIO_NUM_17, GPIO_MODE_OUTPUT)); // Blue
// 0 on, 1 off
ESP_ERROR_CHECK(gpio_set_level(GPIO_NUM_4, 1)); // Red
ESP_ERROR_CHECK(gpio_set_level(GPIO_NUM_16, 1)); // Green
ESP_ERROR_CHECK(gpio_set_level(GPIO_NUM_17, 1)); // Blue
return driver::pwmbacklight::init(LCD_PIN_BACKLIGHT);
}
static DeviceVector createDevices() {
return {
createDisplay(),
};
}
extern const Configuration hardwareConfiguration = {
.initBoot = initBoot,
.createDevices = createDevices
};

View File

@ -1,50 +0,0 @@
#include "Display.h"
#include "Xpt2046SoftSpi.h"
#include <St7789Display.h>
#include <PwmBacklight.h>
constexpr auto* TAG = "CYD";
static std::shared_ptr<tt::hal::touch::TouchDevice> createTouch() {
auto configuration = std::make_unique<Xpt2046SoftSpi::Configuration>(
TOUCH_MOSI_PIN,
TOUCH_MISO_PIN,
TOUCH_SCK_PIN,
TOUCH_CS_PIN,
LCD_HORIZONTAL_RESOLUTION,
LCD_VERTICAL_RESOLUTION,
false, // swapXY
true, // mirrorX
false // mirrorY
);
return std::make_shared<Xpt2046SoftSpi>(std::move(configuration));
}
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
St7789Display::Configuration panel_configuration = {
.horizontalResolution = LCD_HORIZONTAL_RESOLUTION,
.verticalResolution = LCD_VERTICAL_RESOLUTION,
.gapX = 0,
.gapY = 0,
.swapXY = false,
.mirrorX = false,
.mirrorY = false,
.invertColor = false,
.bufferSize = LCD_BUFFER_SIZE,
.touch = createTouch(),
.backlightDutyFunction = driver::pwmbacklight::setBacklightDuty,
.resetPin = GPIO_NUM_NC,
.lvglSwapBytes = false
};
auto spi_configuration = std::make_shared<St7789Display::SpiConfiguration>(St7789Display::SpiConfiguration {
.spiHostDevice = LCD_SPI_HOST,
.csPin = LCD_PIN_CS,
.dcPin = LCD_PIN_DC,
.pixelClockFrequency = 62'500'000,
.transactionQueueDepth = 10
});
return std::make_shared<St7789Display>(panel_configuration, spi_configuration);
}

View File

@ -1,27 +0,0 @@
#pragma once
#include <Tactility/hal/display/DisplayDevice.h>
#include <driver/gpio.h>
#include <driver/spi_common.h>
#include <memory>
// Display
constexpr auto LCD_SPI_HOST = SPI2_HOST;
constexpr auto LCD_PIN_CS = GPIO_NUM_15;
constexpr auto LCD_PIN_DC = GPIO_NUM_2;
constexpr auto LCD_PIN_BACKLIGHT = GPIO_NUM_21;
constexpr auto LCD_HORIZONTAL_RESOLUTION = 240;
constexpr auto LCD_VERTICAL_RESOLUTION = 320;
constexpr auto LCD_BUFFER_HEIGHT = LCD_VERTICAL_RESOLUTION / 10;
constexpr auto LCD_BUFFER_SIZE = LCD_HORIZONTAL_RESOLUTION * LCD_BUFFER_HEIGHT;
// Touch (Software SPI)
constexpr auto TOUCH_MISO_PIN = GPIO_NUM_39;
constexpr auto TOUCH_MOSI_PIN = GPIO_NUM_32;
constexpr auto TOUCH_SCK_PIN = GPIO_NUM_25;
constexpr auto TOUCH_CS_PIN = GPIO_NUM_33;
constexpr auto TOUCH_IRQ_PIN = GPIO_NUM_36;
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay();

View File

@ -1,23 +0,0 @@
#include <tactility/module.h>
extern "C" {
static error_t start() {
// Empty for now
return ERROR_NONE;
}
static error_t stop() {
// Empty for now
return ERROR_NONE;
}
struct Module cyd_2432s028rv3_module = {
.name = "cyd-2432s028rv3",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
};
}

View File

@ -7,8 +7,9 @@
#include <tactility/bindings/esp32_uart.h>
#include <tactility/bindings/esp32_spi.h>
#include <tactility/bindings/esp32_sdspi.h>
#include <tactility/bindings/display_placeholder.h>
#include <tactility/bindings/pointer_placeholder.h>
#include <tactility/bindings/esp32_ledc_backlight.h>
#include <bindings/st7789.h>
#include <bindings/xpt2046_softspi.h>
/ {
compatible = "root";
@ -32,21 +33,41 @@
pin-scl = <&gpio0 22 GPIO_FLAG_NONE>;
};
display_backlight {
compatible = "espressif,esp32-ledc-backlight";
// Off by default so display power-on won't show the screen from before the last power loss.
// The display backlight is turned on during the boot process.
status = "disabled";
pin-backlight = <&gpio0 21 GPIO_FLAG_NONE>;
frequency-hz = <512>;
};
touch {
compatible = "xptek,xpt2046-softspi";
pin-mosi = <&gpio0 32 GPIO_FLAG_NONE>;
pin-miso = <&gpio0 39 GPIO_FLAG_NONE>;
pin-sck = <&gpio0 25 GPIO_FLAG_NONE>;
pin-cs = <&gpio0 33 GPIO_FLAG_NONE>;
x-max = <240>;
y-max = <320>;
mirror-x;
};
spi0 {
compatible = "espressif,esp32-spi";
host = <SPI2_HOST>;
cs-gpios = <&gpio0 15 GPIO_FLAG_NONE>, // Display
<&gpio0 33 GPIO_FLAG_NONE>; // Touch
cs-gpios = <&gpio0 15 GPIO_FLAG_NONE>;
pin-mosi = <&gpio0 13 GPIO_FLAG_NONE>;
pin-miso = <&gpio0 12 GPIO_FLAG_NONE>;
pin-sclk = <&gpio0 14 GPIO_FLAG_NONE>;
display@0 {
compatible = "display-placeholder";
};
touch@1 {
compatible = "pointer-placeholder";
display@0 {
compatible = "sitronix,st7789";
horizontal-resolution = <240>;
vertical-resolution = <320>;
pixel-clock-hz = <62500000>;
pin-dc = <&gpio0 2 GPIO_FLAG_NONE>;
backlight = <&display_backlight>;
};
};
@ -57,7 +78,7 @@
pin-mosi = <&gpio0 23 GPIO_FLAG_NONE>;
pin-miso = <&gpio0 19 GPIO_FLAG_NONE>;
pin-sclk = <&gpio0 18 GPIO_FLAG_NONE>;
sdcard@0 {
compatible = "espressif,esp32-sdspi";
frequency-khz = <20000>;

View File

@ -7,12 +7,17 @@ hardware.target=ESP32
hardware.flashSize=4MB
hardware.spiRam=false
dependencies.useDeprecatedHal=false
storage.userDataLocation=SD
display.size=2.8"
display.shape=rectangle
display.dpi=143
touch.calibrationSupported=true
touch.calibrationRequired=false
cdn.warningMessage=There are 3 hardware variants of this board. This build only supports board version 3.
lvgl.colorDepth=16

View File

@ -1,3 +1,5 @@
dependencies:
- Platforms/platform-esp32
- Drivers/st7789-module
- Drivers/xpt2046-softspi-module
dts: cyd,2432s028rv3.dts

View File

@ -0,0 +1,33 @@
#include <tactility/module.h>
#include <tactility/error.h>
#include <driver/gpio.h>
extern "C" {
static error_t start() {
// Set the RGB LED pins to output and turn them off (0 on, 1 off)
gpio_set_direction(GPIO_NUM_4, GPIO_MODE_OUTPUT); // Red
gpio_set_direction(GPIO_NUM_16, GPIO_MODE_OUTPUT); // Green
gpio_set_direction(GPIO_NUM_17, GPIO_MODE_OUTPUT); // Blue
gpio_set_level(GPIO_NUM_4, 1); // Red
gpio_set_level(GPIO_NUM_16, 1); // Green
gpio_set_level(GPIO_NUM_17, 1); // Blue
return ERROR_NONE;
}
static error_t stop() {
return ERROR_NONE;
}
struct Module cyd_2432s028rv3_module = {
.name = "cyd-2432s028rv3",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
};
}

View File

@ -1,7 +1,6 @@
file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
file(GLOB_RECURSE SOURCE_FILES source/*.c*)
idf_component_register(
SRCS ${SOURCE_FILES}
INCLUDE_DIRS "Source"
REQUIRES Tactility esp_lvgl_port ILI934x XPT2046SoftSPI PwmBacklight driver vfs fatfs
REQUIRES TactilityKernel driver
)

View File

@ -1,19 +0,0 @@
#include "devices/Display.h"
#include <Tactility/hal/Configuration.h>
#include <PwmBacklight.h>
static bool initBoot() {
return driver::pwmbacklight::init(LCD_BACKLIGHT_PIN);
}
static tt::hal::DeviceVector createDevices() {
return {
createDisplay(),
};
}
extern const tt::hal::Configuration hardwareConfiguration = {
.initBoot = initBoot,
.createDevices = createDevices
};

View File

@ -1,51 +0,0 @@
#include "Display.h"
#include <Xpt2046SoftSpi.h>
#include <Ili934xDisplay.h>
#include <PwmBacklight.h>
#include <Tactility/hal/touch/TouchDevice.h>
static std::shared_ptr<tt::hal::touch::TouchDevice> createTouch() {
auto config = std::make_unique<Xpt2046SoftSpi::Configuration>(
TOUCH_MOSI_PIN,
TOUCH_MISO_PIN,
TOUCH_SCK_PIN,
TOUCH_CS_PIN,
LCD_HORIZONTAL_RESOLUTION,
LCD_VERTICAL_RESOLUTION,
false,
true,
false
);
return std::make_shared<Xpt2046SoftSpi>(std::move(config));
}
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
Ili934xDisplay::Configuration panel_configuration = {
.horizontalResolution = LCD_HORIZONTAL_RESOLUTION,
.verticalResolution = LCD_VERTICAL_RESOLUTION,
.gapX = 0,
.gapY = 0,
.swapXY = false,
.mirrorX = true,
.mirrorY = false,
.invertColor = false,
.swapBytes = true,
.bufferSize = LCD_BUFFER_SIZE,
.touch = createTouch(),
.backlightDutyFunction = driver::pwmbacklight::setBacklightDuty,
.resetPin = GPIO_NUM_NC,
.rgbElementOrder = LCD_RGB_ELEMENT_ORDER_BGR
};
auto spi_configuration = std::make_shared<Ili934xDisplay::SpiConfiguration>(Ili934xDisplay::SpiConfiguration {
.spiHostDevice = LCD_SPI_HOST,
.csPin = LCD_PIN_CS,
.dcPin = LCD_PIN_DC,
.pixelClockFrequency = 40'000'000,
.transactionQueueDepth = 10
});
return std::make_shared<Ili934xDisplay>(panel_configuration, spi_configuration, true);
}

View File

@ -1,27 +0,0 @@
#pragma once
#include <Tactility/hal/display/DisplayDevice.h>
#include <driver/gpio.h>
#include <driver/spi_common.h>
#include <memory>
// Display
constexpr auto LCD_SPI_HOST = SPI2_HOST;
constexpr auto LCD_PIN_CS = GPIO_NUM_15;
constexpr auto LCD_PIN_DC = GPIO_NUM_2;
constexpr auto LCD_HORIZONTAL_RESOLUTION = 240;
constexpr auto LCD_VERTICAL_RESOLUTION = 320;
constexpr auto LCD_BUFFER_HEIGHT = (LCD_VERTICAL_RESOLUTION / 10);
constexpr auto LCD_BUFFER_SIZE = (LCD_HORIZONTAL_RESOLUTION * LCD_BUFFER_HEIGHT);
// Touch (Software SPI)
constexpr auto TOUCH_MISO_PIN = GPIO_NUM_39;
constexpr auto TOUCH_MOSI_PIN = GPIO_NUM_32;
constexpr auto TOUCH_SCK_PIN = GPIO_NUM_25;
constexpr auto TOUCH_CS_PIN = GPIO_NUM_33;
constexpr auto TOUCH_IRQ_PIN = GPIO_NUM_36;
// Backlight
constexpr auto LCD_BACKLIGHT_PIN = GPIO_NUM_21;
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay();

View File

@ -5,8 +5,9 @@
#include <tactility/bindings/esp32_gpio.h>
#include <tactility/bindings/esp32_spi.h>
#include <tactility/bindings/esp32_sdspi.h>
#include <tactility/bindings/display_placeholder.h>
#include <tactility/bindings/pointer_placeholder.h>
#include <tactility/bindings/esp32_ledc_backlight.h>
#include <bindings/ili9341.h>
#include <bindings/xpt2046_softspi.h>
/ {
compatible = "root";
@ -22,21 +23,43 @@
gpio-count = <40>;
};
display_backlight {
compatible = "espressif,esp32-ledc-backlight";
// Off by default so display power-on won't show the screen from before the last power loss.
// The display backlight is turned on during the boot process.
status = "disabled";
pin-backlight = <&gpio0 21 GPIO_FLAG_NONE>;
frequency-hz = <512>;
};
touch {
compatible = "xptek,xpt2046-softspi";
pin-mosi = <&gpio0 32 GPIO_FLAG_NONE>;
pin-miso = <&gpio0 39 GPIO_FLAG_NONE>;
pin-sck = <&gpio0 25 GPIO_FLAG_NONE>;
pin-cs = <&gpio0 33 GPIO_FLAG_NONE>;
x-max = <240>;
y-max = <320>;
mirror-x;
};
spi0 {
compatible = "espressif,esp32-spi";
host = <SPI2_HOST>;
cs-gpios = <&gpio0 15 GPIO_FLAG_NONE>, // Display
<&gpio0 33 GPIO_FLAG_NONE>; // Touch
cs-gpios = <&gpio0 15 GPIO_FLAG_NONE>;
pin-mosi = <&gpio0 13 GPIO_FLAG_NONE>;
pin-miso = <&gpio0 12 GPIO_FLAG_NONE>;
pin-sclk = <&gpio0 14 GPIO_FLAG_NONE>;
display@0 {
compatible = "display-placeholder";
};
touch@1 {
compatible = "pointer-placeholder";
display@0 {
compatible = "ilitek,ili9341";
horizontal-resolution = <240>;
vertical-resolution = <320>;
mirror-x;
bgr-order;
pixel-clock-hz = <40000000>;
pin-dc = <&gpio0 2 GPIO_FLAG_NONE>;
backlight = <&display_backlight>;
};
};
@ -47,7 +70,7 @@
pin-mosi = <&gpio0 23 GPIO_FLAG_NONE>;
pin-miso = <&gpio0 19 GPIO_FLAG_NONE>;
pin-sclk = <&gpio0 18 GPIO_FLAG_NONE>;
sdcard@0 {
compatible = "espressif,esp32-sdspi";
frequency-khz = <20000>;

View File

@ -7,10 +7,15 @@ hardware.target=ESP32
hardware.flashSize=4MB
hardware.spiRam=false
dependencies.useDeprecatedHal=false
storage.userDataLocation=SD
display.size=2.8"
display.shape=rectangle
display.dpi=143
touch.calibrationSupported=true
touch.calibrationRequired=false
lvgl.colorDepth=16

View File

@ -1,3 +1,5 @@
dependencies:
- Platforms/platform-esp32
- Drivers/ili9341-module
- Drivers/xpt2046-softspi-module
dts: cyd,e32r28t.dts

View File

@ -1,7 +1,6 @@
file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
file(GLOB_RECURSE SOURCE_FILES source/*.c*)
idf_component_register(
SRCS ${SOURCE_FILES}
INCLUDE_DIRS "Source"
REQUIRES Tactility esp_lvgl_port ST7789 XPT2046 PwmBacklight EstimatedPower driver vfs fatfs
REQUIRES TactilityKernel driver
)

View File

@ -1,23 +0,0 @@
#include "devices/Display.h"
#include "devices/Power.h"
#include <Tactility/hal/Configuration.h>
#include <PwmBacklight.h>
using namespace tt::hal;
static bool initBoot() {
return driver::pwmbacklight::init(DISPLAY_BACKLIGHT_PIN);
}
static tt::hal::DeviceVector createDevices() {
return {
createPower(),
createDisplay(),
};
}
extern const Configuration hardwareConfiguration = {
.initBoot = initBoot,
.createDevices = createDevices
};

View File

@ -1,52 +0,0 @@
#include "Display.h"
#include <Xpt2046Touch.h>
#include <St7789Display.h>
#include <PwmBacklight.h>
#include <Tactility/hal/touch/TouchDevice.h>
// Create the XPT2046 touch device (hardware/esp_lcd driver)
static std::shared_ptr<tt::hal::touch::TouchDevice> createTouch() {
auto config = std::make_unique<Xpt2046Touch::Configuration>(
DISPLAY_SPI_HOST, // spi device / bus (SPI2_HOST)
TOUCH_CS_PIN, // touch CS (IO33)
(uint16_t)DISPLAY_HORIZONTAL_RESOLUTION, // x max
(uint16_t)DISPLAY_VERTICAL_RESOLUTION, // y max
false, // swapXy
true, // mirrorX
true // mirrorY
);
return std::make_shared<Xpt2046Touch>(std::move(config));
}
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
// Create the ST7789 panel configuration
St7789Display::Configuration panel_configuration = {
.horizontalResolution = DISPLAY_HORIZONTAL_RESOLUTION,
.verticalResolution = DISPLAY_VERTICAL_RESOLUTION,
.gapX = 0,
.gapY = 0,
.swapXY = false,
.mirrorX = false,
.mirrorY = false,
.invertColor = false,
.bufferSize = DISPLAY_DRAW_BUFFER_SIZE, // 0 -> default 1/10 screen
.touch = createTouch(),
.backlightDutyFunction = driver::pwmbacklight::setBacklightDuty,
.resetPin = GPIO_NUM_NC,
.lvglSwapBytes = false,
.rgbElementOrder = LCD_RGB_ELEMENT_ORDER_BGR // BGR for this display
};
// Create the SPI configuration (from EspLcdSpiDisplay base class)
auto spi_configuration = std::make_shared<EspLcdSpiDisplay::SpiConfiguration>(EspLcdSpiDisplay::SpiConfiguration {
.spiHostDevice = DISPLAY_SPI_HOST,
.csPin = DISPLAY_PIN_CS,
.dcPin = DISPLAY_PIN_DC,
.pixelClockFrequency = 40'000'000,
.transactionQueueDepth = 10
});
return std::make_shared<St7789Display>(panel_configuration, spi_configuration);
}

View File

@ -1,26 +0,0 @@
#pragma once
#include <Tactility/hal/display/DisplayDevice.h>
#include "driver/gpio.h"
#include "driver/spi_common.h"
#include <memory>
// Display (ST7789P3 on this board)
constexpr auto DISPLAY_SPI_HOST = SPI2_HOST;
constexpr auto DISPLAY_PIN_CS = GPIO_NUM_15;
constexpr auto DISPLAY_PIN_DC = GPIO_NUM_2;
constexpr auto DISPLAY_HORIZONTAL_RESOLUTION = 240;
constexpr auto DISPLAY_VERTICAL_RESOLUTION = 320;
constexpr auto DISPLAY_DRAW_BUFFER_HEIGHT = (DISPLAY_VERTICAL_RESOLUTION / 10);
constexpr auto DISPLAY_DRAW_BUFFER_SIZE = (DISPLAY_HORIZONTAL_RESOLUTION * DISPLAY_DRAW_BUFFER_HEIGHT);
constexpr auto DISPLAY_BACKLIGHT_PIN = GPIO_NUM_27;
// Touch (XPT2046, resistive, shared SPI with display)
constexpr auto TOUCH_MISO_PIN = GPIO_NUM_12;
constexpr auto TOUCH_MOSI_PIN = GPIO_NUM_13;
constexpr auto TOUCH_SCK_PIN = GPIO_NUM_14;
constexpr auto TOUCH_CS_PIN = GPIO_NUM_33;
constexpr auto TOUCH_IRQ_PIN = GPIO_NUM_36;
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay();

View File

@ -1,12 +0,0 @@
#include "Power.h"
#include <ChargeFromAdcVoltage.h>
#include <EstimatedPower.h>
std::shared_ptr<tt::hal::power::PowerDevice> createPower() {
ChargeFromAdcVoltage::Configuration configuration;
// 2.0 ratio, but +.11 added as display voltage sag compensation.
configuration.adcMultiplier = 2.11;
return std::make_shared<EstimatedPower>(configuration);
}

View File

@ -1,6 +0,0 @@
#pragma once
#include <memory>
#include <Tactility/hal/power/PowerDevice.h>
std::shared_ptr<tt::hal::power::PowerDevice> createPower();

View File

@ -1,13 +1,16 @@
/dts-v1/;
#include <tactility/bindings/root.h>
#include <tactility/bindings/battery_sense.h>
#include <tactility/bindings/esp32_adc_oneshot.h>
#include <tactility/bindings/esp32_wifi_pinned.h>
#include <tactility/bindings/esp32_gpio.h>
#include <tactility/bindings/esp32_i2c.h>
#include <tactility/bindings/esp32_spi.h>
#include <tactility/bindings/esp32_sdspi.h>
#include <tactility/bindings/display_placeholder.h>
#include <tactility/bindings/pointer_placeholder.h>
#include <tactility/bindings/esp32_ledc_backlight.h>
#include <bindings/st7789.h>
#include <bindings/xpt2046.h>
/ {
compatible = "root";
@ -31,6 +34,31 @@
pin-scl = <&gpio0 25 GPIO_FLAG_NONE>;
};
adc0 {
compatible = "espressif,esp32-adc-oneshot";
unit-id = <ADC_UNIT_1>;
channels = <ADC_CHANNEL_6 ADC_ATTEN_DB_12 ADC_BITWIDTH_DEFAULT>;
};
// Matches the deprecated HAL's old ChargeFromAdcVoltage config: adcMultiplier=2.11,
// adcRefVoltage=3.3 (default), voltageMin/Max=3.2/4.2 (default, same as battery-sense's own
// fixed curve - see TactilityKernel/source/drivers/battery_sense.cpp).
battery-sense {
compatible = "battery-sense";
io-channel = <&adc0 0>;
reference-voltage-mv = <3300>;
multiplier = <2110>;
};
display_backlight {
compatible = "espressif,esp32-ledc-backlight";
// Off by default so display power-on won't show the screen from before the last power loss.
// The display backlight is turned on during the boot process.
status = "disabled";
pin-backlight = <&gpio0 27 GPIO_FLAG_NONE>;
frequency-hz = <40000>;
};
spi0 {
compatible = "espressif,esp32-spi";
host = <SPI2_HOST>;
@ -39,13 +67,23 @@
pin-mosi = <&gpio0 13 GPIO_FLAG_NONE>;
pin-miso = <&gpio0 12 GPIO_FLAG_NONE>;
pin-sclk = <&gpio0 14 GPIO_FLAG_NONE>;
display@0 {
compatible = "display-placeholder";
compatible = "sitronix,st7789";
horizontal-resolution = <240>;
vertical-resolution = <320>;
bgr-order;
pixel-clock-hz = <40000000>;
pin-dc = <&gpio0 2 GPIO_FLAG_NONE>;
backlight = <&display_backlight>;
};
touch@1 {
compatible = "pointer-placeholder";
compatible = "xptek,xpt2046";
x-max = <240>;
y-max = <320>;
mirror-x;
mirror-y;
};
};
@ -56,7 +94,7 @@
pin-mosi = <&gpio0 23 GPIO_FLAG_NONE>;
pin-miso = <&gpio0 19 GPIO_FLAG_NONE>;
pin-sclk = <&gpio0 18 GPIO_FLAG_NONE>;
sdcard@0 {
compatible = "espressif,esp32-sdspi";
frequency-khz = <20000>;

View File

@ -7,10 +7,15 @@ hardware.target=ESP32
hardware.flashSize=4MB
hardware.spiRam=false
dependencies.useDeprecatedHal=false
storage.userDataLocation=SD
display.size=2.8"
display.shape=rectangle
display.dpi=125
touch.calibrationSupported=true
touch.calibrationRequired=false
lvgl.colorDepth=16

View File

@ -1,3 +1,5 @@
dependencies:
- Platforms/platform-esp32
- Drivers/st7789-module
- Drivers/xpt2046-module
dts: cyd,e32r32p.dts

View File

@ -1,7 +1,6 @@
file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
file(GLOB_RECURSE SOURCE_FILES source/*.c*)
idf_component_register(
SRCS ${SOURCE_FILES}
INCLUDE_DIRS "Source"
REQUIRES Tactility esp_lvgl_port ILI934x XPT2046 PwmBacklight driver
REQUIRES TactilityKernel driver
)

View File

@ -1,21 +0,0 @@
#include "PwmBacklight.h"
#include "devices/Display.h"
#include <Tactility/hal/Configuration.h>
using namespace tt::hal;
static bool initBoot() {
return driver::pwmbacklight::init(GPIO_NUM_27);
}
static DeviceVector createDevices() {
return {
createDisplay(),
};
}
extern const Configuration hardwareConfiguration = {
.initBoot = initBoot,
.createDevices = createDevices
};

View File

@ -1,49 +0,0 @@
#include "Display.h"
#include <Ili934xDisplay.h>
#include <Xpt2046Touch.h>
#include <PwmBacklight.h>
std::shared_ptr<Xpt2046Touch> createTouch() {
auto configuration = std::make_unique<Xpt2046Touch::Configuration>(
LCD_SPI_HOST,
TOUCH_PIN_CS,
LCD_HORIZONTAL_RESOLUTION,
LCD_VERTICAL_RESOLUTION,
false,
true,
false
);
return std::make_shared<Xpt2046Touch>(std::move(configuration));
}
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
Ili934xDisplay::Configuration panel_configuration = {
.horizontalResolution = LCD_HORIZONTAL_RESOLUTION,
.verticalResolution = LCD_VERTICAL_RESOLUTION,
.gapX = 0,
.gapY = 0,
.swapXY = false,
.mirrorX = true,
.mirrorY = false,
.invertColor = false,
.swapBytes = true,
.bufferSize = LCD_BUFFER_SIZE,
.touch = createTouch(),
.backlightDutyFunction = driver::pwmbacklight::setBacklightDuty,
.resetPin = GPIO_NUM_NC,
.rgbElementOrder = LCD_RGB_ELEMENT_ORDER_BGR
};
auto spi_configuration = std::make_shared<Ili934xDisplay::SpiConfiguration>(Ili934xDisplay::SpiConfiguration {
.spiHostDevice = LCD_SPI_HOST,
.csPin = LCD_PIN_CS,
.dcPin = LCD_PIN_DC,
.pixelClockFrequency = 40'000'000,
.transactionQueueDepth = 10
});
return std::make_shared<Ili934xDisplay>(panel_configuration, spi_configuration, true);
}

View File

@ -1,17 +0,0 @@
#pragma once
#include <Tactility/hal/display/DisplayDevice.h>
#include <driver/gpio.h>
#include <driver/spi_common.h>
constexpr auto LCD_SPI_HOST = SPI2_HOST;
constexpr auto LCD_PIN_CS = GPIO_NUM_15;
constexpr auto TOUCH_PIN_CS = GPIO_NUM_33;
constexpr auto LCD_PIN_DC = GPIO_NUM_2; // RS
constexpr auto LCD_HORIZONTAL_RESOLUTION = 240;
constexpr auto LCD_VERTICAL_RESOLUTION = 320;
constexpr auto LCD_BUFFER_HEIGHT = LCD_VERTICAL_RESOLUTION / 10;
constexpr auto LCD_BUFFER_SIZE = LCD_HORIZONTAL_RESOLUTION * LCD_BUFFER_HEIGHT;
constexpr auto LCD_SPI_TRANSFER_SIZE_LIMIT = LCD_BUFFER_SIZE * LV_COLOR_DEPTH / 8;
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay();

View File

@ -7,10 +7,15 @@ hardware.target=ESP32
hardware.flashSize=4MB
hardware.spiRam=false
dependencies.useDeprecatedHal=false
storage.userDataLocation=SD
display.size=2.8"
display.shape=rectangle
display.dpi=143
touch.calibrationSupported=true
touch.calibrationRequired=false
lvgl.colorDepth=16

View File

@ -1,3 +1,5 @@
dependencies:
- Platforms/platform-esp32
- Drivers/ili9341-module
- Drivers/xpt2046-module
dts: elecrow,crowpanel-basic-28.dts

View File

@ -7,8 +7,9 @@
#include <tactility/bindings/esp32_spi.h>
#include <tactility/bindings/esp32_uart.h>
#include <tactility/bindings/esp32_sdspi.h>
#include <tactility/bindings/display_placeholder.h>
#include <tactility/bindings/pointer_placeholder.h>
#include <tactility/bindings/esp32_ledc_backlight.h>
#include <bindings/ili9341.h>
#include <bindings/xpt2046.h>
/ {
compatible = "root";
@ -32,6 +33,15 @@
pin-scl = <&gpio0 21 GPIO_FLAG_NONE>;
};
display_backlight {
compatible = "espressif,esp32-ledc-backlight";
// Off by default so display power-on won't show the screen from before the last power loss.
// The display backlight is turned on during the boot process.
status = "disabled";
pin-backlight = <&gpio0 27 GPIO_FLAG_NONE>;
frequency-hz = <40000>;
};
spi0 {
compatible = "espressif,esp32-spi";
host = <SPI2_HOST>;
@ -41,13 +51,23 @@
pin-miso = <&gpio0 12 GPIO_FLAG_NONE>;
pin-sclk = <&gpio0 14 GPIO_FLAG_NONE>;
max-transfer-size = <65536>;
display@0 {
compatible = "display-placeholder";
compatible = "ilitek,ili9341";
horizontal-resolution = <240>;
vertical-resolution = <320>;
mirror-x;
bgr-order;
pixel-clock-hz = <40000000>;
pin-dc = <&gpio0 2 GPIO_FLAG_NONE>;
backlight = <&display_backlight>;
};
touch@1 {
compatible = "pointer-placeholder";
compatible = "xptek,xpt2046";
x-max = <240>;
y-max = <320>;
mirror-x;
};
};
@ -58,7 +78,7 @@
pin-mosi = <&gpio0 23 GPIO_FLAG_NONE>;
pin-miso = <&gpio0 19 GPIO_FLAG_NONE>;
pin-sclk = <&gpio0 18 GPIO_FLAG_NONE>;
sdcard@0 {
compatible = "espressif,esp32-sdspi";
frequency-khz = <20000>;

View File

@ -15,4 +15,7 @@ display.size=3.5"
display.shape=rectangle
display.dpi=165
touch.calibrationSupported=true
touch.calibrationRequired=false
lvgl.colorDepth=16

View File

@ -1,7 +1,6 @@
file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
file(GLOB_RECURSE SOURCE_FILES source/*.c*)
idf_component_register(
SRCS ${SOURCE_FILES}
INCLUDE_DIRS "Source"
REQUIRES Tactility ButtonControl XPT2046SoftSPI PwmBacklight EstimatedPower ST7789-i8080 driver vfs fatfs
REQUIRES TactilityKernel driver
)

View File

@ -1,22 +0,0 @@
#include "devices/Power.h"
#include "devices/Display.h"
#include <ButtonControl.h>
#include <Tactility/hal/Configuration.h>
bool initBoot();
using namespace tt::hal;
static std::vector<std::shared_ptr<tt::hal::Device>> createDevices() {
return {
createDisplay(),
std::make_shared<Power>(),
ButtonControl::createOneButtonControl(0)
};
}
extern const Configuration hardwareConfiguration = {
.initBoot = initBoot,
.createDevices = createDevices
};

View File

@ -1,48 +0,0 @@
#include "devices/Power.h"
#include "devices/Display.h"
#include "PwmBacklight.h"
#include <Tactility/SystemEvents.h>
#include <tactility/log.h>
#include <Tactility/TactilityCore.h>
#define TAG "thmi"
static bool powerOn() {
gpio_config_t power_signal_config = {
.pin_bit_mask = (1ULL << THMI_POWERON_GPIO) | (1ULL << THMI_POWEREN_GPIO),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
if (gpio_config(&power_signal_config) != ESP_OK) {
return false;
}
if (gpio_set_level(THMI_POWERON_GPIO, 1) != ESP_OK) {
return false;
}
if (gpio_set_level(THMI_POWEREN_GPIO, 1) != ESP_OK) {
return false;
}
return true;
}
bool initBoot() {
LOG_I(TAG, "Powering on the board...");
if (!powerOn()) {
LOG_E(TAG, "Failed to power on the board.");
return false;
}
if (!driver::pwmbacklight::init(DISPLAY_BL, 30000)) {
LOG_E(TAG, "Failed to initialize backlight.");
return false;
}
return true;
}

View File

@ -1,45 +0,0 @@
#include <Xpt2046SoftSpi.h>
#include <Tactility/hal/touch/TouchDevice.h>
#include "Display.h"
#include "PwmBacklight.h"
#include "St7789i8080Display.h"
static bool touchSpiInitialized = false;
static std::shared_ptr<tt::hal::touch::TouchDevice> createTouch() {
auto config = std::make_unique<Xpt2046SoftSpi::Configuration>(
TOUCH_MOSI_PIN,
TOUCH_MISO_PIN,
TOUCH_SCK_PIN,
TOUCH_CS_PIN,
DISPLAY_HORIZONTAL_RESOLUTION,
DISPLAY_VERTICAL_RESOLUTION
);
return std::make_shared<Xpt2046SoftSpi>(std::move(config));
}
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
// Create configuration
auto config = St7789i8080Display::Configuration(
DISPLAY_CS, // CS
DISPLAY_DC, // DC
DISPLAY_WR, // WR
DISPLAY_RD, // RD
{ DISPLAY_I80_D0, DISPLAY_I80_D1, DISPLAY_I80_D2, DISPLAY_I80_D3,
DISPLAY_I80_D4, DISPLAY_I80_D5, DISPLAY_I80_D6, DISPLAY_I80_D7 }, // D0..D7
DISPLAY_RST, // RST
DISPLAY_BL // BL
);
// Set resolution explicitly
config.horizontalResolution = DISPLAY_HORIZONTAL_RESOLUTION;
config.verticalResolution = DISPLAY_VERTICAL_RESOLUTION;
config.backlightDutyFunction = driver::pwmbacklight::setBacklightDuty;
config.touch = createTouch();
config.invertColor = false;
auto display = std::make_shared<St7789i8080Display>(config);
return display;
}

View File

@ -1,35 +0,0 @@
#pragma once
#include <driver/gpio.h>
#include <Tactility/hal/display/DisplayDevice.h>
#include "driver/spi_common.h"
class St7789i8080Display;
constexpr auto DISPLAY_CS = GPIO_NUM_6;
constexpr auto DISPLAY_DC = GPIO_NUM_7;
constexpr auto DISPLAY_WR = GPIO_NUM_8;
constexpr auto DISPLAY_RD = GPIO_NUM_NC;
constexpr auto DISPLAY_RST = GPIO_NUM_NC;
constexpr auto DISPLAY_BL = GPIO_NUM_38;
constexpr auto DISPLAY_I80_D0 = GPIO_NUM_48;
constexpr auto DISPLAY_I80_D1 = GPIO_NUM_47;
constexpr auto DISPLAY_I80_D2 = GPIO_NUM_39;
constexpr auto DISPLAY_I80_D3 = GPIO_NUM_40;
constexpr auto DISPLAY_I80_D4 = GPIO_NUM_41;
constexpr auto DISPLAY_I80_D5 = GPIO_NUM_42;
constexpr auto DISPLAY_I80_D6 = GPIO_NUM_45;
constexpr auto DISPLAY_I80_D7 = GPIO_NUM_46;
constexpr auto DISPLAY_HORIZONTAL_RESOLUTION = 240;
constexpr auto DISPLAY_VERTICAL_RESOLUTION = 320;
// Touch (XPT2046, resistive)
constexpr auto TOUCH_SPI_HOST = SPI2_HOST;
constexpr auto TOUCH_MISO_PIN = GPIO_NUM_4;
constexpr auto TOUCH_MOSI_PIN = GPIO_NUM_3;
constexpr auto TOUCH_SCK_PIN = GPIO_NUM_1;
constexpr auto TOUCH_CS_PIN = GPIO_NUM_2;
constexpr auto TOUCH_IRQ_PIN = GPIO_NUM_9;
// Factory function for registration
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay();

View File

@ -1,90 +0,0 @@
#include "Power.h"
#include <driver/adc.h>
#include <tactility/log.h>
constexpr auto* TAG = "Power";
bool Power::adcInitCalibration() {
bool calibrated = false;
esp_err_t efuse_read_result = esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_TP_FIT);
if (efuse_read_result == ESP_ERR_NOT_SUPPORTED) {
LOG_W(TAG, "Calibration scheme not supported, skip software calibration");
} else if (efuse_read_result == ESP_ERR_INVALID_VERSION) {
LOG_W(TAG, "eFuse not burnt, skip software calibration");
} else if (efuse_read_result == ESP_OK) {
calibrated = true;
LOG_I(TAG, "Calibration success");
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, static_cast<adc_bits_width_t>(ADC_WIDTH_BIT_DEFAULT), 0, &adcCharacteristics);
} else {
LOG_W(TAG, "eFuse read failed, skipping calibration");
}
return calibrated;
}
uint32_t Power::adcReadValue() const {
int adc_raw = adc1_get_raw(ADC1_CHANNEL_4);
LOG_D(TAG, "Raw data: %d", adc_raw);
uint32_t voltage;
if (calibrated) {
voltage = esp_adc_cal_raw_to_voltage(adc_raw, &adcCharacteristics);
LOG_D(TAG, "Calibrated data: %d mV", (int)voltage);
} else {
voltage = (adc_raw * 3300) / 4095; // fallback
LOG_D(TAG, "Estimated data: %d mV", (int)voltage);
}
return voltage;
}
bool Power::ensureInitialized() {
if (!initialized) {
if (adc1_config_width(ADC_WIDTH_BIT_12) != ESP_OK) {
LOG_E(TAG, "ADC1 config width failed");
return false;
}
if (adc1_config_channel_atten(ADC1_CHANNEL_4, ADC_ATTEN_DB_11) != ESP_OK) {
LOG_E(TAG, "ADC1 config attenuation failed");
return false;
}
calibrated = adcInitCalibration();
initialized = true;
}
return true;
}
bool Power::supportsMetric(MetricType type) const {
switch (type) {
using enum MetricType;
case BatteryVoltage:
case ChargeLevel:
return true;
default:
return false;
}
}
bool Power::getMetric(MetricType type, MetricData& data) {
if (!ensureInitialized()) {
return false;
}
switch (type) {
case MetricType::BatteryVoltage:
data.valueAsUint32 = adcReadValue() * 2;
return true;
case MetricType::ChargeLevel:
data.valueAsUint8 = chargeFromAdcVoltage.estimateCharge(adcReadValue() * 2);
return true;
default:
return false;
}
}

View File

@ -1,33 +0,0 @@
#pragma once
#include <string>
#include <esp_adc_cal.h>
#include <driver/gpio.h>
#include <ChargeFromVoltage.h>
#include <Tactility/hal/power/PowerDevice.h>
constexpr auto THMI_POWEREN_GPIO = GPIO_NUM_10;
constexpr auto THMI_POWERON_GPIO = GPIO_NUM_14;
using tt::hal::power::PowerDevice;
class Power final : public PowerDevice {
ChargeFromVoltage chargeFromAdcVoltage = ChargeFromVoltage(3.3f, 4.2f);
esp_adc_cal_characteristics_t adcCharacteristics;
bool initialized = false;
bool calibrated = false;
bool adcInitCalibration();
uint32_t adcReadValue() const;
bool ensureInitialized();
public:
std::string getName() const override { return "T-HMI Power"; }
std::string getDescription() const override { return "Power measurement via ADC"; }
bool supportsMetric(MetricType type) const override;
bool getMetric(MetricType type, MetricData& data) override;
};

View File

@ -12,10 +12,15 @@ hardware.tinyUsb=true
hardware.esptoolFlashFreq=120M
hardware.bluetooth=true
dependencies.useDeprecatedHal=false
storage.userDataLocation=SD
display.size=2.8"
display.shape=rectangle
display.dpi=125
touch.calibrationSupported=true
touch.calibrationRequired=false
lvgl.colorDepth=16

View File

@ -1,3 +1,6 @@
dependencies:
- Platforms/platform-esp32
- Drivers/xpt2046-softspi-module
- Drivers/st7789-i8080-module
- Drivers/button-control-module
dts: lilygo,thmi.dts

View File

@ -1,14 +1,19 @@
/dts-v1/;
#include <tactility/bindings/root.h>
#include <tactility/bindings/battery_sense.h>
#include <tactility/bindings/esp32_adc_oneshot.h>
#include <tactility/bindings/esp32_ble.h>
#include <tactility/bindings/esp32_wifi_pinned.h>
#include <tactility/bindings/esp32_gpio.h>
#include <tactility/bindings/esp32_i2c.h>
#include <tactility/bindings/esp32_sdmmc.h>
#include <tactility/bindings/esp32_spi.h>
#include <tactility/bindings/display_placeholder.h>
#include <tactility/bindings/pointer_placeholder.h>
#include <tactility/bindings/esp32_i8080.h>
#include <tactility/bindings/esp32_ledc_backlight.h>
#include <tactility/bindings/gpio_hog.h>
#include <bindings/button_control.h>
#include <bindings/st7789_i8080.h>
#include <bindings/xpt2046_softspi.h>
/ {
compatible = "root";
@ -28,23 +33,90 @@
compatible = "espressif,esp32-gpio";
gpio-count = <49>;
};
spi0 {
compatible = "espressif,esp32-spi";
host = <SPI2_HOST>;
cs-gpios = <&gpio0 6 GPIO_FLAG_NONE>, // Display
<&gpio0 2 GPIO_FLAG_NONE>; // Touch
pin-mosi = <&gpio0 3 GPIO_FLAG_NONE>;
pin-miso = <&gpio0 4 GPIO_FLAG_NONE>;
pin-sclk = <&gpio0 1 GPIO_FLAG_NONE>;
// Board power-enable pins. Must be asserted before the i8080 bus/display below start, since
// devicetree devices are constructed and started earlier (kernel_init()) than the deprecated
// HAL's initBoot() used to run. gpio-hog nodes run in declaration order, so they must stay
// before the i8080 bus node.
power_on {
compatible = "tactility,gpio-hog";
pin = <&gpio0 14 GPIO_FLAG_NONE>;
mode = <GPIO_HOG_MODE_OUTPUT_HIGH>;
};
power_en {
compatible = "tactility,gpio-hog";
pin = <&gpio0 10 GPIO_FLAG_NONE>;
mode = <GPIO_HOG_MODE_OUTPUT_HIGH>;
};
adc0 {
compatible = "espressif,esp32-adc-oneshot";
unit-id = <ADC_UNIT_1>;
channels = <ADC_CHANNEL_4 ADC_ATTEN_DB_12 ADC_BITWIDTH_DEFAULT>;
};
// Battery voltage sits behind a 2:1 divider before reaching the ADC (see the deprecated HAL's
// old Power.cpp: adcReadValue() * 2). 3300mV matches its uncalibrated fallback reference
// voltage. Charge-percent curve is battery-sense's own fixed 3200-4200mV estimate (see
// TactilityKernel/source/drivers/battery_sense.cpp) rather than the old driver's 3300-4200mV
// ChargeFromVoltage curve - a shared kernel driver, not a per-board tunable.
battery-sense {
compatible = "battery-sense";
io-channel = <&adc0 0>;
reference-voltage-mv = <3300>;
multiplier = <2000>;
};
display_backlight {
compatible = "espressif,esp32-ledc-backlight";
// Off by default so display power-on won't show the screen from before the last power loss.
// The display backlight is turned on during the boot process.
status = "disabled";
pin-backlight = <&gpio0 38 GPIO_FLAG_NONE>;
frequency-hz = <30000>;
};
i8080_0 {
compatible = "espressif,esp32-i8080";
pin-dc = <&gpio0 7 GPIO_FLAG_NONE>;
pin-wr = <&gpio0 8 GPIO_FLAG_NONE>;
pin-d0 = <&gpio0 48 GPIO_FLAG_NONE>;
pin-d1 = <&gpio0 47 GPIO_FLAG_NONE>;
pin-d2 = <&gpio0 39 GPIO_FLAG_NONE>;
pin-d3 = <&gpio0 40 GPIO_FLAG_NONE>;
pin-d4 = <&gpio0 41 GPIO_FLAG_NONE>;
pin-d5 = <&gpio0 42 GPIO_FLAG_NONE>;
pin-d6 = <&gpio0 45 GPIO_FLAG_NONE>;
pin-d7 = <&gpio0 46 GPIO_FLAG_NONE>;
// horizontal-resolution * vertical-resolution / 10 (partial buffer) * 2 bytes/pixel
max-transfer-bytes = <15360>;
cs-gpios = <&gpio0 6 GPIO_FLAG_NONE>;
display@0 {
compatible = "display-placeholder";
compatible = "sitronix,st7789-i8080";
horizontal-resolution = <240>;
vertical-resolution = <320>;
pixel-clock-hz = <16000000>;
backlight = <&display_backlight>;
};
};
touch@1 {
compatible = "pointer-placeholder";
};
touch {
compatible = "xptek,xpt2046-softspi";
pin-mosi = <&gpio0 3 GPIO_FLAG_NONE>;
pin-miso = <&gpio0 4 GPIO_FLAG_NONE>;
pin-sck = <&gpio0 1 GPIO_FLAG_NONE>;
pin-cs = <&gpio0 2 GPIO_FLAG_NONE>;
x-max = <240>;
y-max = <320>;
};
// One-button mode: short press = select next, long press = press/enter (matches the
// deprecated HAL's old ButtonControl::createOneButtonControl(0)).
buttons {
compatible = "tactility,button-control";
pin-primary = <&gpio0 0 GPIO_FLAG_NONE>;
};
sdmmc0 {

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_io_expander esp_io_expander_tca95xx_16bit BQ24295 XPT2046
REQUIRES Tactility esp_lvgl_port esp_io_expander esp_io_expander_tca95xx_16bit BQ24295
)

View File

@ -1,17 +1,7 @@
#include "UnPhoneFeatures.h"
#include "devices/Hx8357Display.h"
#include <Tactility/hal/Configuration.h>
bool initBoot();
static tt::hal::DeviceVector createDevices() {
return {
createDisplay(),
};
}
extern const tt::hal::Configuration hardwareConfiguration = {
.initBoot = initBoot,
.createDevices = createDevices
.initBoot = initBoot
};

View File

@ -2,7 +2,6 @@
#include <tactility/device.h>
#include <Tactility/LogMessages.h>
#include <Tactility/Preferences.h>
#include <Tactility/TactilityCore.h>
#include <esp_sleep.h>
#include <tactility/log.h>
@ -161,7 +160,10 @@ static bool unPhonePowerOn() {
bootStats.printInfo();
bootStats.notifyBootStart();
bq24295 = std::make_shared<Bq24295>(device_find_by_name("i2c_internal"));
::Device* i2c_internal = nullptr;
check(device_get_by_name("i2c_internal", &i2c_internal) == ERROR_NONE);
bq24295 = std::make_shared<Bq24295>(i2c_internal);
device_put(i2c_internal);
unPhoneFeatures = std::make_shared<UnPhoneFeatures>(bq24295);
@ -172,7 +174,9 @@ static bool unPhonePowerOn() {
unPhoneFeatures->printInfo();
unPhoneFeatures->setBacklightPower(false);
// Kernel devicetree devices (incl. the hx8357 display) already started by kernel_init()
// before initBoot() runs, so it's safe to turn the backlight on here now.
unPhoneFeatures->setBacklightPower(true);
unPhoneFeatures->setVibePower(false);
unPhoneFeatures->setIrPower(false);
unPhoneFeatures->setExpanderPower(false);

View File

@ -1,119 +0,0 @@
#include "Hx8357Display.h"
#include "Touch.h"
#include <UnPhoneFeatures.h>
#include <hx8357/disp_spi.h>
#include <hx8357/hx8357.h>
#include <tactility/log.h>
constexpr auto* TAG = "Hx8357Display";
constexpr auto BUFFER_SIZE = (UNPHONE_LCD_HORIZONTAL_RESOLUTION * UNPHONE_LCD_DRAW_BUFFER_HEIGHT * LV_COLOR_DEPTH / 8);
extern std::shared_ptr<UnPhoneFeatures> unPhoneFeatures;
bool Hx8357Display::start() {
LOG_I(TAG, "start");
disp_spi_add_device(SPI2_HOST);
hx8357_reset(GPIO_NUM_46);
hx8357_init(UNPHONE_LCD_PIN_DC);
uint8_t madctl = (1U << MADCTL_BIT_INDEX_COLUMN_ADDRESS_ORDER);
hx8357_set_madctl(madctl);
return true;
}
bool Hx8357Display::stop() {
LOG_I(TAG, "stop");
disp_spi_remove_device();
return true;
}
bool Hx8357Display::startLvgl() {
LOG_I(TAG, "startLvgl");
if (lvglDisplay != nullptr) {
LOG_W(TAG, "LVGL was already started");
return false;
}
lvglDisplay = lv_display_create(UNPHONE_LCD_HORIZONTAL_RESOLUTION, UNPHONE_LCD_VERTICAL_RESOLUTION);
lv_display_set_physical_resolution(lvglDisplay, UNPHONE_LCD_HORIZONTAL_RESOLUTION, UNPHONE_LCD_VERTICAL_RESOLUTION);
lv_display_set_color_format(lvglDisplay, LV_COLOR_FORMAT_NATIVE);
// TODO malloc to use SPIRAM
buffer = static_cast<uint8_t*>(heap_caps_malloc(BUFFER_SIZE, MALLOC_CAP_DMA));
assert(buffer != nullptr);
lv_display_set_buffers(
lvglDisplay,
buffer,
nullptr,
BUFFER_SIZE,
LV_DISPLAY_RENDER_MODE_PARTIAL
);
lv_display_set_flush_cb(lvglDisplay, hx8357_flush);
if (lvglDisplay == nullptr) {
LOG_I(TAG, "Failed");
return false;
}
unPhoneFeatures->setBacklightPower(true);
auto touch_device = getTouchDevice();
if (touch_device != nullptr) {
touch_device->startLvgl(lvglDisplay);
}
return true;
}
bool Hx8357Display::stopLvgl() {
LOG_I(TAG, "stopLvgl");
if (lvglDisplay == nullptr) {
LOG_W(TAG, "LVGL was already stopped");
return false;
}
// Just in case
disp_wait_for_pending_transactions();
auto touch_device = getTouchDevice();
if (touch_device != nullptr && touch_device->getLvglIndev() != nullptr) {
LOG_I(TAG, "Stopping touch device");
touch_device->stopLvgl();
}
lv_display_delete(lvglDisplay);
lvglDisplay = nullptr;
heap_caps_free(buffer);
buffer = nullptr;
return true;
}
std::shared_ptr<tt::hal::touch::TouchDevice> Hx8357Display::getTouchDevice() {
if (touchDevice == nullptr) {
touchDevice = std::reinterpret_pointer_cast<tt::hal::touch::TouchDevice>(createTouch());
LOG_I(TAG, "Created touch device");
}
return touchDevice;
}
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
return std::make_shared<Hx8357Display>();
}
bool Hx8357Display::Hx8357Driver::drawBitmap(int xStart, int yStart, int xEnd, int yEnd, const void* pixelData) {
lv_area_t area = { xStart, yStart, xEnd, yEnd };
hx8357_flush(nullptr, &area, (uint8_t*)pixelData);
return true;
}

View File

@ -1,66 +0,0 @@
#pragma once
#include <Tactility/hal/display/DisplayDevice.h>
#include <Tactility/hal/display/DisplayDriver.h>
#include <esp_lcd_types.h>
#include <lvgl.h>
#include <driver/spi_common.h>
#define UNPHONE_LCD_SPI_HOST SPI2_HOST
#define UNPHONE_LCD_PIN_CS GPIO_NUM_48
#define UNPHONE_LCD_PIN_DC GPIO_NUM_47
#define UNPHONE_LCD_PIN_RESET GPIO_NUM_46
#define UNPHONE_LCD_SPI_FREQUENCY 27000000
#define UNPHONE_LCD_HORIZONTAL_RESOLUTION 320
#define UNPHONE_LCD_VERTICAL_RESOLUTION 480
#define UNPHONE_LCD_DRAW_BUFFER_HEIGHT (UNPHONE_LCD_VERTICAL_RESOLUTION / 15)
class Hx8357Display : public tt::hal::display::DisplayDevice {
uint8_t* buffer = nullptr;
lv_display_t* lvglDisplay = nullptr;
std::shared_ptr<tt::hal::touch::TouchDevice> touchDevice;
std::shared_ptr<tt::hal::display::DisplayDriver> nativeDisplay;
class Hx8357Driver : public tt::hal::display::DisplayDriver {
public:
tt::hal::display::ColorFormat getColorFormat() const override { return tt::hal::display::ColorFormat::RGB888; }
uint16_t getPixelWidth() const override { return UNPHONE_LCD_HORIZONTAL_RESOLUTION; }
uint16_t getPixelHeight() const override { return UNPHONE_LCD_VERTICAL_RESOLUTION; }
bool drawBitmap(int xStart, int yStart, int xEnd, int yEnd, const void* pixelData) override;
};
public:
std::string getName() const final { return "HX8357"; }
std::string getDescription() const final { return "SPI display"; }
bool start() override;
bool stop() override;
bool supportsLvgl() const override { return true; }
bool startLvgl() override;
bool stopLvgl() override;
std::shared_ptr<tt::hal::touch::TouchDevice> getTouchDevice() override;
lv_display_t* getLvglDisplay() const override { return lvglDisplay; }
// TODO: Set to true after fixing UnPhoneDisplayDriver
bool supportsDisplayDriver() const override { return false; }
std::shared_ptr<tt::hal::display::DisplayDriver> getDisplayDriver() override {
if (nativeDisplay == nullptr) {
nativeDisplay = std::make_shared<Hx8357Driver>();
}
assert(nativeDisplay != nullptr);
return nativeDisplay;
}
};
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay();

View File

@ -1,12 +0,0 @@
#include "Touch.h"
std::shared_ptr<Xpt2046Touch> createTouch() {
auto configuration = std::make_unique<Xpt2046Touch::Configuration>(
SPI2_HOST,
GPIO_NUM_38,
320,
480
);
return std::make_shared<Xpt2046Touch>(std::move(configuration));
}

View File

@ -1,8 +0,0 @@
#pragma once
#include <memory>
#include <Xpt2046Touch.h>
extern std::shared_ptr<Xpt2046Touch> touchInstance;
std::shared_ptr<Xpt2046Touch> createTouch();

View File

@ -1,4 +0,0 @@
The files in this folder are from https://github.com/lvgl/lvgl_esp32_drivers
The original license is an MIT license: https://github.com/lvgl/lvgl_esp32_drivers/blob/master/LICENSE
You may use the files in this folder under the original license, or under GPL v3 from the main Tactility project.

View File

@ -1,311 +0,0 @@
#define LV_USE_PRIVATE_API 1 // For actual lv_obj_t declaration
/**
* @file disp_spi.c
*
*/
/*********************
* INCLUDES
*********************/
#include "esp_system.h"
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "esp_log.h"
#define TAG "disp_spi"
#include <string.h>
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <freertos/task.h>
#include <lvgl.h>
#include "disp_spi.h"
//#include "disp_driver.h"
//#include "../lvgl_helpers.h"
#include "../lvgl_spi_conf.h"
/******************************************************************************
* Notes about DMA spi_transaction_ext_t structure pooling
*
* An xQueue is used to hold a pool of reusable SPI spi_transaction_ext_t
* structures that get used for all DMA SPI transactions. While an xQueue may
* seem like overkill it is an already built-in RTOS feature that comes at
* little cost. xQueues are also ISR safe if it ever becomes necessary to
* access the pool in the ISR callback.
*
* When a DMA request is sent, a transaction structure is removed from the
* pool, filled out, and passed off to the esp32 SPI driver. Later, when
* servicing pending SPI transaction results, the transaction structure is
* recycled back into the pool for later reuse. This matches the DMA SPI
* transaction life cycle requirements of the esp32 SPI driver.
*
* When polling or synchronously sending SPI requests, and as required by the
* esp32 SPI driver, all pending DMA transactions are first serviced. Then the
* polling SPI request takes place.
*
* When sending an asynchronous DMA SPI request, if the pool is empty, some
* small percentage of pending transactions are first serviced before sending
* any new DMA SPI transactions. Not too many and not too few as this balance
* controls DMA transaction latency.
*
* It is therefore not the design that all pending transactions must be
* serviced and placed back into the pool with DMA SPI requests - that
* will happen eventually. The pool just needs to contain enough to float some
* number of in-flight SPI requests to speed up the overall DMA SPI data rate
* and reduce transaction latency. If however a display driver uses some
* polling SPI requests or calls disp_wait_for_pending_transactions() directly,
* the pool will reach the full state more often and speed up DMA queuing.
*
*****************************************************************************/
/*********************
* DEFINES
*********************/
#define SPI_TRANSACTION_POOL_SIZE 50 /* maximum number of DMA transactions simultaneously in-flight */
/* DMA Transactions to reserve before queueing additional DMA transactions. A 1/10th seems to be a good balance. Too many (or all) and it will increase latency. */
#define SPI_TRANSACTION_POOL_RESERVE_PERCENTAGE 10
#if SPI_TRANSACTION_POOL_SIZE >= SPI_TRANSACTION_POOL_RESERVE_PERCENTAGE
#define SPI_TRANSACTION_POOL_RESERVE (SPI_TRANSACTION_POOL_SIZE / SPI_TRANSACTION_POOL_RESERVE_PERCENTAGE)
#else
#define SPI_TRANSACTION_POOL_RESERVE 1 /* defines minimum size */
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void spi_ready(spi_transaction_t*trans);
/**********************
* STATIC VARIABLES
**********************/
static spi_host_device_t spi_host;
static spi_device_handle_t spi;
static QueueHandle_t TransactionPool = NULL;
static transaction_cb_t chained_post_cb;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void disp_spi_add_device_config(spi_host_device_t host, spi_device_interface_config_t *devcfg)
{
spi_host=host;
chained_post_cb=devcfg->post_cb;
devcfg->post_cb=spi_ready;
esp_err_t ret=spi_bus_add_device(host, devcfg, &spi);
assert(ret==ESP_OK);
}
void disp_spi_add_device(spi_host_device_t host)
{
disp_spi_add_device_with_speed(host, SPI_TFT_CLOCK_SPEED_HZ);
}
void disp_spi_add_device_with_speed(spi_host_device_t host, int clock_speed_hz)
{
ESP_LOGI(TAG, "Adding SPI device");
ESP_LOGI(TAG, "Clock speed: %dHz, mode: %d, CS pin: %d",
clock_speed_hz, SPI_TFT_SPI_MODE, DISP_SPI_CS);
spi_device_interface_config_t devcfg={
.clock_speed_hz = clock_speed_hz,
.mode = SPI_TFT_SPI_MODE,
.spics_io_num=DISP_SPI_CS, // CS pin
.input_delay_ns=DISP_SPI_INPUT_DELAY_NS,
.queue_size=SPI_TRANSACTION_POOL_SIZE,
.pre_cb=NULL,
.post_cb=NULL,
#if defined(DISP_SPI_HALF_DUPLEX)
.flags = SPI_DEVICE_NO_DUMMY | SPI_DEVICE_HALFDUPLEX, /* dummy bits should be explicitly handled via DISP_SPI_VARIABLE_DUMMY as needed */
#else
#if defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_FT81X)
.flags = 0,
#elif defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_RA8875)
.flags = SPI_DEVICE_NO_DUMMY,
#endif
#endif
};
disp_spi_add_device_config(host, &devcfg);
/* create the transaction pool and fill it with ptrs to spi_transaction_ext_t to reuse */
if(TransactionPool == NULL) {
TransactionPool = xQueueCreate(SPI_TRANSACTION_POOL_SIZE, sizeof(spi_transaction_ext_t*));
assert(TransactionPool != NULL);
for (size_t i = 0; i < SPI_TRANSACTION_POOL_SIZE; i++)
{
spi_transaction_ext_t* pTransaction = (spi_transaction_ext_t*)heap_caps_malloc(sizeof(spi_transaction_ext_t), MALLOC_CAP_DMA);
assert(pTransaction != NULL);
memset(pTransaction, 0, sizeof(spi_transaction_ext_t));
xQueueSend(TransactionPool, &pTransaction, portMAX_DELAY);
}
}
}
void disp_spi_change_device_speed(int clock_speed_hz)
{
if (clock_speed_hz <= 0) {
clock_speed_hz = SPI_TFT_CLOCK_SPEED_HZ;
}
ESP_LOGI(TAG, "Changing SPI device clock speed: %d", clock_speed_hz);
disp_spi_remove_device();
disp_spi_add_device_with_speed(spi_host, clock_speed_hz);
}
void disp_spi_remove_device()
{
/* Wait for previous pending transaction results */
disp_wait_for_pending_transactions();
esp_err_t ret=spi_bus_remove_device(spi);
assert(ret==ESP_OK);
}
void disp_spi_transaction(const uint8_t *data, size_t length,
int flags, uint8_t *out,
uint64_t addr, uint8_t dummy_bits)
{
if (0 == length) {
return;
}
spi_transaction_ext_t t = {0};
/* transaction length is in bits */
t.base.length = length * 8;
if (length <= 4 && data != NULL) {
t.base.flags = SPI_TRANS_USE_TXDATA;
memcpy(t.base.tx_data, data, length);
} else {
t.base.tx_buffer = data;
}
if (flags & DISP_SPI_RECEIVE) {
assert(out != NULL && (flags & (DISP_SPI_SEND_POLLING | DISP_SPI_SEND_SYNCHRONOUS)));
t.base.rx_buffer = out;
#if defined(DISP_SPI_HALF_DUPLEX)
t.base.rxlength = t.base.length;
t.base.length = 0; /* no MOSI phase in half-duplex reads */
#else
t.base.rxlength = 0; /* in full-duplex mode, zero means same as tx length */
#endif
}
if (flags & DISP_SPI_ADDRESS_8) {
t.address_bits = 8;
} else if (flags & DISP_SPI_ADDRESS_16) {
t.address_bits = 16;
} else if (flags & DISP_SPI_ADDRESS_24) {
t.address_bits = 24;
} else if (flags & DISP_SPI_ADDRESS_32) {
t.address_bits = 32;
}
if (t.address_bits) {
t.base.addr = addr;
t.base.flags |= SPI_TRANS_VARIABLE_ADDR;
}
#if defined(DISP_SPI_HALF_DUPLEX)
if (flags & DISP_SPI_MODE_DIO) {
t.base.flags |= SPI_TRANS_MODE_DIO;
} else if (flags & DISP_SPI_MODE_QIO) {
t.base.flags |= SPI_TRANS_MODE_QIO;
}
if (flags & DISP_SPI_MODE_DIOQIO_ADDR) {
t.base.flags |= SPI_TRANS_MODE_DIOQIO_ADDR;
}
if ((flags & DISP_SPI_VARIABLE_DUMMY) && dummy_bits) {
t.dummy_bits = dummy_bits;
t.base.flags |= SPI_TRANS_VARIABLE_DUMMY;
}
#endif
/* Save flags for pre/post transaction processing */
t.base.user = (void *) flags;
/* Poll/Complete/Queue transaction */
if (flags & DISP_SPI_SEND_POLLING) {
disp_wait_for_pending_transactions(); /* before polling, all previous pending transactions need to be serviced */
spi_device_polling_transmit(spi, (spi_transaction_t *) &t);
} else if (flags & DISP_SPI_SEND_SYNCHRONOUS) {
disp_wait_for_pending_transactions(); /* before synchronous queueing, all previous pending transactions need to be serviced */
spi_device_transmit(spi, (spi_transaction_t *) &t);
} else {
/* if necessary, ensure we can queue new transactions by servicing some previous transactions */
if(uxQueueMessagesWaiting(TransactionPool) == 0) {
spi_transaction_t *presult;
while(uxQueueMessagesWaiting(TransactionPool) < SPI_TRANSACTION_POOL_RESERVE) {
if (spi_device_get_trans_result(spi, &presult, 1) == ESP_OK) {
xQueueSend(TransactionPool, &presult, portMAX_DELAY); /* back to the pool to be reused */
}
}
}
spi_transaction_ext_t *pTransaction = NULL;
xQueueReceive(TransactionPool, &pTransaction, portMAX_DELAY);
memcpy(pTransaction, &t, sizeof(t));
if (spi_device_queue_trans(spi, (spi_transaction_t *) pTransaction, portMAX_DELAY) != ESP_OK) {
xQueueSend(TransactionPool, &pTransaction, portMAX_DELAY); /* send failed transaction back to the pool to be reused */
}
}
}
void disp_wait_for_pending_transactions(void)
{
spi_transaction_t *presult;
while(uxQueueMessagesWaiting(TransactionPool) < SPI_TRANSACTION_POOL_SIZE) { /* service until the transaction reuse pool is full again */
if (spi_device_get_trans_result(spi, &presult, 1) == ESP_OK) {
xQueueSend(TransactionPool, &presult, portMAX_DELAY);
}
}
}
void disp_spi_acquire(void)
{
esp_err_t ret = spi_device_acquire_bus(spi, portMAX_DELAY);
assert(ret == ESP_OK);
}
void disp_spi_release(void)
{
spi_device_release_bus(spi);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void IRAM_ATTR spi_ready(spi_transaction_t *trans)
{
disp_spi_send_flag_t flags = (disp_spi_send_flag_t) trans->user;
if (flags & DISP_SPI_SIGNAL_FLUSH) {
lv_disp_t* disp = lv_refr_get_disp_refreshing();
lv_disp_flush_ready(disp);
}
if (chained_post_cb) {
chained_post_cb(trans);
}
}

View File

@ -1,81 +0,0 @@
/**
* @file disp_spi.h
*
*/
#ifndef DISP_SPI_H
#define DISP_SPI_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdint.h>
#include <stdbool.h>
#include <driver/spi_master.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef enum _disp_spi_send_flag_t {
DISP_SPI_SEND_QUEUED = 0x00000000,
DISP_SPI_SEND_POLLING = 0x00000001,
DISP_SPI_SEND_SYNCHRONOUS = 0x00000002,
DISP_SPI_SIGNAL_FLUSH = 0x00000004,
DISP_SPI_RECEIVE = 0x00000008,
DISP_SPI_CMD_8 = 0x00000010, /* Reserved */
DISP_SPI_CMD_16 = 0x00000020, /* Reserved */
DISP_SPI_ADDRESS_8 = 0x00000040,
DISP_SPI_ADDRESS_16 = 0x00000080,
DISP_SPI_ADDRESS_24 = 0x00000100,
DISP_SPI_ADDRESS_32 = 0x00000200,
DISP_SPI_MODE_DIO = 0x00000400,
DISP_SPI_MODE_QIO = 0x00000800,
DISP_SPI_MODE_DIOQIO_ADDR = 0x00001000,
DISP_SPI_VARIABLE_DUMMY = 0x00002000,
} disp_spi_send_flag_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void disp_spi_add_device(spi_host_device_t host);
void disp_spi_add_device_config(spi_host_device_t host, spi_device_interface_config_t *devcfg);
void disp_spi_add_device_with_speed(spi_host_device_t host, int clock_speed_hz);
void disp_spi_change_device_speed(int clock_speed_hz);
void disp_spi_remove_device();
/* Important!
All buffers should also be 32-bit aligned and DMA capable to prevent extra allocations and copying.
When DMA reading (even in polling mode) the ESP32 always read in 4-byte chunks even if less is requested.
Extra space will be zero filled. Always ensure the out buffer is large enough to hold at least 4 bytes!
*/
void disp_spi_transaction(const uint8_t *data, size_t length,
int flags, uint8_t *out, uint64_t addr, uint8_t dummy_bits);
void disp_wait_for_pending_transactions(void);
void disp_spi_acquire(void);
void disp_spi_release(void);
static inline void disp_spi_send_data(uint8_t *data, size_t length) {
disp_spi_transaction(data, length, DISP_SPI_SEND_POLLING, NULL, 0, 0);
}
static inline void disp_spi_send_colors(uint8_t *data, size_t length) {
disp_spi_transaction(data, length,
DISP_SPI_SEND_QUEUED | DISP_SPI_SIGNAL_FLUSH,
NULL, 0, 0);
}
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*DISP_SPI_H*/

View File

@ -1,292 +0,0 @@
/**
* @file HX8357.c
*
* Roughly based on the Adafruit_HX8357_Library
*
* This library should work with:
* Adafruit 3.5" TFT 320x480 + Touchscreen Breakout
* http://www.adafruit.com/products/2050
*
* Adafruit TFT FeatherWing - 3.5" 480x320 Touchscreen for Feathers
* https://www.adafruit.com/product/3651
*
*/
/*********************
* INCLUDES
*********************/
#include "hx8357.h"
#include "disp_spi.h"
#include "driver/gpio.h"
#include <esp_log.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
/*********************
* DEFINES
*********************/
#define TAG "HX8357"
/**********************
* TYPEDEFS
**********************/
static gpio_num_t dcPin = GPIO_NUM_NC;
/*The LCD needs a bunch of command/argument values to be initialized. They are stored in this struct. */
typedef struct {
uint8_t cmd;
uint8_t data[16];
uint8_t databytes; //No of data in data; bit 7 = delay after set; 0xFF = end of cmds.
} lcd_init_cmd_t;
/**********************
* STATIC PROTOTYPES
**********************/
static void hx8357_send_cmd(uint8_t cmd);
static void hx8357_send_data(void * data, uint16_t length);
static void hx8357_send_color(void * data, uint16_t length);
/**********************
* INITIALIZATION ARRAYS
**********************/
// Taken from the Adafruit driver
static const uint8_t
initb[] = {
HX8357B_SETPOWER, 3,
0x44, 0x41, 0x06,
HX8357B_SETVCOM, 2,
0x40, 0x10,
HX8357B_SETPWRNORMAL, 2,
0x05, 0x12,
HX8357B_SET_PANEL_DRIVING, 5,
0x14, 0x3b, 0x00, 0x02, 0x11,
HX8357B_SETDISPLAYFRAME, 1,
0x0c, // 6.8mhz
HX8357B_SETPANELRELATED, 1,
0x01, // BGR
0xEA, 3, // seq_undefined1, 3 args
0x03, 0x00, 0x00,
0xEB, 4, // undef2, 4 args
0x40, 0x54, 0x26, 0xdb,
HX8357B_SETGAMMA, 12,
0x00, 0x15, 0x00, 0x22, 0x00, 0x08, 0x77, 0x26, 0x66, 0x22, 0x04, 0x00,
HX8357_MADCTL, 1,
0xC0,
HX8357_COLMOD, 1,
0x55,
HX8357_PASET, 4,
0x00, 0x00, 0x01, 0xDF,
HX8357_CASET, 4,
0x00, 0x00, 0x01, 0x3F,
HX8357B_SETDISPMODE, 1,
0x00, // CPU (DBI) and internal oscillation ??
HX8357_SLPOUT, 0x80 + 120/5, // Exit sleep, then delay 120 ms
HX8357_DISPON, 0x80 + 10/5, // Main screen turn on, delay 10 ms
0 // END OF COMMAND LIST
}, initd[] = {
HX8357_SWRESET, 0x80 + 100/5, // Soft reset, then delay 10 ms
HX8357D_SETC, 3,
0xFF, 0x83, 0x57,
0xFF, 0x80 + 500/5, // No command, just delay 300 ms
HX8357_SETRGB, 4,
0x80, 0x00, 0x06, 0x06, // 0x80 enables SDO pin (0x00 disables)
HX8357D_SETCOM, 1,
0x25, // -1.52V
HX8357_SETOSC, 1,
0x68, // Normal mode 70Hz, Idle mode 55 Hz
HX8357_SETPANEL, 1,
0x05, // BGR, Gate direction swapped
HX8357_SETPWR1, 6,
0x00, // Not deep standby
0x15, // BT
0x1C, // VSPR
0x1C, // VSNR
0x83, // AP
0xAA, // FS
HX8357D_SETSTBA, 6,
0x50, // OPON normal
0x50, // OPON idle
0x01, // STBA
0x3C, // STBA
0x1E, // STBA
0x08, // GEN
HX8357D_SETCYC, 7,
0x02, // NW 0x02
0x40, // RTN
0x00, // DIV
0x2A, // DUM
0x2A, // DUM
0x0D, // GDON
0x78, // GDOFF
HX8357D_SETGAMMA, 34,
0x02, 0x0A, 0x11, 0x1d, 0x23, 0x35, 0x41, 0x4b, 0x4b,
0x42, 0x3A, 0x27, 0x1B, 0x08, 0x09, 0x03, 0x02, 0x0A,
0x11, 0x1d, 0x23, 0x35, 0x41, 0x4b, 0x4b, 0x42, 0x3A,
0x27, 0x1B, 0x08, 0x09, 0x03, 0x00, 0x01,
HX8357_COLMOD, 1,
0x57, // 0x55 = 16 bit, 0x57 = 24bit
HX8357_MADCTL, 1,
0xC0,
HX8357_TEON, 1,
0x00, // TW off
HX8357_TEARLINE, 2,
0x00, 0x02,
HX8357_SLPOUT, 0x80 + 150/5, // Exit Sleep, then delay 150 ms
HX8357_DISPON, 0x80 + 50/5, // Main screen turn on, delay 50 ms
0, // END OF COMMAND LIST
};
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
static uint8_t displayType = HX8357D;
void hx8357_reset(gpio_num_t resetPin) {
if (resetPin != GPIO_NUM_NC) {
esp_rom_gpio_pad_select_gpio(resetPin);
gpio_set_direction(resetPin, GPIO_MODE_OUTPUT);
//Reset the display
gpio_set_level(resetPin, 0);
vTaskDelay(10 / portTICK_PERIOD_MS);
gpio_set_level(resetPin, 1);
vTaskDelay(120 / portTICK_PERIOD_MS);
}
}
void hx8357_init(gpio_num_t newDcPin) {
ESP_LOGI(TAG, "Initialization.");
dcPin = newDcPin;
//Initialize non-SPI GPIOs
esp_rom_gpio_pad_select_gpio(dcPin);
gpio_set_direction(dcPin, GPIO_MODE_OUTPUT);
//Send all the commands
const uint8_t *addr = (displayType == HX8357B) ? initb : initd;
uint8_t cmd, x, numArgs;
while((cmd = *addr++) > 0) { // '0' command ends list
x = *addr++;
numArgs = x & 0x7F;
if (cmd != 0xFF) { // '255' is ignored
if (x & 0x80) { // If high bit set, numArgs is a delay time
hx8357_send_cmd(cmd);
} else {
hx8357_send_cmd(cmd);
hx8357_send_data((void *) addr, numArgs);
addr += numArgs;
}
}
if (x & 0x80) { // If high bit set...
vTaskDelay(numArgs * 5 / portTICK_PERIOD_MS); // numArgs is actually a delay time (5ms units)
}
}
#if HX8357_INVERT_COLORS
hx8357_send_cmd(HX8357_INVON);
#else
hx8357_send_cmd(HX8357_INVOFF);
#endif
}
//(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map);
void hx8357_flush(lv_disp_t* drv, const lv_area_t * area, uint8_t * color_map)
{
uint32_t size = lv_area_get_width(area) * lv_area_get_height(area);
/* Column addresses */
uint8_t xb[] = {
(uint8_t) (area->x1 >> 8) & 0xFF,
(uint8_t) (area->x1) & 0xFF,
(uint8_t) (area->x2 >> 8) & 0xFF,
(uint8_t) (area->x2) & 0xFF,
};
/* Page addresses */
uint8_t yb[] = {
(uint8_t) (area->y1 >> 8) & 0xFF,
(uint8_t) (area->y1) & 0xFF,
(uint8_t) (area->y2 >> 8) & 0xFF,
(uint8_t) (area->y2) & 0xFF,
};
/*Column addresses*/
hx8357_send_cmd(HX8357_CASET);
hx8357_send_data(xb, 4);
/*Page addresses*/
hx8357_send_cmd(HX8357_PASET);
hx8357_send_data(yb, 4);
/*Memory write*/
hx8357_send_cmd(HX8357_RAMWR);
hx8357_send_color((void*)color_map, size * (LV_COLOR_DEPTH / 8));
}
void hx8357_set_madctl(uint8_t value) {
hx8357_send_cmd(HX8357_MADCTL);
hx8357_send_data(&value, 1);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void hx8357_send_cmd(uint8_t cmd)
{
disp_wait_for_pending_transactions();
gpio_set_level(dcPin, 0); /*Command mode*/
disp_spi_send_data(&cmd, 1);
}
static void hx8357_send_data(void * data, uint16_t length)
{
disp_wait_for_pending_transactions();
gpio_set_level(dcPin, 1); /*Data mode*/
disp_spi_send_data(data, length);
}
static void hx8357_send_color(void * data, uint16_t length)
{
disp_wait_for_pending_transactions();
gpio_set_level(dcPin, 1); /*Data mode*/
disp_spi_send_colors(data, length);
}
uint8_t hx8357d_get_gamma_curve_count() {
return 4;
}
void hx8357d_set_gamme_curve(uint8_t index) {
uint8_t curve = 1;
switch (index) {
case 0:
curve = 0x01;
break;
case 1:
curve = 0x02;
break;
case 2:
curve = 0x04;
break;
case 3:
curve = 0x08;
break;
}
hx8357_send_cmd(HX8357D_SETGAMMA_BY_ID);
hx8357_send_data(&curve, 1);
}

View File

@ -1,133 +0,0 @@
/**
* @file hx8357.h
*
* Roughly based on the Adafruit_HX8357_Library
*
* This library should work with:
* Adafruit 3.5" TFT 320x480 + Touchscreen Breakout
* http://www.adafruit.com/products/2050
*
* Adafruit TFT FeatherWing - 3.5" 480x320 Touchscreen for Feathers
* https://www.adafruit.com/product/3651
*
* Datasheet:
* https://cdn-shop.adafruit.com/datasheets/HX8357-D_DS_April2012.pdf
*/
#ifndef HX8357_H
#define HX8357_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdbool.h>
#include <stdint.h>
#include <lvgl.h>
#include <soc/gpio_num.h>
#define HX8357D 0xD ///< Our internal const for D type
#define HX8357B 0xB ///< Our internal const for B type
#define HX8357_TFTWIDTH 320 ///< 320 pixels wide
#define HX8357_TFTHEIGHT 480 ///< 480 pixels tall
#define HX8357_NOP 0x00 ///< No op
#define HX8357_SWRESET 0x01 ///< software reset
#define HX8357_RDDID 0x04 ///< Read ID
#define HX8357_RDDST 0x09 ///< (unknown)
#define HX8357_RDPOWMODE 0x0A ///< Read power mode Read power mode
#define HX8357_RDMADCTL 0x0B ///< Read MADCTL
#define HX8357_RDCOLMOD 0x0C ///< Column entry mode
#define HX8357_RDDIM 0x0D ///< Read display image mode
#define HX8357_RDDSDR 0x0F ///< Read dosplay signal mode
#define HX8357_SLPIN 0x10 ///< Enter sleep mode
#define HX8357_SLPOUT 0x11 ///< Exit sleep mode
#define HX8357B_PTLON 0x12 ///< Partial mode on
#define HX8357B_NORON 0x13 ///< Normal mode
#define HX8357_INVOFF 0x20 ///< Turn off invert
#define HX8357_INVON 0x21 ///< Turn on invert
#define HX8357_DISPOFF 0x28 ///< Display on
#define HX8357_DISPON 0x29 ///< Display off
#define HX8357_CASET 0x2A ///< Column addr set
#define HX8357_PASET 0x2B ///< Page addr set
#define HX8357_RAMWR 0x2C ///< Write VRAM
#define HX8357_RAMRD 0x2E ///< Read VRAm
#define HX8357B_PTLAR 0x30 ///< (unknown)
#define HX8357_TEON 0x35 ///< Tear enable on
#define HX8357_TEARLINE 0x44 ///< (unknown)
#define HX8357_MADCTL 0x36 ///< Memory access control
#define HX8357_COLMOD 0x3A ///< Color mode
#define HX8357_SETOSC 0xB0 ///< Set oscillator
#define HX8357_SETPWR1 0xB1 ///< Set power control
#define HX8357B_SETDISPLAY 0xB2 ///< Set display mode
#define HX8357_SETRGB 0xB3 ///< Set RGB interface
#define HX8357D_SETCOM 0xB6 ///< Set VCOM voltage
#define HX8357B_SETDISPMODE 0xB4 ///< Set display mode
#define HX8357D_SETCYC 0xB4 ///< Set display cycle reg
#define HX8357B_SETOTP 0xB7 ///< Set OTP memory
#define HX8357D_SETC 0xB9 ///< Enable extension command
#define HX8357B_SET_PANEL_DRIVING 0xC0 ///< Set panel drive mode
#define HX8357D_SETSTBA 0xC0 ///< Set source option
#define HX8357B_SETDGC 0xC1 ///< Set DGC settings
#define HX8357B_SETID 0xC3 ///< Set ID
#define HX8357B_SETDDB 0xC4 ///< Set DDB
#define HX8357B_SETDISPLAYFRAME 0xC5 ///< Set display frame
#define HX8357B_GAMMASET 0xC8 ///< Set Gamma correction
#define HX8357B_SETCABC 0xC9 ///< Set CABC
#define HX8357_SETPANEL 0xCC ///< Set Panel
#define HX8357B_SETPOWER 0xD0 ///< Set power control
#define HX8357B_SETVCOM 0xD1 ///< Set VCOM
#define HX8357B_SETPWRNORMAL 0xD2 ///< Set power normal
#define HX8357B_RDID1 0xDA ///< Read ID #1
#define HX8357B_RDID2 0xDB ///< Read ID #2
#define HX8357B_RDID3 0xDC ///< Read ID #3
#define HX8357B_RDID4 0xDD ///< Read ID #4
#define HX8357D_SETGAMMA 0xE0 ///< Set Gamma curve data
#define HX8357D_SETGAMMA_BY_ID 0x26 ///< Set Gamma curve by curve identifier (0x01, 0x02, 0x04, 0x08)
#define HX8357B_SETGAMMA 0xC8 ///< Set Gamma
#define HX8357B_SETPANELRELATED 0xE9 ///< Set panel related
// MADCTL
// See datasheet page 123: https://cdn-shop.adafruit.com/datasheets/HX8357-D_DS_April2012.pdf
#define MADCTL_BIT_INDEX_COMMON_OUTPUTS_RAM 0 // N/A - set to 0
#define MADCTL_BIT_INDEX_SEGMENT_OUTPUTS_RAM 1 // N/A - set to 0
#define MADCTL_BIT_INDEX_DATA_LATCH_ORDER 2 // 0 = left-to-right refresh, 1 = right-to-left
#define MADCTL_BIT_INDEX_RGB_BGR_ORDER 3 // 0 = RGB, 1 = BGR
#define MADCTL_BIT_INDEX_LINE_ADDRESS_ORDER 4 // 0 = top-to-bottom refresh, 1 = bottom-to-top
#define MADCTL_BIT_INDEX_PAGE_COLUMN_ORDER 5 // 0 = normal, 1 = reverse
#define MADCTL_BIT_INDEX_COLUMN_ADDRESS_ORDER 6 // 0 = left-to-right, 1 = right-to-left
#define MADCTL_BIT_INDEX_PAGE_ADDRESS_ORDER 7 // 0 = top-to-bottom, 1 = bottom-to-top
void hx8357_reset(gpio_num_t resetPin);
void hx8357_init(gpio_num_t dcPin);
void hx8357_set_madctl(uint8_t value);
void hx8357_flush(lv_disp_t* drv, const lv_area_t* area, uint8_t* color_map);
uint8_t hx8357d_get_gamma_curve_count();
/**
* Note: this doesn't work, even though the manual says it should
* Page 141: https://cdn-shop.adafruit.com/datasheets/HX8357-D_DS_April2012.pdf
*/
void hx8357d_set_gamme_curve(uint8_t index);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*HX8357_H*/

View File

@ -1,7 +0,0 @@
#pragma once
#define SPI_TFT_CLOCK_SPEED_HZ (26*1000*1000)
#define SPI_TFT_SPI_MODE (0)
#define DISP_SPI_CS GPIO_NUM_48
#define DISP_SPI_INPUT_DELAY_NS 0

View File

@ -16,6 +16,9 @@ display.size=3.5"
display.shape=rectangle
display.dpi=165
touch.calibrationSupported=true
touch.calibrationRequired=true
cdn.warningMessage=Put the device into bootloader mode by pressing the center nav button and reset for 2-3 seconds, then release reset, then release the nav button.<br/>After flashing is finished, press the reset button to reboot.
lvgl.colorDepth=24

View File

@ -1,3 +1,5 @@
dependencies:
- Platforms/platform-esp32
- Drivers/hx8357-module
- Drivers/xpt2046-module
dts: unphone.dts

View File

@ -7,8 +7,8 @@
#include <tactility/bindings/esp32_i2c.h>
#include <tactility/bindings/esp32_spi.h>
#include <tactility/bindings/esp32_sdspi.h>
#include <tactility/bindings/display_placeholder.h>
#include <tactility/bindings/pointer_placeholder.h>
#include <bindings/hx8357.h>
#include <bindings/xpt2046.h>
/ {
compatible = "root";
@ -37,7 +37,7 @@
pin-scl = <&gpio0 4 GPIO_FLAG_NONE>;
};
sdcard_spi: spi0 {
spi0 {
compatible = "espressif,esp32-spi";
host = <SPI2_HOST>;
cs-gpios = <&gpio0 48 GPIO_FLAG_NONE>, // Display
@ -49,11 +49,19 @@
max-transfer-size = <65536>;
display@0 {
compatible = "display-placeholder";
compatible = "himax,hx8357";
horizontal-resolution = <320>;
vertical-resolution = <480>;
mirror-x;
pixel-clock-hz = <26000000>;
pin-dc = <&gpio0 47 GPIO_FLAG_NONE>;
pin-reset = <&gpio0 46 GPIO_FLAG_NONE>;
};
touch@1 {
compatible = "pointer-placeholder";
compatible = "xptek,xpt2046";
x-max = <320>;
y-max = <480>;
};
sdcard@2 {

View File

@ -1,5 +0,0 @@
idf_component_register(
SRC_DIRS "Source"
INCLUDE_DIRS "Source"
REQUIRES Tactility EspLcdCompat esp_lcd_touch_xpt2046
)

View File

@ -1,3 +0,0 @@
# XPT2046
A basic XPT2046 touch driver.

View File

@ -1,57 +0,0 @@
#include "Xpt2046Touch.h"
#include <Tactility/settings/TouchCalibrationSettings.h>
#include <Tactility/lvgl/LvglSync.h>
#include <algorithm>
#include <esp_err.h>
#include <esp_lcd_touch_xpt2046.h>
static void processCoordinates(esp_lcd_touch_handle_t tp, uint16_t* x, uint16_t* y, uint16_t* strength, uint8_t* pointCount, uint8_t maxPointCount) {
(void)strength;
if (tp == nullptr || x == nullptr || y == nullptr || pointCount == nullptr || *pointCount == 0) {
return;
}
auto* config = static_cast<Xpt2046Touch::Configuration*>(tp->config.user_data);
if (config == nullptr) {
return;
}
const auto settings = tt::settings::touch::getActive();
const auto points = std::min<uint8_t>(*pointCount, maxPointCount);
for (uint8_t i = 0; i < points; i++) {
tt::settings::touch::applyCalibration(settings, config->xMax, config->yMax, x[i], y[i]);
}
}
bool Xpt2046Touch::createIoHandle(esp_lcd_panel_io_handle_t& outHandle) {
const esp_lcd_panel_io_spi_config_t io_config = ESP_LCD_TOUCH_IO_SPI_XPT2046_CONFIG(configuration->spiPinCs);
return esp_lcd_new_panel_io_spi(configuration->spiDevice, &io_config, &outHandle) == ESP_OK;
}
bool Xpt2046Touch::createTouchHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_touch_config_t& config, esp_lcd_touch_handle_t& panelHandle) {
return esp_lcd_touch_new_spi_xpt2046(ioHandle, &config, &panelHandle) == ESP_OK;
}
esp_lcd_touch_config_t Xpt2046Touch::createEspLcdTouchConfig() {
return {
.x_max = configuration->xMax,
.y_max = configuration->yMax,
.rst_gpio_num = GPIO_NUM_NC,
.int_gpio_num = GPIO_NUM_NC,
.levels = {
.reset = 0,
.interrupt = 0,
},
.flags = {
.swap_xy = configuration->swapXy,
.mirror_x = configuration->mirrorX,
.mirror_y = configuration->mirrorY,
},
.process_coordinates = processCoordinates,
.interrupt_callback = nullptr,
.user_data = configuration.get(),
.driver_data = nullptr
};
}

View File

@ -1,61 +0,0 @@
#pragma once
#include <Tactility/hal/touch/TouchDevice.h>
#include <EspLcdTouch.h>
class Xpt2046Touch : public EspLcdTouch {
public:
class Configuration {
public:
Configuration(
esp_lcd_spi_bus_handle_t spiDevice,
gpio_num_t spiPinCs,
uint16_t xMax,
uint16_t yMax,
bool swapXy = false,
bool mirrorX = false,
bool mirrorY = false
) : spiDevice(spiDevice),
spiPinCs(spiPinCs),
xMax(xMax),
yMax(yMax),
swapXy(swapXy),
mirrorX(mirrorX),
mirrorY(mirrorY)
{}
esp_lcd_spi_bus_handle_t spiDevice;
gpio_num_t spiPinCs;
uint16_t xMax;
uint16_t yMax;
bool swapXy;
bool mirrorX;
bool mirrorY;
};
private:
std::unique_ptr<Configuration> configuration;
bool createIoHandle(esp_lcd_panel_io_handle_t& outHandle) override;
bool createTouchHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_touch_config_t& configuration, esp_lcd_touch_handle_t& panelHandle) override;
esp_lcd_touch_config_t createEspLcdTouchConfig() override;
public:
explicit Xpt2046Touch(std::unique_ptr<Configuration> inConfiguration) : configuration(std::move(inConfiguration)) {
assert(configuration != nullptr);
}
std::string getName() const final { return "XPT2046"; }
std::string getDescription() const final { return "XPT2046 SPI touch driver"; }
bool supportsCalibration() const override { return true; }
};

View File

@ -1,5 +0,0 @@
idf_component_register(
SRC_DIRS "Source"
INCLUDE_DIRS "Source"
REQUIRES Tactility driver esp_lvgl_port
)

View File

@ -1,4 +0,0 @@
# XPT2046_SoftSPI Driver
XPT2046_SoftSPI is a driver for the XPT2046 resistive touchscreen controller that uses SoftSPI.
Inspiration from: https://github.com/ddxfish/XPT2046_Bitbang_Arduino_Library/

View File

@ -1,241 +0,0 @@
#include "Xpt2046SoftSpi.h"
#include <tactility/log.h>
#include <Tactility/settings/TouchCalibrationSettings.h>
#include <algorithm>
#include <driver/gpio.h>
#include <esp_err.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <rom/ets_sys.h>
constexpr auto* TAG = "Xpt2046SoftSpi";
constexpr auto CMD_READ_Y = 0x90;
constexpr auto CMD_READ_X = 0xD0;
constexpr int RAW_MIN_DEFAULT = 100;
constexpr int RAW_MAX_DEFAULT = 1900;
constexpr int RAW_VALID_MIN = 100;
constexpr int RAW_VALID_MAX = 3900;
Xpt2046SoftSpi::Xpt2046SoftSpi(std::unique_ptr<Configuration> inConfiguration)
: configuration(std::move(inConfiguration)) {
assert(configuration != nullptr);
}
bool Xpt2046SoftSpi::start() {
LOG_I(TAG, "Starting Xpt2046SoftSpi touch driver");
// Configure GPIO pins
gpio_config_t io_conf = {};
// Configure MOSI, CLK, CS as outputs
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = (1ULL << configuration->mosiPin) |
(1ULL << configuration->clkPin) |
(1ULL << configuration->csPin);
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
if (gpio_config(&io_conf) != ESP_OK) {
LOG_E(TAG, "Failed to configure output pins");
return false;
}
// Configure MISO as input
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pin_bit_mask = (1ULL << configuration->misoPin);
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
if (gpio_config(&io_conf) != ESP_OK) {
LOG_E(TAG, "Failed to configure input pin");
return false;
}
// Initialize pin states
gpio_set_level(configuration->csPin, 1); // CS high
gpio_set_level(configuration->clkPin, 0); // CLK low
gpio_set_level(configuration->mosiPin, 0); // MOSI low
LOG_I(
TAG,
"GPIO configured: MOSI=%d, MISO=%d, CLK=%d, CS=%d",
static_cast<int>(configuration->mosiPin),
static_cast<int>(configuration->misoPin),
static_cast<int>(configuration->clkPin),
static_cast<int>(configuration->csPin)
);
return true;
}
bool Xpt2046SoftSpi::stop() {
LOG_I(TAG, "Stopping Xpt2046SoftSpi touch driver");
// Stop LVLG if needed
if (lvglDevice != nullptr) {
stopLvgl();
}
return true;
}
bool Xpt2046SoftSpi::startLvgl(lv_display_t* display) {
(void)display;
if (lvglDevice != nullptr) {
LOG_E(TAG, "LVGL was already started");
return false;
}
lvglDevice = lv_indev_create();
if (lvglDevice == nullptr) {
LOG_E(TAG, "Failed to create LVGL input device");
return false;
}
lv_indev_set_type(lvglDevice, LV_INDEV_TYPE_POINTER);
lv_indev_set_read_cb(lvglDevice, touchReadCallback);
lv_indev_set_user_data(lvglDevice, this);
LOG_I(TAG, "Xpt2046SoftSpi touch driver started successfully");
return true;
}
bool Xpt2046SoftSpi::stopLvgl() {
if (lvglDevice != nullptr) {
lv_indev_delete(lvglDevice);
lvglDevice = nullptr;
}
return true;
}
int Xpt2046SoftSpi::readSPI(uint8_t command) {
int result = 0;
// Pull CS low for this transaction
gpio_set_level(configuration->csPin, 0);
ets_delay_us(1);
// Send 8-bit command
for (int i = 7; i >= 0; i--) {
gpio_set_level(configuration->mosiPin, (command & (1 << i)) ? 1 : 0);
gpio_set_level(configuration->clkPin, 1);
ets_delay_us(1);
gpio_set_level(configuration->clkPin, 0);
ets_delay_us(1);
}
for (int i = 11; i >= 0; i--) {
gpio_set_level(configuration->clkPin, 1);
ets_delay_us(1);
if (gpio_get_level(configuration->misoPin)) {
result |= (1 << i);
}
gpio_set_level(configuration->clkPin, 0);
ets_delay_us(1);
}
// Pull CS high for this transaction
gpio_set_level(configuration->csPin, 1);
return result;
}
bool Xpt2046SoftSpi::readRawPoint(uint16_t& x, uint16_t& y) {
constexpr int sampleCount = 8;
int totalX = 0;
int totalY = 0;
int validSamples = 0;
for (int i = 0; i < sampleCount; i++) {
const int rawX = readSPI(CMD_READ_X);
const int rawY = readSPI(CMD_READ_Y);
if (rawX > RAW_VALID_MIN && rawX < RAW_VALID_MAX && rawY > RAW_VALID_MIN && rawY < RAW_VALID_MAX) {
totalX += rawX;
totalY += rawY;
validSamples++;
}
vTaskDelay(pdMS_TO_TICKS(1));
}
if (validSamples < 3) {
return false;
}
x = static_cast<uint16_t>(totalX / validSamples);
y = static_cast<uint16_t>(totalY / validSamples);
return true;
}
bool Xpt2046SoftSpi::getTouchPoint(Point& point) {
uint16_t rawX = 0;
uint16_t rawY = 0;
if (!readRawPoint(rawX, rawY)) {
return false;
}
int mappedX = (static_cast<int>(rawX) - RAW_MIN_DEFAULT) * static_cast<int>(configuration->xMax) /
(RAW_MAX_DEFAULT - RAW_MIN_DEFAULT);
int mappedY = (static_cast<int>(rawY) - RAW_MIN_DEFAULT) * static_cast<int>(configuration->yMax) /
(RAW_MAX_DEFAULT - RAW_MIN_DEFAULT);
if (configuration->swapXy) {
std::swap(mappedX, mappedY);
}
if (configuration->mirrorX) {
mappedX = static_cast<int>(configuration->xMax) - mappedX;
}
if (configuration->mirrorY) {
mappedY = static_cast<int>(configuration->yMax) - mappedY;
}
uint16_t x = static_cast<uint16_t>(std::clamp(mappedX, 0, static_cast<int>(configuration->xMax)));
uint16_t y = static_cast<uint16_t>(std::clamp(mappedY, 0, static_cast<int>(configuration->yMax)));
const auto calibration = tt::settings::touch::getActive();
tt::settings::touch::applyCalibration(calibration, configuration->xMax, configuration->yMax, x, y);
point.x = x;
point.y = y;
return true;
}
bool Xpt2046SoftSpi::isTouched() {
uint16_t x = 0;
uint16_t y = 0;
return readRawPoint(x, y);
}
void Xpt2046SoftSpi::touchReadCallback(lv_indev_t* indev, lv_indev_data_t* data) {
auto* touch = static_cast<Xpt2046SoftSpi*>(lv_indev_get_user_data(indev));
if (touch == nullptr) {
data->state = LV_INDEV_STATE_RELEASED;
return;
}
Point point;
if (touch->getTouchPoint(point)) {
data->point.x = point.x;
data->point.y = point.y;
data->state = LV_INDEV_STATE_PRESSED;
} else {
data->state = LV_INDEV_STATE_RELEASED;
}
}
// Return driver instance if any
std::shared_ptr<tt::hal::touch::TouchDriver> Xpt2046SoftSpi::getTouchDriver() {
assert(lvglDevice == nullptr); // Still attached to LVGL context. Call stopLvgl() first.
if (touchDriver == nullptr) {
touchDriver = std::make_shared<Xpt2046SoftSpiDriver>(this);
}
return touchDriver;
}

View File

@ -1,109 +0,0 @@
#pragma once
#include "Tactility/hal/touch/TouchDevice.h"
#include "Tactility/hal/touch/TouchDriver.h"
#include "lvgl.h"
#include <driver/gpio.h>
#include <memory>
#include <string>
#ifndef TFT_WIDTH
#define TFT_WIDTH 240
#endif
#ifndef TFT_HEIGHT
#define TFT_HEIGHT 320
#endif
struct Point {
int x;
int y;
};
class Xpt2046SoftSpi : public tt::hal::touch::TouchDevice {
public:
class Configuration {
public:
Configuration(
gpio_num_t mosiPin,
gpio_num_t misoPin,
gpio_num_t clkPin,
gpio_num_t csPin,
uint16_t xMax = TFT_WIDTH,
uint16_t yMax = TFT_HEIGHT,
bool swapXy = false,
bool mirrorX = false,
bool mirrorY = false
) : mosiPin(mosiPin),
misoPin(misoPin),
clkPin(clkPin),
csPin(csPin),
xMax(xMax),
yMax(yMax),
swapXy(swapXy),
mirrorX(mirrorX),
mirrorY(mirrorY)
{}
gpio_num_t mosiPin;
gpio_num_t misoPin;
gpio_num_t clkPin;
gpio_num_t csPin;
uint16_t xMax;
uint16_t yMax;
bool swapXy;
bool mirrorX;
bool mirrorY;
};
private:
class Xpt2046SoftSpiDriver final : public tt::hal::touch::TouchDriver {
Xpt2046SoftSpi* device;
public:
Xpt2046SoftSpiDriver(Xpt2046SoftSpi* device) : device(device) {}
bool getTouchedPoints(uint16_t* x, uint16_t* y, uint16_t* strength, uint8_t* pointCount, uint8_t maxPointCount) override {
Point point;
if (device->isTouched() && device->getTouchPoint(point)) {
*x = point.x;
*y = point.y;
*pointCount = 1;
return true;
} else {
*pointCount = 0;
return false;
}
}
};
std::unique_ptr<Configuration> configuration;
lv_indev_t* lvglDevice = nullptr;
std::shared_ptr<tt::hal::touch::TouchDriver> touchDriver;
int readSPI(uint8_t command);
bool readRawPoint(uint16_t& x, uint16_t& y);
static void touchReadCallback(lv_indev_t* indev, lv_indev_data_t* data);
public:
explicit Xpt2046SoftSpi(std::unique_ptr<Configuration> inConfiguration);
// TouchDevice interface
std::string getName() const final { return "Xpt2046SoftSpi"; }
std::string getDescription() const final { return "Xpt2046 Soft SPI touch driver"; }
bool start() override;
bool stop() override;
bool supportsLvgl() const override { return true; }
bool startLvgl(lv_display_t* display) override;
bool stopLvgl() override;
bool supportsTouchDriver() override { return true; }
bool supportsCalibration() const override { return true; }
std::shared_ptr<tt::hal::touch::TouchDriver> getTouchDriver() override;
lv_indev_t* getLvglIndev() override { return lvglDevice; }
// XPT2046-specific methods
bool getTouchPoint(Point& point);
bool isTouched();
};

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(hx8357-module
SRCS ${SOURCE_FILES}
INCLUDE_DIRS include/
REQUIRES TactilityKernel platform-esp32 driver
)

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,7 @@
# HX8357 Display Driver
A kernel driver for the `HX8357-D` display panel (24bpp/RGB888). No ESP-IDF `esp_lcd` component exists for this controller, so this driver speaks its raw SPI command protocol directly rather than wrapping `esp_lcd_panel_io`/`esp_lcd_panel`.
See https://cdn-shop.adafruit.com/datasheets/HX8357-D_DS_April2012.pdf
License: [Apache v2.0](LICENSE-Apache-2.0.md)

View File

@ -0,0 +1,64 @@
description: >
Himax HX8357-D display panel. 24bpp/RGB888 only. No ESP-IDF esp_lcd component exists for this
controller, so this driver speaks the panel's raw SPI command protocol directly (bit-banged
DC line, blocking spi_device_transmit()) rather than wrapping esp_lcd_panel_io/esp_lcd_panel.
No bgr-order property: unlike the RGB565 panels (ili9341-module, st7789-module), there is no
DISPLAY_COLOR_FORMAT_BGR888 in the kernel model, so a BGR-wired panel isn't representable here.
compatible: "himax,hx8357"
bus: spi
properties:
horizontal-resolution:
type: int
required: true
description: Horizontal resolution in pixels
vertical-resolution:
type: int
required: true
description: Vertical resolution in pixels
gap-x:
type: int
default: 0
description: X offset applied to all draw operations
gap-y:
type: int
default: 0
description: Y offset applied to all draw operations
swap-xy:
type: boolean
default: false
description: Swap the X and Y axes
mirror-x:
type: boolean
default: true
description: Mirror the X axis
mirror-y:
type: boolean
default: false
description: Mirror the Y axis
invert-color:
type: boolean
default: false
description: Invert the panel's color output
pixel-clock-hz:
type: int
default: 26000000
description: SPI pixel clock frequency in Hz
pin-dc:
type: phandles
required: true
description: Data/Command GPIO pin
pin-reset:
type: phandles
default: GPIO_PIN_SPEC_NONE
description: Reset GPIO pin
reset-active-high:
type: boolean
default: false
description: Whether the reset pin is active high
backlight:
type: phandle
default: "NULL"
description: Optional reference to this display's backlight device

View File

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

View File

@ -0,0 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <tactility/bindings/bindings.h>
#include <drivers/hx8357.h>
DEFINE_DEVICETREE(hx8357, struct Hx8357Config)

View File

@ -0,0 +1,33 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdbool.h>
#include <tactility/device.h>
#include <tactility/drivers/gpio.h>
struct Hx8357Config {
uint16_t horizontal_resolution;
uint16_t vertical_resolution;
int32_t gap_x;
int32_t gap_y;
bool swap_xy;
bool mirror_x;
bool mirror_y;
bool invert_color;
uint32_t pixel_clock_hz;
struct GpioPinSpec pin_dc;
struct GpioPinSpec pin_reset;
bool reset_active_high;
// Optional reference to this display's backlight device, NULL if none.
struct Device* backlight;
};
#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 hx8357_module;
#ifdef __cplusplus
}
#endif

Some files were not shown because too many files have changed in this diff Show More