New kernel drivers and device implementation updates (#561)
- Added modular device support and devicetree bindings for ILI9341, ILI9488, CST816S, XPT2046, and GPIO button input, updating several board configurations for display/touch/backlight/keyboard/battery. - Added a setting to control deprecated HAL usage (device property + Kconfig).
This commit is contained in:
committed by
GitHub
parent
50c0a14a93
commit
fa4a6e255c
@@ -69,6 +69,10 @@ if (DEFINED ENV{ESP_IDF_VERSION})
|
|||||||
|
|
||||||
else ()
|
else ()
|
||||||
message("Building for sim target")
|
message("Building for sim target")
|
||||||
|
# Devices/simulator/Source/Simulator.cpp always defines its own hardwareConfiguration; without
|
||||||
|
# this, Firmware/Source/Main.cpp's #else branch also defines an empty one (its ESP32-only
|
||||||
|
# fallback for devices migrated off the deprecated HAL), causing a duplicate-definition link error.
|
||||||
|
add_compile_definitions(CONFIG_TT_USE_DEPRECATED_HAL)
|
||||||
add_compile_definitions(CONFIG_TT_DEVICE_ID="simulator")
|
add_compile_definitions(CONFIG_TT_DEVICE_ID="simulator")
|
||||||
add_compile_definitions(CONFIG_TT_DEVICE_NAME="Simulator")
|
add_compile_definitions(CONFIG_TT_DEVICE_NAME="Simulator")
|
||||||
add_compile_definitions(CONFIG_TT_DEVICE_VENDOR="")
|
add_compile_definitions(CONFIG_TT_DEVICE_VENDOR="")
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
|
file(GLOB_RECURSE SOURCE_FILES source/*.c*)
|
||||||
|
|
||||||
idf_component_register(
|
idf_component_register(
|
||||||
SRCS ${SOURCE_FILES}
|
SRCS ${SOURCE_FILES}
|
||||||
INCLUDE_DIRS "Source"
|
REQUIRES TactilityKernel driver
|
||||||
REQUIRES Tactility esp_lvgl_port ILI934x CST816S PwmBacklight driver vfs fatfs
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
#include "devices/Display.h"
|
|
||||||
#include <driver/gpio.h>
|
|
||||||
|
|
||||||
#include <Tactility/hal/Configuration.h>
|
|
||||||
#include <PwmBacklight.h>
|
|
||||||
|
|
||||||
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 tt::hal::DeviceVector createDevices() {
|
|
||||||
return {
|
|
||||||
createDisplay(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
extern const tt::hal::Configuration hardwareConfiguration = {
|
|
||||||
.initBoot = initBoot,
|
|
||||||
.createDevices = createDevices
|
|
||||||
};
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
#include "Display.h"
|
|
||||||
#include "Cst816Touch.h"
|
|
||||||
|
|
||||||
#include <Ili934xDisplay.h>
|
|
||||||
#include <PwmBacklight.h>
|
|
||||||
|
|
||||||
static std::shared_ptr<tt::hal::touch::TouchDevice> createTouch() {
|
|
||||||
// Note: GPIO 25 for reset and GPIO 21 for interrupt?
|
|
||||||
auto configuration = std::make_unique<Cst816sTouch::Configuration>(
|
|
||||||
I2C_NUM_0,
|
|
||||||
240,
|
|
||||||
320
|
|
||||||
);
|
|
||||||
|
|
||||||
return std::make_shared<Cst816sTouch>(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,18 +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_PIN_BACKLIGHT = GPIO_NUM_27;
|
|
||||||
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;
|
|
||||||
|
|
||||||
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_2432s024c_module = {
|
|
||||||
.name = "cyd-2432s024c",
|
|
||||||
.start = start,
|
|
||||||
.stop = stop,
|
|
||||||
.symbols = nullptr,
|
|
||||||
.internal = nullptr
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -6,7 +6,9 @@
|
|||||||
#include <tactility/bindings/esp32_i2c.h>
|
#include <tactility/bindings/esp32_i2c.h>
|
||||||
#include <tactility/bindings/esp32_spi.h>
|
#include <tactility/bindings/esp32_spi.h>
|
||||||
#include <tactility/bindings/esp32_sdspi.h>
|
#include <tactility/bindings/esp32_sdspi.h>
|
||||||
#include <tactility/bindings/display_placeholder.h>
|
#include <tactility/bindings/esp32_ledc_backlight.h>
|
||||||
|
#include <bindings/ili9341.h>
|
||||||
|
#include <bindings/cst816s.h>
|
||||||
|
|
||||||
/ {
|
/ {
|
||||||
compatible = "root";
|
compatible = "root";
|
||||||
@@ -28,6 +30,22 @@
|
|||||||
clock-frequency = <400000>;
|
clock-frequency = <400000>;
|
||||||
pin-sda = <&gpio0 33 GPIO_FLAG_NONE>;
|
pin-sda = <&gpio0 33 GPIO_FLAG_NONE>;
|
||||||
pin-scl = <&gpio0 32 GPIO_FLAG_NONE>;
|
pin-scl = <&gpio0 32 GPIO_FLAG_NONE>;
|
||||||
|
|
||||||
|
touch {
|
||||||
|
compatible = "hynitron,cst816s";
|
||||||
|
reg = <0x15>;
|
||||||
|
x-max = <240>;
|
||||||
|
y-max = <320>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
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 {
|
spi0 {
|
||||||
@@ -37,8 +55,15 @@
|
|||||||
pin-mosi = <&gpio0 13 GPIO_FLAG_NONE>;
|
pin-mosi = <&gpio0 13 GPIO_FLAG_NONE>;
|
||||||
pin-sclk = <&gpio0 14 GPIO_FLAG_NONE>;
|
pin-sclk = <&gpio0 14 GPIO_FLAG_NONE>;
|
||||||
|
|
||||||
display {
|
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>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ hardware.target=ESP32
|
|||||||
hardware.flashSize=4MB
|
hardware.flashSize=4MB
|
||||||
hardware.spiRam=false
|
hardware.spiRam=false
|
||||||
|
|
||||||
|
dependencies.useDeprecatedHal=false
|
||||||
|
|
||||||
storage.userDataLocation=SD
|
storage.userDataLocation=SD
|
||||||
|
|
||||||
display.size=2.4"
|
display.size=2.4"
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
- Platforms/platform-esp32
|
- Platforms/platform-esp32
|
||||||
|
- Drivers/ili9341-module
|
||||||
|
- Drivers/cst816s-module
|
||||||
dts: cyd,2432s024c.dts
|
dts: cyd,2432s024c.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
Module cyd_2432s024c_module = {
|
||||||
|
.name = "cyd-2432s024c",
|
||||||
|
.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(
|
idf_component_register(
|
||||||
SRCS ${SOURCE_FILES}
|
SRCS ${SOURCE_FILES}
|
||||||
INCLUDE_DIRS "Source"
|
REQUIRES TactilityKernel driver
|
||||||
REQUIRES Tactility esp_lvgl_port ILI9488 GT911 PwmBacklight 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(GPIO_NUM_38);
|
|
||||||
}
|
|
||||||
|
|
||||||
static DeviceVector createDevices() {
|
|
||||||
return {
|
|
||||||
createDisplay(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
extern const Configuration hardwareConfiguration = {
|
|
||||||
.initBoot = initBoot,
|
|
||||||
.createDevices = createDevices
|
|
||||||
};
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
#include "Display.h"
|
|
||||||
|
|
||||||
#include <Gt911Touch.h>
|
|
||||||
#include <PwmBacklight.h>
|
|
||||||
#include <Ili9488Display.h>
|
|
||||||
#include <tactility/check.h>
|
|
||||||
#include <tactility/device.h>
|
|
||||||
|
|
||||||
static std::shared_ptr<tt::hal::touch::TouchDevice> createTouch() {
|
|
||||||
auto* i2c = device_find_by_name("i2c0");
|
|
||||||
check(i2c);
|
|
||||||
auto configuration = std::make_unique<Gt911Touch::Configuration>(
|
|
||||||
i2c,
|
|
||||||
320,
|
|
||||||
480
|
|
||||||
);
|
|
||||||
|
|
||||||
return std::make_shared<Gt911Touch>(std::move(configuration));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
|
|
||||||
auto touch = createTouch();
|
|
||||||
|
|
||||||
auto configuration = std::make_unique<Ili9488Display::Configuration>(
|
|
||||||
CROWPANEL_LCD_SPI_HOST,
|
|
||||||
CROWPANEL_LCD_PIN_CS,
|
|
||||||
CROWPANEL_LCD_PIN_DC,
|
|
||||||
CROWPANEL_LCD_HORIZONTAL_RESOLUTION,
|
|
||||||
CROWPANEL_LCD_VERTICAL_RESOLUTION,
|
|
||||||
touch,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
configuration->backlightDutyFunction = driver::pwmbacklight::setBacklightDuty;
|
|
||||||
|
|
||||||
auto display = std::make_shared<Ili9488Display>(std::move(configuration));
|
|
||||||
return std::reinterpret_pointer_cast<tt::hal::display::DisplayDevice>(display);
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Tactility/hal/display/DisplayDevice.h>
|
|
||||||
|
|
||||||
#define CROWPANEL_LCD_SPI_HOST SPI2_HOST
|
|
||||||
#define CROWPANEL_LCD_PIN_CS GPIO_NUM_40
|
|
||||||
#define CROWPANEL_LCD_PIN_DC GPIO_NUM_41 // RS
|
|
||||||
#define CROWPANEL_LCD_HORIZONTAL_RESOLUTION 320
|
|
||||||
#define CROWPANEL_LCD_VERTICAL_RESOLUTION 480
|
|
||||||
#define CROWPANEL_LCD_SPI_TRANSFER_HEIGHT (CROWPANEL_LCD_VERTICAL_RESOLUTION / 10)
|
|
||||||
|
|
||||||
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay();
|
|
||||||
@@ -12,6 +12,8 @@ hardware.tinyUsb=true
|
|||||||
hardware.esptoolFlashFreq=120M
|
hardware.esptoolFlashFreq=120M
|
||||||
hardware.bluetooth=true
|
hardware.bluetooth=true
|
||||||
|
|
||||||
|
dependencies.useDeprecatedHal=false
|
||||||
|
|
||||||
storage.userDataLocation=SD
|
storage.userDataLocation=SD
|
||||||
|
|
||||||
display.size=3.5"
|
display.size=3.5"
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
- Platforms/platform-esp32
|
- Platforms/platform-esp32
|
||||||
|
- Drivers/ili9488-module
|
||||||
|
- Drivers/gt911-module
|
||||||
dts: elecrow,crowpanel-advance-35.dts
|
dts: elecrow,crowpanel-advance-35.dts
|
||||||
|
|||||||
@@ -8,7 +8,9 @@
|
|||||||
#include <tactility/bindings/esp32_spi.h>
|
#include <tactility/bindings/esp32_spi.h>
|
||||||
#include <tactility/bindings/esp32_uart.h>
|
#include <tactility/bindings/esp32_uart.h>
|
||||||
#include <tactility/bindings/esp32_sdspi.h>
|
#include <tactility/bindings/esp32_sdspi.h>
|
||||||
#include <tactility/bindings/display_placeholder.h>
|
#include <tactility/bindings/esp32_ledc_backlight.h>
|
||||||
|
#include <bindings/ili9488.h>
|
||||||
|
#include <bindings/gt911.h>
|
||||||
|
|
||||||
/ {
|
/ {
|
||||||
compatible = "root";
|
compatible = "root";
|
||||||
@@ -35,6 +37,22 @@
|
|||||||
clock-frequency = <400000>;
|
clock-frequency = <400000>;
|
||||||
pin-sda = <&gpio0 15 GPIO_FLAG_NONE>;
|
pin-sda = <&gpio0 15 GPIO_FLAG_NONE>;
|
||||||
pin-scl = <&gpio0 16 GPIO_FLAG_NONE>;
|
pin-scl = <&gpio0 16 GPIO_FLAG_NONE>;
|
||||||
|
|
||||||
|
touch {
|
||||||
|
compatible = "goodix,gt911";
|
||||||
|
reg = <0x5D>;
|
||||||
|
x-max = <320>;
|
||||||
|
y-max = <480>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
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 = <512>;
|
||||||
};
|
};
|
||||||
|
|
||||||
spi0 {
|
spi0 {
|
||||||
@@ -44,8 +62,15 @@
|
|||||||
pin-mosi = <&gpio0 39 GPIO_FLAG_NONE>;
|
pin-mosi = <&gpio0 39 GPIO_FLAG_NONE>;
|
||||||
pin-sclk = <&gpio0 42 GPIO_FLAG_NONE>;
|
pin-sclk = <&gpio0 42 GPIO_FLAG_NONE>;
|
||||||
|
|
||||||
display {
|
display@0 {
|
||||||
compatible = "display-placeholder";
|
compatible = "ilitek,ili9488";
|
||||||
|
horizontal-resolution = <320>;
|
||||||
|
vertical-resolution = <480>;
|
||||||
|
invert-color;
|
||||||
|
color-conversion-buffer-size = <15360>;
|
||||||
|
pixel-clock-hz = <40000000>;
|
||||||
|
pin-dc = <&gpio0 41 GPIO_FLAG_NONE>;
|
||||||
|
backlight = <&display_backlight>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+1
-3
@@ -3,16 +3,14 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
static error_t start() {
|
static error_t start() {
|
||||||
// Empty for now
|
|
||||||
return ERROR_NONE;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static error_t stop() {
|
static error_t stop() {
|
||||||
// Empty for now
|
|
||||||
return ERROR_NONE;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Module elecrow_crowpanel_advance_35_module = {
|
Module elecrow_crowpanel_advance_35_module = {
|
||||||
.name = "elecrow-crowpanel-advance-35",
|
.name = "elecrow-crowpanel-advance-35",
|
||||||
.start = start,
|
.start = start,
|
||||||
.stop = stop,
|
.stop = stop,
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
|
file(GLOB_RECURSE SOURCE_FILES source/*.c*)
|
||||||
|
|
||||||
idf_component_register(
|
idf_component_register(
|
||||||
SRCS ${SOURCE_FILES}
|
SRCS ${SOURCE_FILES}
|
||||||
INCLUDE_DIRS "Source"
|
REQUIRES TactilityKernel driver
|
||||||
REQUIRES Tactility esp_lvgl_port ILI9488 XPT2046 PwmBacklight driver
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
#include "PwmBacklight.h"
|
|
||||||
#include "devices/Display.h"
|
|
||||||
|
|
||||||
#include <Tactility/hal/Configuration.h>
|
|
||||||
|
|
||||||
using namespace tt::hal;
|
|
||||||
|
|
||||||
static bool initBoot() {
|
|
||||||
driver::pwmbacklight::init(GPIO_NUM_27);
|
|
||||||
// Delay is required, or GUI will lock up.
|
|
||||||
// It's possibly related to the increased power draw when turning on the backlight.
|
|
||||||
vTaskDelay(100);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static DeviceVector createDevices() {
|
|
||||||
return {
|
|
||||||
createDisplay(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
extern const Configuration hardwareConfiguration = {
|
|
||||||
.initBoot = initBoot,
|
|
||||||
.createDevices = createDevices
|
|
||||||
};
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
#include "Display.h"
|
|
||||||
|
|
||||||
#include <Ili9488Display.h>
|
|
||||||
#include <PwmBacklight.h>
|
|
||||||
#include <Xpt2046Touch.h>
|
|
||||||
|
|
||||||
std::shared_ptr<Xpt2046Touch> createTouch() {
|
|
||||||
auto configuration = std::make_unique<Xpt2046Touch::Configuration>(
|
|
||||||
CROWPANEL_LCD_SPI_HOST,
|
|
||||||
CROWPANEL_TOUCH_PIN_CS,
|
|
||||||
320,
|
|
||||||
480,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
return std::make_shared<Xpt2046Touch>(std::move(configuration));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
|
|
||||||
auto touch = createTouch();
|
|
||||||
|
|
||||||
auto configuration = std::make_unique<Ili9488Display::Configuration>(
|
|
||||||
CROWPANEL_LCD_SPI_HOST,
|
|
||||||
CROWPANEL_LCD_PIN_CS,
|
|
||||||
CROWPANEL_LCD_PIN_DC,
|
|
||||||
320,
|
|
||||||
480,
|
|
||||||
touch,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
configuration->backlightDutyFunction = driver::pwmbacklight::setBacklightDuty;
|
|
||||||
|
|
||||||
auto display = std::make_shared<Ili9488Display>(std::move(configuration));
|
|
||||||
return std::reinterpret_pointer_cast<tt::hal::display::DisplayDevice>(display);
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Tactility/hal/display/DisplayDevice.h>
|
|
||||||
|
|
||||||
#define CROWPANEL_LCD_SPI_HOST SPI2_HOST
|
|
||||||
#define CROWPANEL_LCD_PIN_CS GPIO_NUM_15
|
|
||||||
#define CROWPANEL_TOUCH_PIN_CS GPIO_NUM_12
|
|
||||||
#define CROWPANEL_LCD_PIN_DC GPIO_NUM_2 // RS
|
|
||||||
#define CROWPANEL_LCD_HORIZONTAL_RESOLUTION 320
|
|
||||||
#define CROWPANEL_LCD_VERTICAL_RESOLUTION 240
|
|
||||||
#define CROWPANEL_LCD_SPI_TRANSFER_HEIGHT (CROWPANEL_LCD_VERTICAL_RESOLUTION / 10)
|
|
||||||
|
|
||||||
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay();
|
|
||||||
@@ -7,6 +7,8 @@ hardware.target=ESP32
|
|||||||
hardware.flashSize=4MB
|
hardware.flashSize=4MB
|
||||||
hardware.spiRam=false
|
hardware.spiRam=false
|
||||||
|
|
||||||
|
dependencies.useDeprecatedHal=false
|
||||||
|
|
||||||
storage.userDataLocation=SD
|
storage.userDataLocation=SD
|
||||||
|
|
||||||
display.size=3.5"
|
display.size=3.5"
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
- Platforms/platform-esp32
|
- Platforms/platform-esp32
|
||||||
|
- Drivers/ili9488-module
|
||||||
|
- Drivers/xpt2046-module
|
||||||
dts: elecrow,crowpanel-basic-35.dts
|
dts: elecrow,crowpanel-basic-35.dts
|
||||||
|
|||||||
@@ -7,8 +7,9 @@
|
|||||||
#include <tactility/bindings/esp32_spi.h>
|
#include <tactility/bindings/esp32_spi.h>
|
||||||
#include <tactility/bindings/esp32_uart.h>
|
#include <tactility/bindings/esp32_uart.h>
|
||||||
#include <tactility/bindings/esp32_sdspi.h>
|
#include <tactility/bindings/esp32_sdspi.h>
|
||||||
#include <tactility/bindings/display_placeholder.h>
|
#include <tactility/bindings/esp32_ledc_backlight.h>
|
||||||
#include <tactility/bindings/pointer_placeholder.h>
|
#include <bindings/ili9488.h>
|
||||||
|
#include <bindings/xpt2046.h>
|
||||||
|
|
||||||
/ {
|
/ {
|
||||||
compatible = "root";
|
compatible = "root";
|
||||||
@@ -32,6 +33,15 @@
|
|||||||
pin-scl = <&gpio0 21 GPIO_FLAG_NONE>;
|
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 = <512>;
|
||||||
|
};
|
||||||
|
|
||||||
spi0 {
|
spi0 {
|
||||||
compatible = "espressif,esp32-spi";
|
compatible = "espressif,esp32-spi";
|
||||||
host = <SPI2_HOST>;
|
host = <SPI2_HOST>;
|
||||||
@@ -43,11 +53,20 @@
|
|||||||
max-transfer-size = <65536>;
|
max-transfer-size = <65536>;
|
||||||
|
|
||||||
display@0 {
|
display@0 {
|
||||||
compatible = "display-placeholder";
|
compatible = "ilitek,ili9488";
|
||||||
|
horizontal-resolution = <320>;
|
||||||
|
vertical-resolution = <480>;
|
||||||
|
color-conversion-buffer-size = <15360>;
|
||||||
|
pixel-clock-hz = <40000000>;
|
||||||
|
pin-dc = <&gpio0 2 GPIO_FLAG_NONE>;
|
||||||
|
backlight = <&display_backlight>;
|
||||||
};
|
};
|
||||||
|
|
||||||
touch@1 {
|
touch@1 {
|
||||||
compatible = "pointer-placeholder";
|
compatible = "xptek,xpt2046";
|
||||||
|
x-max = <320>;
|
||||||
|
y-max = <480>;
|
||||||
|
mirror-y;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+1
-3
@@ -3,16 +3,14 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
static error_t start() {
|
static error_t start() {
|
||||||
// Empty for now
|
|
||||||
return ERROR_NONE;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static error_t stop() {
|
static error_t stop() {
|
||||||
// Empty for now
|
|
||||||
return ERROR_NONE;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Module elecrow_crowpanel_basic_35_module = {
|
Module elecrow_crowpanel_basic_35_module = {
|
||||||
.name = "elecrow-crowpanel-basic-35",
|
.name = "elecrow-crowpanel-basic-35",
|
||||||
.start = start,
|
.start = start,
|
||||||
.stop = stop,
|
.stop = stop,
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
|
file(GLOB_RECURSE SOURCE_FILES source/*.c*)
|
||||||
|
|
||||||
idf_component_register(
|
idf_component_register(
|
||||||
SRCS ${SOURCE_FILES}
|
SRCS ${SOURCE_FILES}
|
||||||
INCLUDE_DIRS "Source"
|
INCLUDE_DIRS "source"
|
||||||
REQUIRES Tactility esp_lvgl_port esp_lcd ST7789 PwmBacklight driver esp_adc EstimatedPower vfs fatfs TCA8418
|
REQUIRES TactilityKernel
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
#include "devices/Display.h"
|
|
||||||
#include "devices/CardputerKeyboard.h"
|
|
||||||
#include "devices/CardputerPower.h"
|
|
||||||
|
|
||||||
#include <tactility/device.h>
|
|
||||||
#include <Tactility/hal/Configuration.h>
|
|
||||||
|
|
||||||
#include <PwmBacklight.h>
|
|
||||||
#include <Tca8418.h>
|
|
||||||
|
|
||||||
using namespace tt::hal;
|
|
||||||
|
|
||||||
static bool initBoot() {
|
|
||||||
return driver::pwmbacklight::init(LCD_PIN_BACKLIGHT, 512);
|
|
||||||
}
|
|
||||||
|
|
||||||
static DeviceVector createDevices() {
|
|
||||||
auto tca8418 = std::make_shared<Tca8418>(device_find_by_name("i2c_internal"));
|
|
||||||
return {
|
|
||||||
createDisplay(),
|
|
||||||
tca8418,
|
|
||||||
std::make_shared<CardputerKeyboard>(tca8418),
|
|
||||||
std::make_shared<CardputerPower>()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
extern const Configuration hardwareConfiguration = {
|
|
||||||
.initBoot = initBoot,
|
|
||||||
.createDevices = createDevices
|
|
||||||
};
|
|
||||||
@@ -1,153 +0,0 @@
|
|||||||
#include "CardputerKeyboard.h"
|
|
||||||
#include <tactility/drivers/i2c_controller.h>
|
|
||||||
|
|
||||||
constexpr auto* TAG = "CardputerKeyb";
|
|
||||||
|
|
||||||
constexpr auto KB_ROWS = 14;
|
|
||||||
constexpr auto KB_COLS = 4;
|
|
||||||
|
|
||||||
// Lowercase Keymap
|
|
||||||
static constexpr char keymap_lc[KB_COLS][KB_ROWS] = {
|
|
||||||
{'`', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '_', '=', LV_KEY_BACKSPACE},
|
|
||||||
{'\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\\'},
|
|
||||||
{'\0', '\0', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', LV_KEY_ENTER},
|
|
||||||
{'\0', '\0', '\0', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', ' '}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Uppercase Keymap
|
|
||||||
static constexpr char keymap_uc[KB_COLS][KB_ROWS] = {
|
|
||||||
{'~', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '+', LV_KEY_DEL},
|
|
||||||
{'\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '|'},
|
|
||||||
{'\0', '\0', 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', LV_KEY_ENTER},
|
|
||||||
{'\0', '\0', '\0', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', ' '}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Symbol Keymap
|
|
||||||
static constexpr char keymap_sy[KB_COLS][KB_ROWS] = {
|
|
||||||
{LV_KEY_ESC, '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'},
|
|
||||||
{'\t', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'},
|
|
||||||
{'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', LV_KEY_PREV, '\0', LV_KEY_ENTER},
|
|
||||||
{'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', LV_KEY_LEFT, LV_KEY_NEXT, LV_KEY_RIGHT, '\0'}
|
|
||||||
};
|
|
||||||
|
|
||||||
void CardputerKeyboard::readCallback(lv_indev_t* indev, lv_indev_data_t* data) {
|
|
||||||
auto keyboard = static_cast<CardputerKeyboard*>(lv_indev_get_user_data(indev));
|
|
||||||
char keypress = 0;
|
|
||||||
|
|
||||||
if (xQueueReceive(keyboard->queue, &keypress, 0) == pdPASS) {
|
|
||||||
data->key = keypress;
|
|
||||||
data->state = LV_INDEV_STATE_PRESSED;
|
|
||||||
} else {
|
|
||||||
data->key = 0;
|
|
||||||
data->state = LV_INDEV_STATE_RELEASED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CardputerKeyboard::remap(uint8_t& row, uint8_t& col) {
|
|
||||||
// Col
|
|
||||||
uint8_t coltemp = row * 2;
|
|
||||||
if (col > 3) coltemp++;
|
|
||||||
|
|
||||||
// Row
|
|
||||||
uint8_t rowtemp = (col + 4) % 4;
|
|
||||||
|
|
||||||
row = rowtemp;
|
|
||||||
col = coltemp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CardputerKeyboard::processKeyboard() {
|
|
||||||
static bool shift_pressed = false;
|
|
||||||
static bool sym_pressed = false;
|
|
||||||
static bool cap_toggle = false;
|
|
||||||
static bool cap_toggle_armed = true;
|
|
||||||
|
|
||||||
if (keypad->update()) {
|
|
||||||
// Check if symbol or shift is pressed
|
|
||||||
for (int i = 0; i < keypad->pressed_key_count; i++) {
|
|
||||||
// Swap rows and columns
|
|
||||||
uint8_t row = keypad->pressed_list[i].row;
|
|
||||||
uint8_t column = keypad->pressed_list[i].col;
|
|
||||||
remap(row, column);
|
|
||||||
|
|
||||||
if ((row == 2) && (column == 0)) {
|
|
||||||
sym_pressed = true;
|
|
||||||
}
|
|
||||||
if ((row == 2) && (column == 1)) {
|
|
||||||
shift_pressed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Toggle caps lock
|
|
||||||
if ((sym_pressed && shift_pressed) && cap_toggle_armed) {
|
|
||||||
cap_toggle = !cap_toggle;
|
|
||||||
cap_toggle_armed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process regular key input given the processed modifiers
|
|
||||||
for (int i = 0; i < keypad->pressed_key_count; i++) {
|
|
||||||
auto row = keypad->pressed_list[i].row;
|
|
||||||
auto column = keypad->pressed_list[i].col;
|
|
||||||
remap(row, column);
|
|
||||||
char chr = '\0';
|
|
||||||
if (sym_pressed) {
|
|
||||||
chr = keymap_sy[row][column];
|
|
||||||
} else if (shift_pressed || cap_toggle) {
|
|
||||||
chr = keymap_uc[row][column];
|
|
||||||
} else {
|
|
||||||
chr = keymap_lc[row][column];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chr != '\0') xQueueSend(queue, &chr, 50 / portTICK_PERIOD_MS);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < keypad->released_key_count; i++) {
|
|
||||||
auto row = keypad->released_list[i].row;
|
|
||||||
auto column = keypad->released_list[i].col;
|
|
||||||
remap(row, column);
|
|
||||||
|
|
||||||
if ((row == 2) && (column == 0)) {
|
|
||||||
sym_pressed = false;
|
|
||||||
}
|
|
||||||
if ((row == 2) && (column == 1)) {
|
|
||||||
shift_pressed = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!sym_pressed && !shift_pressed) && !cap_toggle_armed) {
|
|
||||||
cap_toggle_armed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CardputerKeyboard::startLvgl(lv_display_t* display) {
|
|
||||||
keypad->init(7, 8);
|
|
||||||
|
|
||||||
assert(inputTimer == nullptr);
|
|
||||||
inputTimer = std::make_unique<tt::Timer>(tt::Timer::Type::Periodic, pdMS_TO_TICKS(20), [this] {
|
|
||||||
processKeyboard();
|
|
||||||
});
|
|
||||||
|
|
||||||
kbHandle = lv_indev_create();
|
|
||||||
lv_indev_set_type(kbHandle, LV_INDEV_TYPE_KEYPAD);
|
|
||||||
lv_indev_set_read_cb(kbHandle, &readCallback);
|
|
||||||
lv_indev_set_display(kbHandle, display);
|
|
||||||
lv_indev_set_user_data(kbHandle, this);
|
|
||||||
|
|
||||||
inputTimer->start();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CardputerKeyboard::stopLvgl() {
|
|
||||||
assert(inputTimer);
|
|
||||||
inputTimer->stop();
|
|
||||||
inputTimer = nullptr;
|
|
||||||
|
|
||||||
lv_indev_delete(kbHandle);
|
|
||||||
kbHandle = nullptr;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CardputerKeyboard::isAttached() const {
|
|
||||||
return i2c_controller_has_device_at_address(keypad->getController(), keypad->getAddress(), 100) == ERROR_NONE;
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Tactility/hal/keyboard/KeyboardDevice.h>
|
|
||||||
|
|
||||||
#include <Tca8418.h>
|
|
||||||
|
|
||||||
#include <Tactility/Timer.h>
|
|
||||||
#include <freertos/queue.h>
|
|
||||||
|
|
||||||
class CardputerKeyboard final : public tt::hal::keyboard::KeyboardDevice {
|
|
||||||
|
|
||||||
lv_indev_t* kbHandle = nullptr;
|
|
||||||
QueueHandle_t queue = nullptr;
|
|
||||||
|
|
||||||
std::shared_ptr<Tca8418> keypad;
|
|
||||||
std::unique_ptr<tt::Timer> inputTimer;
|
|
||||||
|
|
||||||
void processKeyboard();
|
|
||||||
|
|
||||||
static void readCallback(lv_indev_t* indev, lv_indev_data_t* data);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remaps wiring coordinates to keyboard mapping coordinates.
|
|
||||||
* Wiring is 7x8 (rows & colums), but our keyboard definition is 4x14)
|
|
||||||
*/
|
|
||||||
static void remap(uint8_t& row, uint8_t& column);
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
explicit CardputerKeyboard(const std::shared_ptr<Tca8418>& tca) : keypad(tca) {
|
|
||||||
queue = xQueueCreate(20, sizeof(char));
|
|
||||||
}
|
|
||||||
|
|
||||||
~CardputerKeyboard() override {
|
|
||||||
vQueueDelete(queue);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string getName() const override { return "TCA8418"; }
|
|
||||||
std::string getDescription() const override { return "TCA8418 I2C keyboard"; }
|
|
||||||
|
|
||||||
bool startLvgl(lv_display_t* display) override;
|
|
||||||
bool stopLvgl() override;
|
|
||||||
|
|
||||||
bool isAttached() const override;
|
|
||||||
lv_indev_t* getLvglIndev() override { return kbHandle; }
|
|
||||||
};
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
#include "CardputerPower.h"
|
|
||||||
|
|
||||||
#include <driver/adc.h>
|
|
||||||
#include <tactility/log.h>
|
|
||||||
|
|
||||||
constexpr auto* TAG = "CardputerPower";
|
|
||||||
|
|
||||||
bool CardputerPower::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 CardputerPower::adcReadValue() const {
|
|
||||||
int adc_raw = adc1_get_raw(ADC1_CHANNEL_9);
|
|
||||||
LOG_D(TAG, "Raw data: %d", adc_raw);
|
|
||||||
float voltage;
|
|
||||||
if (calibrated) {
|
|
||||||
voltage = esp_adc_cal_raw_to_voltage(adc_raw, &adcCharacteristics);
|
|
||||||
LOG_D(TAG, "Calibrated data: %f mV", voltage);
|
|
||||||
} else {
|
|
||||||
voltage = 0.0f;
|
|
||||||
}
|
|
||||||
return voltage;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CardputerPower::ensureInitialized() {
|
|
||||||
if (!initialized) {
|
|
||||||
calibrated = adcInitCalibration();
|
|
||||||
|
|
||||||
if (adc1_config_width(static_cast<adc_bits_width_t>(ADC_WIDTH_BIT_DEFAULT)) != ESP_OK) {
|
|
||||||
LOG_E(TAG, "ADC1 config width failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (adc1_config_channel_atten(ADC1_CHANNEL_9, ADC_ATTEN_DB_11) != ESP_OK) {
|
|
||||||
LOG_E(TAG, "ADC1 config attenuation failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
initialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CardputerPower::supportsMetric(MetricType type) const {
|
|
||||||
switch (type) {
|
|
||||||
using enum MetricType;
|
|
||||||
case BatteryVoltage:
|
|
||||||
case ChargeLevel:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CardputerPower::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,29 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Tactility/hal/power/PowerDevice.h>
|
|
||||||
#include <ChargeFromVoltage.h>
|
|
||||||
#include <esp_adc_cal.h>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
using tt::hal::power::PowerDevice;
|
|
||||||
|
|
||||||
class CardputerPower final : public PowerDevice {
|
|
||||||
|
|
||||||
ChargeFromVoltage chargeFromAdcVoltage = ChargeFromVoltage(3.3f, 4.2f);
|
|
||||||
bool initialized = false;
|
|
||||||
esp_adc_cal_characteristics_t adcCharacteristics;
|
|
||||||
bool calibrated = false;
|
|
||||||
|
|
||||||
bool adcInitCalibration();
|
|
||||||
uint32_t adcReadValue() const;
|
|
||||||
|
|
||||||
bool ensureInitialized();
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
std::string getName() const override { return "Cardputer Power"; }
|
|
||||||
std::string getDescription() const override { return "Power measurement via ADC"; }
|
|
||||||
|
|
||||||
bool supportsMetric(MetricType type) const override;
|
|
||||||
bool getMetric(MetricType type, MetricData& data) override;
|
|
||||||
};
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
#include "Display.h"
|
|
||||||
|
|
||||||
#include <PwmBacklight.h>
|
|
||||||
#include <St7789Display.h>
|
|
||||||
|
|
||||||
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
|
|
||||||
St7789Display::Configuration panel_configuration = {
|
|
||||||
.horizontalResolution = LCD_HORIZONTAL_RESOLUTION,
|
|
||||||
.verticalResolution = LCD_VERTICAL_RESOLUTION,
|
|
||||||
.gapX = 53, // Should be 52 according to https://github.com/m5stack/M5GFX/blob/master/src/M5GFX.cpp but this leaves a gap at the bottom
|
|
||||||
.gapY = 40,
|
|
||||||
.swapXY = true,
|
|
||||||
.mirrorX = true,
|
|
||||||
.mirrorY = false,
|
|
||||||
.invertColor = true,
|
|
||||||
.bufferSize = LCD_BUFFER_SIZE,
|
|
||||||
.touch = nullptr,
|
|
||||||
.backlightDutyFunction = driver::pwmbacklight::setBacklightDuty,
|
|
||||||
.resetPin = LCD_PIN_RESET,
|
|
||||||
.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,19 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Tactility/hal/display/DisplayDevice.h>
|
|
||||||
#include <memory>
|
|
||||||
#include <driver/gpio.h>
|
|
||||||
#include <driver/spi_common.h>
|
|
||||||
|
|
||||||
constexpr auto LCD_SPI_HOST = SPI2_HOST;
|
|
||||||
constexpr auto LCD_PIN_CS = GPIO_NUM_37;
|
|
||||||
constexpr auto LCD_PIN_DC = GPIO_NUM_34; // RS
|
|
||||||
constexpr auto LCD_PIN_RESET = GPIO_NUM_33;
|
|
||||||
constexpr auto LCD_PIN_BACKLIGHT = GPIO_NUM_38;
|
|
||||||
constexpr auto LCD_HORIZONTAL_RESOLUTION = 240;
|
|
||||||
constexpr auto LCD_VERTICAL_RESOLUTION = 135;
|
|
||||||
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();
|
|
||||||
@@ -10,6 +10,8 @@ hardware.tinyUsb=true
|
|||||||
hardware.esptoolFlashFreq=120M
|
hardware.esptoolFlashFreq=120M
|
||||||
hardware.bluetooth=true
|
hardware.bluetooth=true
|
||||||
|
|
||||||
|
dependencies.useDeprecatedHal=false
|
||||||
|
|
||||||
storage.userDataLocation=SD
|
storage.userDataLocation=SD
|
||||||
|
|
||||||
display.size=1.14"
|
display.size=1.14"
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
- Platforms/platform-esp32
|
- Platforms/platform-esp32
|
||||||
- Drivers/bmi270-module
|
- Drivers/bmi270-module
|
||||||
|
- Drivers/st7789-module
|
||||||
|
- Drivers/m5stack-module
|
||||||
dts: m5stack,cardputer-adv.dts
|
dts: m5stack,cardputer-adv.dts
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
/dts-v1/;
|
/dts-v1/;
|
||||||
|
|
||||||
#include <tactility/bindings/root.h>
|
#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_ble.h>
|
||||||
#include <tactility/bindings/esp32_wifi_pinned.h>
|
#include <tactility/bindings/esp32_wifi_pinned.h>
|
||||||
#include <tactility/bindings/esp32_gpio.h>
|
#include <tactility/bindings/esp32_gpio.h>
|
||||||
@@ -11,7 +13,10 @@
|
|||||||
#include <tactility/bindings/esp32_uart.h>
|
#include <tactility/bindings/esp32_uart.h>
|
||||||
#include <bindings/bmi270.h>
|
#include <bindings/bmi270.h>
|
||||||
#include <tactility/bindings/esp32_sdspi.h>
|
#include <tactility/bindings/esp32_sdspi.h>
|
||||||
#include <tactility/bindings/display_placeholder.h>
|
#include <tactility/bindings/esp32_ledc_backlight.h>
|
||||||
|
|
||||||
|
#include <bindings/st7789.h>
|
||||||
|
#include <bindings/cardputer_adv_keyboard.h>
|
||||||
|
|
||||||
// Reference: https://docs.m5stack.com/en/core/Cardputer-Adv
|
// Reference: https://docs.m5stack.com/en/core/Cardputer-Adv
|
||||||
/ {
|
/ {
|
||||||
@@ -33,6 +38,21 @@
|
|||||||
gpio-count = <49>;
|
gpio-count = <49>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
adc0 {
|
||||||
|
compatible = "espressif,esp32-adc-oneshot";
|
||||||
|
unit-id = <ADC_UNIT_1>;
|
||||||
|
clk-src = <ADC_RTC_CLK_SRC_DEFAULT>;
|
||||||
|
// GPIO10 (ADC1_CHANNEL_9 on ESP32-S3): battery voltage sense, behind a 1:2 divider.
|
||||||
|
channels = <ADC_CHANNEL_9 ADC_ATTEN_DB_12 ADC_BITWIDTH_DEFAULT>;
|
||||||
|
};
|
||||||
|
|
||||||
|
battery-sense {
|
||||||
|
compatible = "battery-sense";
|
||||||
|
io-channel = <&adc0 0>;
|
||||||
|
reference-voltage-mv = <3300>;
|
||||||
|
multiplier = <2000>;
|
||||||
|
};
|
||||||
|
|
||||||
i2c_internal {
|
i2c_internal {
|
||||||
compatible = "espressif,esp32-i2c";
|
compatible = "espressif,esp32-i2c";
|
||||||
port = <I2C_NUM_0>;
|
port = <I2C_NUM_0>;
|
||||||
@@ -44,6 +64,11 @@
|
|||||||
compatible = "bosch,bmi270";
|
compatible = "bosch,bmi270";
|
||||||
reg = <0x69>;
|
reg = <0x69>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
keyboard {
|
||||||
|
compatible = "m5stack,cardputer-adv-keyboard";
|
||||||
|
reg = <0x34>;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
port_a: grove0 {
|
port_a: grove0 {
|
||||||
@@ -56,6 +81,15 @@
|
|||||||
i2cClockFrequency = <400000>;
|
i2cClockFrequency = <400000>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 = <512>;
|
||||||
|
};
|
||||||
|
|
||||||
spi0 {
|
spi0 {
|
||||||
compatible = "espressif,esp32-spi";
|
compatible = "espressif,esp32-spi";
|
||||||
host = <SPI2_HOST>;
|
host = <SPI2_HOST>;
|
||||||
@@ -63,8 +97,20 @@
|
|||||||
pin-mosi = <&gpio0 35 GPIO_FLAG_NONE>;
|
pin-mosi = <&gpio0 35 GPIO_FLAG_NONE>;
|
||||||
pin-sclk = <&gpio0 36 GPIO_FLAG_NONE>;
|
pin-sclk = <&gpio0 36 GPIO_FLAG_NONE>;
|
||||||
|
|
||||||
display {
|
display@0 {
|
||||||
compatible = "display-placeholder";
|
compatible = "sitronix,st7789";
|
||||||
|
horizontal-resolution = <240>;
|
||||||
|
vertical-resolution = <135>;
|
||||||
|
// Should be 52 according to https://github.com/m5stack/M5GFX/blob/master/src/M5GFX.cpp but this leaves a gap at the bottom
|
||||||
|
gap-x = <53>;
|
||||||
|
gap-y = <40>;
|
||||||
|
swap-xy;
|
||||||
|
mirror-x;
|
||||||
|
invert-color;
|
||||||
|
pixel-clock-hz = <62500000>;
|
||||||
|
pin-dc = <&gpio0 34 GPIO_FLAG_NONE>;
|
||||||
|
pin-reset = <&gpio0 33 GPIO_FLAG_NONE>;
|
||||||
|
backlight = <&display_backlight>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
|
file(GLOB_RECURSE SOURCE_FILES source/*.c*)
|
||||||
|
|
||||||
idf_component_register(
|
idf_component_register(
|
||||||
SRCS ${SOURCE_FILES}
|
SRCS ${SOURCE_FILES}
|
||||||
INCLUDE_DIRS "Source"
|
INCLUDE_DIRS "source"
|
||||||
REQUIRES Tactility esp_lvgl_port esp_lcd ST7789 PwmBacklight driver esp_adc EstimatedPower vfs fatfs
|
REQUIRES TactilityKernel
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
#include "devices/Display.h"
|
|
||||||
#include "devices/CardputerEncoder.h"
|
|
||||||
#include "devices/CardputerKeyboard.h"
|
|
||||||
#include "devices/CardputerPower.h"
|
|
||||||
|
|
||||||
#include <PwmBacklight.h>
|
|
||||||
#include <Tactility/hal/Configuration.h>
|
|
||||||
|
|
||||||
using namespace tt::hal;
|
|
||||||
|
|
||||||
static bool initBoot() {
|
|
||||||
return driver::pwmbacklight::init(LCD_PIN_BACKLIGHT, 512);
|
|
||||||
}
|
|
||||||
|
|
||||||
static DeviceVector createDevices() {
|
|
||||||
return {
|
|
||||||
createDisplay(),
|
|
||||||
std::make_shared<CardputerKeyboard>(),
|
|
||||||
std::make_shared<CardputerEncoder>(),
|
|
||||||
std::make_shared<CardputerPower>()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
extern const Configuration hardwareConfiguration = {
|
|
||||||
.initBoot = initBoot,
|
|
||||||
.createDevices = createDevices
|
|
||||||
};
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
#include "CardputerEncoder.h"
|
|
||||||
|
|
||||||
void CardputerEncoder::readCallback(lv_indev_t* indev, lv_indev_data_t* data) {
|
|
||||||
CardputerEncoder* self = static_cast<CardputerEncoder*>(lv_indev_get_user_data(indev));
|
|
||||||
|
|
||||||
self->keyboard.updateKeyList();
|
|
||||||
|
|
||||||
data->state = LV_INDEV_STATE_RELEASED;
|
|
||||||
|
|
||||||
if (self->keyboard.keyList().size() != self->lastKeyNum) {
|
|
||||||
// If key pressed
|
|
||||||
if (self->keyboard.keyList().size() != 0) {
|
|
||||||
// Update states and values
|
|
||||||
self->keyboard.updateKeysState();
|
|
||||||
if (self->keyboard.keysState().fn) {
|
|
||||||
if (self->keyboard.keysState().enter) {
|
|
||||||
data->key = LV_KEY_ENTER;
|
|
||||||
data->state = LV_INDEV_STATE_PRESSED;
|
|
||||||
} else {
|
|
||||||
for (auto& i : self->keyboard.keysState().values) {
|
|
||||||
if (i == ';') { // Up
|
|
||||||
data->enc_diff = -1;
|
|
||||||
} else if (i == '.') { // Down
|
|
||||||
data->enc_diff = 1;
|
|
||||||
}
|
|
||||||
break; // We only care about the first value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self->lastKeyNum = self->keyboard.keyList().size();
|
|
||||||
} else {
|
|
||||||
self->lastKeyNum = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CardputerEncoder::startLvgl(lv_display_t* display) {
|
|
||||||
keyboard.init();
|
|
||||||
|
|
||||||
lvglDevice = lv_indev_create();
|
|
||||||
lv_indev_set_type(lvglDevice, LV_INDEV_TYPE_ENCODER);
|
|
||||||
lv_indev_set_read_cb(lvglDevice, &readCallback);
|
|
||||||
lv_indev_set_display(lvglDevice, display);
|
|
||||||
lv_indev_set_user_data(lvglDevice, this);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CardputerEncoder::stopLvgl() {
|
|
||||||
lv_indev_delete(lvglDevice);
|
|
||||||
lvglDevice = nullptr;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Tactility/hal/encoder/EncoderDevice.h>
|
|
||||||
|
|
||||||
#include "keyboard/keyboard.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper around the keyboard that uses the following buttons to simulate an encoder:
|
|
||||||
* - Up
|
|
||||||
* - Down
|
|
||||||
* - ok (fn + enter)
|
|
||||||
*/
|
|
||||||
class CardputerEncoder final : public tt::hal::encoder::EncoderDevice {
|
|
||||||
KEYBOARD::Keyboard keyboard;
|
|
||||||
int lastKeyNum = 0;
|
|
||||||
lv_indev_t* lvglDevice = nullptr;
|
|
||||||
|
|
||||||
static void readCallback(lv_indev_t* indev, lv_indev_data_t* data);
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
std::string getName() const override { return "Cardputer Encoder"; }
|
|
||||||
std::string getDescription() const override { return "Cardputer keyboard up/down acting as encoder"; }
|
|
||||||
|
|
||||||
bool startLvgl(lv_display_t* display) override;
|
|
||||||
bool stopLvgl() override;
|
|
||||||
|
|
||||||
lv_indev_t* getLvglIndev() override { return lvglDevice; }
|
|
||||||
};
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
#include "CardputerKeyboard.h"
|
|
||||||
|
|
||||||
#include <tactility/log.h>
|
|
||||||
|
|
||||||
constexpr auto* TAG = "Keyboard";
|
|
||||||
|
|
||||||
bool CardputerKeyboard::startLvgl(lv_display_t* display) {
|
|
||||||
keyboard.init();
|
|
||||||
|
|
||||||
lvglDevice = lv_indev_create();
|
|
||||||
lv_indev_set_type(lvglDevice, LV_INDEV_TYPE_KEYPAD);
|
|
||||||
lv_indev_set_read_cb(lvglDevice, &readCallback);
|
|
||||||
lv_indev_set_display(lvglDevice, display);
|
|
||||||
lv_indev_set_user_data(lvglDevice, this);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CardputerKeyboard::stopLvgl() {
|
|
||||||
lv_indev_delete(lvglDevice);
|
|
||||||
lvglDevice = nullptr;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CardputerKeyboard::readCallback(lv_indev_t* indev, lv_indev_data_t* data) {
|
|
||||||
CardputerKeyboard* self = static_cast<CardputerKeyboard*>(lv_indev_get_user_data(indev));
|
|
||||||
|
|
||||||
data->key = 0;
|
|
||||||
data->state = LV_INDEV_STATE_RELEASED;
|
|
||||||
|
|
||||||
self->keyboard.updateKeyList();
|
|
||||||
|
|
||||||
if (self->keyboard.keyList().size() != self->lastKeyNum) {
|
|
||||||
// If key pressed
|
|
||||||
if (self->keyboard.keyList().size() != 0) {
|
|
||||||
// Update states and values
|
|
||||||
self->keyboard.updateKeysState();
|
|
||||||
if (!self->keyboard.keysState().fn) {
|
|
||||||
if (self->keyboard.keysState().enter) {
|
|
||||||
data->key = LV_KEY_ENTER;
|
|
||||||
data->state = LV_INDEV_STATE_PRESSED;
|
|
||||||
} else if (self->keyboard.keysState().space) {
|
|
||||||
data->key = ' ';
|
|
||||||
data->state = LV_INDEV_STATE_PRESSED;
|
|
||||||
} else if (self->keyboard.keysState().del) {
|
|
||||||
data->key = LV_KEY_BACKSPACE;
|
|
||||||
data->state = LV_INDEV_STATE_PRESSED;
|
|
||||||
} else {
|
|
||||||
// Normal chars
|
|
||||||
for (auto& i : self->keyboard.keysState().values) {
|
|
||||||
data->key = i;
|
|
||||||
data->state = LV_INDEV_STATE_PRESSED;
|
|
||||||
break; // We only support 1 keypress for now
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (self->keyboard.keysState().del) {
|
|
||||||
LOG_I(TAG, "del");
|
|
||||||
data->key = LV_KEY_DEL;
|
|
||||||
data->state = LV_INDEV_STATE_PRESSED;
|
|
||||||
} else {
|
|
||||||
for (auto& i : self->keyboard.keysState().values) {
|
|
||||||
if (i == ';') { // Up
|
|
||||||
/*
|
|
||||||
* WARNING:
|
|
||||||
* lv_switch picks up on this and toggles it, while the CardputerEncoder uses it for scrolling.
|
|
||||||
* We disable the keypress so the encoder can work properly.
|
|
||||||
*/
|
|
||||||
// TODO: Can we detect the active widget and ignore it in case it's a switch?
|
|
||||||
// data->key = LV_KEY_UP;
|
|
||||||
// data->state = LV_INDEV_STATE_PRESSED;
|
|
||||||
} else if (i == '.') { // Down
|
|
||||||
/*
|
|
||||||
* WARNING:
|
|
||||||
* lv_switch picks up on this and toggles it, while the CardputerEncoder uses it for scrolling.
|
|
||||||
* We disable the keypress so the encoder can work properly.
|
|
||||||
*/
|
|
||||||
// TODO: Can we detect the active widget and ignore it in case it's a switch?
|
|
||||||
// data->key = LV_KEY_DOWN;
|
|
||||||
// data->state = LV_INDEV_STATE_PRESSED;
|
|
||||||
} else if (i == ',') { // Left
|
|
||||||
data->key = LV_KEY_LEFT;
|
|
||||||
data->state = LV_INDEV_STATE_PRESSED;
|
|
||||||
} else if (i == '/') { // Right
|
|
||||||
data->key = LV_KEY_RIGHT;
|
|
||||||
data->state = LV_INDEV_STATE_PRESSED;
|
|
||||||
} else if (i == '`') { // Escape
|
|
||||||
data->key = LV_KEY_ESC;
|
|
||||||
data->state = LV_INDEV_STATE_PRESSED;
|
|
||||||
}
|
|
||||||
|
|
||||||
break; // We only support 1 keypress for now
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self->lastKeyNum = self->keyboard.keyList().size();
|
|
||||||
} else {
|
|
||||||
self->lastKeyNum = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Tactility/Thread.h>
|
|
||||||
#include <Tactility/hal/keyboard/KeyboardDevice.h>
|
|
||||||
|
|
||||||
#include "keyboard/keyboard.h"
|
|
||||||
|
|
||||||
class CardputerKeyboard : public tt::hal::keyboard::KeyboardDevice {
|
|
||||||
KEYBOARD::Keyboard keyboard;
|
|
||||||
int lastKeyNum = 0;
|
|
||||||
lv_indev_t* lvglDevice = nullptr;
|
|
||||||
|
|
||||||
static void readCallback(lv_indev_t* indev, lv_indev_data_t* data);
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
std::string getName() const override { return "Cardputer Keyboard"; }
|
|
||||||
std::string getDescription() const override { return "Cardputer internal keyboard"; }
|
|
||||||
|
|
||||||
bool startLvgl(lv_display_t* display) override;
|
|
||||||
bool stopLvgl() override;
|
|
||||||
|
|
||||||
bool isAttached() const override { return true; }
|
|
||||||
|
|
||||||
lv_indev_t* getLvglIndev() override { return lvglDevice; }
|
|
||||||
};
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
#include "CardputerPower.h"
|
|
||||||
|
|
||||||
#include <driver/adc.h>
|
|
||||||
#include <tactility/log.h>
|
|
||||||
|
|
||||||
constexpr auto* TAG = "CardputerPower";
|
|
||||||
|
|
||||||
bool CardputerPower::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 CardputerPower::adcReadValue() const {
|
|
||||||
int adc_raw = adc1_get_raw(ADC1_CHANNEL_9);
|
|
||||||
LOG_D(TAG, "Raw data: %d", adc_raw);
|
|
||||||
float voltage;
|
|
||||||
if (calibrated) {
|
|
||||||
voltage = esp_adc_cal_raw_to_voltage(adc_raw, &adcCharacteristics);
|
|
||||||
LOG_D(TAG, "Calibrated data: %f mV", voltage);
|
|
||||||
} else {
|
|
||||||
voltage = 0.0f;
|
|
||||||
}
|
|
||||||
return voltage;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CardputerPower::ensureInitialized() {
|
|
||||||
if (!initialized) {
|
|
||||||
calibrated = adcInitCalibration();
|
|
||||||
|
|
||||||
if (adc1_config_width(static_cast<adc_bits_width_t>(ADC_WIDTH_BIT_DEFAULT)) != ESP_OK) {
|
|
||||||
LOG_E(TAG, "ADC1 config width failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (adc1_config_channel_atten(ADC1_CHANNEL_9, ADC_ATTEN_DB_11) != ESP_OK) {
|
|
||||||
LOG_E(TAG, "ADC1 config attenuation failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
initialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CardputerPower::supportsMetric(MetricType type) const {
|
|
||||||
switch (type) {
|
|
||||||
using enum MetricType;
|
|
||||||
case BatteryVoltage:
|
|
||||||
case ChargeLevel:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CardputerPower::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,29 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Tactility/hal/power/PowerDevice.h>
|
|
||||||
#include <ChargeFromVoltage.h>
|
|
||||||
#include <esp_adc_cal.h>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
using tt::hal::power::PowerDevice;
|
|
||||||
|
|
||||||
class CardputerPower final : public PowerDevice {
|
|
||||||
|
|
||||||
ChargeFromVoltage chargeFromAdcVoltage = ChargeFromVoltage(3.3f, 4.2f);
|
|
||||||
bool initialized = false;
|
|
||||||
esp_adc_cal_characteristics_t adcCharacteristics;
|
|
||||||
bool calibrated = false;
|
|
||||||
|
|
||||||
bool adcInitCalibration();
|
|
||||||
uint32_t adcReadValue() const;
|
|
||||||
|
|
||||||
bool ensureInitialized();
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
std::string getName() const override { return "Cardputer Power"; }
|
|
||||||
std::string getDescription() const override { return "Power measurement via ADC"; }
|
|
||||||
|
|
||||||
bool supportsMetric(MetricType type) const override;
|
|
||||||
bool getMetric(MetricType type, MetricData& data) override;
|
|
||||||
};
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
#include "Display.h"
|
|
||||||
|
|
||||||
#include <PwmBacklight.h>
|
|
||||||
#include <St7789Display.h>
|
|
||||||
|
|
||||||
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
|
|
||||||
St7789Display::Configuration panel_configuration = {
|
|
||||||
.horizontalResolution = LCD_HORIZONTAL_RESOLUTION,
|
|
||||||
.verticalResolution = LCD_VERTICAL_RESOLUTION,
|
|
||||||
.gapX = 53, // Should be 52 according to https://github.com/m5stack/M5GFX/blob/master/src/M5GFX.cpp but this leaves a gap at the bottom
|
|
||||||
.gapY = 40,
|
|
||||||
.swapXY = true,
|
|
||||||
.mirrorX = true,
|
|
||||||
.mirrorY = false,
|
|
||||||
.invertColor = true,
|
|
||||||
.bufferSize = LCD_BUFFER_SIZE,
|
|
||||||
.touch = nullptr,
|
|
||||||
.backlightDutyFunction = driver::pwmbacklight::setBacklightDuty,
|
|
||||||
.resetPin = LCD_PIN_RESET,
|
|
||||||
.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,19 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Tactility/hal/display/DisplayDevice.h>
|
|
||||||
#include <memory>
|
|
||||||
#include <driver/gpio.h>
|
|
||||||
#include <driver/spi_common.h>
|
|
||||||
|
|
||||||
constexpr auto LCD_SPI_HOST = SPI2_HOST;
|
|
||||||
constexpr auto LCD_PIN_CS = GPIO_NUM_37;
|
|
||||||
constexpr auto LCD_PIN_DC = GPIO_NUM_34; // RS
|
|
||||||
constexpr auto LCD_PIN_RESET = GPIO_NUM_33;
|
|
||||||
constexpr auto LCD_PIN_BACKLIGHT = GPIO_NUM_38;
|
|
||||||
constexpr auto LCD_HORIZONTAL_RESOLUTION = 240;
|
|
||||||
constexpr auto LCD_VERTICAL_RESOLUTION = 135;
|
|
||||||
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();
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
# keyboard folder
|
|
||||||
|
|
||||||
The keyboard code in this folder is from https://github.com/m5stack/M5Cardputer-UserDemo/tree/main/main/hal/keyboard
|
|
||||||
|
|
||||||
# License
|
|
||||||
|
|
||||||
The original license is an MIT license:
|
|
||||||
|
|
||||||
```
|
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2023 M5Stack
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
```
|
|
||||||
@@ -1,266 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file keyboard.cpp
|
|
||||||
* @author Forairaaaaa
|
|
||||||
* @brief
|
|
||||||
* @version 0.1
|
|
||||||
* @date 2023-09-22
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2023
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#include "keyboard.h"
|
|
||||||
#include <driver/gpio.h>
|
|
||||||
|
|
||||||
#define digitalWrite(pin, level) gpio_set_level((gpio_num_t)pin, level)
|
|
||||||
#define digitalRead(pin) gpio_get_level((gpio_num_t)pin)
|
|
||||||
|
|
||||||
|
|
||||||
using namespace KEYBOARD;
|
|
||||||
|
|
||||||
void Keyboard::_set_output(const std::vector<int>& pinList, uint8_t output)
|
|
||||||
{
|
|
||||||
output = output & 0B00000111;
|
|
||||||
|
|
||||||
digitalWrite(pinList[0], (output & 0B00000001));
|
|
||||||
digitalWrite(pinList[1], (output & 0B00000010));
|
|
||||||
digitalWrite(pinList[2], (output & 0B00000100));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
uint8_t Keyboard::_get_input(const std::vector<int>& pinList)
|
|
||||||
{
|
|
||||||
uint8_t buffer = 0x00;
|
|
||||||
uint8_t pin_value = 0x00;
|
|
||||||
|
|
||||||
for (int i = 0; i < 7; i++)
|
|
||||||
{
|
|
||||||
pin_value = (digitalRead(pinList[i]) == 1) ? 0x00 : 0x01;
|
|
||||||
pin_value = pin_value << i;
|
|
||||||
buffer = buffer | pin_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Keyboard::init()
|
|
||||||
{
|
|
||||||
for (auto i : output_list)
|
|
||||||
{
|
|
||||||
gpio_reset_pin((gpio_num_t)i);
|
|
||||||
gpio_set_direction((gpio_num_t)i, GPIO_MODE_OUTPUT);
|
|
||||||
gpio_set_pull_mode((gpio_num_t)i, GPIO_PULLUP_PULLDOWN);
|
|
||||||
digitalWrite(i, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto i : input_list)
|
|
||||||
{
|
|
||||||
gpio_reset_pin((gpio_num_t)i);
|
|
||||||
gpio_set_direction((gpio_num_t)i, GPIO_MODE_INPUT);
|
|
||||||
gpio_set_pull_mode((gpio_num_t)i, GPIO_PULLUP_ONLY);
|
|
||||||
}
|
|
||||||
|
|
||||||
_set_output(output_list, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Point2D_t Keyboard::getKey()
|
|
||||||
{
|
|
||||||
Point2D_t coor;
|
|
||||||
coor.x = -1;
|
|
||||||
coor.y = -1;
|
|
||||||
|
|
||||||
uint8_t input_value = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
_set_output(output_list, i);
|
|
||||||
// printf("% 3d,\t", get_input(inputList));
|
|
||||||
|
|
||||||
input_value = _get_input(input_list);
|
|
||||||
|
|
||||||
/* If key pressed */
|
|
||||||
if (input_value) {
|
|
||||||
|
|
||||||
/* Get X */
|
|
||||||
for (int j = 0; j < 7; j++) {
|
|
||||||
if (input_value == X_map_chart[j].value) {
|
|
||||||
coor.x = (i > 3) ? X_map_chart[j].x_1 : X_map_chart[j].x_2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get Y */
|
|
||||||
coor.y = (i > 3) ? (i - 4) : i;
|
|
||||||
|
|
||||||
/* Keep the same as picture */
|
|
||||||
coor.y = -coor.y;
|
|
||||||
coor.y = coor.y + 3;
|
|
||||||
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// printf("%d,%d\n", x, y);
|
|
||||||
return coor;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
uint8_t Keyboard::getKeyNum(Point2D_t keyCoor)
|
|
||||||
{
|
|
||||||
uint8_t ret = 0;
|
|
||||||
|
|
||||||
if ((keyCoor.x < 0) || (keyCoor.y < 0)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = (keyCoor.y * 14) + (keyCoor.x + 1);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Keyboard::updateKeyList()
|
|
||||||
{
|
|
||||||
_key_list_buffer.clear();
|
|
||||||
|
|
||||||
Point2D_t coor;
|
|
||||||
|
|
||||||
uint8_t input_value = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
_set_output(output_list, i);
|
|
||||||
input_value = _get_input(input_list);
|
|
||||||
|
|
||||||
/* If key pressed */
|
|
||||||
if (input_value) {
|
|
||||||
/* Get X */
|
|
||||||
for (int j = 0; j < 7; j++) {
|
|
||||||
if (input_value & (0x01 << j)) {
|
|
||||||
coor.x = (i > 3) ? X_map_chart[j].x_1 : X_map_chart[j].x_2;
|
|
||||||
|
|
||||||
/* Get Y */
|
|
||||||
coor.y = (i > 3) ? (i - 4) : i;
|
|
||||||
// printf("%d,%d\t", coor.x, coor.y);
|
|
||||||
|
|
||||||
/* Keep the same as picture */
|
|
||||||
coor.y = -coor.y;
|
|
||||||
coor.y = coor.y + 3;
|
|
||||||
|
|
||||||
_key_list_buffer.push_back(coor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool Keyboard::isKeyPressing(int keyNum)
|
|
||||||
{
|
|
||||||
if (_key_list_buffer.size())
|
|
||||||
{
|
|
||||||
for (const auto& i : _key_list_buffer)
|
|
||||||
{
|
|
||||||
if (getKeyNum(i) == keyNum)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
void Keyboard::updateKeysState()
|
|
||||||
{
|
|
||||||
_keys_state_buffer.reset();
|
|
||||||
_key_values_without_special_keys.clear();
|
|
||||||
|
|
||||||
// Get special keys
|
|
||||||
for (auto& i : _key_list_buffer)
|
|
||||||
{
|
|
||||||
if (strcmp(getKeyValue(i).value_first, "tab") == 0)
|
|
||||||
{
|
|
||||||
_keys_state_buffer.tab = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(getKeyValue(i).value_first, "fn") == 0)
|
|
||||||
{
|
|
||||||
_keys_state_buffer.fn = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(getKeyValue(i).value_first, "shift") == 0)
|
|
||||||
{
|
|
||||||
_keys_state_buffer.shift = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(getKeyValue(i).value_first, "ctrl") == 0)
|
|
||||||
{
|
|
||||||
_keys_state_buffer.ctrl = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (strcmp(getKeyValue(i).value_first, "opt") == 0)
|
|
||||||
{
|
|
||||||
_keys_state_buffer.opt = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(getKeyValue(i).value_first, "alt") == 0)
|
|
||||||
{
|
|
||||||
_keys_state_buffer.alt = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(getKeyValue(i).value_first, "del") == 0)
|
|
||||||
{
|
|
||||||
_keys_state_buffer.del = true;
|
|
||||||
_keys_state_buffer.hidKey.push_back(KEY_BACKSPACE);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(getKeyValue(i).value_first, "enter") == 0)
|
|
||||||
{
|
|
||||||
_keys_state_buffer.enter = true;
|
|
||||||
_keys_state_buffer.hidKey.push_back(KEY_ENTER);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(getKeyValue(i).value_first, "space") == 0)
|
|
||||||
{
|
|
||||||
_keys_state_buffer.space = true;
|
|
||||||
_keys_state_buffer.hidKey.push_back(KEY_SPACE);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_key_values_without_special_keys.push_back(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deal what left
|
|
||||||
for (auto& i : _key_values_without_special_keys)
|
|
||||||
{
|
|
||||||
if (_keys_state_buffer.ctrl || _keys_state_buffer.shift || _is_caps_locked)
|
|
||||||
{
|
|
||||||
_keys_state_buffer.values.push_back(*getKeyValue(i).value_second);
|
|
||||||
_keys_state_buffer.hidKey.push_back(getKeyValue(i).value_num_second);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_keys_state_buffer.values.push_back(*getKeyValue(i).value_first);
|
|
||||||
_keys_state_buffer.hidKey.push_back(getKeyValue(i).value_num_first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool Keyboard::isChanged()
|
|
||||||
{
|
|
||||||
if (_last_key_size != _key_list_buffer.size())
|
|
||||||
{
|
|
||||||
_last_key_size = _key_list_buffer.size();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
@@ -1,192 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file keyboard.h
|
|
||||||
* @author Forairaaaaa
|
|
||||||
* @brief
|
|
||||||
* @version 0.1
|
|
||||||
* @date 2023-09-22
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2023
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <iostream>
|
|
||||||
#include <vector>
|
|
||||||
#include "keymap.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace KEYBOARD
|
|
||||||
{
|
|
||||||
struct Chart_t
|
|
||||||
{
|
|
||||||
uint8_t value;
|
|
||||||
uint8_t x_1;
|
|
||||||
uint8_t x_2;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Point2D_t
|
|
||||||
{
|
|
||||||
int x;
|
|
||||||
int y;
|
|
||||||
};
|
|
||||||
|
|
||||||
const std::vector<int> output_list = {8, 9, 11};
|
|
||||||
const std::vector<int> input_list = {13, 15, 3, 4, 5, 6, 7};
|
|
||||||
// const std::vector<int> input_list = {1, 2, 3, 4, 5, 6, 7};
|
|
||||||
|
|
||||||
const Chart_t X_map_chart[7] =
|
|
||||||
{
|
|
||||||
{1, 0, 1},
|
|
||||||
{2, 2, 3},
|
|
||||||
{4, 4, 5},
|
|
||||||
{8, 6, 7},
|
|
||||||
{16, 8, 9},
|
|
||||||
{32, 10, 11},
|
|
||||||
{64, 12, 13}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct KeyValue_t
|
|
||||||
{
|
|
||||||
const char* value_first;
|
|
||||||
const int value_num_first;
|
|
||||||
const char* value_second;
|
|
||||||
const int value_num_second;
|
|
||||||
};
|
|
||||||
|
|
||||||
const KeyValue_t _key_value_map[4][14] = {
|
|
||||||
{{"`", KEY_GRAVE, "~", KEY_GRAVE},
|
|
||||||
{"1", KEY_1, "!", KEY_1},
|
|
||||||
{"2", KEY_2, "@", KEY_2},
|
|
||||||
{"3", KEY_3, "#", KEY_3},
|
|
||||||
{"4", KEY_4, "$", KEY_4},
|
|
||||||
{"5", KEY_5, "%", KEY_5},
|
|
||||||
{"6", KEY_6, "^", KEY_6},
|
|
||||||
{"7", KEY_7, "&", KEY_7},
|
|
||||||
{"8", KEY_8, "*", KEY_KPASTERISK},
|
|
||||||
{"9", KEY_9, "(", KEY_KPLEFTPAREN},
|
|
||||||
{"0", KEY_0, ")", KEY_KPRIGHTPAREN},
|
|
||||||
{"-", KEY_MINUS, "_", KEY_KPMINUS},
|
|
||||||
{"=", KEY_EQUAL, "+", KEY_KPPLUS},
|
|
||||||
{"del", KEY_BACKSPACE, "del", KEY_BACKSPACE}},
|
|
||||||
{{"tab", KEY_TAB, "tab", KEY_TAB},
|
|
||||||
{"q", KEY_Q, "Q", KEY_Q},
|
|
||||||
{"w", KEY_W, "W", KEY_W},
|
|
||||||
{"e", KEY_E, "E", KEY_E},
|
|
||||||
{"r", KEY_R, "R", KEY_R},
|
|
||||||
{"t", KEY_T, "T", KEY_T},
|
|
||||||
{"y", KEY_Y, "Y", KEY_Y},
|
|
||||||
{"u", KEY_U, "U", KEY_U},
|
|
||||||
{"i", KEY_I, "I", KEY_I},
|
|
||||||
{"o", KEY_O, "O", KEY_O},
|
|
||||||
{"p", KEY_P, "P", KEY_P},
|
|
||||||
{"[", KEY_LEFTBRACE, "{", KEY_LEFTBRACE},
|
|
||||||
{"]", KEY_RIGHTBRACE, "}", KEY_RIGHTBRACE},
|
|
||||||
{"\\", KEY_BACKSLASH, "|", KEY_BACKSLASH}},
|
|
||||||
{{"fn", 0, "fn", 0},
|
|
||||||
{"shift", 0, "shift", 0},
|
|
||||||
{"a", KEY_A, "A", KEY_A},
|
|
||||||
{"s", KEY_S, "S", KEY_S},
|
|
||||||
{"d", KEY_D, "D", KEY_D},
|
|
||||||
{"f", KEY_F, "F", KEY_F},
|
|
||||||
{"g", KEY_G, "G", KEY_G},
|
|
||||||
{"h", KEY_H, "H", KEY_H},
|
|
||||||
{"j", KEY_J, "J", KEY_J},
|
|
||||||
{"k", KEY_K, "K", KEY_K},
|
|
||||||
{"l", KEY_L, "L", KEY_L},
|
|
||||||
{";", KEY_SEMICOLON, ":", KEY_SEMICOLON},
|
|
||||||
{"'", KEY_APOSTROPHE, "\"", KEY_APOSTROPHE},
|
|
||||||
{"enter", KEY_ENTER, "enter", KEY_ENTER}},
|
|
||||||
{{"ctrl", KEY_LEFTCTRL, "ctrl", KEY_LEFTCTRL},
|
|
||||||
{"opt", 0, "opt", 0},
|
|
||||||
{"alt", KEY_LEFTALT, "alt", KEY_LEFTALT},
|
|
||||||
{"z", KEY_Z, "Z", KEY_Z},
|
|
||||||
{"x", KEY_X, "X", KEY_X},
|
|
||||||
{"c", KEY_C, "C", KEY_C},
|
|
||||||
{"v", KEY_V, "V", KEY_V},
|
|
||||||
{"b", KEY_B, "B", KEY_B},
|
|
||||||
{"n", KEY_N, "N", KEY_N},
|
|
||||||
{"m", KEY_M, "M", KEY_M},
|
|
||||||
{",", KEY_COMMA, "<", KEY_COMMA},
|
|
||||||
{".", KEY_DOT, ">", KEY_DOT},
|
|
||||||
{"/", KEY_KPSLASH, "?", KEY_KPSLASH},
|
|
||||||
{"space", KEY_SPACE, "space", KEY_SPACE}}};
|
|
||||||
|
|
||||||
class Keyboard
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
struct KeysState
|
|
||||||
{
|
|
||||||
bool tab = false;
|
|
||||||
bool fn = false;
|
|
||||||
bool shift = false;
|
|
||||||
bool ctrl = false;
|
|
||||||
bool opt = false;
|
|
||||||
bool alt = false;
|
|
||||||
bool del = false;
|
|
||||||
bool enter = false;
|
|
||||||
bool space = false;
|
|
||||||
|
|
||||||
std::vector<char> values;
|
|
||||||
std::vector<int> hidKey;
|
|
||||||
|
|
||||||
void reset()
|
|
||||||
{
|
|
||||||
tab = false;
|
|
||||||
fn = false;
|
|
||||||
shift = false;
|
|
||||||
ctrl = false;
|
|
||||||
opt = false;
|
|
||||||
alt = false;
|
|
||||||
del = false;
|
|
||||||
enter = false;
|
|
||||||
space = false;
|
|
||||||
|
|
||||||
values.clear();
|
|
||||||
hidKey.clear();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<Point2D_t> _key_list_buffer;
|
|
||||||
|
|
||||||
std::vector<Point2D_t> _key_values_without_special_keys;
|
|
||||||
KeysState _keys_state_buffer;
|
|
||||||
|
|
||||||
bool _is_caps_locked;
|
|
||||||
uint8_t _last_key_size;
|
|
||||||
|
|
||||||
|
|
||||||
void _set_output(const std::vector<int>& pinList, uint8_t output);
|
|
||||||
uint8_t _get_input(const std::vector<int>& pinList);
|
|
||||||
|
|
||||||
|
|
||||||
public:
|
|
||||||
Keyboard() :
|
|
||||||
_is_caps_locked(false),
|
|
||||||
_last_key_size(0)
|
|
||||||
{}
|
|
||||||
|
|
||||||
void init();
|
|
||||||
|
|
||||||
Point2D_t getKey();
|
|
||||||
|
|
||||||
uint8_t getKeyNum(Point2D_t keyCoor);
|
|
||||||
|
|
||||||
void updateKeyList();
|
|
||||||
inline std::vector<KEYBOARD::Point2D_t>& keyList() { return _key_list_buffer; }
|
|
||||||
|
|
||||||
inline KeyValue_t getKeyValue(const Point2D_t& keyCoor) { return _key_value_map[keyCoor.y][keyCoor.x]; }
|
|
||||||
|
|
||||||
bool isKeyPressing(int keyNum);
|
|
||||||
|
|
||||||
void updateKeysState();
|
|
||||||
inline KeysState& keysState() { return _keys_state_buffer; }
|
|
||||||
|
|
||||||
inline bool capslocked(void) { return _is_caps_locked; }
|
|
||||||
inline void setCapsLocked(bool isLocked) { _is_caps_locked = isLocked; }
|
|
||||||
|
|
||||||
bool isChanged();
|
|
||||||
inline uint8_t isPressed() { return _key_list_buffer.size(); }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,366 +0,0 @@
|
|||||||
/**
|
|
||||||
* This file is part of esp32s3-keyboard.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020-2021 Yuquan He <heyuquan20b at ict dot ac dot cn>
|
|
||||||
* (Institute of Computing Technology, Chinese Academy of Sciences)
|
|
||||||
*
|
|
||||||
* esp32s3-keyboard is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* esp32s3-keyboard is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with esp32s3-keyboard. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* USB HID format: 8 bytes
|
|
||||||
* Byte 0: Keyboard modifier bits (SHIFT, ALT, CTRL etc)
|
|
||||||
* Byte 1: reserved
|
|
||||||
* Byte 2-7: Up to six keyboard usage indexes representing the keys that are
|
|
||||||
* currently "pressed". Order is not important, a key is either
|
|
||||||
* pressed (present in the buffer) or not pressed.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef MY_KEYMAP_H
|
|
||||||
#define MY_KEYMAP_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Modifier masks - used for the first byte in the HID report.
|
|
||||||
* NOTE: The second byte in the report is reserved, 0x00
|
|
||||||
*/
|
|
||||||
#define KEY_MOD_LCTRL 0x01
|
|
||||||
#define KEY_MOD_LSHIFT 0x02
|
|
||||||
#define KEY_MOD_LALT 0x04
|
|
||||||
#define KEY_MOD_LMETA 0x08
|
|
||||||
#define KEY_MOD_RCTRL 0x10
|
|
||||||
#define KEY_MOD_RSHIFT 0x20
|
|
||||||
#define KEY_MOD_RALT 0x40
|
|
||||||
#define KEY_MOD_RMETA 0x80
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Scan codes - last N slots in the HID report (usually 6).
|
|
||||||
* 0x00 if no key pressed.
|
|
||||||
*
|
|
||||||
* If more than N keys are pressed, the HID reports
|
|
||||||
* KEY_ERR_OVF in all slots to indicate this condition.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define KEY_NONE 0x00 // No key pressed
|
|
||||||
#define KEY_ERR_OVF \
|
|
||||||
0x01 // Keyboard Error Roll Over - used for all slots if too many keys are
|
|
||||||
// pressed ("Phantom key")
|
|
||||||
// 0x02 // Keyboard POST Fail
|
|
||||||
// 0x03 // Keyboard Error Undefined
|
|
||||||
#define KEY_A 0x04 // Keyboard a and A
|
|
||||||
#define KEY_B 0x05 // Keyboard b and B
|
|
||||||
#define KEY_C 0x06 // Keyboard c and C
|
|
||||||
#define KEY_D 0x07 // Keyboard d and D
|
|
||||||
#define KEY_E 0x08 // Keyboard e and E
|
|
||||||
#define KEY_F 0x09 // Keyboard f and F
|
|
||||||
#define KEY_G 0x0a // Keyboard g and G
|
|
||||||
#define KEY_H 0x0b // Keyboard h and H
|
|
||||||
#define KEY_I 0x0c // Keyboard i and I
|
|
||||||
#define KEY_J 0x0d // Keyboard j and J
|
|
||||||
#define KEY_K 0x0e // Keyboard k and K
|
|
||||||
#define KEY_L 0x0f // Keyboard l and L
|
|
||||||
#define KEY_M 0x10 // Keyboard m and M
|
|
||||||
#define KEY_N 0x11 // Keyboard n and N
|
|
||||||
#define KEY_O 0x12 // Keyboard o and O
|
|
||||||
#define KEY_P 0x13 // Keyboard p and P
|
|
||||||
#define KEY_Q 0x14 // Keyboard q and Q
|
|
||||||
#define KEY_R 0x15 // Keyboard r and R
|
|
||||||
#define KEY_S 0x16 // Keyboard s and S
|
|
||||||
#define KEY_T 0x17 // Keyboard t and T
|
|
||||||
#define KEY_U 0x18 // Keyboard u and U
|
|
||||||
#define KEY_V 0x19 // Keyboard v and V
|
|
||||||
#define KEY_W 0x1a // Keyboard w and W
|
|
||||||
#define KEY_X 0x1b // Keyboard x and X
|
|
||||||
#define KEY_Y 0x1c // Keyboard y and Y
|
|
||||||
#define KEY_Z 0x1d // Keyboard z and Z
|
|
||||||
|
|
||||||
#define KEY_1 0x1e // Keyboard 1 and !
|
|
||||||
#define KEY_2 0x1f // Keyboard 2 and @
|
|
||||||
#define KEY_3 0x20 // Keyboard 3 and #
|
|
||||||
#define KEY_4 0x21 // Keyboard 4 and $
|
|
||||||
#define KEY_5 0x22 // Keyboard 5 and %
|
|
||||||
#define KEY_6 0x23 // Keyboard 6 and ^
|
|
||||||
#define KEY_7 0x24 // Keyboard 7 and &
|
|
||||||
#define KEY_8 0x25 // Keyboard 8 and *
|
|
||||||
#define KEY_9 0x26 // Keyboard 9 and (
|
|
||||||
#define KEY_0 0x27 // Keyboard 0 and )
|
|
||||||
|
|
||||||
#define KEY_ENTER 0x28 // Keyboard Return (ENTER)
|
|
||||||
#define KEY_ESC 0x29 // Keyboard ESCAPE
|
|
||||||
#define KEY_BACKSPACE 0x2a // Keyboard DELETE (Backspace)
|
|
||||||
#define KEY_TAB 0x2b // Keyboard Tab
|
|
||||||
#define KEY_SPACE 0x2c // Keyboard Spacebar
|
|
||||||
#define KEY_MINUS 0x2d // Keyboard - and _
|
|
||||||
#define KEY_EQUAL 0x2e // Keyboard = and +
|
|
||||||
#define KEY_LEFTBRACE 0x2f // Keyboard [ and {
|
|
||||||
#define KEY_RIGHTBRACE 0x30 // Keyboard ] and }
|
|
||||||
#define KEY_BACKSLASH 0x31 // Keyboard \ and |
|
|
||||||
#define KEY_HASHTILDE 0x32 // Keyboard Non-US # and ~
|
|
||||||
#define KEY_SEMICOLON 0x33 // Keyboard ; and :
|
|
||||||
#define KEY_APOSTROPHE 0x34 // Keyboard ' and "
|
|
||||||
#define KEY_GRAVE 0x35 // Keyboard ` and ~
|
|
||||||
#define KEY_COMMA 0x36 // Keyboard , and <
|
|
||||||
#define KEY_DOT 0x37 // Keyboard . and >
|
|
||||||
#define KEY_SLASH 0x38 // Keyboard / and ?
|
|
||||||
#define KEY_CAPSLOCK 0x39 // Keyboard Caps Lock
|
|
||||||
|
|
||||||
#define KEY_F1 0x3a // Keyboard F1
|
|
||||||
#define KEY_F2 0x3b // Keyboard F2
|
|
||||||
#define KEY_F3 0x3c // Keyboard F3
|
|
||||||
#define KEY_F4 0x3d // Keyboard F4
|
|
||||||
#define KEY_F5 0x3e // Keyboard F5
|
|
||||||
#define KEY_F6 0x3f // Keyboard F6
|
|
||||||
#define KEY_F7 0x40 // Keyboard F7
|
|
||||||
#define KEY_F8 0x41 // Keyboard F8
|
|
||||||
#define KEY_F9 0x42 // Keyboard F9
|
|
||||||
#define KEY_F10 0x43 // Keyboard F10
|
|
||||||
#define KEY_F11 0x44 // Keyboard F11
|
|
||||||
#define KEY_F12 0x45 // Keyboard F12
|
|
||||||
|
|
||||||
#define KEY_PRTSC 0x46 // Keyboard Print Screen
|
|
||||||
#define KEY_SCROLLLOCK 0x47 // Keyboard Scroll Lock
|
|
||||||
#define KEY_PAUSE 0x48 // Keyboard Pause
|
|
||||||
#define KEY_INSERT 0x49 // Keyboard Insert
|
|
||||||
#define KEY_HOME 0x4a // Keyboard Home
|
|
||||||
#define KEY_PAGEUP 0x4b // Keyboard Page Up
|
|
||||||
// #define KEY_DELETE 0x4c // Keyboard Delete Forward
|
|
||||||
#define KEY_DELETE 0xD4 // Keyboard Delete Forward
|
|
||||||
#define KEY_END 0x4d // Keyboard End
|
|
||||||
#define KEY_PAGEDOWN 0x4e // Keyboard Page Down
|
|
||||||
#define KEY_RIGHT 0x4f // Keyboard Right Arrow
|
|
||||||
#define KEY_LEFT 0x50 // Keyboard Left Arrow
|
|
||||||
#define KEY_DOWN 0x51 // Keyboard Down Arrow
|
|
||||||
#define KEY_UP 0x52 // Keyboard Up Arrow
|
|
||||||
|
|
||||||
#define KEY_NUMLOCK 0x53 // Keyboard Num Lock and Clear
|
|
||||||
#define KEY_KPSLASH 0x54 // Keypad /
|
|
||||||
#define KEY_KPASTERISK 0x55 // Keypad *
|
|
||||||
#define KEY_KPMINUS 0x56 // Keypad -
|
|
||||||
#define KEY_KPPLUS 0x57 // Keypad +
|
|
||||||
#define KEY_KPENTER 0x58 // Keypad ENTER
|
|
||||||
#define KEY_KP1 0x59 // Keypad 1 and End
|
|
||||||
#define KEY_KP2 0x5a // Keypad 2 and Down Arrow
|
|
||||||
#define KEY_KP3 0x5b // Keypad 3 and PageDn
|
|
||||||
#define KEY_KP4 0x5c // Keypad 4 and Left Arrow
|
|
||||||
#define KEY_KP5 0x5d // Keypad 5
|
|
||||||
#define KEY_KP6 0x5e // Keypad 6 and Right Arrow
|
|
||||||
#define KEY_KP7 0x5f // Keypad 7 and Home
|
|
||||||
#define KEY_KP8 0x60 // Keypad 8 and Up Arrow
|
|
||||||
#define KEY_KP9 0x61 // Keypad 9 and Page Up
|
|
||||||
#define KEY_KP0 0x62 // Keypad 0 and Insert
|
|
||||||
#define KEY_KPDOT 0x63 // Keypad . and Delete
|
|
||||||
|
|
||||||
#define KEY_102ND 0x64 // Keyboard Non-US \ and |
|
|
||||||
#define KEY_COMPOSE 0x65 // Keyboard Application
|
|
||||||
#define KEY_POWER 0x66 // Keyboard Power
|
|
||||||
#define KEY_KPEQUAL 0x67 // Keypad =
|
|
||||||
|
|
||||||
#define KEY_F13 0x68 // Keyboard F13
|
|
||||||
#define KEY_F14 0x69 // Keyboard F14
|
|
||||||
#define KEY_F15 0x6a // Keyboard F15
|
|
||||||
#define KEY_F16 0x6b // Keyboard F16
|
|
||||||
#define KEY_F17 0x6c // Keyboard F17
|
|
||||||
#define KEY_F18 0x6d // Keyboard F18
|
|
||||||
#define KEY_F19 0x6e // Keyboard F19
|
|
||||||
#define KEY_F20 0x6f // Keyboard F20
|
|
||||||
#define KEY_F21 0x70 // Keyboard F21
|
|
||||||
#define KEY_F22 0x71 // Keyboard F22
|
|
||||||
#define KEY_F23 0x72 // Keyboard F23
|
|
||||||
#define KEY_F24 0x73 // Keyboard F24
|
|
||||||
|
|
||||||
#define KEY_OPEN 0x74 // Keyboard Execute
|
|
||||||
#define KEY_HELP 0x75 // Keyboard Help
|
|
||||||
#define KEY_PROPS 0x76 // Keyboard Menu
|
|
||||||
#define KEY_FRONT 0x77 // Keyboard Select
|
|
||||||
#define KEY_STOP 0x78 // Keyboard Stop
|
|
||||||
#define KEY_AGAIN 0x79 // Keyboard Again
|
|
||||||
#define KEY_UNDO 0x7a // Keyboard Undo
|
|
||||||
#define KEY_CUT 0x7b // Keyboard Cut
|
|
||||||
#define KEY_COPY 0x7c // Keyboard Copy
|
|
||||||
#define KEY_PASTE 0x7d // Keyboard Paste
|
|
||||||
#define KEY_FIND 0x7e // Keyboard Find
|
|
||||||
#define KEY_MUTE 0x7f // Keyboard Mute
|
|
||||||
#define KEY_VOLUMEUP 0x80 // Keyboard Volume Up
|
|
||||||
#define KEY_VOLUMEDOWN 0x81 // Keyboard Volume Down
|
|
||||||
// 0x82 Keyboard Locking Caps Lock
|
|
||||||
// 0x83 Keyboard Locking Num Lock
|
|
||||||
// 0x84 Keyboard Locking Scroll Lock
|
|
||||||
#define KEY_KPCOMMA 0x85 // Keypad Comma
|
|
||||||
// 0x86 Keypad Equal Sign
|
|
||||||
#define KEY_RO 0x87 // Keyboard International1
|
|
||||||
#define KEY_KATAKANAHIRAGANA 0x88 // Keyboard International2
|
|
||||||
#define KEY_YEN 0x89 // Keyboard International3
|
|
||||||
#define KEY_HENKAN 0x8a // Keyboard International4
|
|
||||||
#define KEY_MUHENKAN 0x8b // Keyboard International5
|
|
||||||
#define KEY_KPJPCOMMA 0x8c // Keyboard International6
|
|
||||||
// 0x8d Keyboard International7
|
|
||||||
// 0x8e Keyboard International8
|
|
||||||
// 0x8f Keyboard International9
|
|
||||||
#define KEY_HANGEUL 0x90 // Keyboard LANG1
|
|
||||||
#define KEY_HANJA 0x91 // Keyboard LANG2
|
|
||||||
#define KEY_KATAKANA 0x92 // Keyboard LANG3
|
|
||||||
#define KEY_HIRAGANA 0x93 // Keyboard LANG4
|
|
||||||
#define KEY_ZENKAKUHANKAKU 0x94 // Keyboard LANG5
|
|
||||||
// 0x95 Keyboard LANG6
|
|
||||||
// 0x96 Keyboard LANG7
|
|
||||||
// 0x97 Keyboard LANG8
|
|
||||||
// 0x98 Keyboard LANG9
|
|
||||||
// 0x99 Keyboard Alternate Erase
|
|
||||||
// 0x9a Keyboard SysReq/Attention
|
|
||||||
// 0x9b Keyboard Cancel
|
|
||||||
// 0x9c Keyboard Clear
|
|
||||||
// 0x9d Keyboard Prior
|
|
||||||
// 0x9e Keyboard Return
|
|
||||||
// 0x9f Keyboard Separator
|
|
||||||
// 0xa0 Keyboard Out
|
|
||||||
// 0xa1 Keyboard Oper
|
|
||||||
// 0xa2 Keyboard Clear/Again
|
|
||||||
// 0xa3 Keyboard CrSel/Props
|
|
||||||
// 0xa4 Keyboard ExSel
|
|
||||||
|
|
||||||
// 0xb0 Keypad 00
|
|
||||||
// 0xb1 Keypad 000
|
|
||||||
// 0xb2 Thousands Separator
|
|
||||||
// 0xb3 Decimal Separator
|
|
||||||
// 0xb4 Currency Unit
|
|
||||||
// 0xb5 Currency Sub-unit
|
|
||||||
#define KEY_KPLEFTPAREN 0xb6 // Keypad (
|
|
||||||
#define KEY_KPRIGHTPAREN 0xb7 // Keypad )
|
|
||||||
// 0xb8 Keypad {
|
|
||||||
// 0xb9 Keypad }
|
|
||||||
// 0xba Keypad Tab
|
|
||||||
#define KEY_KPBACKSPACE 0xbb // Keypad Backspace
|
|
||||||
// 0xbc Keypad A
|
|
||||||
// 0xbd Keypad B
|
|
||||||
// 0xbe Keypad C
|
|
||||||
// 0xbf Keypad D
|
|
||||||
// 0xc0 Keypad E
|
|
||||||
// 0xc1 Keypad F
|
|
||||||
// 0xc2 Keypad XOR
|
|
||||||
// 0xc3 Keypad ^
|
|
||||||
// 0xc4 Keypad %
|
|
||||||
// 0xc5 Keypad <
|
|
||||||
// 0xc6 Keypad >
|
|
||||||
// 0xc7 Keypad &
|
|
||||||
// 0xc8 Keypad &&
|
|
||||||
// 0xc9 Keypad |
|
|
||||||
// 0xca Keypad ||
|
|
||||||
// 0xcb Keypad :
|
|
||||||
// 0xcc Keypad #
|
|
||||||
// 0xcd Keypad Space
|
|
||||||
// 0xce Keypad @
|
|
||||||
// 0xcf Keypad !
|
|
||||||
// 0xd0 Keypad Memory Store
|
|
||||||
// 0xd1 Keypad Memory Recall
|
|
||||||
// 0xd2 Keypad Memory Clear
|
|
||||||
// 0xd3 Keypad Memory Add
|
|
||||||
// 0xd4 Keypad Memory Subtract
|
|
||||||
// 0xd5 Keypad Memory Multiply
|
|
||||||
// 0xd6 Keypad Memory Divide
|
|
||||||
// 0xd7 Keypad +/-
|
|
||||||
// 0xd8 Keypad Clear
|
|
||||||
// 0xd9 Keypad Clear Entry
|
|
||||||
// 0xda Keypad Binary
|
|
||||||
// 0xdb Keypad Octal
|
|
||||||
// 0xdc Keypad Decimal
|
|
||||||
// 0xdd Keypad Hexadecimal
|
|
||||||
|
|
||||||
#define KEY_LEFTCTRL 0xe0 // Keyboard Left Control
|
|
||||||
#define KEY_LEFTSHIFT 0xe1 // Keyboard Left Shift
|
|
||||||
#define KEY_LEFTALT 0xe2 // Keyboard Left Alt
|
|
||||||
#define KEY_LEFTMETA 0xe3 // Keyboard Left GUI
|
|
||||||
#define KEY_RIGHTCTRL 0xe4 // Keyboard Right Control
|
|
||||||
#define KEY_RIGHTSHIFT 0xe5 // Keyboard Right Shift
|
|
||||||
#define KEY_RIGHTALT 0xe6 // Keyboard Right Alt
|
|
||||||
#define KEY_RIGHTMETA 0xe7 // Keyboard Right GUI
|
|
||||||
|
|
||||||
#define KEY_MEDIA_PLAYPAUSE 0xe8
|
|
||||||
#define KEY_MEDIA_STOPCD 0xe9
|
|
||||||
#define KEY_MEDIA_PREVIOUSSONG 0xea
|
|
||||||
#define KEY_MEDIA_NEXTSONG 0xeb
|
|
||||||
#define KEY_MEDIA_EJECTCD 0xec
|
|
||||||
#define KEY_MEDIA_VOLUMEUP 0xed
|
|
||||||
#define KEY_MEDIA_VOLUMEDOWN 0xee
|
|
||||||
#define KEY_MEDIA_MUTE 0xef
|
|
||||||
#define KEY_MEDIA_WWW 0xf0
|
|
||||||
#define KEY_MEDIA_BACK 0xf1
|
|
||||||
#define KEY_MEDIA_FORWARD 0xf2
|
|
||||||
#define KEY_MEDIA_STOP 0xf3
|
|
||||||
#define KEY_MEDIA_FIND 0xf4
|
|
||||||
#define KEY_MEDIA_SCROLLUP 0xf5
|
|
||||||
#define KEY_MEDIA_SCROLLDOWN 0xf6
|
|
||||||
#define KEY_MEDIA_EDIT 0xf7
|
|
||||||
#define KEY_MEDIA_SLEEP 0xf8
|
|
||||||
#define KEY_MEDIA_COFFEE 0xf9
|
|
||||||
#define KEY_MEDIA_REFRESH 0xfa
|
|
||||||
#define KEY_MEDIA_CALC 0xfb
|
|
||||||
|
|
||||||
enum {
|
|
||||||
// Generic Control
|
|
||||||
KEY_CONSUMER_CONTROL = 0x0001,
|
|
||||||
|
|
||||||
// Power Control
|
|
||||||
KEY_CONSUMER_POWER = 0x0030,
|
|
||||||
KEY_CONSUMER_RESET = 0x0031,
|
|
||||||
KEY_CONSUMER_SLEEP = 0x0032,
|
|
||||||
|
|
||||||
// Screen Brightness
|
|
||||||
KEY_CONSUMER_BRIGHTNESS_INCREMENT = 0x006F,
|
|
||||||
KEY_CONSUMER_BRIGHTNESS_DECREMENT = 0x0070,
|
|
||||||
|
|
||||||
// These HID usages operate only on mobile systems (battery powered) and
|
|
||||||
// require Windows 8 (build 8302 or greater).
|
|
||||||
KEY_CONSUMER_WIRELESS_RADIO_CONTROLS = 0x000C,
|
|
||||||
KEY_CONSUMER_WIRELESS_RADIO_BUTTONS = 0x00C6,
|
|
||||||
KEY_CONSUMER_WIRELESS_RADIO_LED = 0x00C7,
|
|
||||||
KEY_CONSUMER_WIRELESS_RADIO_SLIDER_SWITCH = 0x00C8,
|
|
||||||
|
|
||||||
// Media Control
|
|
||||||
KEY_CONSUMER_PLAY_PAUSE = 0x00CD,
|
|
||||||
KEY_CONSUMER_SCAN_NEXT = 0x00B5,
|
|
||||||
KEY_CONSUMER_SCAN_PREVIOUS = 0x00B6,
|
|
||||||
KEY_CONSUMER_STOP = 0x00B7,
|
|
||||||
KEY_CONSUMER_VOLUME = 0x00E0,
|
|
||||||
KEY_CONSUMER_MUTE = 0x00E2,
|
|
||||||
KEY_CONSUMER_BASS = 0x00E3,
|
|
||||||
KEY_CONSUMER_TREBLE = 0x00E4,
|
|
||||||
KEY_CONSUMER_BASS_BOOST = 0x00E5,
|
|
||||||
KEY_CONSUMER_VOLUME_INCREMENT = 0x00E9,
|
|
||||||
KEY_CONSUMER_VOLUME_DECREMENT = 0x00EA,
|
|
||||||
KEY_CONSUMER_BASS_INCREMENT = 0x0152,
|
|
||||||
KEY_CONSUMER_BASS_DECREMENT = 0x0153,
|
|
||||||
KEY_CONSUMER_TREBLE_INCREMENT = 0x0154,
|
|
||||||
KEY_CONSUMER_TREBLE_DECREMENT = 0x0155,
|
|
||||||
|
|
||||||
// Application Launcher
|
|
||||||
KEY_CONSUMER_AL_CONSUMER_CONTROL_CONFIGURATION = 0x0183,
|
|
||||||
KEY_CONSUMER_AL_EMAIL_READER = 0x018A,
|
|
||||||
KEY_CONSUMER_AL_CALCULATOR = 0x0192,
|
|
||||||
KEY_CONSUMER_AL_LOCAL_BROWSER = 0x0194,
|
|
||||||
|
|
||||||
// Browser/Explorer Specific
|
|
||||||
KEY_CONSUMER_AC_SEARCH = 0x0221,
|
|
||||||
KEY_CONSUMER_AC_HOME = 0x0223,
|
|
||||||
KEY_CONSUMER_AC_BACK = 0x0224,
|
|
||||||
KEY_CONSUMER_AC_FORWARD = 0x0225,
|
|
||||||
KEY_CONSUMER_AC_STOP = 0x0226,
|
|
||||||
KEY_CONSUMER_AC_REFRESH = 0x0227,
|
|
||||||
KEY_CONSUMER_AC_BOOKMARKS = 0x022A,
|
|
||||||
|
|
||||||
// Mouse Horizontal scroll
|
|
||||||
KEY_CONSUMER_AC_PAN = 0x0238,
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -10,6 +10,8 @@ hardware.tinyUsb=true
|
|||||||
hardware.esptoolFlashFreq=120M
|
hardware.esptoolFlashFreq=120M
|
||||||
hardware.bluetooth=true
|
hardware.bluetooth=true
|
||||||
|
|
||||||
|
dependencies.useDeprecatedHal=false
|
||||||
|
|
||||||
storage.userDataLocation=SD
|
storage.userDataLocation=SD
|
||||||
|
|
||||||
display.size=1.14"
|
display.size=1.14"
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
- Platforms/platform-esp32
|
- Platforms/platform-esp32
|
||||||
|
- Drivers/st7789-module
|
||||||
|
- Drivers/m5stack-module
|
||||||
dts: m5stack,cardputer.dts
|
dts: m5stack,cardputer.dts
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
/dts-v1/;
|
/dts-v1/;
|
||||||
|
|
||||||
#include <tactility/bindings/root.h>
|
#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_ble.h>
|
||||||
#include <tactility/bindings/esp32_wifi_pinned.h>
|
#include <tactility/bindings/esp32_wifi_pinned.h>
|
||||||
#include <tactility/bindings/esp32_gpio.h>
|
#include <tactility/bindings/esp32_gpio.h>
|
||||||
@@ -10,7 +12,10 @@
|
|||||||
#include <tactility/bindings/esp32_spi.h>
|
#include <tactility/bindings/esp32_spi.h>
|
||||||
#include <tactility/bindings/esp32_uart.h>
|
#include <tactility/bindings/esp32_uart.h>
|
||||||
#include <tactility/bindings/esp32_sdspi.h>
|
#include <tactility/bindings/esp32_sdspi.h>
|
||||||
#include <tactility/bindings/display_placeholder.h>
|
#include <tactility/bindings/esp32_ledc_backlight.h>
|
||||||
|
|
||||||
|
#include <bindings/st7789.h>
|
||||||
|
#include <bindings/cardputer_keyboard.h>
|
||||||
|
|
||||||
// Reference: https://docs.m5stack.com/en/core/Cardputer
|
// Reference: https://docs.m5stack.com/en/core/Cardputer
|
||||||
/ {
|
/ {
|
||||||
@@ -32,6 +37,21 @@
|
|||||||
gpio-count = <49>;
|
gpio-count = <49>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
adc0 {
|
||||||
|
compatible = "espressif,esp32-adc-oneshot";
|
||||||
|
unit-id = <ADC_UNIT_1>;
|
||||||
|
clk-src = <ADC_RTC_CLK_SRC_DEFAULT>;
|
||||||
|
// GPIO10 (ADC1_CHANNEL_9 on ESP32-S3): battery voltage sense, behind a 1:2 divider.
|
||||||
|
channels = <ADC_CHANNEL_9 ADC_ATTEN_DB_12 ADC_BITWIDTH_DEFAULT>;
|
||||||
|
};
|
||||||
|
|
||||||
|
battery-sense {
|
||||||
|
compatible = "battery-sense";
|
||||||
|
io-channel = <&adc0 0>;
|
||||||
|
reference-voltage-mv = <3300>;
|
||||||
|
multiplier = <2000>;
|
||||||
|
};
|
||||||
|
|
||||||
port_a: grove0 {
|
port_a: grove0 {
|
||||||
compatible = "espressif,esp32-grove";
|
compatible = "espressif,esp32-grove";
|
||||||
defaultMode = <GROVE_MODE_I2C>;
|
defaultMode = <GROVE_MODE_I2C>;
|
||||||
@@ -42,6 +62,15 @@
|
|||||||
i2cClockFrequency = <400000>;
|
i2cClockFrequency = <400000>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 = <512>;
|
||||||
|
};
|
||||||
|
|
||||||
spi0 {
|
spi0 {
|
||||||
compatible = "espressif,esp32-spi";
|
compatible = "espressif,esp32-spi";
|
||||||
host = <SPI2_HOST>;
|
host = <SPI2_HOST>;
|
||||||
@@ -49,8 +78,20 @@
|
|||||||
pin-mosi = <&gpio0 35 GPIO_FLAG_NONE>;
|
pin-mosi = <&gpio0 35 GPIO_FLAG_NONE>;
|
||||||
pin-sclk = <&gpio0 36 GPIO_FLAG_NONE>;
|
pin-sclk = <&gpio0 36 GPIO_FLAG_NONE>;
|
||||||
|
|
||||||
display {
|
display@0 {
|
||||||
compatible = "display-placeholder";
|
compatible = "sitronix,st7789";
|
||||||
|
horizontal-resolution = <240>;
|
||||||
|
vertical-resolution = <135>;
|
||||||
|
// Should be 52 according to https://github.com/m5stack/M5GFX/blob/master/src/M5GFX.cpp but this leaves a gap at the bottom
|
||||||
|
gap-x = <53>;
|
||||||
|
gap-y = <40>;
|
||||||
|
swap-xy;
|
||||||
|
mirror-x;
|
||||||
|
invert-color;
|
||||||
|
pixel-clock-hz = <62500000>;
|
||||||
|
pin-dc = <&gpio0 34 GPIO_FLAG_NONE>;
|
||||||
|
pin-reset = <&gpio0 33 GPIO_FLAG_NONE>;
|
||||||
|
backlight = <&display_backlight>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -68,6 +109,20 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
keyboard {
|
||||||
|
compatible = "m5stack,cardputer-keyboard";
|
||||||
|
pins-output = <&gpio0 8 GPIO_FLAG_NONE>,
|
||||||
|
<&gpio0 9 GPIO_FLAG_NONE>,
|
||||||
|
<&gpio0 11 GPIO_FLAG_NONE>;
|
||||||
|
pins-input = <&gpio0 13 GPIO_FLAG_NONE>,
|
||||||
|
<&gpio0 15 GPIO_FLAG_NONE>,
|
||||||
|
<&gpio0 3 GPIO_FLAG_NONE>,
|
||||||
|
<&gpio0 4 GPIO_FLAG_NONE>,
|
||||||
|
<&gpio0 5 GPIO_FLAG_NONE>,
|
||||||
|
<&gpio0 6 GPIO_FLAG_NONE>,
|
||||||
|
<&gpio0 7 GPIO_FLAG_NONE>;
|
||||||
|
};
|
||||||
|
|
||||||
// Speaker and microphone
|
// Speaker and microphone
|
||||||
// TODO: Microphone I2C init code from https://github.com/m5stack/M5Unified/blob/a6256725481f1bc366655fa48cf03b6095e30ad1/src/M5Unified.cpp#L936
|
// TODO: Microphone I2C init code from https://github.com/m5stack/M5Unified/blob/a6256725481f1bc366655fa48cf03b6095e30ad1/src/M5Unified.cpp#L936
|
||||||
i2s0 {
|
i2s0 {
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@ static error_t stop() {
|
|||||||
return ERROR_NONE;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Module m5stack_cardputer_module = {
|
Module m5stack_cardputer_module = {
|
||||||
.name = "m5stack-cardputer",
|
.name = "m5stack-cardputer",
|
||||||
.start = start,
|
.start = start,
|
||||||
.stop = stop,
|
.stop = stop,
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
|
file(GLOB_RECURSE SOURCE_FILES source/*.c*)
|
||||||
|
|
||||||
idf_component_register(
|
idf_component_register(
|
||||||
SRCS ${SOURCE_FILES}
|
SRCS ${SOURCE_FILES}
|
||||||
INCLUDE_DIRS "Source"
|
INCLUDE_DIRS "source"
|
||||||
REQUIRES Tactility esp_lvgl_port esp_lcd ST7789 PwmBacklight ButtonControl
|
REQUIRES TactilityKernel driver
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
#include "devices/Display.h"
|
|
||||||
|
|
||||||
#include <Tactility/hal/Configuration.h>
|
|
||||||
#include <ButtonControl.h>
|
|
||||||
#include <PwmBacklight.h>
|
|
||||||
|
|
||||||
using namespace tt::hal;
|
|
||||||
|
|
||||||
bool initBoot() {
|
|
||||||
// CH552 applies 4 V to GPIO 0, which reduces Wi-Fi sensitivity
|
|
||||||
// Setting output to high adds a bias of 3.3 V and suppresses over-voltage:
|
|
||||||
gpio::configure(0, gpio::Mode::Output, false, false);
|
|
||||||
gpio::setLevel(0, true);
|
|
||||||
|
|
||||||
// "Hold power" pin: must be set to high to keep the device powered on:
|
|
||||||
gpio::configure(4, gpio::Mode::Output, false, false);
|
|
||||||
gpio::setLevel(4, true);
|
|
||||||
return driver::pwmbacklight::init(GPIO_NUM_27, 512);
|
|
||||||
}
|
|
||||||
|
|
||||||
static DeviceVector createDevices() {
|
|
||||||
return {
|
|
||||||
ButtonControl::createTwoButtonControl(37, 39),
|
|
||||||
createDisplay()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
extern const Configuration hardwareConfiguration = {
|
|
||||||
.initBoot = initBoot,
|
|
||||||
.createDevices = createDevices
|
|
||||||
};
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
#include "Display.h"
|
|
||||||
|
|
||||||
#include <PwmBacklight.h>
|
|
||||||
#include <St7789Display.h>
|
|
||||||
|
|
||||||
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
|
|
||||||
St7789Display::Configuration panel_configuration = {
|
|
||||||
.horizontalResolution = LCD_HORIZONTAL_RESOLUTION,
|
|
||||||
.verticalResolution = LCD_VERTICAL_RESOLUTION,
|
|
||||||
.gapX = 52,
|
|
||||||
.gapY = 40,
|
|
||||||
.swapXY = false,
|
|
||||||
.mirrorX = false,
|
|
||||||
.mirrorY = false,
|
|
||||||
.invertColor = true,
|
|
||||||
.bufferSize = LCD_BUFFER_SIZE,
|
|
||||||
.touch = nullptr,
|
|
||||||
.backlightDutyFunction = driver::pwmbacklight::setBacklightDuty,
|
|
||||||
.resetPin = LCD_PIN_RESET,
|
|
||||||
.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 = 40'000'000,
|
|
||||||
.transactionQueueDepth = 10
|
|
||||||
});
|
|
||||||
|
|
||||||
return std::make_shared<St7789Display>(panel_configuration, spi_configuration);
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Tactility/hal/display/DisplayDevice.h"
|
|
||||||
#include <memory>
|
|
||||||
#include <driver/gpio.h>
|
|
||||||
#include <driver/spi_common.h>
|
|
||||||
|
|
||||||
constexpr auto LCD_SPI_HOST = SPI2_HOST;
|
|
||||||
constexpr auto LCD_PIN_CS = GPIO_NUM_5;
|
|
||||||
constexpr auto LCD_PIN_DC = GPIO_NUM_14;
|
|
||||||
constexpr auto LCD_PIN_RESET = GPIO_NUM_12;
|
|
||||||
constexpr auto LCD_HORIZONTAL_RESOLUTION = 135;
|
|
||||||
constexpr auto LCD_VERTICAL_RESOLUTION = 240;
|
|
||||||
constexpr auto LCD_BUFFER_HEIGHT = LCD_VERTICAL_RESOLUTION / 3;
|
|
||||||
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();
|
|
||||||
@@ -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 m5stack_stickc_plus2_module = {
|
|
||||||
.name = "m5stack-stickc-plus2",
|
|
||||||
.start = start,
|
|
||||||
.stop = stop,
|
|
||||||
.symbols = nullptr,
|
|
||||||
.internal = nullptr
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -10,6 +10,8 @@ hardware.spiRamMode=QUAD
|
|||||||
hardware.spiRamSpeed=80M
|
hardware.spiRamSpeed=80M
|
||||||
hardware.esptoolFlashFreq=80M
|
hardware.esptoolFlashFreq=80M
|
||||||
|
|
||||||
|
dependencies.useDeprecatedHal=false
|
||||||
|
|
||||||
storage.userDataLocation=Internal
|
storage.userDataLocation=Internal
|
||||||
|
|
||||||
display.size=1.14"
|
display.size=1.14"
|
||||||
|
|||||||
@@ -2,4 +2,6 @@ dependencies:
|
|||||||
- Platforms/platform-esp32
|
- Platforms/platform-esp32
|
||||||
- Drivers/mpu6886-module
|
- Drivers/mpu6886-module
|
||||||
- Drivers/bm8563-module
|
- Drivers/bm8563-module
|
||||||
|
- Drivers/st7789-module
|
||||||
|
- Drivers/button-control-module
|
||||||
dts: m5stack,stickc-plus2.dts
|
dts: m5stack,stickc-plus2.dts
|
||||||
|
|||||||
@@ -7,7 +7,10 @@
|
|||||||
#include <tactility/bindings/esp32_uart.h>
|
#include <tactility/bindings/esp32_uart.h>
|
||||||
#include <bindings/mpu6886.h>
|
#include <bindings/mpu6886.h>
|
||||||
#include <bindings/bm8563.h>
|
#include <bindings/bm8563.h>
|
||||||
#include <tactility/bindings/display_placeholder.h>
|
#include <tactility/bindings/esp32_ledc_backlight.h>
|
||||||
|
|
||||||
|
#include <bindings/st7789.h>
|
||||||
|
#include <bindings/button_control.h>
|
||||||
|
|
||||||
/ {
|
/ {
|
||||||
compatible = "root";
|
compatible = "root";
|
||||||
@@ -49,6 +52,15 @@
|
|||||||
pin-scl = <&gpio0 33 GPIO_FLAG_NONE>;
|
pin-scl = <&gpio0 33 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 = <512>;
|
||||||
|
};
|
||||||
|
|
||||||
spi0 {
|
spi0 {
|
||||||
compatible = "espressif,esp32-spi";
|
compatible = "espressif,esp32-spi";
|
||||||
host = <SPI2_HOST>;
|
host = <SPI2_HOST>;
|
||||||
@@ -56,11 +68,26 @@
|
|||||||
pin-mosi = <&gpio0 15 GPIO_FLAG_NONE>;
|
pin-mosi = <&gpio0 15 GPIO_FLAG_NONE>;
|
||||||
pin-sclk = <&gpio0 13 GPIO_FLAG_NONE>;
|
pin-sclk = <&gpio0 13 GPIO_FLAG_NONE>;
|
||||||
|
|
||||||
display {
|
display@0 {
|
||||||
compatible = "display-placeholder";
|
compatible = "sitronix,st7789";
|
||||||
|
horizontal-resolution = <135>;
|
||||||
|
vertical-resolution = <240>;
|
||||||
|
gap-x = <52>;
|
||||||
|
gap-y = <40>;
|
||||||
|
invert-color;
|
||||||
|
pixel-clock-hz = <40000000>;
|
||||||
|
pin-dc = <&gpio0 14 GPIO_FLAG_NONE>;
|
||||||
|
pin-reset = <&gpio0 12 GPIO_FLAG_NONE>;
|
||||||
|
backlight = <&display_backlight>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
buttons {
|
||||||
|
compatible = "tactility,button-control";
|
||||||
|
pin-primary = <&gpio0 37 GPIO_FLAG_NONE>;
|
||||||
|
pin-secondary = <&gpio0 39 GPIO_FLAG_NONE>;
|
||||||
|
};
|
||||||
|
|
||||||
uart_grove: uart1 {
|
uart_grove: uart1 {
|
||||||
compatible = "espressif,esp32-uart";
|
compatible = "espressif,esp32-uart";
|
||||||
status = "disabled";
|
status = "disabled";
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
#include <tactility/module.h>
|
||||||
|
#include <tactility/error.h>
|
||||||
|
|
||||||
|
#include <driver/gpio.h>
|
||||||
|
|
||||||
|
constexpr auto* TAG = "StickCPlus2";
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
static bool init_power() {
|
||||||
|
// CH552 applies 4 V to GPIO 0, which reduces Wi-Fi sensitivity.
|
||||||
|
// Setting output to high adds a bias of 3.3 V and suppresses over-voltage:
|
||||||
|
gpio_set_direction(GPIO_NUM_0, GPIO_MODE_OUTPUT);
|
||||||
|
gpio_set_level(GPIO_NUM_0, 1);
|
||||||
|
|
||||||
|
// "Hold power" pin: must be set to high to keep the device powered on:
|
||||||
|
gpio_set_direction(GPIO_NUM_4, GPIO_MODE_OUTPUT);
|
||||||
|
gpio_set_level(GPIO_NUM_4, 1);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static error_t start() {
|
||||||
|
init_power();
|
||||||
|
return ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static error_t stop() {
|
||||||
|
// Empty for now
|
||||||
|
return ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Module m5stack_stickc_plus2_module = {
|
||||||
|
.name = "m5stack-stickc-plus2",
|
||||||
|
.start = start,
|
||||||
|
.stop = stop,
|
||||||
|
.symbols = nullptr,
|
||||||
|
.internal = nullptr
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -37,6 +37,7 @@
|
|||||||
|
|
||||||
## Medium Priority
|
## Medium Priority
|
||||||
|
|
||||||
|
- Implement a LED kernel driver (single colour and RGB, plain GPIO and PWM)
|
||||||
- Make USB host driver disabled by default, so it doesn't consume memory
|
- Make USB host driver disabled by default, so it doesn't consume memory
|
||||||
- Filtering for apps in App Hub:
|
- Filtering for apps in App Hub:
|
||||||
- apps that only work on a specific device
|
- apps that only work on a specific device
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
#include <Tactility/hal/encoder/EncoderDevice.h>
|
#include <Tactility/hal/encoder/EncoderDevice.h>
|
||||||
#include <Tactility/hal/gpio/Gpio.h>
|
#include <Tactility/hal/gpio/Gpio.h>
|
||||||
#include <Tactility/MessageQueue.h>
|
#include <Tactility/MessageQueue.h>
|
||||||
#include <Tactility/TactilityCore.h>
|
|
||||||
#include <Tactility/Thread.h>
|
#include <Tactility/Thread.h>
|
||||||
|
|
||||||
#include <driver/gpio.h>
|
#include <driver/gpio.h>
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.20)
|
||||||
|
|
||||||
|
include("${CMAKE_CURRENT_LIST_DIR}/../../Buildscripts/module.cmake")
|
||||||
|
|
||||||
|
file(GLOB_RECURSE SOURCE_FILES "source/*.c*")
|
||||||
|
|
||||||
|
tactility_add_module(button-control-module
|
||||||
|
SRCS ${SOURCE_FILES}
|
||||||
|
INCLUDE_DIRS include/
|
||||||
|
REQUIRES TactilityKernel platform-esp32 lvgl driver
|
||||||
|
)
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
Apache License
|
||||||
|
==============
|
||||||
|
|
||||||
|
_Version 2.0, January 2004_
|
||||||
|
_<<http://www.apache.org/licenses/>>_
|
||||||
|
|
||||||
|
### Terms and Conditions for use, reproduction, and distribution
|
||||||
|
|
||||||
|
#### 1. Definitions
|
||||||
|
|
||||||
|
“License” shall mean the terms and conditions for use, reproduction, and
|
||||||
|
distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
“Licensor” shall mean the copyright owner or entity authorized by the copyright
|
||||||
|
owner that is granting the License.
|
||||||
|
|
||||||
|
“Legal Entity” shall mean the union of the acting entity and all other entities
|
||||||
|
that control, are controlled by, or are under common control with that entity.
|
||||||
|
For the purposes of this definition, “control” means **(i)** the power, direct or
|
||||||
|
indirect, to cause the direction or management of such entity, whether by
|
||||||
|
contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or **(iii)** beneficial ownership of such entity.
|
||||||
|
|
||||||
|
“You” (or “Your”) shall mean an individual or Legal Entity exercising
|
||||||
|
permissions granted by this License.
|
||||||
|
|
||||||
|
“Source” form shall mean the preferred form for making modifications, including
|
||||||
|
but not limited to software source code, documentation source, and configuration
|
||||||
|
files.
|
||||||
|
|
||||||
|
“Object” form shall mean any form resulting from mechanical transformation or
|
||||||
|
translation of a Source form, including but not limited to compiled object code,
|
||||||
|
generated documentation, and conversions to other media types.
|
||||||
|
|
||||||
|
“Work” shall mean the work of authorship, whether in Source or Object form, made
|
||||||
|
available under the License, as indicated by a copyright notice that is included
|
||||||
|
in or attached to the work (an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
“Derivative Works” shall mean any work, whether in Source or Object form, that
|
||||||
|
is based on (or derived from) the Work and for which the editorial revisions,
|
||||||
|
annotations, elaborations, or other modifications represent, as a whole, an
|
||||||
|
original work of authorship. For the purposes of this License, Derivative Works
|
||||||
|
shall not include works that remain separable from, or merely link (or bind by
|
||||||
|
name) to the interfaces of, the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
“Contribution” shall mean any work of authorship, including the original version
|
||||||
|
of the Work and any modifications or additions to that Work or Derivative Works
|
||||||
|
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
||||||
|
by the copyright owner or by an individual or Legal Entity authorized to submit
|
||||||
|
on behalf of the copyright owner. For the purposes of this definition,
|
||||||
|
“submitted” means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems, and
|
||||||
|
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
||||||
|
the purpose of discussing and improving the Work, but excluding communication
|
||||||
|
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||||
|
owner as “Not a Contribution.”
|
||||||
|
|
||||||
|
“Contributor” shall mean Licensor and any individual or Legal Entity on behalf
|
||||||
|
of whom a Contribution has been received by Licensor and subsequently
|
||||||
|
incorporated within the Work.
|
||||||
|
|
||||||
|
#### 2. Grant of Copyright License
|
||||||
|
|
||||||
|
Subject to the terms and conditions of this License, each Contributor hereby
|
||||||
|
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||||
|
irrevocable copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the Work and such
|
||||||
|
Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
#### 3. Grant of Patent License
|
||||||
|
|
||||||
|
Subject to the terms and conditions of this License, each Contributor hereby
|
||||||
|
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||||
|
irrevocable (except as stated in this section) patent license to make, have
|
||||||
|
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
|
||||||
|
such license applies only to those patent claims licensable by such Contributor
|
||||||
|
that are necessarily infringed by their Contribution(s) alone or by combination
|
||||||
|
of their Contribution(s) with the Work to which such Contribution(s) was
|
||||||
|
submitted. If You institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
||||||
|
Contribution incorporated within the Work constitutes direct or contributory
|
||||||
|
patent infringement, then any patent licenses granted to You under this License
|
||||||
|
for that Work shall terminate as of the date such litigation is filed.
|
||||||
|
|
||||||
|
#### 4. Redistribution
|
||||||
|
|
||||||
|
You may reproduce and distribute copies of the Work or Derivative Works thereof
|
||||||
|
in any medium, with or without modifications, and in Source or Object form,
|
||||||
|
provided that You meet the following conditions:
|
||||||
|
|
||||||
|
* **(a)** You must give any other recipients of the Work or Derivative Works a copy of
|
||||||
|
this License; and
|
||||||
|
* **(b)** You must cause any modified files to carry prominent notices stating that You
|
||||||
|
changed the files; and
|
||||||
|
* **(c)** You must retain, in the Source form of any Derivative Works that You distribute,
|
||||||
|
all copyright, patent, trademark, and attribution notices from the Source form
|
||||||
|
of the Work, excluding those notices that do not pertain to any part of the
|
||||||
|
Derivative Works; and
|
||||||
|
* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any
|
||||||
|
Derivative Works that You distribute must include a readable copy of the
|
||||||
|
attribution notices contained within such NOTICE file, excluding those notices
|
||||||
|
that do not pertain to any part of the Derivative Works, in at least one of the
|
||||||
|
following places: within a NOTICE text file distributed as part of the
|
||||||
|
Derivative Works; within the Source form or documentation, if provided along
|
||||||
|
with the Derivative Works; or, within a display generated by the Derivative
|
||||||
|
Works, if and wherever such third-party notices normally appear. The contents of
|
||||||
|
the NOTICE file are for informational purposes only and do not modify the
|
||||||
|
License. You may add Your own attribution notices within Derivative Works that
|
||||||
|
You distribute, alongside or as an addendum to the NOTICE text from the Work,
|
||||||
|
provided that such additional attribution notices cannot be construed as
|
||||||
|
modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and may provide
|
||||||
|
additional or different license terms and conditions for use, reproduction, or
|
||||||
|
distribution of Your modifications, or for any such Derivative Works as a whole,
|
||||||
|
provided Your use, reproduction, and distribution of the Work otherwise complies
|
||||||
|
with the conditions stated in this License.
|
||||||
|
|
||||||
|
#### 5. Submission of Contributions
|
||||||
|
|
||||||
|
Unless You explicitly state otherwise, any Contribution intentionally submitted
|
||||||
|
for inclusion in the Work by You to the Licensor shall be under the terms and
|
||||||
|
conditions of this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify the terms of
|
||||||
|
any separate license agreement you may have executed with Licensor regarding
|
||||||
|
such Contributions.
|
||||||
|
|
||||||
|
#### 6. Trademarks
|
||||||
|
|
||||||
|
This License does not grant permission to use the trade names, trademarks,
|
||||||
|
service marks, or product names of the Licensor, except as required for
|
||||||
|
reasonable and customary use in describing the origin of the Work and
|
||||||
|
reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
#### 7. Disclaimer of Warranty
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, Licensor provides the
|
||||||
|
Work (and each Contributor provides its Contributions) on an “AS IS” BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
||||||
|
including, without limitation, any warranties or conditions of TITLE,
|
||||||
|
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
|
||||||
|
solely responsible for determining the appropriateness of using or
|
||||||
|
redistributing the Work and assume any risks associated with Your exercise of
|
||||||
|
permissions under this License.
|
||||||
|
|
||||||
|
#### 8. Limitation of Liability
|
||||||
|
|
||||||
|
In no event and under no legal theory, whether in tort (including negligence),
|
||||||
|
contract, or otherwise, unless required by applicable law (such as deliberate
|
||||||
|
and grossly negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special, incidental,
|
||||||
|
or consequential damages of any character arising as a result of this License or
|
||||||
|
out of the use or inability to use the Work (including but not limited to
|
||||||
|
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
|
||||||
|
any and all other commercial damages or losses), even if such Contributor has
|
||||||
|
been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
#### 9. Accepting Warranty or Additional Liability
|
||||||
|
|
||||||
|
While redistributing the Work or Derivative Works thereof, You may choose to
|
||||||
|
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
|
||||||
|
other liability obligations and/or rights consistent with this License. However,
|
||||||
|
in accepting such obligations, You may act only on Your own behalf and on Your
|
||||||
|
sole responsibility, not on behalf of any other Contributor, and only if You
|
||||||
|
agree to indemnify, defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason of your
|
||||||
|
accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
_END OF TERMS AND CONDITIONS_
|
||||||
|
|
||||||
|
### APPENDIX: How to apply the Apache License to your work
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following boilerplate
|
||||||
|
notice, with the fields enclosed by brackets `[]` replaced with your own
|
||||||
|
identifying information. (Don't include the brackets!) The text should be
|
||||||
|
enclosed in the appropriate comment syntax for the file format. We also
|
||||||
|
recommend that a file or class name and description of purpose be included on
|
||||||
|
the same “printed page” as the copyright notice for easier identification within
|
||||||
|
third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# Button Control
|
||||||
|
|
||||||
|
Kernel driver for navigating apps with 1 or 2 GPIO buttons. Kernel-driver counterpart to
|
||||||
|
`Drivers/ButtonControl`, exposing a `KEYBOARD_TYPE` device (`tactility/drivers/keyboard.h`)
|
||||||
|
instead of an LVGL encoder indev.
|
||||||
|
|
||||||
|
Devicetree binding: `tactility,button-control` (see `bindings/tactility,button-control.yaml`).
|
||||||
|
|
||||||
|
## Modes
|
||||||
|
|
||||||
|
- **One-button** (`pin-secondary` absent): short press = `LV_KEY_NEXT`, long press = `LV_KEY_ENTER`.
|
||||||
|
- **Two-button** (`pin-secondary` present): primary short = `LV_KEY_ENTER`, primary long =
|
||||||
|
`LV_KEY_ESC`, secondary short = `LV_KEY_NEXT`, secondary long = `LV_KEY_PREV`.
|
||||||
|
|
||||||
|
Short vs. long press is only decided when the button is released, by comparing the hold
|
||||||
|
duration against `long-press-ms` - not at the moment it's pressed.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```dts
|
||||||
|
buttons {
|
||||||
|
compatible = "tactility,button-control";
|
||||||
|
pin-primary = <&gpio0 37 GPIO_FLAG_NONE>;
|
||||||
|
pin-secondary = <&gpio0 39 GPIO_FLAG_NONE>;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[Apache License Version 2.0](LICENSE-Apache-2.0.md)
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
description: >
|
||||||
|
GPIO button(s) that emulate UI navigation keys, for boards with only 1 or 2 physical
|
||||||
|
buttons instead of a full keyboard or touchscreen. Exposes a KEYBOARD_TYPE device: each
|
||||||
|
button's short/long press is translated to an LVGL navigation key, decided once the
|
||||||
|
button is released (so a long press is only recognized after release, matching how long
|
||||||
|
a button was actually held).
|
||||||
|
|
||||||
|
compatible: "tactility,button-control"
|
||||||
|
|
||||||
|
properties:
|
||||||
|
pin-primary:
|
||||||
|
type: phandles
|
||||||
|
required: true
|
||||||
|
description: >
|
||||||
|
Primary button GPIO pin. One-button mode (pin-secondary absent): short press = select
|
||||||
|
next, long press = press/enter. Two-button mode (pin-secondary present): short press =
|
||||||
|
press/enter, long press = close app.
|
||||||
|
pin-secondary:
|
||||||
|
type: phandles
|
||||||
|
default: GPIO_PIN_SPEC_NONE
|
||||||
|
description: >
|
||||||
|
Optional secondary button GPIO pin. When present, enables two-button mode: short press
|
||||||
|
= select next, long press = select previous. When absent, the device runs in
|
||||||
|
one-button mode (see pin-primary).
|
||||||
|
active-low:
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
description: Whether a pressed button reads as a logic low level
|
||||||
|
debounce-ms:
|
||||||
|
type: int
|
||||||
|
default: 20
|
||||||
|
description: Minimum time between recognized level changes, for software debouncing
|
||||||
|
long-press-ms:
|
||||||
|
type: int
|
||||||
|
default: 500
|
||||||
|
description: Minimum hold duration (from press to release) recognized as a long press
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
dependencies:
|
||||||
|
- TactilityKernel
|
||||||
|
bindings: bindings
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <tactility/bindings/bindings.h>
|
||||||
|
#include <drivers/button_control.h>
|
||||||
|
|
||||||
|
DEFINE_DEVICETREE(button_control, struct ButtonControlConfig)
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <tactility/module.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern struct Module button_control_module;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include <tactility/drivers/gpio.h>
|
||||||
|
|
||||||
|
struct ButtonControlConfig {
|
||||||
|
struct GpioPinSpec pin_primary;
|
||||||
|
// GPIO_PIN_SPEC_NONE selects one-button mode; see the binding description for the
|
||||||
|
// resulting short/long press key mapping in each mode.
|
||||||
|
struct GpioPinSpec pin_secondary;
|
||||||
|
bool active_low;
|
||||||
|
uint32_t debounce_ms;
|
||||||
|
uint32_t long_press_ms;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,226 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
#include <drivers/button_control.h>
|
||||||
|
#include <button_control_module.h>
|
||||||
|
|
||||||
|
#include <tactility/device.h>
|
||||||
|
#include <tactility/driver.h>
|
||||||
|
#include <tactility/drivers/gpio_controller.h>
|
||||||
|
#include <tactility/drivers/keyboard.h>
|
||||||
|
#include <tactility/error.h>
|
||||||
|
#include <tactility/log.h>
|
||||||
|
#include <tactility/time.h>
|
||||||
|
|
||||||
|
#include <lvgl.h>
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#define TAG "ButtonControl"
|
||||||
|
#define GET_CONFIG(device) (static_cast<const ButtonControlConfig*>((device)->config))
|
||||||
|
|
||||||
|
// Worst case: both buttons complete a short/long press gesture (press + release, 2 events each)
|
||||||
|
// within the same read_key() polling interval.
|
||||||
|
constexpr auto BUTTON_CONTROL_PENDING_CAPACITY = 4;
|
||||||
|
|
||||||
|
struct ButtonControlPendingEvent {
|
||||||
|
uint32_t key;
|
||||||
|
bool pressed;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ButtonControlButtonState {
|
||||||
|
GpioDescriptor* descriptor;
|
||||||
|
bool in_use;
|
||||||
|
bool debounced_pressed;
|
||||||
|
uint32_t press_start_time;
|
||||||
|
uint32_t last_change_time;
|
||||||
|
uint32_t short_press_key;
|
||||||
|
uint32_t long_press_key;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ButtonControlInternal {
|
||||||
|
ButtonControlButtonState primary;
|
||||||
|
ButtonControlButtonState secondary;
|
||||||
|
ButtonControlPendingEvent pending[BUTTON_CONTROL_PENDING_CAPACITY];
|
||||||
|
uint8_t pending_head;
|
||||||
|
uint8_t pending_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void push_pending(ButtonControlInternal* internal, uint32_t key, bool pressed) {
|
||||||
|
if (internal->pending_count >= BUTTON_CONTROL_PENDING_CAPACITY) {
|
||||||
|
LOG_W(TAG, "Pending event queue full, dropping event");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint8_t tail = (internal->pending_head + internal->pending_count) % BUTTON_CONTROL_PENDING_CAPACITY;
|
||||||
|
internal->pending[tail] = { .key = key, .pressed = pressed };
|
||||||
|
internal->pending_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool pop_pending(ButtonControlInternal* internal, ButtonControlPendingEvent* out_event) {
|
||||||
|
if (internal->pending_count == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*out_event = internal->pending[internal->pending_head];
|
||||||
|
internal->pending_head = (internal->pending_head + 1) % BUTTON_CONTROL_PENDING_CAPACITY;
|
||||||
|
internal->pending_count--;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// region Driver lifecycle
|
||||||
|
|
||||||
|
static error_t acquire_button(const GpioPinSpec& pin, uint32_t short_press_key, uint32_t long_press_key, ButtonControlButtonState* out_state) {
|
||||||
|
if (pin.gpio_controller == nullptr) {
|
||||||
|
*out_state = {};
|
||||||
|
return ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* descriptor = gpio_descriptor_acquire(pin.gpio_controller, 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, pin.flags | GPIO_FLAG_DIRECTION_INPUT) != ERROR_NONE) {
|
||||||
|
LOG_E(TAG, "Failed to configure GPIO as input");
|
||||||
|
gpio_descriptor_release(descriptor);
|
||||||
|
return ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_state = {
|
||||||
|
.descriptor = descriptor,
|
||||||
|
.in_use = true,
|
||||||
|
.debounced_pressed = false,
|
||||||
|
.press_start_time = 0,
|
||||||
|
.last_change_time = 0,
|
||||||
|
.short_press_key = short_press_key,
|
||||||
|
.long_press_key = long_press_key,
|
||||||
|
};
|
||||||
|
return ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static error_t start(Device* device) {
|
||||||
|
const auto* config = GET_CONFIG(device);
|
||||||
|
|
||||||
|
auto* internal = static_cast<ButtonControlInternal*>(malloc(sizeof(ButtonControlInternal)));
|
||||||
|
if (internal == nullptr) {
|
||||||
|
return ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
*internal = {};
|
||||||
|
|
||||||
|
bool two_button_mode = config->pin_secondary.gpio_controller != nullptr;
|
||||||
|
|
||||||
|
error_t error = acquire_button(
|
||||||
|
config->pin_primary,
|
||||||
|
two_button_mode ? LV_KEY_ENTER : LV_KEY_NEXT,
|
||||||
|
two_button_mode ? LV_KEY_ESC : LV_KEY_ENTER,
|
||||||
|
&internal->primary
|
||||||
|
);
|
||||||
|
if (error != ERROR_NONE) {
|
||||||
|
free(internal);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = acquire_button(config->pin_secondary, LV_KEY_NEXT, LV_KEY_PREV, &internal->secondary);
|
||||||
|
if (error != ERROR_NONE) {
|
||||||
|
if (internal->primary.in_use) {
|
||||||
|
gpio_descriptor_release(internal->primary.descriptor);
|
||||||
|
}
|
||||||
|
free(internal);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
device_set_driver_data(device, internal);
|
||||||
|
return ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static error_t stop(Device* device) {
|
||||||
|
auto* internal = static_cast<ButtonControlInternal*>(device_get_driver_data(device));
|
||||||
|
|
||||||
|
if (internal->primary.in_use) {
|
||||||
|
gpio_descriptor_release(internal->primary.descriptor);
|
||||||
|
}
|
||||||
|
if (internal->secondary.in_use) {
|
||||||
|
gpio_descriptor_release(internal->secondary.descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(internal);
|
||||||
|
return ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region KeyboardApi
|
||||||
|
|
||||||
|
// Long press is only distinguished from short press at release time, by how long the button
|
||||||
|
// was held - not at the moment it's first pressed. This matches physical button behavior:
|
||||||
|
// you can't know a press is "long" until it either ends or the threshold is reached.
|
||||||
|
static void poll_button(const ButtonControlConfig* config, ButtonControlInternal* internal, ButtonControlButtonState* state) {
|
||||||
|
if (!state->in_use) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool high = false;
|
||||||
|
if (gpio_descriptor_get_level(state->descriptor, &high) != ERROR_NONE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bool raw_pressed = config->active_low ? !high : high;
|
||||||
|
|
||||||
|
uint32_t now = get_millis();
|
||||||
|
if ((now - state->last_change_time) < config->debounce_ms) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (raw_pressed == state->debounced_pressed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state->last_change_time = now;
|
||||||
|
state->debounced_pressed = raw_pressed;
|
||||||
|
|
||||||
|
if (raw_pressed) {
|
||||||
|
state->press_start_time = now;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release: decide short vs. long press by elapsed hold duration, then queue a synthetic
|
||||||
|
// key tap (press followed by release) for the LVGL key this gesture maps to.
|
||||||
|
uint32_t held_ms = now - state->press_start_time;
|
||||||
|
uint32_t key = held_ms < config->long_press_ms ? state->short_press_key : state->long_press_key;
|
||||||
|
push_pending(internal, key, true);
|
||||||
|
push_pending(internal, key, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static error_t button_control_read_key(Device* device, KeyboardKeyData* data) {
|
||||||
|
const auto* config = GET_CONFIG(device);
|
||||||
|
auto* internal = static_cast<ButtonControlInternal*>(device_get_driver_data(device));
|
||||||
|
|
||||||
|
poll_button(config, internal, &internal->primary);
|
||||||
|
poll_button(config, internal, &internal->secondary);
|
||||||
|
|
||||||
|
ButtonControlPendingEvent event;
|
||||||
|
if (pop_pending(internal, &event)) {
|
||||||
|
data->key = event.key;
|
||||||
|
data->pressed = event.pressed;
|
||||||
|
data->continue_reading = internal->pending_count > 0;
|
||||||
|
} else {
|
||||||
|
data->key = 0;
|
||||||
|
data->pressed = false;
|
||||||
|
data->continue_reading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
static constexpr KeyboardApi button_control_api = {
|
||||||
|
.read_key = button_control_read_key,
|
||||||
|
};
|
||||||
|
|
||||||
|
Driver button_control_driver = {
|
||||||
|
.name = "button_control",
|
||||||
|
.compatible = (const char*[]) { "tactility,button-control", nullptr },
|
||||||
|
.start_device = start,
|
||||||
|
.stop_device = stop,
|
||||||
|
.api = &button_control_api,
|
||||||
|
.device_type = &KEYBOARD_TYPE,
|
||||||
|
.owner = &button_control_module,
|
||||||
|
.internal = nullptr
|
||||||
|
};
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
#include <tactility/check.h>
|
||||||
|
#include <tactility/driver.h>
|
||||||
|
#include <tactility/module.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
extern Driver button_control_driver;
|
||||||
|
|
||||||
|
static error_t start() {
|
||||||
|
/* We crash when construct fails, because if a single driver fails to construct,
|
||||||
|
* there is no guarantee that the previously constructed drivers can be destroyed */
|
||||||
|
check(driver_construct_add(&button_control_driver) == ERROR_NONE);
|
||||||
|
return ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static error_t stop() {
|
||||||
|
/* We crash when destruct fails, because if a single driver fails to destruct,
|
||||||
|
* there is no guarantee that the previously destroyed drivers can be recovered */
|
||||||
|
check(driver_remove_destruct(&button_control_driver) == ERROR_NONE);
|
||||||
|
return ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Module button_control_module = {
|
||||||
|
.name = "button-control",
|
||||||
|
.start = start,
|
||||||
|
.stop = stop,
|
||||||
|
.symbols = nullptr,
|
||||||
|
.internal = nullptr
|
||||||
|
};
|
||||||
|
|
||||||
|
} // extern "C"
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.20)
|
||||||
|
|
||||||
|
include("${CMAKE_CURRENT_LIST_DIR}/../../Buildscripts/module.cmake")
|
||||||
|
|
||||||
|
file(GLOB_RECURSE SOURCE_FILES "source/*.c*")
|
||||||
|
|
||||||
|
tactility_add_module(cst816s-module
|
||||||
|
SRCS ${SOURCE_FILES}
|
||||||
|
INCLUDE_DIRS include/
|
||||||
|
REQUIRES TactilityKernel platform-esp32 esp_lcd_touch_cst816s esp_lcd driver
|
||||||
|
)
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
description: Hynitron CST816S capacitive touch controller
|
||||||
|
|
||||||
|
include: ["i2c-device.yaml"]
|
||||||
|
|
||||||
|
compatible: "hynitron,cst816s"
|
||||||
|
|
||||||
|
bus: i2c
|
||||||
|
|
||||||
|
properties:
|
||||||
|
x-max:
|
||||||
|
type: int
|
||||||
|
required: true
|
||||||
|
description: Maximum X coordinate reported by the controller (typically the panel's horizontal resolution)
|
||||||
|
y-max:
|
||||||
|
type: int
|
||||||
|
required: true
|
||||||
|
description: Maximum Y coordinate reported by the controller (typically the panel's vertical resolution)
|
||||||
|
swap-xy:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
description: Swap the X and Y axes
|
||||||
|
mirror-x:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
description: Mirror the X axis
|
||||||
|
mirror-y:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
description: Mirror the Y axis
|
||||||
|
pin-reset:
|
||||||
|
type: phandles
|
||||||
|
default: GPIO_PIN_SPEC_NONE
|
||||||
|
description: Reset GPIO pin
|
||||||
|
pin-interrupt:
|
||||||
|
type: phandles
|
||||||
|
default: GPIO_PIN_SPEC_NONE
|
||||||
|
description: Interrupt GPIO pin
|
||||||
|
reset-active-high:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
description: Whether the reset pin is active high
|
||||||
|
interrupt-active-high:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
description: Whether the interrupt pin is active high
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
dependencies:
|
||||||
|
- TactilityKernel
|
||||||
|
bindings: bindings
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <tactility/bindings/bindings.h>
|
||||||
|
#include <drivers/cst816s.h>
|
||||||
|
|
||||||
|
DEFINE_DEVICETREE(cst816s, struct Cst816sConfig)
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <tactility/module.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern struct Module cst816s_module;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include <tactility/drivers/gpio.h>
|
||||||
|
|
||||||
|
struct Cst816sConfig {
|
||||||
|
// Devicetree address hint. Unused by the driver: the CST816S always sits at a fixed
|
||||||
|
// I2C address (0x15, see ESP_LCD_TOUCH_IO_I2C_CST816S_ADDRESS), unlike GT911's
|
||||||
|
// strapping-dependent address.
|
||||||
|
uint8_t address;
|
||||||
|
uint16_t x_max;
|
||||||
|
uint16_t y_max;
|
||||||
|
bool swap_xy;
|
||||||
|
bool mirror_x;
|
||||||
|
bool mirror_y;
|
||||||
|
struct GpioPinSpec pin_reset;
|
||||||
|
struct GpioPinSpec pin_interrupt;
|
||||||
|
bool reset_active_high;
|
||||||
|
bool interrupt_active_high;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,205 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
#include <drivers/cst816s.h>
|
||||||
|
#include <cst816s_module.h>
|
||||||
|
|
||||||
|
#include <tactility/check.h>
|
||||||
|
#include <tactility/device.h>
|
||||||
|
#include <tactility/driver.h>
|
||||||
|
#include <tactility/drivers/esp32_i2c.h>
|
||||||
|
#include <tactility/drivers/esp32_i2c_master.h>
|
||||||
|
#include <tactility/drivers/i2c_controller.h>
|
||||||
|
#include <tactility/drivers/pointer.h>
|
||||||
|
#include <tactility/log.h>
|
||||||
|
|
||||||
|
#include <esp_err.h>
|
||||||
|
#include <esp_lcd_io_i2c.h>
|
||||||
|
#include <esp_lcd_panel_io.h>
|
||||||
|
#include <esp_lcd_touch.h>
|
||||||
|
#include <esp_lcd_touch_cst816s.h>
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#define TAG "CST816S"
|
||||||
|
#define GET_CONFIG(device) (static_cast<const Cst816sConfig*>((device)->config))
|
||||||
|
|
||||||
|
struct Cst816sInternal {
|
||||||
|
esp_lcd_panel_io_handle_t io_handle;
|
||||||
|
esp_lcd_touch_handle_t touch_handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline gpio_num_t pin_or_nc(const struct GpioPinSpec& pin) {
|
||||||
|
return pin.gpio_controller == nullptr ? GPIO_NUM_NC : static_cast<gpio_num_t>(pin.pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
// region Driver lifecycle
|
||||||
|
|
||||||
|
// CST816S always sits at a fixed I2C address, unlike GT911's strapping-dependent address,
|
||||||
|
// so no bus probing is needed here.
|
||||||
|
static esp_err_t create_io_handle(Device* parent, esp_lcd_panel_io_handle_t* out_handle) {
|
||||||
|
esp_lcd_panel_io_i2c_config_t io_config = ESP_LCD_TOUCH_IO_I2C_CST816S_CONFIG();
|
||||||
|
|
||||||
|
auto* parent_driver = device_get_driver(parent);
|
||||||
|
if (driver_is_compatible(parent_driver, "espressif,esp32-i2c")) {
|
||||||
|
auto port = static_cast<const Esp32I2cConfig*>(parent->config)->port;
|
||||||
|
return esp_lcd_new_panel_io_i2c_v1(port, &io_config, out_handle);
|
||||||
|
}
|
||||||
|
if (driver_is_compatible(parent_driver, "espressif,esp32-i2c-master")) {
|
||||||
|
auto bus = esp32_i2c_master_get_bus_handle(parent);
|
||||||
|
io_config.scl_speed_hz = esp32_i2c_master_get_clock_frequency(parent);
|
||||||
|
return esp_lcd_new_panel_io_i2c_v2(bus, &io_config, out_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_E(TAG, "Unsupported I2C driver");
|
||||||
|
return ESP_ERR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static error_t start(Device* device) {
|
||||||
|
auto* parent = device_get_parent(device);
|
||||||
|
check(device_get_type(parent) == &I2C_CONTROLLER_TYPE);
|
||||||
|
|
||||||
|
const auto* config = GET_CONFIG(device);
|
||||||
|
|
||||||
|
auto* internal = static_cast<Cst816sInternal*>(malloc(sizeof(Cst816sInternal)));
|
||||||
|
if (internal == nullptr) {
|
||||||
|
return ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t ret = create_io_handle(parent, &internal->io_handle);
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
free(internal);
|
||||||
|
return ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_lcd_touch_config_t touch_config = {
|
||||||
|
.x_max = config->x_max,
|
||||||
|
.y_max = config->y_max,
|
||||||
|
.rst_gpio_num = pin_or_nc(config->pin_reset),
|
||||||
|
.int_gpio_num = pin_or_nc(config->pin_interrupt),
|
||||||
|
.levels = {
|
||||||
|
.reset = config->reset_active_high ? 1u : 0u,
|
||||||
|
.interrupt = config->interrupt_active_high ? 1u : 0u,
|
||||||
|
},
|
||||||
|
.flags = {
|
||||||
|
.swap_xy = config->swap_xy ? 1u : 0u,
|
||||||
|
.mirror_x = config->mirror_x ? 1u : 0u,
|
||||||
|
.mirror_y = config->mirror_y ? 1u : 0u,
|
||||||
|
},
|
||||||
|
.process_coordinates = nullptr,
|
||||||
|
.interrupt_callback = nullptr,
|
||||||
|
.user_data = nullptr,
|
||||||
|
.driver_data = nullptr,
|
||||||
|
};
|
||||||
|
|
||||||
|
ret = esp_lcd_touch_new_i2c_cst816s(internal->io_handle, &touch_config, &internal->touch_handle);
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
LOG_E(TAG, "Failed to create touch handle: %s", esp_err_to_name(ret));
|
||||||
|
esp_lcd_panel_io_del(internal->io_handle);
|
||||||
|
free(internal);
|
||||||
|
return ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
device_set_driver_data(device, internal);
|
||||||
|
return ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static error_t stop(Device* device) {
|
||||||
|
auto* internal = static_cast<Cst816sInternal*>(device_get_driver_data(device));
|
||||||
|
|
||||||
|
bool ok = true;
|
||||||
|
|
||||||
|
// esp_lcd_touch_del() only releases the touch-side resources; the panel IO handle is owned
|
||||||
|
// separately and needs its own deletion.
|
||||||
|
if (esp_lcd_touch_del(internal->touch_handle) != ESP_OK) {
|
||||||
|
LOG_E(TAG, "Failed to delete touch handle");
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esp_lcd_panel_io_del(internal->io_handle) != ESP_OK) {
|
||||||
|
LOG_E(TAG, "Failed to delete panel IO handle");
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(internal);
|
||||||
|
return ok ? ERROR_NONE : ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region PointerApi
|
||||||
|
|
||||||
|
static error_t cst816s_enter_sleep(Device* device) {
|
||||||
|
auto* internal = static_cast<Cst816sInternal*>(device_get_driver_data(device));
|
||||||
|
return esp_lcd_touch_enter_sleep(internal->touch_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static error_t cst816s_exit_sleep(Device* device) {
|
||||||
|
auto* internal = static_cast<Cst816sInternal*>(device_get_driver_data(device));
|
||||||
|
return esp_lcd_touch_exit_sleep(internal->touch_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static error_t cst816s_read_data(Device* device, TickType_t timeout) {
|
||||||
|
(void)timeout; // esp_lcd_touch_read_data() has no timeout parameter
|
||||||
|
auto* internal = static_cast<Cst816sInternal*>(device_get_driver_data(device));
|
||||||
|
return esp_lcd_touch_read_data(internal->touch_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool cst816s_get_touched_points(Device* device, uint16_t* x, uint16_t* y, uint16_t* strength, uint8_t* point_count, uint8_t max_point_count) {
|
||||||
|
auto* internal = static_cast<Cst816sInternal*>(device_get_driver_data(device));
|
||||||
|
return esp_lcd_touch_get_coordinates(internal->touch_handle, x, y, strength, point_count, max_point_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
static error_t cst816s_set_swap_xy(Device* device, bool swap) {
|
||||||
|
auto* internal = static_cast<Cst816sInternal*>(device_get_driver_data(device));
|
||||||
|
return esp_lcd_touch_set_swap_xy(internal->touch_handle, swap) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static error_t cst816s_get_swap_xy(Device* device, bool* swap) {
|
||||||
|
auto* internal = static_cast<Cst816sInternal*>(device_get_driver_data(device));
|
||||||
|
return esp_lcd_touch_get_swap_xy(internal->touch_handle, swap) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static error_t cst816s_set_mirror_x(Device* device, bool mirror) {
|
||||||
|
auto* internal = static_cast<Cst816sInternal*>(device_get_driver_data(device));
|
||||||
|
return esp_lcd_touch_set_mirror_x(internal->touch_handle, mirror) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static error_t cst816s_get_mirror_x(Device* device, bool* mirror) {
|
||||||
|
auto* internal = static_cast<Cst816sInternal*>(device_get_driver_data(device));
|
||||||
|
return esp_lcd_touch_get_mirror_x(internal->touch_handle, mirror) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static error_t cst816s_set_mirror_y(Device* device, bool mirror) {
|
||||||
|
auto* internal = static_cast<Cst816sInternal*>(device_get_driver_data(device));
|
||||||
|
return esp_lcd_touch_set_mirror_y(internal->touch_handle, mirror) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static error_t cst816s_get_mirror_y(Device* device, bool* mirror) {
|
||||||
|
auto* internal = static_cast<Cst816sInternal*>(device_get_driver_data(device));
|
||||||
|
return esp_lcd_touch_get_mirror_y(internal->touch_handle, mirror) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
static const PointerApi cst816s_pointer_api = {
|
||||||
|
.enter_sleep = cst816s_enter_sleep,
|
||||||
|
.exit_sleep = cst816s_exit_sleep,
|
||||||
|
.read_data = cst816s_read_data,
|
||||||
|
.get_touched_points = cst816s_get_touched_points,
|
||||||
|
.set_swap_xy = cst816s_set_swap_xy,
|
||||||
|
.get_swap_xy = cst816s_get_swap_xy,
|
||||||
|
.set_mirror_x = cst816s_set_mirror_x,
|
||||||
|
.get_mirror_x = cst816s_get_mirror_x,
|
||||||
|
.set_mirror_y = cst816s_set_mirror_y,
|
||||||
|
.get_mirror_y = cst816s_get_mirror_y,
|
||||||
|
};
|
||||||
|
|
||||||
|
Driver cst816s_driver = {
|
||||||
|
.name = "cst816s",
|
||||||
|
.compatible = (const char*[]) { "hynitron,cst816s", nullptr },
|
||||||
|
.start_device = start,
|
||||||
|
.stop_device = stop,
|
||||||
|
.api = &cst816s_pointer_api,
|
||||||
|
.device_type = &POINTER_TYPE,
|
||||||
|
.owner = &cst816s_module,
|
||||||
|
.internal = nullptr
|
||||||
|
};
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
#include <tactility/check.h>
|
||||||
|
#include <tactility/driver.h>
|
||||||
|
#include <tactility/module.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
extern Driver cst816s_driver;
|
||||||
|
|
||||||
|
static error_t start() {
|
||||||
|
/* We crash when construct fails, because if a single driver fails to construct,
|
||||||
|
* there is no guarantee that the previously constructed drivers can be destroyed */
|
||||||
|
check(driver_construct_add(&cst816s_driver) == ERROR_NONE);
|
||||||
|
return ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static error_t stop() {
|
||||||
|
/* We crash when destruct fails, because if a single driver fails to destruct,
|
||||||
|
* there is no guarantee that the previously destroyed drivers can be recovered */
|
||||||
|
check(driver_remove_destruct(&cst816s_driver) == ERROR_NONE);
|
||||||
|
return ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Module cst816s_module = {
|
||||||
|
.name = "cst816s",
|
||||||
|
.start = start,
|
||||||
|
.stop = stop,
|
||||||
|
.symbols = nullptr,
|
||||||
|
.internal = nullptr
|
||||||
|
};
|
||||||
|
|
||||||
|
} // extern "C"
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
Apache License
|
||||||
|
==============
|
||||||
|
|
||||||
|
_Version 2.0, January 2004_
|
||||||
|
_<<http://www.apache.org/licenses/>>_
|
||||||
|
|
||||||
|
### Terms and Conditions for use, reproduction, and distribution
|
||||||
|
|
||||||
|
#### 1. Definitions
|
||||||
|
|
||||||
|
“License” shall mean the terms and conditions for use, reproduction, and
|
||||||
|
distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
“Licensor” shall mean the copyright owner or entity authorized by the copyright
|
||||||
|
owner that is granting the License.
|
||||||
|
|
||||||
|
“Legal Entity” shall mean the union of the acting entity and all other entities
|
||||||
|
that control, are controlled by, or are under common control with that entity.
|
||||||
|
For the purposes of this definition, “control” means **(i)** the power, direct or
|
||||||
|
indirect, to cause the direction or management of such entity, whether by
|
||||||
|
contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or **(iii)** beneficial ownership of such entity.
|
||||||
|
|
||||||
|
“You” (or “Your”) shall mean an individual or Legal Entity exercising
|
||||||
|
permissions granted by this License.
|
||||||
|
|
||||||
|
“Source” form shall mean the preferred form for making modifications, including
|
||||||
|
but not limited to software source code, documentation source, and configuration
|
||||||
|
files.
|
||||||
|
|
||||||
|
“Object” form shall mean any form resulting from mechanical transformation or
|
||||||
|
translation of a Source form, including but not limited to compiled object code,
|
||||||
|
generated documentation, and conversions to other media types.
|
||||||
|
|
||||||
|
“Work” shall mean the work of authorship, whether in Source or Object form, made
|
||||||
|
available under the License, as indicated by a copyright notice that is included
|
||||||
|
in or attached to the work (an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
“Derivative Works” shall mean any work, whether in Source or Object form, that
|
||||||
|
is based on (or derived from) the Work and for which the editorial revisions,
|
||||||
|
annotations, elaborations, or other modifications represent, as a whole, an
|
||||||
|
original work of authorship. For the purposes of this License, Derivative Works
|
||||||
|
shall not include works that remain separable from, or merely link (or bind by
|
||||||
|
name) to the interfaces of, the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
“Contribution” shall mean any work of authorship, including the original version
|
||||||
|
of the Work and any modifications or additions to that Work or Derivative Works
|
||||||
|
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
||||||
|
by the copyright owner or by an individual or Legal Entity authorized to submit
|
||||||
|
on behalf of the copyright owner. For the purposes of this definition,
|
||||||
|
“submitted” means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems, and
|
||||||
|
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
||||||
|
the purpose of discussing and improving the Work, but excluding communication
|
||||||
|
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||||
|
owner as “Not a Contribution.”
|
||||||
|
|
||||||
|
“Contributor” shall mean Licensor and any individual or Legal Entity on behalf
|
||||||
|
of whom a Contribution has been received by Licensor and subsequently
|
||||||
|
incorporated within the Work.
|
||||||
|
|
||||||
|
#### 2. Grant of Copyright License
|
||||||
|
|
||||||
|
Subject to the terms and conditions of this License, each Contributor hereby
|
||||||
|
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||||
|
irrevocable copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the Work and such
|
||||||
|
Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
#### 3. Grant of Patent License
|
||||||
|
|
||||||
|
Subject to the terms and conditions of this License, each Contributor hereby
|
||||||
|
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||||
|
irrevocable (except as stated in this section) patent license to make, have
|
||||||
|
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
|
||||||
|
such license applies only to those patent claims licensable by such Contributor
|
||||||
|
that are necessarily infringed by their Contribution(s) alone or by combination
|
||||||
|
of their Contribution(s) with the Work to which such Contribution(s) was
|
||||||
|
submitted. If You institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
||||||
|
Contribution incorporated within the Work constitutes direct or contributory
|
||||||
|
patent infringement, then any patent licenses granted to You under this License
|
||||||
|
for that Work shall terminate as of the date such litigation is filed.
|
||||||
|
|
||||||
|
#### 4. Redistribution
|
||||||
|
|
||||||
|
You may reproduce and distribute copies of the Work or Derivative Works thereof
|
||||||
|
in any medium, with or without modifications, and in Source or Object form,
|
||||||
|
provided that You meet the following conditions:
|
||||||
|
|
||||||
|
* **(a)** You must give any other recipients of the Work or Derivative Works a copy of
|
||||||
|
this License; and
|
||||||
|
* **(b)** You must cause any modified files to carry prominent notices stating that You
|
||||||
|
changed the files; and
|
||||||
|
* **(c)** You must retain, in the Source form of any Derivative Works that You distribute,
|
||||||
|
all copyright, patent, trademark, and attribution notices from the Source form
|
||||||
|
of the Work, excluding those notices that do not pertain to any part of the
|
||||||
|
Derivative Works; and
|
||||||
|
* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any
|
||||||
|
Derivative Works that You distribute must include a readable copy of the
|
||||||
|
attribution notices contained within such NOTICE file, excluding those notices
|
||||||
|
that do not pertain to any part of the Derivative Works, in at least one of the
|
||||||
|
following places: within a NOTICE text file distributed as part of the
|
||||||
|
Derivative Works; within the Source form or documentation, if provided along
|
||||||
|
with the Derivative Works; or, within a display generated by the Derivative
|
||||||
|
Works, if and wherever such third-party notices normally appear. The contents of
|
||||||
|
the NOTICE file are for informational purposes only and do not modify the
|
||||||
|
License. You may add Your own attribution notices within Derivative Works that
|
||||||
|
You distribute, alongside or as an addendum to the NOTICE text from the Work,
|
||||||
|
provided that such additional attribution notices cannot be construed as
|
||||||
|
modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and may provide
|
||||||
|
additional or different license terms and conditions for use, reproduction, or
|
||||||
|
distribution of Your modifications, or for any such Derivative Works as a whole,
|
||||||
|
provided Your use, reproduction, and distribution of the Work otherwise complies
|
||||||
|
with the conditions stated in this License.
|
||||||
|
|
||||||
|
#### 5. Submission of Contributions
|
||||||
|
|
||||||
|
Unless You explicitly state otherwise, any Contribution intentionally submitted
|
||||||
|
for inclusion in the Work by You to the Licensor shall be under the terms and
|
||||||
|
conditions of this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify the terms of
|
||||||
|
any separate license agreement you may have executed with Licensor regarding
|
||||||
|
such Contributions.
|
||||||
|
|
||||||
|
#### 6. Trademarks
|
||||||
|
|
||||||
|
This License does not grant permission to use the trade names, trademarks,
|
||||||
|
service marks, or product names of the Licensor, except as required for
|
||||||
|
reasonable and customary use in describing the origin of the Work and
|
||||||
|
reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
#### 7. Disclaimer of Warranty
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, Licensor provides the
|
||||||
|
Work (and each Contributor provides its Contributions) on an “AS IS” BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
||||||
|
including, without limitation, any warranties or conditions of TITLE,
|
||||||
|
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
|
||||||
|
solely responsible for determining the appropriateness of using or
|
||||||
|
redistributing the Work and assume any risks associated with Your exercise of
|
||||||
|
permissions under this License.
|
||||||
|
|
||||||
|
#### 8. Limitation of Liability
|
||||||
|
|
||||||
|
In no event and under no legal theory, whether in tort (including negligence),
|
||||||
|
contract, or otherwise, unless required by applicable law (such as deliberate
|
||||||
|
and grossly negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special, incidental,
|
||||||
|
or consequential damages of any character arising as a result of this License or
|
||||||
|
out of the use or inability to use the Work (including but not limited to
|
||||||
|
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
|
||||||
|
any and all other commercial damages or losses), even if such Contributor has
|
||||||
|
been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
#### 9. Accepting Warranty or Additional Liability
|
||||||
|
|
||||||
|
While redistributing the Work or Derivative Works thereof, You may choose to
|
||||||
|
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
|
||||||
|
other liability obligations and/or rights consistent with this License. However,
|
||||||
|
in accepting such obligations, You may act only on Your own behalf and on Your
|
||||||
|
sole responsibility, not on behalf of any other Contributor, and only if You
|
||||||
|
agree to indemnify, defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason of your
|
||||||
|
accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
_END OF TERMS AND CONDITIONS_
|
||||||
|
|
||||||
|
### APPENDIX: How to apply the Apache License to your work
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following boilerplate
|
||||||
|
notice, with the fields enclosed by brackets `[]` replaced with your own
|
||||||
|
identifying information. (Don't include the brackets!) The text should be
|
||||||
|
enclosed in the appropriate comment syntax for the file format. We also
|
||||||
|
recommend that a file or class name and description of purpose be included on
|
||||||
|
the same “printed page” as the copyright notice for easier identification within
|
||||||
|
third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.20)
|
||||||
|
|
||||||
|
include("${CMAKE_CURRENT_LIST_DIR}/../../Buildscripts/module.cmake")
|
||||||
|
|
||||||
|
file(GLOB_RECURSE SOURCE_FILES "source/*.c*")
|
||||||
|
|
||||||
|
tactility_add_module(ili9341-module
|
||||||
|
SRCS ${SOURCE_FILES}
|
||||||
|
INCLUDE_DIRS include/
|
||||||
|
REQUIRES TactilityKernel platform-esp32 esp_lcd_ili9341 driver
|
||||||
|
)
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
Apache License
|
||||||
|
==============
|
||||||
|
|
||||||
|
_Version 2.0, January 2004_
|
||||||
|
_<<http://www.apache.org/licenses/>>_
|
||||||
|
|
||||||
|
### Terms and Conditions for use, reproduction, and distribution
|
||||||
|
|
||||||
|
#### 1. Definitions
|
||||||
|
|
||||||
|
“License” shall mean the terms and conditions for use, reproduction, and
|
||||||
|
distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
“Licensor” shall mean the copyright owner or entity authorized by the copyright
|
||||||
|
owner that is granting the License.
|
||||||
|
|
||||||
|
“Legal Entity” shall mean the union of the acting entity and all other entities
|
||||||
|
that control, are controlled by, or are under common control with that entity.
|
||||||
|
For the purposes of this definition, “control” means **(i)** the power, direct or
|
||||||
|
indirect, to cause the direction or management of such entity, whether by
|
||||||
|
contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or **(iii)** beneficial ownership of such entity.
|
||||||
|
|
||||||
|
“You” (or “Your”) shall mean an individual or Legal Entity exercising
|
||||||
|
permissions granted by this License.
|
||||||
|
|
||||||
|
“Source” form shall mean the preferred form for making modifications, including
|
||||||
|
but not limited to software source code, documentation source, and configuration
|
||||||
|
files.
|
||||||
|
|
||||||
|
“Object” form shall mean any form resulting from mechanical transformation or
|
||||||
|
translation of a Source form, including but not limited to compiled object code,
|
||||||
|
generated documentation, and conversions to other media types.
|
||||||
|
|
||||||
|
“Work” shall mean the work of authorship, whether in Source or Object form, made
|
||||||
|
available under the License, as indicated by a copyright notice that is included
|
||||||
|
in or attached to the work (an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
“Derivative Works” shall mean any work, whether in Source or Object form, that
|
||||||
|
is based on (or derived from) the Work and for which the editorial revisions,
|
||||||
|
annotations, elaborations, or other modifications represent, as a whole, an
|
||||||
|
original work of authorship. For the purposes of this License, Derivative Works
|
||||||
|
shall not include works that remain separable from, or merely link (or bind by
|
||||||
|
name) to the interfaces of, the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
“Contribution” shall mean any work of authorship, including the original version
|
||||||
|
of the Work and any modifications or additions to that Work or Derivative Works
|
||||||
|
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
||||||
|
by the copyright owner or by an individual or Legal Entity authorized to submit
|
||||||
|
on behalf of the copyright owner. For the purposes of this definition,
|
||||||
|
“submitted” means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems, and
|
||||||
|
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
||||||
|
the purpose of discussing and improving the Work, but excluding communication
|
||||||
|
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||||
|
owner as “Not a Contribution.”
|
||||||
|
|
||||||
|
“Contributor” shall mean Licensor and any individual or Legal Entity on behalf
|
||||||
|
of whom a Contribution has been received by Licensor and subsequently
|
||||||
|
incorporated within the Work.
|
||||||
|
|
||||||
|
#### 2. Grant of Copyright License
|
||||||
|
|
||||||
|
Subject to the terms and conditions of this License, each Contributor hereby
|
||||||
|
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||||
|
irrevocable copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the Work and such
|
||||||
|
Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
#### 3. Grant of Patent License
|
||||||
|
|
||||||
|
Subject to the terms and conditions of this License, each Contributor hereby
|
||||||
|
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||||
|
irrevocable (except as stated in this section) patent license to make, have
|
||||||
|
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
|
||||||
|
such license applies only to those patent claims licensable by such Contributor
|
||||||
|
that are necessarily infringed by their Contribution(s) alone or by combination
|
||||||
|
of their Contribution(s) with the Work to which such Contribution(s) was
|
||||||
|
submitted. If You institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
||||||
|
Contribution incorporated within the Work constitutes direct or contributory
|
||||||
|
patent infringement, then any patent licenses granted to You under this License
|
||||||
|
for that Work shall terminate as of the date such litigation is filed.
|
||||||
|
|
||||||
|
#### 4. Redistribution
|
||||||
|
|
||||||
|
You may reproduce and distribute copies of the Work or Derivative Works thereof
|
||||||
|
in any medium, with or without modifications, and in Source or Object form,
|
||||||
|
provided that You meet the following conditions:
|
||||||
|
|
||||||
|
* **(a)** You must give any other recipients of the Work or Derivative Works a copy of
|
||||||
|
this License; and
|
||||||
|
* **(b)** You must cause any modified files to carry prominent notices stating that You
|
||||||
|
changed the files; and
|
||||||
|
* **(c)** You must retain, in the Source form of any Derivative Works that You distribute,
|
||||||
|
all copyright, patent, trademark, and attribution notices from the Source form
|
||||||
|
of the Work, excluding those notices that do not pertain to any part of the
|
||||||
|
Derivative Works; and
|
||||||
|
* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any
|
||||||
|
Derivative Works that You distribute must include a readable copy of the
|
||||||
|
attribution notices contained within such NOTICE file, excluding those notices
|
||||||
|
that do not pertain to any part of the Derivative Works, in at least one of the
|
||||||
|
following places: within a NOTICE text file distributed as part of the
|
||||||
|
Derivative Works; within the Source form or documentation, if provided along
|
||||||
|
with the Derivative Works; or, within a display generated by the Derivative
|
||||||
|
Works, if and wherever such third-party notices normally appear. The contents of
|
||||||
|
the NOTICE file are for informational purposes only and do not modify the
|
||||||
|
License. You may add Your own attribution notices within Derivative Works that
|
||||||
|
You distribute, alongside or as an addendum to the NOTICE text from the Work,
|
||||||
|
provided that such additional attribution notices cannot be construed as
|
||||||
|
modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and may provide
|
||||||
|
additional or different license terms and conditions for use, reproduction, or
|
||||||
|
distribution of Your modifications, or for any such Derivative Works as a whole,
|
||||||
|
provided Your use, reproduction, and distribution of the Work otherwise complies
|
||||||
|
with the conditions stated in this License.
|
||||||
|
|
||||||
|
#### 5. Submission of Contributions
|
||||||
|
|
||||||
|
Unless You explicitly state otherwise, any Contribution intentionally submitted
|
||||||
|
for inclusion in the Work by You to the Licensor shall be under the terms and
|
||||||
|
conditions of this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify the terms of
|
||||||
|
any separate license agreement you may have executed with Licensor regarding
|
||||||
|
such Contributions.
|
||||||
|
|
||||||
|
#### 6. Trademarks
|
||||||
|
|
||||||
|
This License does not grant permission to use the trade names, trademarks,
|
||||||
|
service marks, or product names of the Licensor, except as required for
|
||||||
|
reasonable and customary use in describing the origin of the Work and
|
||||||
|
reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
#### 7. Disclaimer of Warranty
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, Licensor provides the
|
||||||
|
Work (and each Contributor provides its Contributions) on an “AS IS” BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
||||||
|
including, without limitation, any warranties or conditions of TITLE,
|
||||||
|
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
|
||||||
|
solely responsible for determining the appropriateness of using or
|
||||||
|
redistributing the Work and assume any risks associated with Your exercise of
|
||||||
|
permissions under this License.
|
||||||
|
|
||||||
|
#### 8. Limitation of Liability
|
||||||
|
|
||||||
|
In no event and under no legal theory, whether in tort (including negligence),
|
||||||
|
contract, or otherwise, unless required by applicable law (such as deliberate
|
||||||
|
and grossly negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special, incidental,
|
||||||
|
or consequential damages of any character arising as a result of this License or
|
||||||
|
out of the use or inability to use the Work (including but not limited to
|
||||||
|
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
|
||||||
|
any and all other commercial damages or losses), even if such Contributor has
|
||||||
|
been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
#### 9. Accepting Warranty or Additional Liability
|
||||||
|
|
||||||
|
While redistributing the Work or Derivative Works thereof, You may choose to
|
||||||
|
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
|
||||||
|
other liability obligations and/or rights consistent with this License. However,
|
||||||
|
in accepting such obligations, You may act only on Your own behalf and on Your
|
||||||
|
sole responsibility, not on behalf of any other Contributor, and only if You
|
||||||
|
agree to indemnify, defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason of your
|
||||||
|
accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
_END OF TERMS AND CONDITIONS_
|
||||||
|
|
||||||
|
### APPENDIX: How to apply the Apache License to your work
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following boilerplate
|
||||||
|
notice, with the fields enclosed by brackets `[]` replaced with your own
|
||||||
|
identifying information. (Don't include the brackets!) The text should be
|
||||||
|
enclosed in the appropriate comment syntax for the file format. We also
|
||||||
|
recommend that a file or class name and description of purpose be included on
|
||||||
|
the same “printed page” as the copyright notice for easier identification within
|
||||||
|
third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
description: Ilitek ILI9341 display panel
|
||||||
|
|
||||||
|
compatible: "ilitek,ili9341"
|
||||||
|
|
||||||
|
bus: spi
|
||||||
|
|
||||||
|
properties:
|
||||||
|
horizontal-resolution:
|
||||||
|
type: int
|
||||||
|
required: true
|
||||||
|
description: Horizontal resolution in pixels
|
||||||
|
vertical-resolution:
|
||||||
|
type: int
|
||||||
|
required: true
|
||||||
|
description: Vertical resolution in pixels
|
||||||
|
gap-x:
|
||||||
|
type: int
|
||||||
|
default: 0
|
||||||
|
description: X offset applied to all draw operations
|
||||||
|
gap-y:
|
||||||
|
type: int
|
||||||
|
default: 0
|
||||||
|
description: Y offset applied to all draw operations
|
||||||
|
swap-xy:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
description: Swap the X and Y axes
|
||||||
|
mirror-x:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
description: Mirror the X axis
|
||||||
|
mirror-y:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
description: Mirror the Y axis
|
||||||
|
invert-color:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
description: Invert the panel's color output
|
||||||
|
bgr-order:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
description: Use BGR element order instead of RGB
|
||||||
|
bits-per-pixel:
|
||||||
|
type: int
|
||||||
|
default: 16
|
||||||
|
description: Color depth in bits per pixel
|
||||||
|
pixel-clock-hz:
|
||||||
|
type: int
|
||||||
|
default: 40000000
|
||||||
|
description: SPI pixel clock frequency in Hz
|
||||||
|
transaction-queue-depth:
|
||||||
|
type: int
|
||||||
|
default: 10
|
||||||
|
description: Size of the internal SPI transaction queue
|
||||||
|
pin-dc:
|
||||||
|
type: phandles
|
||||||
|
required: true
|
||||||
|
description: Data/Command GPIO pin
|
||||||
|
pin-reset:
|
||||||
|
type: phandles
|
||||||
|
default: GPIO_PIN_SPEC_NONE
|
||||||
|
description: Reset GPIO pin
|
||||||
|
reset-active-high:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
description: Whether the reset pin is active high
|
||||||
|
backlight:
|
||||||
|
type: phandle
|
||||||
|
default: NULL
|
||||||
|
description: Optional reference to this display's backlight device
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
dependencies:
|
||||||
|
- TactilityKernel
|
||||||
|
bindings: bindings
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <tactility/bindings/bindings.h>
|
||||||
|
#include <drivers/ili9341.h>
|
||||||
|
|
||||||
|
DEFINE_DEVICETREE(ili9341, struct Ili9341Config)
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include <tactility/device.h>
|
||||||
|
#include <tactility/drivers/gpio.h>
|
||||||
|
|
||||||
|
struct Ili9341Config {
|
||||||
|
uint16_t horizontal_resolution;
|
||||||
|
uint16_t vertical_resolution;
|
||||||
|
int32_t gap_x;
|
||||||
|
int32_t gap_y;
|
||||||
|
bool swap_xy;
|
||||||
|
bool mirror_x;
|
||||||
|
bool mirror_y;
|
||||||
|
bool invert_color;
|
||||||
|
bool bgr_order;
|
||||||
|
uint32_t bits_per_pixel;
|
||||||
|
uint32_t pixel_clock_hz;
|
||||||
|
uint8_t transaction_queue_depth;
|
||||||
|
struct GpioPinSpec pin_dc;
|
||||||
|
struct GpioPinSpec pin_reset;
|
||||||
|
bool reset_active_high;
|
||||||
|
// Optional reference to this display's backlight device, NULL if none.
|
||||||
|
struct Device* backlight;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <tactility/module.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern struct Module ili9341_module;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,325 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
#include <drivers/ili9341.h>
|
||||||
|
#include <ili9341_module.h>
|
||||||
|
|
||||||
|
#include <tactility/check.h>
|
||||||
|
#include <tactility/device.h>
|
||||||
|
#include <tactility/driver.h>
|
||||||
|
#include <tactility/drivers/display.h>
|
||||||
|
#include <tactility/drivers/esp32_spi.h>
|
||||||
|
#include <tactility/drivers/spi_controller.h>
|
||||||
|
#include <tactility/error.h>
|
||||||
|
#include <tactility/log.h>
|
||||||
|
|
||||||
|
#include <esp_err.h>
|
||||||
|
#include <esp_lcd_io_spi.h>
|
||||||
|
#include <esp_lcd_panel_io.h>
|
||||||
|
#include <esp_lcd_panel_ops.h>
|
||||||
|
#include <esp_lcd_ili9341.h>
|
||||||
|
|
||||||
|
#include <freertos/semphr.h>
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#define TAG "ILI9341"
|
||||||
|
|
||||||
|
#define GET_CONFIG(device) (static_cast<const Ili9341Config*>((device)->config))
|
||||||
|
|
||||||
|
struct Ili9341Internal {
|
||||||
|
esp_lcd_panel_io_handle_t io_handle;
|
||||||
|
esp_lcd_panel_handle_t panel_handle;
|
||||||
|
// Given from ISR context by on_color_trans_done() once a queued SPI transfer physically
|
||||||
|
// completes. draw_bitmap() blocks on this so it can honor DisplayApi's synchronous contract
|
||||||
|
// (see lvgl_display.c: the caller reuses/overwrites the color buffer as soon as draw_bitmap
|
||||||
|
// returns) - esp_lcd_panel_draw_bitmap() itself only queues the transfer and returns early.
|
||||||
|
SemaphoreHandle_t draw_done_semaphore;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fires for every completed SPI transaction on this IO (not just draw_bitmap's color transfers -
|
||||||
|
// bring-up commands like reset/init/gap go through the same IO), called from ISR context.
|
||||||
|
static bool IRAM_ATTR on_color_trans_done(esp_lcd_panel_io_handle_t, esp_lcd_panel_io_event_data_t*, void* user_ctx) {
|
||||||
|
auto* internal = static_cast<Ili9341Internal*>(user_ctx);
|
||||||
|
BaseType_t high_task_woken = pdFALSE;
|
||||||
|
xSemaphoreGiveFromISR(internal->draw_done_semaphore, &high_task_woken);
|
||||||
|
return high_task_woken == pdTRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pin_or_unused(const struct GpioPinSpec& pin) {
|
||||||
|
return pin.gpio_controller == nullptr ? -1 : static_cast<int>(pin.pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
// region Driver lifecycle
|
||||||
|
|
||||||
|
static error_t start(Device* device) {
|
||||||
|
auto* parent = device_get_parent(device);
|
||||||
|
check(device_get_type(parent) == &SPI_CONTROLLER_TYPE);
|
||||||
|
|
||||||
|
const auto* spi_config = static_cast<const Esp32SpiConfig*>(parent->config);
|
||||||
|
const auto* config = GET_CONFIG(device);
|
||||||
|
|
||||||
|
struct GpioPinSpec cs_pin;
|
||||||
|
if (esp32_spi_get_cs_pin(device, &cs_pin) != ERROR_NONE) {
|
||||||
|
LOG_E(TAG, "Failed to resolve CS pin");
|
||||||
|
return ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* internal = static_cast<Ili9341Internal*>(malloc(sizeof(Ili9341Internal)));
|
||||||
|
if (internal == nullptr) {
|
||||||
|
return ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal->draw_done_semaphore = xSemaphoreCreateBinary();
|
||||||
|
if (internal->draw_done_semaphore == nullptr) {
|
||||||
|
free(internal);
|
||||||
|
return ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_lcd_panel_io_spi_config_t io_config = {
|
||||||
|
.cs_gpio_num = pin_or_unused(cs_pin),
|
||||||
|
.dc_gpio_num = pin_or_unused(config->pin_dc),
|
||||||
|
.spi_mode = 0,
|
||||||
|
.pclk_hz = config->pixel_clock_hz,
|
||||||
|
.trans_queue_depth = config->transaction_queue_depth,
|
||||||
|
.on_color_trans_done = on_color_trans_done,
|
||||||
|
.user_ctx = internal,
|
||||||
|
.lcd_cmd_bits = 8,
|
||||||
|
.lcd_param_bits = 8,
|
||||||
|
.cs_ena_pretrans = 0,
|
||||||
|
.cs_ena_posttrans = 0,
|
||||||
|
.flags = {
|
||||||
|
.dc_high_on_cmd = 0,
|
||||||
|
.dc_low_on_data = 0,
|
||||||
|
.dc_low_on_param = 0,
|
||||||
|
.octal_mode = 0,
|
||||||
|
.quad_mode = 0,
|
||||||
|
.sio_mode = 1,
|
||||||
|
.lsb_first = 0,
|
||||||
|
.cs_high_active = 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
esp_err_t ret = esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)spi_config->host, &io_config, &internal->io_handle);
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
LOG_E(TAG, "Failed to create panel IO: %s", esp_err_to_name(ret));
|
||||||
|
vSemaphoreDelete(internal->draw_done_semaphore);
|
||||||
|
free(internal);
|
||||||
|
return ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_lcd_panel_dev_config_t panel_config = {
|
||||||
|
.reset_gpio_num = pin_or_unused(config->pin_reset),
|
||||||
|
.rgb_ele_order = config->bgr_order ? LCD_RGB_ELEMENT_ORDER_BGR : LCD_RGB_ELEMENT_ORDER_RGB,
|
||||||
|
.data_endian = LCD_RGB_DATA_ENDIAN_LITTLE,
|
||||||
|
.bits_per_pixel = config->bits_per_pixel,
|
||||||
|
.flags = { .reset_active_high = config->reset_active_high },
|
||||||
|
.vendor_config = nullptr,
|
||||||
|
};
|
||||||
|
|
||||||
|
ret = esp_lcd_new_panel_ili9341(internal->io_handle, &panel_config, &internal->panel_handle);
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
LOG_E(TAG, "Failed to create panel: %s", esp_err_to_name(ret));
|
||||||
|
esp_lcd_panel_io_del(internal->io_handle);
|
||||||
|
vSemaphoreDelete(internal->draw_done_semaphore);
|
||||||
|
free(internal);
|
||||||
|
return ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bring-up sequence, order matches EspLcdDisplayV2::applyConfiguration (proven correct on real ILI9341 panels).
|
||||||
|
// Every failure path below must clean up fully: unlike stop_device, this is never retried by the kernel
|
||||||
|
// if start_device fails (see device_start() in TactilityKernel), so a partial failure here would leak.
|
||||||
|
bool ok =
|
||||||
|
esp_lcd_panel_reset(internal->panel_handle) == ESP_OK &&
|
||||||
|
esp_lcd_panel_init(internal->panel_handle) == ESP_OK &&
|
||||||
|
(!config->invert_color || esp_lcd_panel_invert_color(internal->panel_handle, true) == ESP_OK);
|
||||||
|
|
||||||
|
if (ok) {
|
||||||
|
int gap_x = config->swap_xy ? config->gap_y : config->gap_x;
|
||||||
|
int gap_y = config->swap_xy ? config->gap_x : config->gap_y;
|
||||||
|
ok = (gap_x == 0 && gap_y == 0) || esp_lcd_panel_set_gap(internal->panel_handle, gap_x, gap_y) == ESP_OK;
|
||||||
|
}
|
||||||
|
ok = ok && (!config->swap_xy || esp_lcd_panel_swap_xy(internal->panel_handle, true) == ESP_OK);
|
||||||
|
ok = ok && ((!config->mirror_x && !config->mirror_y) || esp_lcd_panel_mirror(internal->panel_handle, config->mirror_x, config->mirror_y) == ESP_OK);
|
||||||
|
ok = ok && (!config->invert_color || esp_lcd_panel_invert_color(internal->panel_handle, true) == ESP_OK);
|
||||||
|
ok = ok && esp_lcd_panel_disp_on_off(internal->panel_handle, true) == ESP_OK;
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
LOG_E(TAG, "Failed to bring up panel");
|
||||||
|
esp_lcd_panel_del(internal->panel_handle);
|
||||||
|
esp_lcd_panel_io_del(internal->io_handle);
|
||||||
|
vSemaphoreDelete(internal->draw_done_semaphore);
|
||||||
|
free(internal);
|
||||||
|
return ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
device_set_driver_data(device, internal);
|
||||||
|
return ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static error_t stop(Device* device) {
|
||||||
|
auto* internal = static_cast<Ili9341Internal*>(device_get_driver_data(device));
|
||||||
|
|
||||||
|
error_t result = ERROR_NONE;
|
||||||
|
|
||||||
|
if (esp_lcd_panel_del(internal->panel_handle) != ESP_OK) {
|
||||||
|
LOG_E(TAG, "Failed to delete panel");
|
||||||
|
result = ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
if (esp_lcd_panel_io_del(internal->io_handle) != ESP_OK) {
|
||||||
|
LOG_E(TAG, "Failed to delete panel IO");
|
||||||
|
result = ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
vSemaphoreDelete(internal->draw_done_semaphore);
|
||||||
|
free(internal);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region DisplayApi
|
||||||
|
|
||||||
|
static error_t ili9341_reset(Device* device) {
|
||||||
|
auto* internal = static_cast<Ili9341Internal*>(device_get_driver_data(device));
|
||||||
|
return esp_lcd_panel_reset(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static error_t ili9341_init(Device* device) {
|
||||||
|
auto* internal = static_cast<Ili9341Internal*>(device_get_driver_data(device));
|
||||||
|
return esp_lcd_panel_init(internal->panel_handle) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static error_t ili9341_draw_bitmap(Device* device, int32_t x_start, int32_t y_start, int32_t x_end, int32_t y_end, const void* color_data) {
|
||||||
|
auto* internal = static_cast<Ili9341Internal*>(device_get_driver_data(device));
|
||||||
|
|
||||||
|
// Drain any stale signal left over from a prior non-draw transaction (bring-up commands like
|
||||||
|
// reset/gap also complete through on_color_trans_done), so the take() below can only be
|
||||||
|
// satisfied by this draw's own transfer completing.
|
||||||
|
xSemaphoreTake(internal->draw_done_semaphore, 0);
|
||||||
|
|
||||||
|
if (esp_lcd_panel_draw_bitmap(internal->panel_handle, x_start, y_start, x_end, y_end, color_data) != ESP_OK) {
|
||||||
|
return ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block until the SPI transfer physically completes: DisplayApi's draw_bitmap is a synchronous
|
||||||
|
// contract (see lvgl_display.c), so the caller must be able to safely reuse/overwrite
|
||||||
|
// color_data as soon as this call returns. esp_lcd_panel_draw_bitmap() only queues the
|
||||||
|
// transfer and returns once it's handed to the SPI peripheral, not once it's finished.
|
||||||
|
xSemaphoreTake(internal->draw_done_semaphore, portMAX_DELAY);
|
||||||
|
return ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static error_t ili9341_mirror(Device* device, bool x_axis, bool y_axis) {
|
||||||
|
auto* internal = static_cast<Ili9341Internal*>(device_get_driver_data(device));
|
||||||
|
return esp_lcd_panel_mirror(internal->panel_handle, x_axis, y_axis) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static error_t ili9341_swap_xy(Device* device, bool swap_axes) {
|
||||||
|
auto* internal = static_cast<Ili9341Internal*>(device_get_driver_data(device));
|
||||||
|
return esp_lcd_panel_swap_xy(internal->panel_handle, swap_axes) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads the devicetree-configured baseline, not live hardware state: swap_xy()/mirror() calls made after
|
||||||
|
// start_device() (e.g. by an LVGL rotation binding) intentionally don't change what "rotation 0" means here.
|
||||||
|
static bool ili9341_get_swap_xy(Device* device) {
|
||||||
|
return GET_CONFIG(device)->swap_xy;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ili9341_get_mirror_x(Device* device) {
|
||||||
|
return GET_CONFIG(device)->mirror_x;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ili9341_get_mirror_y(Device* device) {
|
||||||
|
return GET_CONFIG(device)->mirror_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
static error_t ili9341_set_gap(Device* device, int32_t x_gap, int32_t y_gap) {
|
||||||
|
auto* internal = static_cast<Ili9341Internal*>(device_get_driver_data(device));
|
||||||
|
return esp_lcd_panel_set_gap(internal->panel_handle, x_gap, y_gap) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static error_t ili9341_invert_color(Device* device, bool invert_color_data) {
|
||||||
|
auto* internal = static_cast<Ili9341Internal*>(device_get_driver_data(device));
|
||||||
|
return esp_lcd_panel_invert_color(internal->panel_handle, invert_color_data) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static error_t ili9341_disp_on_off(Device* device, bool on_off) {
|
||||||
|
auto* internal = static_cast<Ili9341Internal*>(device_get_driver_data(device));
|
||||||
|
return esp_lcd_panel_disp_on_off(internal->panel_handle, on_off) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static error_t ili9341_disp_sleep(Device* device, bool sleep) {
|
||||||
|
auto* internal = static_cast<Ili9341Internal*>(device_get_driver_data(device));
|
||||||
|
return esp_lcd_panel_disp_sleep(internal->panel_handle, sleep) == ESP_OK ? ERROR_NONE : ERROR_RESOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// bgr_order only selects the panel controller's rgb_ele_order (applied in start(), below) so the
|
||||||
|
// R/B swap happens in hardware over SPI. LVGL always fills RGB565 buffers either way - it has no
|
||||||
|
// BGR565 format (lvgl_display.c rejects it, "no LVGL equivalent"), and none is needed here.
|
||||||
|
//
|
||||||
|
// _SWAPPED (not plain RGB565): the panel expects each 16-bit pixel high-byte-first over SPI, but
|
||||||
|
// this CPU is little-endian, so a plain RGB565 buffer arrives byte-swapped per pixel - confirmed
|
||||||
|
// on real hardware: black/white rendered correctly (0x0000/0xFFFF are swap-invariant) while every
|
||||||
|
// other color was wrong. The old deprecated-HAL driver had the same requirement (its equivalent
|
||||||
|
// knob was esp_lvgl_port's `swapBytes = true` in Devices/*/Source/devices/Display.cpp).
|
||||||
|
static enum DisplayColorFormat ili9341_get_color_format(Device*) {
|
||||||
|
return DISPLAY_COLOR_FORMAT_RGB565_SWAPPED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t ili9341_get_resolution_x(Device* device) {
|
||||||
|
return GET_CONFIG(device)->horizontal_resolution;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t ili9341_get_resolution_y(Device* device) {
|
||||||
|
return GET_CONFIG(device)->vertical_resolution;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ili9341_get_frame_buffer(Device*, uint8_t, void** out_buffer) {
|
||||||
|
*out_buffer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t ili9341_get_frame_buffer_count(Device*) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static error_t ili9341_get_backlight(Device* device, Device** backlight) {
|
||||||
|
auto* configured_backlight = GET_CONFIG(device)->backlight;
|
||||||
|
if (configured_backlight == nullptr) {
|
||||||
|
return ERROR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
*backlight = configured_backlight;
|
||||||
|
return ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
static const DisplayApi ili9341_display_api = {
|
||||||
|
.reset = ili9341_reset,
|
||||||
|
.init = ili9341_init,
|
||||||
|
.draw_bitmap = ili9341_draw_bitmap,
|
||||||
|
.mirror = ili9341_mirror,
|
||||||
|
.swap_xy = ili9341_swap_xy,
|
||||||
|
.get_swap_xy = ili9341_get_swap_xy,
|
||||||
|
.get_mirror_x = ili9341_get_mirror_x,
|
||||||
|
.get_mirror_y = ili9341_get_mirror_y,
|
||||||
|
.set_gap = ili9341_set_gap,
|
||||||
|
.invert_color = ili9341_invert_color,
|
||||||
|
.disp_on_off = ili9341_disp_on_off,
|
||||||
|
.disp_sleep = ili9341_disp_sleep,
|
||||||
|
.get_color_format = ili9341_get_color_format,
|
||||||
|
.get_resolution_x = ili9341_get_resolution_x,
|
||||||
|
.get_resolution_y = ili9341_get_resolution_y,
|
||||||
|
.get_frame_buffer = ili9341_get_frame_buffer,
|
||||||
|
.get_frame_buffer_count = ili9341_get_frame_buffer_count,
|
||||||
|
.get_backlight = ili9341_get_backlight,
|
||||||
|
};
|
||||||
|
|
||||||
|
Driver ili9341_driver = {
|
||||||
|
.name = "ili9341",
|
||||||
|
.compatible = (const char*[]) { "ilitek,ili9341", nullptr },
|
||||||
|
.start_device = start,
|
||||||
|
.stop_device = stop,
|
||||||
|
.api = &ili9341_display_api,
|
||||||
|
.device_type = &DISPLAY_TYPE,
|
||||||
|
.owner = &ili9341_module,
|
||||||
|
.internal = nullptr
|
||||||
|
};
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
#include <tactility/check.h>
|
||||||
|
#include <tactility/driver.h>
|
||||||
|
#include <tactility/module.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
extern Driver ili9341_driver;
|
||||||
|
|
||||||
|
static error_t start() {
|
||||||
|
/* We crash when construct fails, because if a single driver fails to construct,
|
||||||
|
* there is no guarantee that the previously constructed drivers can be destroyed */
|
||||||
|
check(driver_construct_add(&ili9341_driver) == ERROR_NONE);
|
||||||
|
return ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static error_t stop() {
|
||||||
|
/* We crash when destruct fails, because if a single driver fails to destruct,
|
||||||
|
* there is no guarantee that the previously destroyed drivers can be recovered */
|
||||||
|
check(driver_remove_destruct(&ili9341_driver) == ERROR_NONE);
|
||||||
|
return ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Module ili9341_module = {
|
||||||
|
.name = "ili9341",
|
||||||
|
.start = start,
|
||||||
|
.stop = stop,
|
||||||
|
.symbols = nullptr,
|
||||||
|
.internal = nullptr
|
||||||
|
};
|
||||||
|
|
||||||
|
} // extern "C"
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.20)
|
||||||
|
|
||||||
|
include("${CMAKE_CURRENT_LIST_DIR}/../../Buildscripts/module.cmake")
|
||||||
|
|
||||||
|
file(GLOB_RECURSE SOURCE_FILES "source/*.c*")
|
||||||
|
|
||||||
|
tactility_add_module(ili9488-module
|
||||||
|
SRCS ${SOURCE_FILES}
|
||||||
|
INCLUDE_DIRS include/
|
||||||
|
REQUIRES TactilityKernel platform-esp32 esp_lcd_ili9488 driver
|
||||||
|
)
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
Apache License
|
||||||
|
==============
|
||||||
|
|
||||||
|
_Version 2.0, January 2004_
|
||||||
|
_<<http://www.apache.org/licenses/>>_
|
||||||
|
|
||||||
|
### Terms and Conditions for use, reproduction, and distribution
|
||||||
|
|
||||||
|
#### 1. Definitions
|
||||||
|
|
||||||
|
“License” shall mean the terms and conditions for use, reproduction, and
|
||||||
|
distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
“Licensor” shall mean the copyright owner or entity authorized by the copyright
|
||||||
|
owner that is granting the License.
|
||||||
|
|
||||||
|
“Legal Entity” shall mean the union of the acting entity and all other entities
|
||||||
|
that control, are controlled by, or are under common control with that entity.
|
||||||
|
For the purposes of this definition, “control” means **(i)** the power, direct or
|
||||||
|
indirect, to cause the direction or management of such entity, whether by
|
||||||
|
contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or **(iii)** beneficial ownership of such entity.
|
||||||
|
|
||||||
|
“You” (or “Your”) shall mean an individual or Legal Entity exercising
|
||||||
|
permissions granted by this License.
|
||||||
|
|
||||||
|
“Source” form shall mean the preferred form for making modifications, including
|
||||||
|
but not limited to software source code, documentation source, and configuration
|
||||||
|
files.
|
||||||
|
|
||||||
|
“Object” form shall mean any form resulting from mechanical transformation or
|
||||||
|
translation of a Source form, including but not limited to compiled object code,
|
||||||
|
generated documentation, and conversions to other media types.
|
||||||
|
|
||||||
|
“Work” shall mean the work of authorship, whether in Source or Object form, made
|
||||||
|
available under the License, as indicated by a copyright notice that is included
|
||||||
|
in or attached to the work (an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
“Derivative Works” shall mean any work, whether in Source or Object form, that
|
||||||
|
is based on (or derived from) the Work and for which the editorial revisions,
|
||||||
|
annotations, elaborations, or other modifications represent, as a whole, an
|
||||||
|
original work of authorship. For the purposes of this License, Derivative Works
|
||||||
|
shall not include works that remain separable from, or merely link (or bind by
|
||||||
|
name) to the interfaces of, the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
“Contribution” shall mean any work of authorship, including the original version
|
||||||
|
of the Work and any modifications or additions to that Work or Derivative Works
|
||||||
|
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
||||||
|
by the copyright owner or by an individual or Legal Entity authorized to submit
|
||||||
|
on behalf of the copyright owner. For the purposes of this definition,
|
||||||
|
“submitted” means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems, and
|
||||||
|
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
||||||
|
the purpose of discussing and improving the Work, but excluding communication
|
||||||
|
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||||
|
owner as “Not a Contribution.”
|
||||||
|
|
||||||
|
“Contributor” shall mean Licensor and any individual or Legal Entity on behalf
|
||||||
|
of whom a Contribution has been received by Licensor and subsequently
|
||||||
|
incorporated within the Work.
|
||||||
|
|
||||||
|
#### 2. Grant of Copyright License
|
||||||
|
|
||||||
|
Subject to the terms and conditions of this License, each Contributor hereby
|
||||||
|
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||||
|
irrevocable copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the Work and such
|
||||||
|
Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
#### 3. Grant of Patent License
|
||||||
|
|
||||||
|
Subject to the terms and conditions of this License, each Contributor hereby
|
||||||
|
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||||
|
irrevocable (except as stated in this section) patent license to make, have
|
||||||
|
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
|
||||||
|
such license applies only to those patent claims licensable by such Contributor
|
||||||
|
that are necessarily infringed by their Contribution(s) alone or by combination
|
||||||
|
of their Contribution(s) with the Work to which such Contribution(s) was
|
||||||
|
submitted. If You institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
||||||
|
Contribution incorporated within the Work constitutes direct or contributory
|
||||||
|
patent infringement, then any patent licenses granted to You under this License
|
||||||
|
for that Work shall terminate as of the date such litigation is filed.
|
||||||
|
|
||||||
|
#### 4. Redistribution
|
||||||
|
|
||||||
|
You may reproduce and distribute copies of the Work or Derivative Works thereof
|
||||||
|
in any medium, with or without modifications, and in Source or Object form,
|
||||||
|
provided that You meet the following conditions:
|
||||||
|
|
||||||
|
* **(a)** You must give any other recipients of the Work or Derivative Works a copy of
|
||||||
|
this License; and
|
||||||
|
* **(b)** You must cause any modified files to carry prominent notices stating that You
|
||||||
|
changed the files; and
|
||||||
|
* **(c)** You must retain, in the Source form of any Derivative Works that You distribute,
|
||||||
|
all copyright, patent, trademark, and attribution notices from the Source form
|
||||||
|
of the Work, excluding those notices that do not pertain to any part of the
|
||||||
|
Derivative Works; and
|
||||||
|
* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any
|
||||||
|
Derivative Works that You distribute must include a readable copy of the
|
||||||
|
attribution notices contained within such NOTICE file, excluding those notices
|
||||||
|
that do not pertain to any part of the Derivative Works, in at least one of the
|
||||||
|
following places: within a NOTICE text file distributed as part of the
|
||||||
|
Derivative Works; within the Source form or documentation, if provided along
|
||||||
|
with the Derivative Works; or, within a display generated by the Derivative
|
||||||
|
Works, if and wherever such third-party notices normally appear. The contents of
|
||||||
|
the NOTICE file are for informational purposes only and do not modify the
|
||||||
|
License. You may add Your own attribution notices within Derivative Works that
|
||||||
|
You distribute, alongside or as an addendum to the NOTICE text from the Work,
|
||||||
|
provided that such additional attribution notices cannot be construed as
|
||||||
|
modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and may provide
|
||||||
|
additional or different license terms and conditions for use, reproduction, or
|
||||||
|
distribution of Your modifications, or for any such Derivative Works as a whole,
|
||||||
|
provided Your use, reproduction, and distribution of the Work otherwise complies
|
||||||
|
with the conditions stated in this License.
|
||||||
|
|
||||||
|
#### 5. Submission of Contributions
|
||||||
|
|
||||||
|
Unless You explicitly state otherwise, any Contribution intentionally submitted
|
||||||
|
for inclusion in the Work by You to the Licensor shall be under the terms and
|
||||||
|
conditions of this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify the terms of
|
||||||
|
any separate license agreement you may have executed with Licensor regarding
|
||||||
|
such Contributions.
|
||||||
|
|
||||||
|
#### 6. Trademarks
|
||||||
|
|
||||||
|
This License does not grant permission to use the trade names, trademarks,
|
||||||
|
service marks, or product names of the Licensor, except as required for
|
||||||
|
reasonable and customary use in describing the origin of the Work and
|
||||||
|
reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
#### 7. Disclaimer of Warranty
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, Licensor provides the
|
||||||
|
Work (and each Contributor provides its Contributions) on an “AS IS” BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
||||||
|
including, without limitation, any warranties or conditions of TITLE,
|
||||||
|
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
|
||||||
|
solely responsible for determining the appropriateness of using or
|
||||||
|
redistributing the Work and assume any risks associated with Your exercise of
|
||||||
|
permissions under this License.
|
||||||
|
|
||||||
|
#### 8. Limitation of Liability
|
||||||
|
|
||||||
|
In no event and under no legal theory, whether in tort (including negligence),
|
||||||
|
contract, or otherwise, unless required by applicable law (such as deliberate
|
||||||
|
and grossly negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special, incidental,
|
||||||
|
or consequential damages of any character arising as a result of this License or
|
||||||
|
out of the use or inability to use the Work (including but not limited to
|
||||||
|
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
|
||||||
|
any and all other commercial damages or losses), even if such Contributor has
|
||||||
|
been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
#### 9. Accepting Warranty or Additional Liability
|
||||||
|
|
||||||
|
While redistributing the Work or Derivative Works thereof, You may choose to
|
||||||
|
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
|
||||||
|
other liability obligations and/or rights consistent with this License. However,
|
||||||
|
in accepting such obligations, You may act only on Your own behalf and on Your
|
||||||
|
sole responsibility, not on behalf of any other Contributor, and only if You
|
||||||
|
agree to indemnify, defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason of your
|
||||||
|
accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
_END OF TERMS AND CONDITIONS_
|
||||||
|
|
||||||
|
### APPENDIX: How to apply the Apache License to your work
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following boilerplate
|
||||||
|
notice, with the fields enclosed by brackets `[]` replaced with your own
|
||||||
|
identifying information. (Don't include the brackets!) The text should be
|
||||||
|
enclosed in the appropriate comment syntax for the file format. We also
|
||||||
|
recommend that a file or class name and description of purpose be included on
|
||||||
|
the same “printed page” as the copyright notice for easier identification within
|
||||||
|
third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
description: Ilitek ILI9488 display panel
|
||||||
|
|
||||||
|
compatible: "ilitek,ili9488"
|
||||||
|
|
||||||
|
bus: spi
|
||||||
|
|
||||||
|
properties:
|
||||||
|
horizontal-resolution:
|
||||||
|
type: int
|
||||||
|
required: true
|
||||||
|
description: Horizontal resolution in pixels
|
||||||
|
vertical-resolution:
|
||||||
|
type: int
|
||||||
|
required: true
|
||||||
|
description: Vertical resolution in pixels
|
||||||
|
gap-x:
|
||||||
|
type: int
|
||||||
|
default: 0
|
||||||
|
description: X offset applied to all draw operations
|
||||||
|
gap-y:
|
||||||
|
type: int
|
||||||
|
default: 0
|
||||||
|
description: Y offset applied to all draw operations
|
||||||
|
swap-xy:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
description: Swap the X and Y axes
|
||||||
|
mirror-x:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
description: Mirror the X axis
|
||||||
|
mirror-y:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
description: Mirror the Y axis
|
||||||
|
invert-color:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
description: Invert the panel's color output
|
||||||
|
bgr-order:
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
description: Use BGR element order instead of RGB
|
||||||
|
bits-per-pixel:
|
||||||
|
type: int
|
||||||
|
default: 18
|
||||||
|
description: Color depth in bits per pixel. The panel requires 18 over SPI
|
||||||
|
color-conversion-buffer-size:
|
||||||
|
type: int
|
||||||
|
required: true
|
||||||
|
description: Size (in pixels) of the internal buffer used to convert 16bpp draw data to the panel's 18bpp format
|
||||||
|
pixel-clock-hz:
|
||||||
|
type: int
|
||||||
|
default: 40000000
|
||||||
|
description: SPI pixel clock frequency in Hz
|
||||||
|
transaction-queue-depth:
|
||||||
|
type: int
|
||||||
|
default: 10
|
||||||
|
description: Size of the internal SPI transaction queue
|
||||||
|
pin-dc:
|
||||||
|
type: phandles
|
||||||
|
required: true
|
||||||
|
description: Data/Command GPIO pin
|
||||||
|
pin-reset:
|
||||||
|
type: phandles
|
||||||
|
default: GPIO_PIN_SPEC_NONE
|
||||||
|
description: Reset GPIO pin
|
||||||
|
reset-active-high:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
description: Whether the reset pin is active high
|
||||||
|
backlight:
|
||||||
|
type: phandle
|
||||||
|
default: NULL
|
||||||
|
description: Optional reference to this display's backlight device
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
dependencies:
|
||||||
|
- TactilityKernel
|
||||||
|
bindings: bindings
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <tactility/bindings/bindings.h>
|
||||||
|
#include <drivers/ili9488.h>
|
||||||
|
|
||||||
|
DEFINE_DEVICETREE(ili9488, struct Ili9488Config)
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user