New kernel drivers and device migrations (#563)
This commit is contained in:
committed by
GitHub
parent
955416dac8
commit
8af6204ba1
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -40,6 +40,8 @@ class BindingProperty:
|
||||
description: str
|
||||
default: object = None
|
||||
element_type: str = None
|
||||
min: object = None
|
||||
max: object = None
|
||||
|
||||
@dataclass
|
||||
class Binding:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
@@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -7,6 +7,8 @@ hardware.target=ESP32
|
||||
hardware.flashSize=4MB
|
||||
hardware.spiRam=false
|
||||
|
||||
dependencies.useDeprecatedHal=false
|
||||
|
||||
storage.userDataLocation=SD
|
||||
|
||||
display.size=2.4"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
dependencies:
|
||||
- Platforms/platform-esp32
|
||||
- Drivers/ili9341-module
|
||||
- Drivers/xpt2046-module
|
||||
dts: cyd,2432s024r.dts
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
@@ -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
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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>;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
dependencies:
|
||||
- Platforms/platform-esp32
|
||||
- Drivers/ili9341-module
|
||||
- Drivers/xpt2046-softspi-module
|
||||
dts: cyd,2432s028r.dts
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
@@ -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
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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>;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
dependencies:
|
||||
- Platforms/platform-esp32
|
||||
- Drivers/st7789-module
|
||||
- Drivers/xpt2046-softspi-module
|
||||
dts: cyd,2432s028rv3.dts
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#include <PwmBacklight.h>
|
||||
|
||||
static bool initBoot() {
|
||||
static bool init_boot() {
|
||||
if (!driver::pwmbacklight::init(LCD_PIN_BACKLIGHT)) {
|
||||
return false;
|
||||
}
|
||||
@@ -42,6 +42,6 @@ static tt::hal::DeviceVector createDevices() {
|
||||
}
|
||||
|
||||
extern const tt::hal::Configuration hardwareConfiguration = {
|
||||
.initBoot = initBoot,
|
||||
.initBoot = init_boot,
|
||||
.createDevices = createDevices
|
||||
};
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
@@ -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>;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
dependencies:
|
||||
- Platforms/platform-esp32
|
||||
- Drivers/ili9341-module
|
||||
- Drivers/xpt2046-softspi-module
|
||||
dts: cyd,e32r28t.dts
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
@@ -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);
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <Tactility/hal/power/PowerDevice.h>
|
||||
|
||||
std::shared_ptr<tt::hal::power::PowerDevice> createPower();
|
||||
@@ -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>;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
dependencies:
|
||||
- Platforms/platform-esp32
|
||||
- Drivers/st7789-module
|
||||
- Drivers/xpt2046-module
|
||||
dts: cyd,e32r32p.dts
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
dependencies:
|
||||
- Platforms/platform-esp32
|
||||
- Drivers/ili9341-module
|
||||
- Drivers/xpt2046-module
|
||||
dts: elecrow,crowpanel-basic-28.dts
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -15,4 +15,7 @@ display.size=3.5"
|
||||
display.shape=rectangle
|
||||
display.dpi=165
|
||||
|
||||
touch.calibrationSupported=true
|
||||
touch.calibrationRequired=false
|
||||
|
||||
lvgl.colorDepth=16
|
||||
|
||||
@@ -21,3 +21,5 @@ display.dpi=143
|
||||
cdn.infoMessage=To put the device into bootloader mode: <br/>1. Press the trackball and then the reset button at the same time,<br/>2. Let go of the reset button, then the trackball.<br/><br/>When this website reports that flashing is finished, you likely have to press the reset button.
|
||||
|
||||
lvgl.colorDepth=16
|
||||
|
||||
sdkconfig.CONFIG_CODEC_DUMMY_SUPPORT=y
|
||||
@@ -3,4 +3,7 @@ dependencies:
|
||||
- Drivers/st7789-module
|
||||
- Drivers/gt911-module
|
||||
- Drivers/lilygo-module
|
||||
- Drivers/es7210-module
|
||||
- Drivers/dummy-i2s-amp-module
|
||||
- Drivers/audio-stream-module
|
||||
dts: lilygo,tdeck.dts
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
#include <bindings/gt911.h>
|
||||
#include <bindings/st7789.h>
|
||||
#include <bindings/es7210.h>
|
||||
#include <bindings/dummy_i2s_amp.h>
|
||||
|
||||
#include <lilygo/bindings/tdeck_keyboard.h>
|
||||
#include <lilygo/bindings/tdeck_keyboard_backlight.h>
|
||||
@@ -64,6 +66,34 @@
|
||||
pin-click = <&gpio0 0 GPIO_FLAG_NONE>;
|
||||
};
|
||||
|
||||
// i2s0 and i2s1 are declared before i2c0 so i2s1 has started before the es7210 codec (below) references it.
|
||||
// Speaker I2S (MAX98357A amplifier)
|
||||
i2s0 {
|
||||
compatible = "espressif,esp32-i2s";
|
||||
port = <I2S_NUM_0>;
|
||||
pin-bclk = <&gpio0 7 GPIO_FLAG_NONE>;
|
||||
pin-ws = <&gpio0 5 GPIO_FLAG_NONE>;
|
||||
pin-data-out = <&gpio0 6 GPIO_FLAG_NONE>;
|
||||
};
|
||||
|
||||
// Microphone I2S (ES7210), separate port from the speaker
|
||||
i2s1 {
|
||||
compatible = "espressif,esp32-i2s";
|
||||
port = <I2S_NUM_1>;
|
||||
pin-bclk = <&gpio0 47 GPIO_FLAG_NONE>;
|
||||
pin-ws = <&gpio0 21 GPIO_FLAG_NONE>;
|
||||
pin-data-in = <&gpio0 14 GPIO_FLAG_NONE>;
|
||||
pin-mclk = <&gpio0 48 GPIO_FLAG_NONE>;
|
||||
};
|
||||
|
||||
// MAX98357A class-D speaker amplifier. No I2C. Per schematic, SD_MODE/GAIN_SLOT is
|
||||
// tied via fixed resistors (R21/R22), not driven by an MCU GPIO -- the amp is always
|
||||
// enabled when powered, so no enable-gpio is wired here.
|
||||
speaker0 {
|
||||
compatible = "maxim,max98357a";
|
||||
i2s = <&i2s0>;
|
||||
};
|
||||
|
||||
i2c_internal: i2c0 {
|
||||
compatible = "espressif,esp32-i2c";
|
||||
port = <I2C_NUM_0>;
|
||||
@@ -90,14 +120,18 @@
|
||||
compatible = "lilygo,tdeck-keyboard-backlight";
|
||||
reg = <0x55>;
|
||||
};
|
||||
};
|
||||
|
||||
i2s0 {
|
||||
compatible = "espressif,esp32-i2s";
|
||||
port = <I2S_NUM_0>;
|
||||
pin-bclk = <&gpio0 7 GPIO_FLAG_NONE>;
|
||||
pin-ws = <&gpio0 5 GPIO_FLAG_NONE>;
|
||||
pin-data-out = <&gpio0 6 GPIO_FLAG_NONE>;
|
||||
// ES7210 microphone ADC. Per schematic all 4 MSM381A3729H9CP mic capsules are
|
||||
// populated (MIC1-4), and AD0/AD1 are unconnected (default low -> address 0x40).
|
||||
// Stock LilyGO firmware drives MIC1/MIC2 at 0dB and MIC3/MIC4 at 37.5dB gain --
|
||||
// the es7210 driver here applies a single gain to all active mics, so per-pair
|
||||
// gain differentiation is not yet replicated.
|
||||
es7210 {
|
||||
compatible = "everest,es7210";
|
||||
reg = <0x40>;
|
||||
i2s = <&i2s1>;
|
||||
mic-mask = <15>;
|
||||
};
|
||||
};
|
||||
|
||||
display_backlight {
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
dependencies:
|
||||
- Platforms/platform-esp32
|
||||
- Drivers/xpt2046-softspi-module
|
||||
- Drivers/st7789-i8080-module
|
||||
- Drivers/button-control-module
|
||||
dts: lilygo,thmi.dts
|
||||
|
||||
@@ -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,84 @@
|
||||
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>;
|
||||
gamma-curve = <2>;
|
||||
};
|
||||
};
|
||||
|
||||
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>;
|
||||
};
|
||||
|
||||
sdmmc0 {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
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 esp_io_expander esp_io_expander_tca95xx_16bit BQ24295 XPT2046
|
||||
INCLUDE_DIRS "source"
|
||||
REQUIRES Tactility bq24295-module
|
||||
)
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
#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
|
||||
};
|
||||
@@ -1,197 +0,0 @@
|
||||
#include "UnPhoneFeatures.h"
|
||||
#include <tactility/device.h>
|
||||
#include <Tactility/LogMessages.h>
|
||||
#include <Tactility/Preferences.h>
|
||||
#include <Tactility/TactilityCore.h>
|
||||
#include <esp_sleep.h>
|
||||
#include <tactility/log.h>
|
||||
|
||||
constexpr auto* TAG = "unPhone";
|
||||
|
||||
std::shared_ptr<UnPhoneFeatures> unPhoneFeatures;
|
||||
static std::unique_ptr<tt::Thread> powerThread;
|
||||
|
||||
static const char* bootCountKey = "boot_count";
|
||||
static const char* powerOffCountKey = "power_off_count";
|
||||
static const char* powerSleepKey = "power_sleep_key";
|
||||
|
||||
class DeviceStats {
|
||||
|
||||
tt::Preferences preferences = tt::Preferences("unphone");
|
||||
|
||||
int32_t getValue(const char* key) {
|
||||
int32_t value = 0;
|
||||
preferences.optInt32(key, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
void setValue(const char* key, int32_t value) {
|
||||
preferences.putInt32(key, value);
|
||||
}
|
||||
|
||||
void increaseValue(const char* key) {
|
||||
int32_t new_value = getValue(key) + 1;
|
||||
setValue(key, new_value);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
void notifyBootStart() {
|
||||
increaseValue(bootCountKey);
|
||||
}
|
||||
|
||||
void notifyPowerOff() {
|
||||
increaseValue(powerOffCountKey);
|
||||
}
|
||||
|
||||
void notifyPowerSleep() {
|
||||
increaseValue(powerSleepKey);
|
||||
}
|
||||
|
||||
void printInfo() {
|
||||
LOG_I(TAG, "Device stats:");
|
||||
LOG_I(TAG, " boot: %d", (int)getValue(bootCountKey));
|
||||
LOG_I(TAG, " power off: %d", (int)getValue(powerOffCountKey));
|
||||
LOG_I(TAG, " power sleep: %d", (int)getValue(powerSleepKey));
|
||||
}
|
||||
};
|
||||
|
||||
DeviceStats bootStats;
|
||||
|
||||
enum class PowerState {
|
||||
Initial,
|
||||
On,
|
||||
Off
|
||||
};
|
||||
|
||||
#define DEBUG_POWER_STATES false
|
||||
|
||||
#if DEBUG_POWER_STATES
|
||||
/** Helper method to use the buzzer to signal the different power stages */
|
||||
static void powerInfoBuzz(uint8_t count) {
|
||||
if (DEBUG_POWER_STATES) {
|
||||
uint8_t index = 0;
|
||||
while (index < count) {
|
||||
unPhoneFeatures.setVibePower(true);
|
||||
tt::kernel::delayMillis(50);
|
||||
unPhoneFeatures.setVibePower(false);
|
||||
|
||||
index++;
|
||||
|
||||
if (index < count) {
|
||||
tt::kernel::delayMillis(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void updatePowerSwitch() {
|
||||
static PowerState last_state = PowerState::Initial;
|
||||
|
||||
if (!unPhoneFeatures->isPowerSwitchOn()) {
|
||||
if (last_state != PowerState::Off) {
|
||||
last_state = PowerState::Off;
|
||||
LOG_W(TAG, "Power off");
|
||||
}
|
||||
|
||||
if (!unPhoneFeatures->isUsbPowerConnected()) { // and usb unplugged we go into shipping mode
|
||||
LOG_W(TAG, "Shipping mode until USB connects");
|
||||
|
||||
#if DEBUG_POWER_STATES
|
||||
unPhoneFeatures.setExpanderPower(true);
|
||||
powerInfoBuzz(3);
|
||||
unPhoneFeatures.setExpanderPower(false);
|
||||
#endif
|
||||
|
||||
unPhoneFeatures->turnPeripheralsOff();
|
||||
|
||||
bootStats.notifyPowerOff();
|
||||
|
||||
unPhoneFeatures->setShipping(true); // tell BM to stop supplying power until USB connects
|
||||
} else { // When power switch is off, but USB is plugged in, we wait (deep sleep) until USB is unplugged.
|
||||
LOG_W(TAG, "Waiting for USB disconnect to power off");
|
||||
|
||||
#if DEBUG_POWER_STATES
|
||||
powerInfoBuzz(2);
|
||||
#endif
|
||||
|
||||
unPhoneFeatures->turnPeripheralsOff();
|
||||
|
||||
bootStats.notifyPowerSleep();
|
||||
|
||||
// Deep sleep for 1 minute, then awaken to check power state again
|
||||
// GPIO trigger from power switch also awakens the device
|
||||
unPhoneFeatures->wakeOnPowerSwitch();
|
||||
esp_sleep_enable_timer_wakeup(60000000);
|
||||
esp_deep_sleep_start();
|
||||
}
|
||||
} else {
|
||||
if (last_state != PowerState::On) {
|
||||
last_state = PowerState::On;
|
||||
LOG_W(TAG, "Power on");
|
||||
|
||||
#if DEBUG_POWER_STATES
|
||||
powerInfoBuzz(1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t powerSwitchMain() { // check power switch every 10th of sec
|
||||
while (true) {
|
||||
updatePowerSwitch();
|
||||
tt::kernel::delayMillis(200);
|
||||
}
|
||||
}
|
||||
|
||||
static void startPowerSwitchThread() {
|
||||
powerThread = std::make_unique<tt::Thread>(
|
||||
"unphone_power_switch",
|
||||
4096,
|
||||
[]() { return powerSwitchMain(); }
|
||||
);
|
||||
powerThread->start();
|
||||
}
|
||||
|
||||
std::shared_ptr<Bq24295> bq24295;
|
||||
|
||||
static bool unPhonePowerOn() {
|
||||
// Print early, in case of early crash (info will be from previous boot)
|
||||
bootStats.printInfo();
|
||||
bootStats.notifyBootStart();
|
||||
|
||||
bq24295 = std::make_shared<Bq24295>(device_find_by_name("i2c_internal"));
|
||||
|
||||
unPhoneFeatures = std::make_shared<UnPhoneFeatures>(bq24295);
|
||||
|
||||
if (!unPhoneFeatures->init()) {
|
||||
LOG_E(TAG, "UnPhoneFeatures init failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
unPhoneFeatures->printInfo();
|
||||
|
||||
unPhoneFeatures->setBacklightPower(false);
|
||||
unPhoneFeatures->setVibePower(false);
|
||||
unPhoneFeatures->setIrPower(false);
|
||||
unPhoneFeatures->setExpanderPower(false);
|
||||
|
||||
// Turn off the device if power switch is on off state,
|
||||
// instead of waiting for the Thread to start and continue booting
|
||||
updatePowerSwitch();
|
||||
startPowerSwitchThread();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool initBoot() {
|
||||
LOG_I(TAG, LOG_MESSAGE_POWER_ON_START);
|
||||
|
||||
if (!unPhonePowerOn()) {
|
||||
LOG_E(TAG, LOG_MESSAGE_POWER_ON_FAILED);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,306 +0,0 @@
|
||||
#include "UnPhoneFeatures.h"
|
||||
|
||||
#include <Tactility/app/App.h>
|
||||
#include <Tactility/kernel/Kernel.h>
|
||||
|
||||
#include <driver/gpio.h>
|
||||
#include <driver/rtc_io.h>
|
||||
#include <esp_io_expander.h>
|
||||
#include <esp_sleep.h>
|
||||
#include <tactility/log.h>
|
||||
|
||||
constexpr auto* TAG = "unPhoneFeatures";
|
||||
|
||||
namespace pin {
|
||||
static const gpio_num_t BUTTON1 = GPIO_NUM_45; // left button
|
||||
static const gpio_num_t BUTTON2 = GPIO_NUM_0; // middle button
|
||||
static const gpio_num_t BUTTON3 = GPIO_NUM_21; // right button
|
||||
static const gpio_num_t IR_LEDS = GPIO_NUM_12;
|
||||
static const gpio_num_t LED_RED = GPIO_NUM_13;
|
||||
static const gpio_num_t POWER_SWITCH = GPIO_NUM_18;
|
||||
} // namespace pin
|
||||
|
||||
namespace expanderpin {
|
||||
static const esp_io_expander_pin_num_t BACKLIGHT = IO_EXPANDER_PIN_NUM_2;
|
||||
static const esp_io_expander_pin_num_t EXPANDER_POWER = IO_EXPANDER_PIN_NUM_0; // enable exp brd if high
|
||||
static const esp_io_expander_pin_num_t LED_GREEN = IO_EXPANDER_PIN_NUM_9;
|
||||
static const esp_io_expander_pin_num_t LED_BLUE = IO_EXPANDER_PIN_NUM_13;
|
||||
static const esp_io_expander_pin_num_t USB_VSENSE = IO_EXPANDER_PIN_NUM_14;
|
||||
static const esp_io_expander_pin_num_t VIBE = IO_EXPANDER_PIN_NUM_7;
|
||||
} // namespace expanderpin
|
||||
|
||||
// TODO: Make part of a new type of UnPhoneFeatures data struct that holds all the thread-related data
|
||||
QueueHandle_t interruptQueue;
|
||||
|
||||
static void IRAM_ATTR navButtonInterruptHandler(void* args) {
|
||||
int pinNumber = (int)args;
|
||||
xQueueSendFromISR(interruptQueue, &pinNumber, NULL);
|
||||
}
|
||||
|
||||
static int32_t buttonHandlingThreadMain(const bool* interrupted) {
|
||||
int pinNumber;
|
||||
while (!*interrupted) {
|
||||
if (xQueueReceive(interruptQueue, &pinNumber, portMAX_DELAY)) {
|
||||
// The buttons might generate more than 1 click because of how they are built
|
||||
LOG_I(TAG, "Pressed button %d", pinNumber);
|
||||
if (pinNumber == pin::BUTTON1) {
|
||||
tt::app::stop();
|
||||
}
|
||||
|
||||
// Debounce all events for a short period of time
|
||||
// This is easier than keeping track when each button was last pressed
|
||||
tt::kernel::delayMillis(50);
|
||||
xQueueReset(interruptQueue);
|
||||
tt::kernel::delayMillis(50);
|
||||
xQueueReset(interruptQueue);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
UnPhoneFeatures::~UnPhoneFeatures() {
|
||||
if (buttonHandlingThread.getState() != tt::Thread::State::Stopped) {
|
||||
buttonHandlingThreadInterruptRequest = true;
|
||||
buttonHandlingThread.join();
|
||||
}
|
||||
}
|
||||
|
||||
bool UnPhoneFeatures::initPowerSwitch() {
|
||||
gpio_config_t config = {
|
||||
.pin_bit_mask = BIT64(pin::POWER_SWITCH),
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_POSEDGE,
|
||||
};
|
||||
|
||||
if (gpio_config(&config) != ESP_OK) {
|
||||
LOG_E(TAG, "Power pin init failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rtc_gpio_pullup_en(pin::POWER_SWITCH) == ESP_OK &&
|
||||
rtc_gpio_pulldown_en(pin::POWER_SWITCH) == ESP_OK) {
|
||||
return true;
|
||||
} else {
|
||||
LOG_E(TAG, "Failed to set RTC for power switch");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool UnPhoneFeatures::initNavButtons() {
|
||||
if (!initGpioExpander()) {
|
||||
LOG_E(TAG, "GPIO expander init failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
interruptQueue = xQueueCreate(4, sizeof(int));
|
||||
|
||||
buttonHandlingThread.setName("unphone_buttons");
|
||||
buttonHandlingThread.setPriority(tt::Thread::Priority::High);
|
||||
buttonHandlingThread.setStackSize(3072);
|
||||
buttonHandlingThread.setMainFunction(
|
||||
[this] {
|
||||
return buttonHandlingThreadMain(&this->buttonHandlingThreadInterruptRequest);
|
||||
}
|
||||
);
|
||||
buttonHandlingThread.start();
|
||||
|
||||
uint64_t pin_mask =
|
||||
BIT64(pin::BUTTON1) |
|
||||
BIT64(pin::BUTTON2) |
|
||||
BIT64(pin::BUTTON3);
|
||||
|
||||
gpio_config_t config = {
|
||||
.pin_bit_mask = pin_mask,
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
.pull_up_en = GPIO_PULLUP_ENABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
/**
|
||||
* We have to listen to the button release (= positive signal).
|
||||
* If we listen to button press, the buttons might create more than 1 signal
|
||||
* when they are continuously pressed.
|
||||
*/
|
||||
.intr_type = GPIO_INTR_POSEDGE,
|
||||
};
|
||||
|
||||
if (gpio_config(&config) != ESP_OK) {
|
||||
LOG_E(TAG, "Nav button pin init failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
gpio_install_isr_service(0) != ESP_OK ||
|
||||
gpio_isr_handler_add(pin::BUTTON1, navButtonInterruptHandler, reinterpret_cast<void*>(pin::BUTTON1)) != ESP_OK ||
|
||||
gpio_isr_handler_add(pin::BUTTON2, navButtonInterruptHandler, reinterpret_cast<void*>(pin::BUTTON2)) != ESP_OK ||
|
||||
gpio_isr_handler_add(pin::BUTTON3, navButtonInterruptHandler, reinterpret_cast<void*>(pin::BUTTON3)) != ESP_OK
|
||||
) {
|
||||
LOG_E(TAG, "Nav buttons ISR init failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnPhoneFeatures::initOutputPins() {
|
||||
uint64_t output_pin_mask =
|
||||
BIT64(pin::IR_LEDS) |
|
||||
BIT64(pin::LED_RED);
|
||||
|
||||
gpio_config_t config = {
|
||||
.pin_bit_mask = output_pin_mask,
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
};
|
||||
|
||||
if (gpio_config(&config) != ESP_OK) {
|
||||
LOG_E(TAG, "Output pin init failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnPhoneFeatures::initGpioExpander() {
|
||||
// ESP_IO_EXPANDER_I2C_TCA9555_ADDRESS_110 corresponds with 0x26 from the docs at
|
||||
// https://gitlab.com/hamishcunningham/unphonelibrary/-/blob/main/unPhone.h?ref_type=heads#L206
|
||||
if (esp_io_expander_new_i2c_tca95xx_16bit(I2C_NUM_0, ESP_IO_EXPANDER_I2C_TCA9555_ADDRESS_110, &ioExpander) != ESP_OK) {
|
||||
LOG_E(TAG, "IO expander init failed");
|
||||
return false;
|
||||
}
|
||||
assert(ioExpander != nullptr);
|
||||
|
||||
// Output pins
|
||||
|
||||
/**
|
||||
* Important:
|
||||
* If you clear the pins too late, the display or vibration motor might briefly turn on.
|
||||
*/
|
||||
|
||||
esp_io_expander_set_dir(ioExpander, expanderpin::BACKLIGHT, IO_EXPANDER_OUTPUT);
|
||||
esp_io_expander_set_level(ioExpander, expanderpin::BACKLIGHT, 0);
|
||||
|
||||
esp_io_expander_set_dir(ioExpander, expanderpin::EXPANDER_POWER, IO_EXPANDER_OUTPUT);
|
||||
|
||||
esp_io_expander_set_dir(ioExpander, expanderpin::LED_GREEN, IO_EXPANDER_OUTPUT);
|
||||
esp_io_expander_set_level(ioExpander, expanderpin::LED_GREEN, 0);
|
||||
|
||||
esp_io_expander_set_dir(ioExpander, expanderpin::LED_BLUE, IO_EXPANDER_OUTPUT);
|
||||
esp_io_expander_set_level(ioExpander, expanderpin::LED_BLUE, 0);
|
||||
|
||||
esp_io_expander_set_dir(ioExpander, expanderpin::VIBE, IO_EXPANDER_OUTPUT);
|
||||
esp_io_expander_set_level(ioExpander, expanderpin::VIBE, 0);
|
||||
|
||||
// Input pins
|
||||
|
||||
esp_io_expander_set_dir(ioExpander, expanderpin::USB_VSENSE, IO_EXPANDER_INPUT);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnPhoneFeatures::init() {
|
||||
LOG_I(TAG, "init");
|
||||
|
||||
if (!initGpioExpander()) {
|
||||
LOG_E(TAG, "GPIO expander init failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!initNavButtons()) {
|
||||
LOG_E(TAG, "Input pin init failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!initOutputPins()) {
|
||||
LOG_E(TAG, "Output pin init failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!initPowerSwitch()) {
|
||||
LOG_E(TAG, "Power button init failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UnPhoneFeatures::printInfo() const {
|
||||
esp_io_expander_print_state(ioExpander);
|
||||
batteryManagement->printInfo();
|
||||
bool backlight_power;
|
||||
const char* backlight_power_state = getBacklightPower(backlight_power) && backlight_power ? "on" : "off";
|
||||
LOG_I(TAG, "Backlight: %s", backlight_power_state);
|
||||
}
|
||||
|
||||
bool UnPhoneFeatures::setRgbLed(bool red, bool green, bool blue) const {
|
||||
assert(ioExpander != nullptr);
|
||||
return gpio_set_level(pin::LED_RED, red ? 1U : 0U) == ESP_OK &&
|
||||
esp_io_expander_set_level(ioExpander, expanderpin::LED_GREEN, green ? 1U : 0U) == ESP_OK &&
|
||||
esp_io_expander_set_level(ioExpander, expanderpin::LED_BLUE, blue ? 1U : 0U) == ESP_OK;
|
||||
}
|
||||
|
||||
bool UnPhoneFeatures::setBacklightPower(bool on) const {
|
||||
assert(ioExpander != nullptr);
|
||||
return esp_io_expander_set_level(ioExpander, expanderpin::BACKLIGHT, on ? 1U : 0U) == ESP_OK;
|
||||
}
|
||||
|
||||
bool UnPhoneFeatures::getBacklightPower(bool& on) const {
|
||||
assert(ioExpander != nullptr);
|
||||
uint32_t level_mask;
|
||||
if (esp_io_expander_get_level(ioExpander, expanderpin::BACKLIGHT, &level_mask) == ESP_OK) {
|
||||
on = level_mask != 0U;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool UnPhoneFeatures::setIrPower(bool on) const {
|
||||
assert(ioExpander != nullptr);
|
||||
return gpio_set_level(pin::IR_LEDS, on ? 1U : 0U) == ESP_OK;
|
||||
}
|
||||
|
||||
bool UnPhoneFeatures::setVibePower(bool on) const {
|
||||
assert(ioExpander != nullptr);
|
||||
return esp_io_expander_set_level(ioExpander, expanderpin::VIBE, on ? 1U : 0U) == ESP_OK;
|
||||
}
|
||||
|
||||
bool UnPhoneFeatures::setExpanderPower(bool on) const {
|
||||
assert(ioExpander != nullptr);
|
||||
return esp_io_expander_set_level(ioExpander, expanderpin::EXPANDER_POWER, on ? 1U : 0U) == ESP_OK;
|
||||
}
|
||||
|
||||
bool UnPhoneFeatures::isPowerSwitchOn() const {
|
||||
return gpio_get_level(pin::POWER_SWITCH) > 0;
|
||||
}
|
||||
|
||||
void UnPhoneFeatures::turnPeripheralsOff() const {
|
||||
setExpanderPower(false);
|
||||
setBacklightPower(false);
|
||||
setIrPower(false);
|
||||
setRgbLed(false, false, false);
|
||||
setVibePower(false);
|
||||
}
|
||||
|
||||
bool UnPhoneFeatures::setShipping(bool on) const {
|
||||
if (on) {
|
||||
LOG_W(TAG, "setShipping: on");
|
||||
batteryManagement->setWatchDogTimer(Bq24295::WatchDogTimer::Disabled);
|
||||
batteryManagement->setBatFetOn(false);
|
||||
} else {
|
||||
LOG_W(TAG, "setShipping: off");
|
||||
batteryManagement->setWatchDogTimer(Bq24295::WatchDogTimer::Enabled40s);
|
||||
batteryManagement->setBatFetOn(true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void UnPhoneFeatures::wakeOnPowerSwitch() const {
|
||||
esp_sleep_enable_ext0_wakeup(pin::POWER_SWITCH, 1);
|
||||
}
|
||||
|
||||
bool UnPhoneFeatures::isUsbPowerConnected() const {
|
||||
return batteryManagement->isUsbPowerConnected();
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <Bq24295.h>
|
||||
#include <Tactility/Thread.h>
|
||||
#include <esp_io_expander_tca95xx_16bit.h>
|
||||
|
||||
/**
|
||||
* Easy access to GPIO pins
|
||||
*/
|
||||
class UnPhoneFeatures final {
|
||||
|
||||
private:
|
||||
|
||||
esp_io_expander_handle_t ioExpander = nullptr;
|
||||
tt::Thread buttonHandlingThread;
|
||||
bool buttonHandlingThreadInterruptRequest = false;
|
||||
|
||||
bool initNavButtons();
|
||||
static bool initOutputPins();
|
||||
static bool initPowerSwitch();
|
||||
bool initGpioExpander();
|
||||
|
||||
std::shared_ptr<Bq24295> batteryManagement;
|
||||
|
||||
public:
|
||||
|
||||
explicit UnPhoneFeatures(std::shared_ptr<Bq24295> bq24295) : batteryManagement(std::move(bq24295)) {
|
||||
assert(batteryManagement != nullptr);
|
||||
}
|
||||
|
||||
~UnPhoneFeatures();
|
||||
|
||||
bool init();
|
||||
|
||||
bool setBacklightPower(bool on) const;
|
||||
bool getBacklightPower(bool& on) const;
|
||||
bool setIrPower(bool on) const;
|
||||
bool setVibePower(bool on) const;
|
||||
bool setExpanderPower(bool on) const;
|
||||
|
||||
bool isPowerSwitchOn() const;
|
||||
|
||||
void turnPeripheralsOff() const;
|
||||
|
||||
/** Battery management (BQ24295) will stop supplying power until USB connects */
|
||||
bool setShipping(bool on) const;
|
||||
|
||||
void wakeOnPowerSwitch() const;
|
||||
|
||||
bool isUsbPowerConnected() const;
|
||||
|
||||
bool setRgbLed(bool red, bool green, bool blue) const;
|
||||
|
||||
void printInfo() const;
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
@@ -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));
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <Xpt2046Touch.h>
|
||||
|
||||
extern std::shared_ptr<Xpt2046Touch> touchInstance;
|
||||
|
||||
std::shared_ptr<Xpt2046Touch> createTouch();
|
||||
@@ -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.
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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*/
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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*/
|
||||
@@ -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
|
||||
|
||||
@@ -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 unphone_module = {
|
||||
.name = "unphone",
|
||||
.start = start,
|
||||
.stop = stop,
|
||||
.symbols = nullptr,
|
||||
.internal = nullptr
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
description: >
|
||||
unPhone physical navigation buttons: 3 buttons wired directly to native ESP32 GPIO
|
||||
pins. Button 1 (left) stops the currently running app on release; buttons 2 and 3
|
||||
are read but currently have no assigned action.
|
||||
|
||||
compatible: "unphone,nav-buttons"
|
||||
|
||||
properties:
|
||||
pin-button1:
|
||||
type: phandles
|
||||
required: true
|
||||
description: GPIO pin connected to the left (button 1) nav button
|
||||
pin-button2:
|
||||
type: phandles
|
||||
required: true
|
||||
description: GPIO pin connected to the middle (button 2) nav button
|
||||
pin-button3:
|
||||
type: phandles
|
||||
required: true
|
||||
description: GPIO pin connected to the right (button 3) nav button
|
||||
@@ -0,0 +1,11 @@
|
||||
description: >
|
||||
unPhone physical power slide switch. Reflects whether the switch is in the "on"
|
||||
position and can arm deep-sleep wakeup for when the switch is moved to "on".
|
||||
|
||||
compatible: "unphone,power-switch"
|
||||
|
||||
properties:
|
||||
pin:
|
||||
type: phandles
|
||||
required: true
|
||||
description: GPIO pin connected to the power switch
|
||||
@@ -10,12 +10,17 @@ hardware.spiRamMode=OCT
|
||||
hardware.spiRamSpeed=80M
|
||||
hardware.bluetooth=true
|
||||
|
||||
dependencies.useDeprecatedHal=false
|
||||
|
||||
storage.userDataLocation=SD
|
||||
|
||||
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
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
dependencies:
|
||||
- Platforms/platform-esp32
|
||||
- Drivers/hx8357-module
|
||||
- Drivers/xpt2046-module
|
||||
- Drivers/tca95xx-16bit-module
|
||||
- Drivers/bq24295-module
|
||||
bindings: bindings
|
||||
dts: unphone.dts
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
#include "BatteryManager.h"
|
||||
|
||||
#include <tactility/check.h>
|
||||
#include <tactility/device.h>
|
||||
#include <tactility/drivers/backlight.h>
|
||||
#include <tactility/drivers/gpio.h>
|
||||
#include <tactility/drivers/gpio_controller.h>
|
||||
#include <tactility/log.h>
|
||||
|
||||
#include <drivers/bq24295.h>
|
||||
|
||||
constexpr auto* TAG = "unPhoneFeatures";
|
||||
|
||||
namespace expanderpin {
|
||||
static constexpr gpio_pin_t EXPANDER_POWER = 0; // enable exp brd if high
|
||||
}
|
||||
|
||||
bool BatteryManager::initGpioExpander() {
|
||||
if (device_get_by_name("tca9535", &expander) != ERROR_NONE) {
|
||||
LOG_E(TAG, "IO expander device not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
expanderPowerPin = gpio_descriptor_acquire(expander, expanderpin::EXPANDER_POWER, GPIO_OWNER_GPIO);
|
||||
check(expanderPowerPin != nullptr);
|
||||
gpio_descriptor_set_flags(expanderPowerPin, GPIO_FLAG_DIRECTION_OUTPUT);
|
||||
|
||||
device_put(expander);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BatteryManager::init() {
|
||||
LOG_I(TAG, "init");
|
||||
|
||||
if (!initGpioExpander()) {
|
||||
LOG_E(TAG, "GPIO expander init failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BatteryManager::setExpanderPower(bool on) const {
|
||||
return gpio_descriptor_set_level(expanderPowerPin, on) == ERROR_NONE;
|
||||
}
|
||||
|
||||
void BatteryManager::turnPeripheralsOff() const {
|
||||
setExpanderPower(false);
|
||||
|
||||
Device* backlight = nullptr;
|
||||
check(device_get_by_name("display_backlight", &backlight) == ERROR_NONE);
|
||||
backlight_set_brightness(backlight, 0); // Allowed to fail, we don't care about the result
|
||||
device_put(backlight);
|
||||
}
|
||||
|
||||
void BatteryManager::setShipping(bool on) const {
|
||||
if (on) {
|
||||
LOG_W(TAG, "setShipping: on");
|
||||
bq24295_set_watchdog_timer(batteryManagement, BQ24295_WATCHDOG_DISABLED);
|
||||
bq24295_set_bat_fet_on(batteryManagement, false);
|
||||
} else {
|
||||
LOG_W(TAG, "setShipping: off");
|
||||
bq24295_set_watchdog_timer(batteryManagement, BQ24295_WATCHDOG_ENABLED_40S);
|
||||
bq24295_set_bat_fet_on(batteryManagement, true);
|
||||
}
|
||||
}
|
||||
|
||||
bool BatteryManager::isUsbPowerConnected() const {
|
||||
bool connected = false;
|
||||
return bq24295_is_usb_power_connected(batteryManagement, &connected) == ERROR_NONE && connected;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <tactility/check.h>
|
||||
#include <tactility/device.h>
|
||||
|
||||
struct Device;
|
||||
struct GpioDescriptor;
|
||||
|
||||
/**
|
||||
* Easy access to GPIO pins
|
||||
*/
|
||||
class BatteryManager final {
|
||||
|
||||
::Device* expander = nullptr;
|
||||
::Device* batteryManagement = nullptr;
|
||||
::GpioDescriptor* expanderPowerPin = nullptr;
|
||||
|
||||
bool initGpioExpander();
|
||||
|
||||
|
||||
public:
|
||||
|
||||
explicit BatteryManager() {
|
||||
check(device_get_by_name("bq24295", &batteryManagement) == ERROR_NONE);
|
||||
}
|
||||
|
||||
~BatteryManager() {
|
||||
device_put(batteryManagement);
|
||||
}
|
||||
|
||||
bool init();
|
||||
|
||||
bool setExpanderPower(bool on) const;
|
||||
|
||||
void turnPeripheralsOff() const;
|
||||
|
||||
/** Battery management (BQ24295) will stop supplying power until USB connects */
|
||||
void setShipping(bool on) const;
|
||||
|
||||
bool isUsbPowerConnected() const;
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <tactility/bindings/bindings.h>
|
||||
#include <drivers/unphone_nav_buttons.h>
|
||||
|
||||
// The devicetree compiler derives the expected config typedef name from the compatible
|
||||
// string's suffix (e.g. "unphone,nav-buttons" -> nav_buttons_config_dt), not from the
|
||||
// node name or driver name, so the tag here must match that exactly.
|
||||
DEFINE_DEVICETREE(nav_buttons, struct UnphoneNavButtonsConfig)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <tactility/bindings/bindings.h>
|
||||
#include <drivers/unphone_power_switch.h>
|
||||
|
||||
// The devicetree compiler derives the expected config typedef name from the compatible
|
||||
// string's suffix (e.g. "unphone,power-switch" -> power_switch_config_dt), not from the
|
||||
// node name or driver name, so the tag here must match that exactly.
|
||||
DEFINE_DEVICETREE(power_switch, struct UnphonePowerSwitchConfig)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,209 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#include "unphone_nav_buttons.h"
|
||||
|
||||
#include <tactility/device.h>
|
||||
#include <tactility/driver.h>
|
||||
#include <tactility/drivers/gpio_controller.h>
|
||||
#include <tactility/drivers/gpio_descriptor.h>
|
||||
#include <tactility/error.h>
|
||||
#include <tactility/log.h>
|
||||
|
||||
#include <Tactility/Thread.h>
|
||||
#include <Tactility/app/App.h>
|
||||
#include <Tactility/kernel/Kernel.h>
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/queue.h>
|
||||
|
||||
#include <new>
|
||||
|
||||
#define TAG "UnphoneNavButtons"
|
||||
#define GET_CONFIG(device) (static_cast<const UnphoneNavButtonsConfig*>((device)->config))
|
||||
#define GET_INTERNAL(device) (static_cast<UnphoneNavButtonsInternal*>(device_get_driver_data(device)))
|
||||
|
||||
namespace {
|
||||
// Only button 1 currently drives any behavior (stopping the running app); 2 and 3 are
|
||||
// read and queued like button 1 but nothing consumes them yet (mirrors the original
|
||||
// unPhone nav button behavior).
|
||||
constexpr int BUTTON1_INDEX = 1;
|
||||
constexpr int BUTTON2_INDEX = 2;
|
||||
constexpr int BUTTON3_INDEX = 3;
|
||||
}
|
||||
|
||||
struct UnphoneNavButtonsInternal {
|
||||
GpioDescriptor* pin_button1 = nullptr;
|
||||
GpioDescriptor* pin_button2 = nullptr;
|
||||
GpioDescriptor* pin_button3 = nullptr;
|
||||
|
||||
QueueHandle_t event_queue = nullptr;
|
||||
tt::Thread thread;
|
||||
bool thread_interrupt_requested = false;
|
||||
};
|
||||
|
||||
// region ISR callbacks
|
||||
|
||||
static void IRAM_ATTR on_button1(void* arg) {
|
||||
auto* internal = static_cast<UnphoneNavButtonsInternal*>(arg);
|
||||
int index = BUTTON1_INDEX;
|
||||
xQueueSendFromISR(internal->event_queue, &index, nullptr);
|
||||
}
|
||||
|
||||
static void IRAM_ATTR on_button2(void* arg) {
|
||||
auto* internal = static_cast<UnphoneNavButtonsInternal*>(arg);
|
||||
int index = BUTTON2_INDEX;
|
||||
xQueueSendFromISR(internal->event_queue, &index, nullptr);
|
||||
}
|
||||
|
||||
static void IRAM_ATTR on_button3(void* arg) {
|
||||
auto* internal = static_cast<UnphoneNavButtonsInternal*>(arg);
|
||||
int index = BUTTON3_INDEX;
|
||||
xQueueSendFromISR(internal->event_queue, &index, nullptr);
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Consumer thread
|
||||
|
||||
static int32_t nav_buttons_thread_main(UnphoneNavButtonsInternal* internal) {
|
||||
int button_index;
|
||||
while (!internal->thread_interrupt_requested) {
|
||||
if (xQueueReceive(internal->event_queue, &button_index, portMAX_DELAY)) {
|
||||
// The buttons might generate more than 1 click because of how they are built
|
||||
LOG_I(TAG, "Pressed button %d", button_index);
|
||||
if (button_index == BUTTON1_INDEX) {
|
||||
tt::app::stop();
|
||||
}
|
||||
|
||||
// Debounce all events for a short period of time
|
||||
// This is easier than keeping track when each button was last pressed
|
||||
tt::kernel::delayMillis(50);
|
||||
xQueueReset(internal->event_queue);
|
||||
tt::kernel::delayMillis(50);
|
||||
xQueueReset(internal->event_queue);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Pin acquisition
|
||||
|
||||
static error_t acquire_button(const GpioPinSpec& pin, void (*callback)(void*), void* arg, GpioDescriptor** out_descriptor) {
|
||||
auto* descriptor = gpio_descriptor_acquire(pin.gpio_controller, pin.pin, GPIO_OWNER_GPIO);
|
||||
if (descriptor == nullptr) {
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
|
||||
// Digital pull-up; the buttons pull the pin low while pressed. Listen for the release
|
||||
// (positive edge) rather than the press - if we listen to the press, these buttons can
|
||||
// generate more than one signal when held down (mirrors the original unPhone init).
|
||||
gpio_flags_t flags = GPIO_FLAG_DIRECTION_INPUT | GPIO_FLAG_PULL_UP;
|
||||
flags = GPIO_FLAG_INTERRUPT_TO_OPTIONS(flags, GPIO_INTERRUPT_POS_EDGE);
|
||||
|
||||
error_t error = gpio_descriptor_set_flags(descriptor, flags);
|
||||
if (error == ERROR_NONE) {
|
||||
error = gpio_descriptor_add_callback(descriptor, callback, arg);
|
||||
}
|
||||
if (error == ERROR_NONE) {
|
||||
error = gpio_descriptor_enable_interrupt(descriptor);
|
||||
}
|
||||
|
||||
if (error != ERROR_NONE) {
|
||||
gpio_descriptor_remove_callback(descriptor);
|
||||
gpio_descriptor_release(descriptor);
|
||||
return error;
|
||||
}
|
||||
|
||||
*out_descriptor = descriptor;
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
static void release_button(GpioDescriptor*& descriptor) {
|
||||
if (descriptor == nullptr) {
|
||||
return;
|
||||
}
|
||||
gpio_descriptor_disable_interrupt(descriptor);
|
||||
gpio_descriptor_remove_callback(descriptor);
|
||||
gpio_descriptor_release(descriptor);
|
||||
descriptor = nullptr;
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
extern "C" {
|
||||
|
||||
static error_t start(Device* device) {
|
||||
const auto* config = GET_CONFIG(device);
|
||||
|
||||
auto* internal = new(std::nothrow) UnphoneNavButtonsInternal();
|
||||
if (internal == nullptr) {
|
||||
return ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
internal->event_queue = xQueueCreate(4, sizeof(int));
|
||||
if (internal->event_queue == nullptr) {
|
||||
delete internal;
|
||||
return ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
error_t error = acquire_button(config->pin_button1, on_button1, internal, &internal->pin_button1);
|
||||
if (error == ERROR_NONE) {
|
||||
error = acquire_button(config->pin_button2, on_button2, internal, &internal->pin_button2);
|
||||
}
|
||||
if (error == ERROR_NONE) {
|
||||
error = acquire_button(config->pin_button3, on_button3, internal, &internal->pin_button3);
|
||||
}
|
||||
|
||||
if (error != ERROR_NONE) {
|
||||
LOG_E(TAG, "Failed to acquire nav button pins");
|
||||
release_button(internal->pin_button1);
|
||||
release_button(internal->pin_button2);
|
||||
release_button(internal->pin_button3);
|
||||
vQueueDelete(internal->event_queue);
|
||||
delete internal;
|
||||
return error;
|
||||
}
|
||||
|
||||
internal->thread.setName("unphone_nav_buttons");
|
||||
internal->thread.setPriority(tt::Thread::Priority::High);
|
||||
internal->thread.setStackSize(3072);
|
||||
internal->thread.setMainFunction([internal] { return nav_buttons_thread_main(internal); });
|
||||
internal->thread.start();
|
||||
|
||||
device_set_driver_data(device, internal);
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
static error_t stop(Device* device) {
|
||||
auto* internal = GET_INTERNAL(device);
|
||||
|
||||
if (internal->thread.getState() != tt::Thread::State::Stopped) {
|
||||
internal->thread_interrupt_requested = true;
|
||||
internal->thread.join();
|
||||
}
|
||||
|
||||
release_button(internal->pin_button1);
|
||||
release_button(internal->pin_button2);
|
||||
release_button(internal->pin_button3);
|
||||
vQueueDelete(internal->event_queue);
|
||||
|
||||
device_set_driver_data(device, nullptr);
|
||||
delete internal;
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
extern Module unphone_module;
|
||||
|
||||
Driver unphone_nav_buttons_driver = {
|
||||
.name = "unphone_nav_buttons",
|
||||
.compatible = (const char*[]) { "unphone,nav-buttons", nullptr },
|
||||
.start_device = start,
|
||||
.stop_device = stop,
|
||||
.api = nullptr,
|
||||
.device_type = nullptr,
|
||||
.owner = &unphone_module,
|
||||
.internal = nullptr
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <tactility/drivers/gpio.h>
|
||||
|
||||
struct UnphoneNavButtonsConfig {
|
||||
struct GpioPinSpec pin_button1;
|
||||
struct GpioPinSpec pin_button2;
|
||||
struct GpioPinSpec pin_button3;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,90 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#include "unphone_power_switch.h"
|
||||
|
||||
#include <tactility/driver.h>
|
||||
#include <tactility/drivers/gpio_controller.h>
|
||||
#include <tactility/drivers/gpio_descriptor.h>
|
||||
#include <tactility/error_esp32.h>
|
||||
#include <tactility/log.h>
|
||||
|
||||
#include <driver/rtc_io.h>
|
||||
#include <esp_sleep.h>
|
||||
|
||||
#define TAG "UnphonePowerSwitch"
|
||||
#define GET_CONFIG(device) (static_cast<const UnphonePowerSwitchConfig*>((device)->config))
|
||||
|
||||
struct UnphonePowerSwitchInternal {
|
||||
GpioDescriptor* descriptor;
|
||||
gpio_num_t native_pin;
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
||||
extern Module unphone_module;
|
||||
|
||||
static error_t start(Device* device) {
|
||||
const auto* config = GET_CONFIG(device);
|
||||
|
||||
auto* descriptor = gpio_descriptor_acquire(config->pin.gpio_controller, config->pin.pin, GPIO_OWNER_GPIO);
|
||||
if (descriptor == nullptr) {
|
||||
LOG_E(TAG, "Failed to acquire GPIO descriptor");
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
|
||||
if (gpio_descriptor_set_flags(descriptor, config->pin.flags | GPIO_FLAG_DIRECTION_INPUT) != ERROR_NONE) {
|
||||
LOG_E(TAG, "Failed to configure power switch pin as input");
|
||||
gpio_descriptor_release(descriptor);
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
|
||||
gpio_num_t native_pin;
|
||||
if (gpio_descriptor_get_native_pin_number(descriptor, &native_pin) != ERROR_NONE) {
|
||||
LOG_E(TAG, "Power switch pin has no native pin number");
|
||||
gpio_descriptor_release(descriptor);
|
||||
return ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
// Digital pull resistors are powered down during deep sleep; the RTC domain's own pull
|
||||
// resistors are what actually hold the pin state while asleep, so both must be armed
|
||||
// here (mirrors the original unPhone power switch init).
|
||||
if (rtc_gpio_pullup_en(native_pin) != ESP_OK || rtc_gpio_pulldown_en(native_pin) != ESP_OK) {
|
||||
LOG_E(TAG, "Failed to configure RTC pull resistors for power switch");
|
||||
gpio_descriptor_release(descriptor);
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
|
||||
auto* internal = new UnphonePowerSwitchInternal { .descriptor = descriptor, .native_pin = native_pin };
|
||||
device_set_driver_data(device, internal);
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
static error_t stop(Device* device) {
|
||||
auto* internal = static_cast<UnphonePowerSwitchInternal*>(device_get_driver_data(device));
|
||||
gpio_descriptor_release(internal->descriptor);
|
||||
delete internal;
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
error_t unphone_power_switch_is_on(Device* device, bool* on) {
|
||||
auto* internal = static_cast<UnphonePowerSwitchInternal*>(device_get_driver_data(device));
|
||||
return gpio_descriptor_get_level(internal->descriptor, on);
|
||||
}
|
||||
|
||||
error_t unphone_power_switch_enable_wake(Device* device) {
|
||||
auto* internal = static_cast<UnphonePowerSwitchInternal*>(device_get_driver_data(device));
|
||||
auto esp_error = esp_sleep_enable_ext0_wakeup(internal->native_pin, 1);
|
||||
return esp_err_to_error(esp_error);
|
||||
}
|
||||
|
||||
Driver unphone_power_switch_driver = {
|
||||
.name = "unphone_power_switch",
|
||||
.compatible = (const char*[]) { "unphone,power-switch", nullptr },
|
||||
.start_device = start,
|
||||
.stop_device = stop,
|
||||
.api = nullptr,
|
||||
.device_type = nullptr,
|
||||
.owner = &unphone_module,
|
||||
.internal = nullptr
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <tactility/device.h>
|
||||
#include <tactility/drivers/gpio.h>
|
||||
#include <tactility/error.h>
|
||||
|
||||
struct UnphonePowerSwitchConfig {
|
||||
struct GpioPinSpec pin;
|
||||
};
|
||||
|
||||
/** @brief Reads whether the physical power switch is currently in the "on" position. */
|
||||
error_t unphone_power_switch_is_on(struct Device* device, bool* on);
|
||||
|
||||
/**
|
||||
* @brief Arms deep-sleep wakeup so the device wakes when the switch moves to "on".
|
||||
* Only applies to the deep sleep entered right after this call.
|
||||
*/
|
||||
error_t unphone_power_switch_enable_wake(struct Device* device);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user