mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-08-17 23:55:04 +00:00
Implement Tulip Creative Computer 4r11 (#608)
This commit is contained in:
parent
c6373b79e9
commit
564d8af64c
7
Devices/tulip-4r11/CMakeLists.txt
Normal file
7
Devices/tulip-4r11/CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
||||
file(GLOB_RECURSE SOURCE_FILES source/*.c*)
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${SOURCE_FILES}
|
||||
INCLUDE_DIRS "source"
|
||||
REQUIRES TactilityKernel
|
||||
)
|
||||
195
Devices/tulip-4r11/LICENSE-Apache-2.0.md
Normal file
195
Devices/tulip-4r11/LICENSE-Apache-2.0.md
Normal file
@ -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.
|
||||
|
||||
27
Devices/tulip-4r11/device.properties
Normal file
27
Devices/tulip-4r11/device.properties
Normal file
@ -0,0 +1,27 @@
|
||||
general.vendor=Tulip
|
||||
general.name=Tulip Creative Computer (4r11)
|
||||
|
||||
apps.launcherAppId=Launcher
|
||||
|
||||
hardware.target=ESP32S3
|
||||
hardware.flashSize=32MB
|
||||
hardware.spiRam=true
|
||||
hardware.spiRamMode=OCT
|
||||
hardware.spiRamSpeed=120M
|
||||
hardware.esptoolFlashFreq=120M
|
||||
hardware.usbHostEnabled=true
|
||||
hardware.bluetooth=true
|
||||
|
||||
storage.userDataLocation=Internal
|
||||
|
||||
touch.calibrationSupported=true
|
||||
touch.calibrationRequired=false
|
||||
|
||||
display.size=7"
|
||||
display.shape=rectangle
|
||||
display.dpi=117
|
||||
|
||||
lvgl.colorDepth=16
|
||||
lvgl.fontSize=20
|
||||
|
||||
sdkconfig.CONFIG_CODEC_DUMMY_SUPPORT=y
|
||||
7
Devices/tulip-4r11/devicetree.yaml
Normal file
7
Devices/tulip-4r11/devicetree.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
dependencies:
|
||||
- Platforms/platform-esp32
|
||||
- Drivers/rgb-display-module
|
||||
- Drivers/gt911-module
|
||||
- Drivers/dummy-i2s-amp-module
|
||||
- Drivers/audio-stream-module
|
||||
dts: tulip-4r11.dts
|
||||
9
Devices/tulip-4r11/source/module.cpp
Normal file
9
Devices/tulip-4r11/source/module.cpp
Normal file
@ -0,0 +1,9 @@
|
||||
#include <tactility/module.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
Module tulip_4r11_module = {
|
||||
.name = "tulip-4r11"
|
||||
};
|
||||
|
||||
}
|
||||
195
Devices/tulip-4r11/tulip-4r11.dts
Normal file
195
Devices/tulip-4r11/tulip-4r11.dts
Normal file
@ -0,0 +1,195 @@
|
||||
/dts-v1/;
|
||||
|
||||
#include <tactility/bindings/battery_sense.h>
|
||||
#include <tactility/bindings/root.h>
|
||||
#include <tactility/bindings/esp32_adc_oneshot.h>
|
||||
#include <tactility/bindings/esp32_ble.h>
|
||||
#include <tactility/bindings/esp32_wifi_pinned.h>
|
||||
#include <tactility/bindings/esp32_gpio.h>
|
||||
#include <tactility/bindings/esp32_i2c.h>
|
||||
#include <tactility/bindings/esp32_i2s.h>
|
||||
#include <tactility/bindings/esp32_usbhost.h>
|
||||
#include <tactility/bindings/esp32_pwm_ledc.h>
|
||||
#include <tactility/bindings/gpio_hog.h>
|
||||
#include <tactility/bindings/pwm_backlight.h>
|
||||
|
||||
#include <bindings/rgb_display.h>
|
||||
#include <bindings/gt911.h>
|
||||
#include <bindings/dummy_i2s_amp.h>
|
||||
|
||||
// Reference: https: //github.com/TulipCC/tulipcc/
|
||||
/ {
|
||||
compatible = "root";
|
||||
model = "Tulip 4 (R11)";
|
||||
|
||||
wifi0 {
|
||||
compatible = "espressif,esp32-wifi-pinned";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
ble0 {
|
||||
compatible = "espressif,esp32-ble";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
gpio0 {
|
||||
compatible = "espressif,esp32-gpio";
|
||||
gpio-count = <49>;
|
||||
};
|
||||
|
||||
// Battery voltage is sensed through the 470K/470K divider (R34/R38) into GPIO3
|
||||
// (ADC1_CH2), i.e. a straight 2: 1 divider; the RY3730 charger IC has no I2C
|
||||
// interface to drive. Reference: tulipcc tulip/shared/py/tulip.py battery().
|
||||
adc0 {
|
||||
compatible = "espressif,esp32-adc-oneshot";
|
||||
unit-id = <ADC_UNIT_1>;
|
||||
channels = <ADC_CHANNEL_2 ADC_ATTEN_DB_12 ADC_BITWIDTH_DEFAULT>;
|
||||
};
|
||||
|
||||
battery-sense {
|
||||
compatible = "battery-sense";
|
||||
io-channel = <&adc0 0>;
|
||||
reference-voltage-mv = <3300>;
|
||||
multiplier = <2000>;
|
||||
};
|
||||
|
||||
// The Grove connector shares this I2C bus with the touch controller
|
||||
i2c0 {
|
||||
compatible = "espressif,esp32-i2c";
|
||||
port = <I2C_NUM_0>;
|
||||
clock-frequency = <100000>;
|
||||
pin-sda = <&gpio0 17 GPIO_FLAG_PULL_UP>;
|
||||
pin-scl = <&gpio0 18 GPIO_FLAG_PULL_UP>;
|
||||
|
||||
touch0 {
|
||||
compatible = "goodix,gt911";
|
||||
reg = <0x5D>;
|
||||
x-max = <1024>;
|
||||
y-max = <600>;
|
||||
// R11 inverts the reset line (active-high) and needs two toggle cycles before it
|
||||
// starts responding on I2C.
|
||||
pin-reset = <&gpio0 48 GPIO_FLAG_ACTIVE_HIGH>;
|
||||
reset-pulses = <2>;
|
||||
// The controller's programmed Y range (~750) exceeds the panel's 600 rows and the
|
||||
// sensor sits slightly offset from the panel; calibrate per the reference firmware
|
||||
x-offset = <-2>;
|
||||
y-offset = <-10>;
|
||||
y-scale = <800>;
|
||||
};
|
||||
};
|
||||
|
||||
// The unused panel data lines (G2 and B3 are unconnected on R11) are driven low exactly
|
||||
// like the reference firmware does, so the panel never sees floating inputs on them.
|
||||
panel_data_low_b4 {
|
||||
compatible = "gpio-hog";
|
||||
pin = <&gpio0 38 GPIO_FLAG_NONE>;
|
||||
mode = <GPIO_HOG_MODE_OUTPUT_LOW>;
|
||||
};
|
||||
|
||||
panel_data_low_b5 {
|
||||
compatible = "gpio-hog";
|
||||
pin = <&gpio0 46 GPIO_FLAG_NONE>;
|
||||
mode = <GPIO_HOG_MODE_OUTPUT_LOW>;
|
||||
};
|
||||
|
||||
panel_data_low_g3 {
|
||||
compatible = "gpio-hog";
|
||||
pin = <&gpio0 6 GPIO_FLAG_NONE>;
|
||||
mode = <GPIO_HOG_MODE_OUTPUT_LOW>;
|
||||
};
|
||||
|
||||
panel_data_low_g4 {
|
||||
compatible = "gpio-hog";
|
||||
pin = <&gpio0 7 GPIO_FLAG_NONE>;
|
||||
mode = <GPIO_HOG_MODE_OUTPUT_LOW>;
|
||||
};
|
||||
|
||||
panel_data_low_r3 {
|
||||
compatible = "gpio-hog";
|
||||
pin = <&gpio0 45 GPIO_FLAG_NONE>;
|
||||
mode = <GPIO_HOG_MODE_OUTPUT_LOW>;
|
||||
};
|
||||
|
||||
panel_data_low_r4 {
|
||||
compatible = "gpio-hog";
|
||||
pin = <&gpio0 13 GPIO_FLAG_NONE>;
|
||||
mode = <GPIO_HOG_MODE_OUTPUT_LOW>;
|
||||
};
|
||||
|
||||
// Backlight is active-low: the reference firmware drives duty 0 at max brightness.
|
||||
display_backlight_pwm {
|
||||
compatible = "espressif,esp32-pwm-ledc";
|
||||
pin = <&gpio0 47 GPIO_FLAG_NONE>;
|
||||
period-ns = <3333333>;
|
||||
duty-resolution = <13>;
|
||||
ledc-timer = <1>;
|
||||
ledc-channel = <1>;
|
||||
inverted;
|
||||
};
|
||||
|
||||
display_backlight {
|
||||
compatible = "pwm-backlight";
|
||||
pwm = <&display_backlight_pwm>;
|
||||
brightness-level-range = <0 255>;
|
||||
brightness-default = <200>;
|
||||
};
|
||||
|
||||
display0 {
|
||||
compatible = "espressif,esp32-rgb-display";
|
||||
horizontal-resolution = <1024>;
|
||||
vertical-resolution = <600>;
|
||||
pixel-clock-hz = <28000000>;
|
||||
hsync-pulse-width = <30>;
|
||||
hsync-back-porch = <16>;
|
||||
hsync-front-porch = <210>;
|
||||
vsync-pulse-width = <13>;
|
||||
vsync-back-porch = <10>;
|
||||
vsync-front-porch = <22>;
|
||||
data-width = <8>;
|
||||
bits-per-pixel = <8>;
|
||||
custom-pixel-format = <RGB_DISPLAY_PIXEL_FORMAT_RGB332>;
|
||||
num-fbs = <1>;
|
||||
pin-hsync = <&gpio0 39 GPIO_FLAG_NONE>;
|
||||
pin-vsync = <&gpio0 41 GPIO_FLAG_NONE>;
|
||||
pin-de = <&gpio0 40 GPIO_FLAG_NONE>;
|
||||
pin-pclk = <&gpio0 42 GPIO_FLAG_NONE>;
|
||||
pin-data0 = <&gpio0 9 GPIO_FLAG_NONE>; // B6
|
||||
pin-data1 = <&gpio0 1 GPIO_FLAG_NONE>; // B7
|
||||
pin-data2 = <&gpio0 15 GPIO_FLAG_NONE>; // G5
|
||||
pin-data3 = <&gpio0 16 GPIO_FLAG_NONE>; // G6
|
||||
pin-data4 = <&gpio0 4 GPIO_FLAG_NONE>; // G7
|
||||
pin-data5 = <&gpio0 10 GPIO_FLAG_NONE>; // R5
|
||||
pin-data6 = <&gpio0 21 GPIO_FLAG_NONE>; // R6
|
||||
pin-data7 = <&gpio0 14 GPIO_FLAG_NONE>; // R7
|
||||
backlight = <&display_backlight>;
|
||||
};
|
||||
|
||||
i2s0 {
|
||||
compatible = "espressif,esp32-i2s";
|
||||
port = <I2S_NUM_0>;
|
||||
pin-bclk = <&gpio0 8 GPIO_FLAG_NONE>;
|
||||
pin-ws = <&gpio0 2 GPIO_FLAG_NONE>;
|
||||
pin-data-out = <&gpio0 5 GPIO_FLAG_NONE>;
|
||||
};
|
||||
|
||||
speaker0 {
|
||||
compatible = "ti,pcm5101a";
|
||||
i2s = <&i2s0>;
|
||||
};
|
||||
|
||||
usbhost0 {
|
||||
compatible = "espressif,esp32-usbhost";
|
||||
|
||||
usbhosthid0 {
|
||||
compatible = "espressif,esp32-usbhost-hid";
|
||||
};
|
||||
|
||||
usbhostmidi0 {
|
||||
compatible = "espressif,esp32-usbhost-midi";
|
||||
};
|
||||
|
||||
usbhostmsc0 {
|
||||
compatible = "espressif,esp32-usbhost-msc";
|
||||
};
|
||||
};
|
||||
};
|
||||
9
Drivers/dummy-i2s-amp-module/bindings/ti,pcm5101a.yaml
Normal file
9
Drivers/dummy-i2s-amp-module/bindings/ti,pcm5101a.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
description: TI PCM5101A stereo DAC (I2S input, no register interface, no GPIO enable)
|
||||
|
||||
compatible: "ti,pcm5101a"
|
||||
|
||||
properties:
|
||||
i2s:
|
||||
type: phandle
|
||||
required: true
|
||||
description: "I2S controller device that carries audio data"
|
||||
@ -14,6 +14,7 @@ extern "C" {
|
||||
// typedef even though they all share the same underlying config layout.
|
||||
DEFINE_DEVICETREE(max98357a, struct DummyI2sAmpConfig)
|
||||
DEFINE_DEVICETREE(ns4168, struct DummyI2sAmpConfig)
|
||||
DEFINE_DEVICETREE(pcm5101a, struct DummyI2sAmpConfig)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@ -373,7 +373,7 @@ extern "C" {
|
||||
|
||||
Driver dummy_i2s_amp_driver = {
|
||||
.name = "dummy_i2s_amp",
|
||||
.compatible = (const char*[]) { "maxim,max98357a", "nsiway,ns4168", nullptr },
|
||||
.compatible = (const char*[]) { "maxim,max98357a", "nsiway,ns4168", "ti,pcm5101a", nullptr },
|
||||
.start_device = start_device,
|
||||
.stop_device = stop_device,
|
||||
.api = &API,
|
||||
|
||||
@ -30,8 +30,39 @@ properties:
|
||||
pin-reset:
|
||||
type: phandles
|
||||
default: GPIO_PIN_SPEC_NONE
|
||||
description: Reset GPIO pin
|
||||
description: |
|
||||
Reset GPIO pin. Reset polarity is set with the GPIO descriptor's ACTIVE_HIGH or
|
||||
ACTIVE_LOW flag (active-low is the GT911's native convention; boards that invert the
|
||||
reset line, e.g. Tulip 4 R11, use ACTIVE_HIGH).
|
||||
pin-interrupt:
|
||||
type: phandles
|
||||
default: GPIO_PIN_SPEC_NONE
|
||||
description: Interrupt GPIO pin
|
||||
reset-pulses:
|
||||
type: int
|
||||
default: 1
|
||||
description: |
|
||||
Number of reset pulses applied before the controller is initialized. Some panels
|
||||
(e.g. Tulip 4 R11) require two toggle cycles before they respond on I2C.
|
||||
x-offset:
|
||||
type: int
|
||||
default: 0
|
||||
description: |
|
||||
Signed X coordinate correction added to the raw controller output before scaling.
|
||||
Aligns the sensor with the panel (e.g. -2 on Tulip 4 R11).
|
||||
y-offset:
|
||||
type: int
|
||||
default: 0
|
||||
description: |
|
||||
Signed Y coordinate correction added to the raw controller output before scaling
|
||||
(e.g. -12 on Tulip 4 R11).
|
||||
x-scale:
|
||||
type: int
|
||||
default: 1000
|
||||
description: |
|
||||
X coordinate scale as per-mille (1000 = 1.0), applied after x-offset.
|
||||
y-scale:
|
||||
type: int
|
||||
default: 1000
|
||||
description: |
|
||||
Y coordinate scale as per-mille (1000 = 1.0), applied after y-offset.
|
||||
|
||||
@ -22,6 +22,11 @@ struct Gt911Config {
|
||||
bool mirror_y;
|
||||
struct GpioPinSpec pin_reset;
|
||||
struct GpioPinSpec pin_interrupt;
|
||||
uint8_t reset_pulses;
|
||||
int16_t x_offset;
|
||||
int16_t y_offset;
|
||||
uint16_t x_scale;
|
||||
uint16_t y_scale;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
#include <tactility/driver.h>
|
||||
#include <tactility/drivers/esp32_i2c.h>
|
||||
#include <tactility/drivers/esp32_i2c_master.h>
|
||||
#include <tactility/drivers/gpio_controller.h>
|
||||
#include <tactility/drivers/i2c_controller.h>
|
||||
#include <tactility/drivers/pointer.h>
|
||||
#include <tactility/log.h>
|
||||
@ -16,6 +17,8 @@
|
||||
#include <esp_lcd_panel_io.h>
|
||||
#include <esp_lcd_touch.h>
|
||||
#include <esp_lcd_touch_gt911.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
@ -31,6 +34,42 @@ static gpio_num_t pin_or_nc(const GpioPinSpec& pin) {
|
||||
return pin.gpio_controller == nullptr ? GPIO_NUM_NC : static_cast<gpio_num_t>(pin.pin);
|
||||
}
|
||||
|
||||
// Reset polarity is carried by the pin_reset descriptor's ACTIVE_HIGH/ACTIVE_LOW flag, so the
|
||||
// pin is always pulsed with logical levels here. Some boards also require several toggle cycles
|
||||
// before the controller starts responding on I2C (e.g. Tulip 4 R11). The esp_lcd_touch_gt911
|
||||
// driver only performs a single reset, so the pin is pulsed here first
|
||||
static error_t reset_controller_pin(const GpioPinSpec& pin, uint8_t pulses) {
|
||||
if (pulses == 0 || pin.gpio_controller == nullptr) {
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
auto* descriptor = gpio_descriptor_acquire(pin.gpio_controller, pin.pin, pin.flags | GPIO_FLAG_DIRECTION_OUTPUT, GPIO_OWNER_GPIO);
|
||||
if (descriptor == nullptr) {
|
||||
LOG_E(TAG, "Failed to acquire reset pin");
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < pulses; ++i) {
|
||||
bool last_pulse = i == pulses - 1;
|
||||
error_t error = gpio_descriptor_set_level(descriptor, true);
|
||||
if (error == ERROR_NONE) {
|
||||
vTaskDelay(pdMS_TO_TICKS(11));
|
||||
error = gpio_descriptor_set_level(descriptor, false);
|
||||
}
|
||||
if (error == ERROR_NONE) {
|
||||
vTaskDelay(pdMS_TO_TICKS(last_pulse ? 1000 : 60));
|
||||
}
|
||||
if (error != ERROR_NONE) {
|
||||
LOG_E(TAG, "Failed to pulse reset pin");
|
||||
gpio_descriptor_release(descriptor);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
gpio_descriptor_release(descriptor);
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
// region Driver lifecycle
|
||||
|
||||
// GT911's I2C address depends on the controller's INT pin level at power-up (board-strapped, not
|
||||
@ -62,6 +101,32 @@ static esp_err_t create_io_handle(Device* parent, esp_lcd_panel_io_handle_t* out
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
// Applies the per-device affine calibration (offset + per-mille scale) in the esp_lcd_touch
|
||||
// framework's own coordinate hook, so downstream consumers always receive panel-space coords.
|
||||
// Called before mirror/swap in esp_lcd_touch_get_coordinates().
|
||||
static void gt911_process_coordinates(
|
||||
esp_lcd_touch_handle_t tp,
|
||||
uint16_t* x,
|
||||
uint16_t* y,
|
||||
uint16_t* strength,
|
||||
uint8_t* point_count,
|
||||
uint8_t max_point_count
|
||||
) {
|
||||
(void)strength;
|
||||
(void)max_point_count;
|
||||
auto* config = static_cast<const Gt911Config*>(tp->config.user_data);
|
||||
if (config == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < *point_count; i++) {
|
||||
int32_t mapped_x = (static_cast<int32_t>(x[i]) + config->x_offset) * config->x_scale / 1000;
|
||||
int32_t mapped_y = (static_cast<int32_t>(y[i]) + config->y_offset) * config->y_scale / 1000;
|
||||
x[i] = static_cast<uint16_t>(mapped_x < 0 ? 0 : (mapped_x > config->x_max ? config->x_max : mapped_x));
|
||||
y[i] = static_cast<uint16_t>(mapped_y < 0 ? 0 : (mapped_y > config->y_max ? config->y_max : mapped_y));
|
||||
}
|
||||
}
|
||||
|
||||
static error_t start(Device* device) {
|
||||
auto* parent = device_get_parent(device);
|
||||
check(device_get_type(parent) == &I2C_CONTROLLER_TYPE);
|
||||
@ -73,6 +138,12 @@ static error_t start(Device* device) {
|
||||
return ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
error_t error = reset_controller_pin(config->pin_reset, config->reset_pulses);
|
||||
if (error != ERROR_NONE) {
|
||||
free(internal);
|
||||
return error;
|
||||
}
|
||||
|
||||
esp_err_t ret = create_io_handle(parent, &internal->io_handle);
|
||||
if (ret != ESP_OK) {
|
||||
free(internal);
|
||||
@ -84,9 +155,10 @@ static error_t start(Device* device) {
|
||||
.y_max = config->y_max,
|
||||
.rst_gpio_num = pin_or_nc(config->pin_reset),
|
||||
.int_gpio_num = pin_or_nc(config->pin_interrupt),
|
||||
// GT911's reset and interrupt lines are both fixed active-low in hardware.
|
||||
// Reset polarity comes from the pin_reset descriptor's ACTIVE_HIGH/ACTIVE_LOW flag; the
|
||||
// interrupt line is fixed active-low in hardware.
|
||||
.levels = {
|
||||
.reset = 0u,
|
||||
.reset = (config->pin_reset.flags & GPIO_FLAG_ACTIVE_LOW) != 0 ? 0u : 1u,
|
||||
.interrupt = 0u,
|
||||
},
|
||||
.flags = {
|
||||
@ -94,9 +166,9 @@ static error_t start(Device* device) {
|
||||
.mirror_x = config->mirror_x ? 1u : 0u,
|
||||
.mirror_y = config->mirror_y ? 1u : 0u,
|
||||
},
|
||||
.process_coordinates = nullptr,
|
||||
.process_coordinates = gt911_process_coordinates,
|
||||
.interrupt_callback = nullptr,
|
||||
.user_data = nullptr,
|
||||
.user_data = (void*)config,
|
||||
.driver_data = nullptr,
|
||||
};
|
||||
|
||||
|
||||
@ -222,3 +222,11 @@ properties:
|
||||
type: phandle
|
||||
default: "NULL"
|
||||
description: Optional reference to this display's backlight device
|
||||
custom-pixel-format:
|
||||
type: int
|
||||
default: 0
|
||||
description: |
|
||||
Optional software pixel-format conversion applied to scan-out pixels, when the host renders
|
||||
in a different depth than the panel (LVGL always renders RGB565).
|
||||
0 = no conversion (RGB_DISPLAY_PIXEL_FORMAT_DEFAULT), 1 = RGB332
|
||||
(RGB_DISPLAY_PIXEL_FORMAT_RGB332) for 8 data-line panels.
|
||||
|
||||
@ -11,6 +11,18 @@ extern "C" {
|
||||
#include <tactility/device.h>
|
||||
#include <tactility/drivers/gpio.h>
|
||||
|
||||
/**
|
||||
* Optional software pixel-format conversion applied to scan-out pixels. The panel itself always
|
||||
* runs at bits_per_pixel; when the host renders in a different depth (LVGL always renders RGB565
|
||||
* for this driver), the selected mapper converts each tile before it reaches esp_lcd.
|
||||
*/
|
||||
enum RgbDisplayPixelFormat {
|
||||
/** No conversion: scan-out pixels must already match bits_per_pixel. */
|
||||
RGB_DISPLAY_PIXEL_FORMAT_DEFAULT = 0,
|
||||
/** Convert RGB565 to packed 8-bit RGB332 (3R, 3G, 2B), for 8 data-line panels. */
|
||||
RGB_DISPLAY_PIXEL_FORMAT_RGB332 = 1,
|
||||
};
|
||||
|
||||
struct RgbDisplayConfig {
|
||||
uint16_t horizontal_resolution;
|
||||
uint16_t vertical_resolution;
|
||||
@ -84,6 +96,9 @@ struct RgbDisplayConfig {
|
||||
|
||||
// Optional reference to this display's backlight device, NULL if none.
|
||||
struct Device* backlight;
|
||||
|
||||
// Optional software pixel-format conversion for scan-out (see enum RgbDisplayPixelFormat).
|
||||
enum RgbDisplayPixelFormat pixel_format;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@ -0,0 +1,61 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* Opaque per-instance state for a software pixel mapper, allocated by SoftwarePixelMapperCreateFn
|
||||
* and released by SoftwarePixelMapperDestroyFn. The instance owns its conversion destination
|
||||
* buffer: the driver passes this handle as map()'s dst argument.
|
||||
*/
|
||||
typedef void* SoftwarePixelMapperData;
|
||||
|
||||
/**
|
||||
* Allocates a mapper instance, including its destination buffer, sized for a width x height
|
||||
* source buffer.
|
||||
* @param width source buffer width in pixels
|
||||
* @param height source buffer height in pixels
|
||||
* @return a non-null instance handle, or NULL on allocation failure
|
||||
*/
|
||||
typedef SoftwarePixelMapperData (*SoftwarePixelMapperCreateFn)(uint16_t width, uint16_t height);
|
||||
|
||||
/**
|
||||
* Converts pixel_count pixels from the mapper's source format to its destination format.
|
||||
* @param data instance handle from SoftwarePixelMapperCreateFn
|
||||
* @param src RGB565 source pixels
|
||||
* @param dst destination buffer, typically the instance's own buffer (i.e. the data handle)
|
||||
* @param pixel_count number of pixels to convert
|
||||
*/
|
||||
typedef void (*SoftwarePixelMapperMapFn)(SoftwarePixelMapperData data, const uint16_t* src, uint8_t* dst, uint32_t pixel_count);
|
||||
|
||||
/**
|
||||
* Releases a mapper instance created by SoftwarePixelMapperCreateFn, including its destination
|
||||
* buffer.
|
||||
* @param data instance handle to release
|
||||
*/
|
||||
typedef void (*SoftwarePixelMapperDestroyFn)(SoftwarePixelMapperData data);
|
||||
|
||||
/**
|
||||
* A software pixel-format conversion, used to translate a driver's native source depth into a
|
||||
* panel's scan-out depth when the two differ. Each mapper owns its own destination buffer, so it
|
||||
* can size the allocation for its output format.
|
||||
*/
|
||||
struct SoftwarePixelMapper {
|
||||
SoftwarePixelMapperCreateFn create;
|
||||
SoftwarePixelMapperMapFn map;
|
||||
SoftwarePixelMapperDestroyFn destroy;
|
||||
};
|
||||
|
||||
/**
|
||||
* RGB565 to packed 8-bit RGB332 (3 bits red, 3 bits green, 2 bits blue) mapper for panels wired
|
||||
* with only 8 data lines.
|
||||
*/
|
||||
extern const struct SoftwarePixelMapper software_pixel_mapper_rgb332;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
#include <drivers/rgb_display.h>
|
||||
#include <rgb_display_module.h>
|
||||
#include <drivers/software_pixel_mapper.h>
|
||||
|
||||
#include <tactility/delay.h>
|
||||
#include <tactility/device.h>
|
||||
@ -40,6 +41,12 @@ struct RgbDisplayInternal {
|
||||
// Signaled by on_frame_buf_complete once per real DMA scan-out of a whole frame. Only
|
||||
// waited on in draw_bitmap() when color_data is one of frame_buffers - see the comment there for why.
|
||||
SemaphoreHandle_t frame_complete_semaphore;
|
||||
// Software pixel-format conversion active when custom-pixel-format != DEFAULT (see start()).
|
||||
// LVGL always renders RGB565 for this driver, so draw_bitmap() converts each tile through the
|
||||
// mapper before handing it to esp_lcd. The mapper owns its scratch buffer, held in
|
||||
// pixel_mapper_data. Null when no conversion is active.
|
||||
const struct SoftwarePixelMapper* pixel_mapper;
|
||||
SoftwarePixelMapperData pixel_mapper_data;
|
||||
};
|
||||
|
||||
// esp_lcd_rgb_panel's draw_bitmap() has a zero-copy path when color_data is one of the panel's
|
||||
@ -128,6 +135,8 @@ static error_t start(Device* device) {
|
||||
if (internal == nullptr) {
|
||||
return ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
internal->pixel_mapper = nullptr;
|
||||
internal->pixel_mapper_data = nullptr;
|
||||
|
||||
error_t reset_error = perform_hardware_reset(config);
|
||||
if (reset_error != ERROR_NONE) {
|
||||
@ -231,6 +240,28 @@ static error_t start(Device* device) {
|
||||
return error;
|
||||
}
|
||||
|
||||
if (config->pixel_format != RGB_DISPLAY_PIXEL_FORMAT_DEFAULT) {
|
||||
switch (config->pixel_format) {
|
||||
case RGB_DISPLAY_PIXEL_FORMAT_RGB332:
|
||||
internal->pixel_mapper = &software_pixel_mapper_rgb332;
|
||||
break;
|
||||
default:
|
||||
LOG_E(TAG, "Unsupported pixel format %d", (int)config->pixel_format);
|
||||
free(internal);
|
||||
return ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
// The mapper sizes and allocates its own whole-frame destination buffer, which lands in
|
||||
// PSRAM on boards that have it (see software_pixel_mapper.cpp).
|
||||
internal->pixel_mapper_data = internal->pixel_mapper->create(config->horizontal_resolution, config->vertical_resolution);
|
||||
if (internal->pixel_mapper_data == nullptr) {
|
||||
LOG_E(TAG, "Failed to create pixel mapper");
|
||||
esp_lcd_panel_del(internal->panel_handle);
|
||||
free(internal);
|
||||
return ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
internal->frame_complete_semaphore = xSemaphoreCreateBinary();
|
||||
if (internal->frame_complete_semaphore == nullptr) {
|
||||
esp_lcd_panel_del(internal->panel_handle);
|
||||
@ -264,6 +295,9 @@ static error_t stop(Device* device) {
|
||||
}
|
||||
|
||||
vSemaphoreDelete(internal->frame_complete_semaphore);
|
||||
if (internal->pixel_mapper != nullptr) {
|
||||
internal->pixel_mapper->destroy(internal->pixel_mapper_data);
|
||||
}
|
||||
free(internal);
|
||||
device_set_driver_data(device, nullptr);
|
||||
return ERROR_NONE;
|
||||
@ -299,15 +333,33 @@ static bool rgb_display_color_data_is_frame_buffer(const RgbDisplayInternal* int
|
||||
return false;
|
||||
}
|
||||
|
||||
// Converts a contiguous RGB565 region into the configured scan-out format, if any. The
|
||||
// conversion is delegated to the pixel mapper selected in start(); the result is written into the
|
||||
// mapper's scratch buffer so the zero-copy frame-buffer wait logic below can treat it like any
|
||||
// other caller-owned buffer.
|
||||
static error_t rgb_display_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<RgbDisplayInternal*>(device_get_driver_data(device));
|
||||
|
||||
bool wait_for_scanout = rgb_display_color_data_is_frame_buffer(internal, color_data);
|
||||
const void* source_data = color_data;
|
||||
if (internal->pixel_mapper != nullptr) {
|
||||
uint32_t pixel_count = (uint32_t)(x_end - x_start) * (uint32_t)(y_end - y_start);
|
||||
// The mapper's own destination buffer is its instance data, so it is passed as both the
|
||||
// instance handle and the output buffer.
|
||||
internal->pixel_mapper->map(
|
||||
internal->pixel_mapper_data,
|
||||
static_cast<const uint16_t*>(color_data),
|
||||
static_cast<uint8_t*>(internal->pixel_mapper_data),
|
||||
pixel_count
|
||||
);
|
||||
source_data = internal->pixel_mapper_data;
|
||||
}
|
||||
|
||||
bool wait_for_scanout = rgb_display_color_data_is_frame_buffer(internal, source_data);
|
||||
if (wait_for_scanout) {
|
||||
xSemaphoreTake(internal->frame_complete_semaphore, 0); // clear any already-pending signal
|
||||
}
|
||||
|
||||
if (esp_lcd_panel_draw_bitmap(internal->panel_handle, x_start, y_start, x_end, y_end, color_data) != ESP_OK) {
|
||||
if (esp_lcd_panel_draw_bitmap(internal->panel_handle, x_start, y_start, x_end, y_end, source_data) != ESP_OK) {
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
|
||||
@ -375,7 +427,12 @@ static void rgb_display_get_frame_buffer(Device* device, uint8_t index, void** o
|
||||
|
||||
static uint8_t rgb_display_get_frame_buffer_count(Device* device) {
|
||||
auto* internal = static_cast<RgbDisplayInternal*>(device_get_driver_data(device));
|
||||
return internal->frame_buffer_count;
|
||||
// A converted-format panel's frame buffer runs at the native bits_per_pixel, which LVGL can't
|
||||
// write directly (it always renders RGB565 for this driver). Exposing it would make
|
||||
// lvgl_display.c bind LVGL straight onto it and corrupt the buffer; instead report 0 so LVGL
|
||||
// renders into its own RGB565 buffers and flushes per-tile through the conversion in
|
||||
// draw_bitmap().
|
||||
return GET_CONFIG(device)->pixel_format != RGB_DISPLAY_PIXEL_FORMAT_DEFAULT ? 0 : internal->frame_buffer_count;
|
||||
}
|
||||
|
||||
static error_t rgb_display_get_backlight(Device* device, Device** backlight) {
|
||||
|
||||
42
Drivers/rgb-display-module/source/software_pixel_mapper.cpp
Normal file
42
Drivers/rgb-display-module/source/software_pixel_mapper.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#include <soc/soc_caps.h>
|
||||
#if SOC_LCD_RGB_SUPPORTED
|
||||
|
||||
#include <drivers/software_pixel_mapper.h>
|
||||
|
||||
#include <esp_heap_caps.h>
|
||||
|
||||
// RGB332 packs each pixel into one byte: 3 bits red, 3 bits green, 2 bits blue. The byte layout
|
||||
// maps straight onto an 8-data-line panel's significant color inputs (R7..R5, G7..G5, B7..B6),
|
||||
// so each RGB565 channel's top bits land on the corresponding MSB lines
|
||||
static void rgb332_map(SoftwarePixelMapperData data, const uint16_t* src, uint8_t* dst, uint32_t pixel_count) {
|
||||
(void)data;
|
||||
for (uint32_t i = 0; i < pixel_count; i++) {
|
||||
uint16_t px = src[i];
|
||||
dst[i] = (uint8_t)(((px >> 13) & 0x07) << 5) | (((px >> 8) & 0x07) << 2) | ((px >> 3) & 0x03);
|
||||
}
|
||||
}
|
||||
|
||||
// The destination buffer must hold a whole frame (1 byte/pixel for RGB332). A 1024x600 panel
|
||||
// needs ~600KB, which only fits in PSRAM on most boards, prefer SPIRAM and fall back to whatever
|
||||
// internal RAM is available. The returned handle is this buffer, passed back as map()'s dst.
|
||||
static SoftwarePixelMapperData rgb332_create(uint16_t width, uint16_t height) {
|
||||
size_t buffer_size = (size_t)width * height;
|
||||
void* buffer = heap_caps_malloc(buffer_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
if (buffer == nullptr) {
|
||||
buffer = heap_caps_malloc(buffer_size, MALLOC_CAP_DEFAULT);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void rgb332_destroy(SoftwarePixelMapperData data) {
|
||||
heap_caps_free(data);
|
||||
}
|
||||
|
||||
const struct SoftwarePixelMapper software_pixel_mapper_rgb332 = {
|
||||
.create = rgb332_create,
|
||||
.map = rgb332_map,
|
||||
.destroy = rgb332_destroy,
|
||||
};
|
||||
|
||||
#endif // SOC_LCD_RGB_SUPPORTED
|
||||
@ -22,12 +22,15 @@
|
||||
#include <Tactility/service/ServiceRegistration.h>
|
||||
#include <Tactility/service/audio/Audio.h>
|
||||
#include <Tactility/settings/TimePrivate.h>
|
||||
#include <Tactility/settings/TouchCalibrationSettings.h>
|
||||
|
||||
#include <gps/module.h>
|
||||
#include <gps_generic/module.h>
|
||||
#include <gps_meshtastic/module.h>
|
||||
|
||||
#include <crypt/module.h>
|
||||
#include <lvgl/devices/pointer.h>
|
||||
#include <lvgl/lvgl.h>
|
||||
#include <lvgl/module.h>
|
||||
#include <lvgl/widgets/toolbar.h>
|
||||
|
||||
@ -360,6 +363,33 @@ static void stopAppFromToolbar(lv_event_t*) {
|
||||
app::stop();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TT_TOUCH_CALIBRATION_SUPPORTED
|
||||
|
||||
// Applies the calibration persisted by the touch calibration app to the live pointer indev.
|
||||
// lvgl_devices_attach() runs before onLvglStarted(), so the default indev already exists here.
|
||||
static void applySavedTouchCalibration() {
|
||||
settings::touch::TouchCalibrationSettings settings = settings::touch::loadOrGetDefault();
|
||||
if (!settings.enabled || !settings::touch::isValid(settings)) {
|
||||
return;
|
||||
}
|
||||
|
||||
LvglPointerCalibration calibration = {
|
||||
.x_min = settings.xMin,
|
||||
.x_max = settings.xMax,
|
||||
.y_min = settings.yMin,
|
||||
.y_max = settings.yMax,
|
||||
};
|
||||
|
||||
lvgl_lock();
|
||||
auto* indev = lvgl_pointer_get_default();
|
||||
if (indev != nullptr) {
|
||||
lvgl_pointer_set_calibration(indev, &calibration);
|
||||
}
|
||||
lvgl_unlock();
|
||||
}
|
||||
|
||||
#endif // CONFIG_TT_TOUCH_CALIBRATION_SUPPORTED
|
||||
|
||||
static void onLvglStarted() {
|
||||
ToolbarConfig toolbar_config = { .nav_action_callback = stopAppFromToolbar };
|
||||
lvgl_toolbar_configure(&toolbar_config);
|
||||
@ -379,6 +409,10 @@ static void onLvglStarted() {
|
||||
|
||||
lvgl::initTrackball();
|
||||
|
||||
#ifdef CONFIG_TT_TOUCH_CALIBRATION_SUPPORTED
|
||||
applySavedTouchCalibration();
|
||||
#endif
|
||||
|
||||
memory_print_stats();
|
||||
}
|
||||
|
||||
|
||||
7
partitions-32mb-no-sd-dev.csv
Normal file
7
partitions-32mb-no-sd-dev.csv
Normal file
@ -0,0 +1,7 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
|
||||
nvs, data, nvs, 0x9000, 0x6000,
|
||||
phy_init, data, phy, 0xf000, 0x1000,
|
||||
factory, app, factory, 0x10000, 4M,
|
||||
system, data, fat, , 128k,
|
||||
data, data, fat, , 1000k,
|
||||
|
7
partitions-32mb-no-sd.csv
Normal file
7
partitions-32mb-no-sd.csv
Normal file
@ -0,0 +1,7 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
|
||||
nvs, data, nvs, 0x9000, 0x6000,
|
||||
phy_init, data, phy, 0xf000, 0x1000,
|
||||
factory, app, factory, 0x10000, 4M,
|
||||
system, data, fat, , 128k,
|
||||
data, data, fat, , 28480k,
|
||||
|
Loading…
x
Reference in New Issue
Block a user