Auswertung des letzten Logs (lv_tick lief 2728 ms hinter millis): Tipp erkannt 49428, Rückfrage aufgebaut 49430, Bildaufbau fertig 49746. Also 318 ms vom erkannten Tipp bis zum fertigen Bild - die gemessene Kette ist schnell. Der Knopfdruck im Dialog liegt bei 55638 (Bereich 645..856, 374..426), die Antwort des S3 bei 59303, also 937 ms Rundlauf. Die Verzögerung liegt damit VOR der Erkennung. Deshalb: - ws7_touch_get_xy() meldet den ersten verworfenen Rohwert nach Ruhe mit Koordinaten und zählt, wie viele bis zur akzeptierten Berührung anfallen. - Alle Messzeilen auf millis() statt lv_tick_get(); die beiden Uhren auseinanderzurechnen hat das Lesen unnötig erschwert. Beide Panel-Varianten mit arduino-cli gegengebaut. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NeFMr439WcLMdebE3kU7fS
787 lines
33 KiB
C
787 lines
33 KiB
C
// =====================================================================================
|
|
// board_bringup.c - ESP32-P4 Hardware-Bringup
|
|
// JC_PANEL_43 = JC4880P443C-I-W (ST7701, 480x800, GT911)
|
|
// JC_PANEL_70 = JC1060P470C-I-W (JD9165, 1024x600, GT911)
|
|
// WS_PANEL_7H = Waveshare ESP32-P4-Pico + 7inch DSI LCD (H) (1280x720, GT9271)
|
|
// =====================================================================================
|
|
// BEWUSST eine .c-Datei (wird als C kompiliert): die esp_lcd-Init-Makros nutzen
|
|
// Compound-Literals und Out-of-Order-Designated-Initializer, die in C++ (Arduino .cpp)
|
|
// zu Fehlern fuehren. Der Ablauf ist 1:1 aus dem Hersteller-Beispiel
|
|
// arduino_examples/lvgl_v9_sw_rotation/lvgl_sw_rotation.c uebernommen - lediglich der
|
|
// Demo-Aufruf (lv_demo_widgets) entfaellt; das UI wird ueber display_hal.cpp aufgebaut.
|
|
//
|
|
// Nur aktiv bei JC_USE_REAL_PANEL == 1. Erwartet die Vendor-BSP-Dateien im Sketch:
|
|
// pins_config.h, lvgl_port_v9.h/.c, src/lcd/*, src/touch/*
|
|
// =====================================================================================
|
|
|
|
#include "config.h" // liefert JC_USE_REAL_PANEL (C-sicher)
|
|
|
|
#if JC_USE_REAL_PANEL
|
|
|
|
#include <string.h>
|
|
#include <stdbool.h>
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
#include "driver/gpio.h"
|
|
#include "driver/ledc.h"
|
|
#include "driver/i2c_master.h"
|
|
#include "esp_log.h"
|
|
#include "esp_cache.h"
|
|
#include "esp_heap_caps.h"
|
|
#include "esp_rom_sys.h"
|
|
#include "esp_timer.h"
|
|
#include "esp_system.h"
|
|
#include "esp_ldo_regulator.h"
|
|
#include "esp_lcd_panel_io.h"
|
|
#include "esp_lcd_panel_ops.h"
|
|
#include "esp_lcd_mipi_dsi.h"
|
|
#include "src/touch/esp_lcd_touch_gt911.h"
|
|
#if JC_PANEL_TYPE == WS_PANEL_7H
|
|
// Das Waveshare-Panel braucht keinen Hersteller-Panel-Treiber: es wird direkt als
|
|
// DPI-Panel angelegt und mit den drei Standard-DCS-Befehlen geweckt.
|
|
#include "hal/mipi_dsi_types.h"
|
|
#elif JC_PANEL_TYPE == JC_PANEL_70
|
|
#include "src/lcd/esp_lcd_jd9165.h"
|
|
#else
|
|
#include "src/lcd/esp_lcd_st7701.h"
|
|
#endif
|
|
#include "pins_config.h"
|
|
#include "lvgl_port_v9.h"
|
|
|
|
#define BSP_MIPI_DSI_PHY_PWR_LDO_CHAN (3)
|
|
#define BSP_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV (2500)
|
|
#define BSP_LCD_H_RES (LCD_H_RES) // native Panel-Aufloesung
|
|
#define BSP_LCD_V_RES (LCD_V_RES)
|
|
#define BSP_I2C_NUM (I2C_NUM_1)
|
|
#define BSP_I2C_SDA (GPIO_NUM_7)
|
|
#define BSP_I2C_SCL (GPIO_NUM_8)
|
|
#define BSP_LCD_TOUCH_RST (GPIO_NUM_NC)
|
|
#define BSP_LCD_TOUCH_INT (GPIO_NUM_NC)
|
|
#define BSP_LCD_RST ((gpio_num_t)LCD_RST)
|
|
#define BSP_LCD_BACKLIGHT ((gpio_num_t)LCD_LED)
|
|
#define LCD_LEDC_CH LEDC_CHANNEL_0
|
|
|
|
// Bytes je Pixel im Framebuffer. Beim Waveshare-Panel waehlbar ueber WS7_COLOR_BITS
|
|
// (pins_config.h); LVGL rechnet mit derselben Tiefe (lv_conf.h). JC-Panels: RGB565.
|
|
#if JC_PANEL_TYPE == WS_PANEL_7H
|
|
#define BSP_LCD_FB_BYTES_PER_PX (WS7_COLOR_BITS / 8)
|
|
#else
|
|
#define BSP_LCD_FB_BYTES_PER_PX (2)
|
|
#endif
|
|
|
|
static i2c_master_bus_handle_t s_i2c_handle = NULL;
|
|
|
|
#if JC_PANEL_TYPE == WS_PANEL_7H
|
|
// ------------------------------------------------------------------------------------
|
|
// Haengenden I2C-Bus befreien
|
|
//
|
|
// Trifft ein Reset des P4 mitten in eine laufende I2C-Uebertragung, bleibt der
|
|
// angesprochene Baustein in seiner Bitausgabe stehen und haelt die Datenleitung dauerhaft
|
|
// auf Masse. Der Bus ist damit tot: Jede weitere Uebertragung schlaegt fehl. Da Bridge und
|
|
// Touch-Controller an Dauerstrom haengen, ueberlebt dieser Zustand jeden Reset und sogar
|
|
// das Flashen - nur echtes Stromabziehen loest ihn.
|
|
//
|
|
// Folge: Die Bridge wird nie initialisiert, das Bild bleibt schwarz, und weil sie ihre
|
|
// Register behaelt, brennt die Hintergrundbeleuchtung weiter. Genau dieses Bild.
|
|
//
|
|
// Abhilfe ist der uebliche Weg aus der I2C-Spezifikation: Vor dem Anlegen des Busses die
|
|
// Taktleitung von Hand takten, bis der Baustein sein letztes Bit ausgegeben hat und die
|
|
// Datenleitung wieder loslaesst, danach eine Stopp-Bedingung erzeugen.
|
|
static void ws7_i2c_bus_recover(void)
|
|
{
|
|
gpio_config_t io = {
|
|
.pin_bit_mask = BIT64(BSP_I2C_SDA) | BIT64(BSP_I2C_SCL),
|
|
.mode = GPIO_MODE_INPUT_OUTPUT_OD, // offener Kollektor, Zustand lesbar
|
|
.pull_up_en = GPIO_PULLUP_ENABLE,
|
|
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
|
.intr_type = GPIO_INTR_DISABLE,
|
|
};
|
|
gpio_config(&io);
|
|
gpio_set_level(BSP_I2C_SDA, 1);
|
|
gpio_set_level(BSP_I2C_SCL, 1);
|
|
esp_rom_delay_us(10);
|
|
|
|
if (gpio_get_level(BSP_I2C_SDA) != 0) {
|
|
return; // Bus frei - nichts zu tun
|
|
}
|
|
|
|
esp_rom_printf("[Panel] I2C-Bus haengt - wird freigetaktet\n");
|
|
|
|
// Bis zu neun Takte: so viele braucht ein Baustein hoechstens, um ein angefangenes
|
|
// Byte samt Bestaetigungsbit zu Ende zu schieben.
|
|
for (int i = 0; i < 9 && gpio_get_level(BSP_I2C_SDA) == 0; i++) {
|
|
gpio_set_level(BSP_I2C_SCL, 0);
|
|
esp_rom_delay_us(5);
|
|
gpio_set_level(BSP_I2C_SCL, 1);
|
|
esp_rom_delay_us(5);
|
|
}
|
|
|
|
// Stopp-Bedingung: Datenleitung geht bei hoher Taktleitung von Masse auf High.
|
|
gpio_set_level(BSP_I2C_SDA, 0);
|
|
esp_rom_delay_us(5);
|
|
gpio_set_level(BSP_I2C_SCL, 1);
|
|
esp_rom_delay_us(5);
|
|
gpio_set_level(BSP_I2C_SDA, 1);
|
|
esp_rom_delay_us(10);
|
|
|
|
esp_rom_printf("[Panel] I2C-Bus nach Freitakten: %s\n",
|
|
gpio_get_level(BSP_I2C_SDA) ? "frei" : "weiterhin belegt");
|
|
|
|
// Pins wieder freigeben, damit der I2C-Treiber sie uebernehmen kann.
|
|
gpio_reset_pin(BSP_I2C_SDA);
|
|
gpio_reset_pin(BSP_I2C_SCL);
|
|
}
|
|
|
|
// Zeigt, welche Bausteine sich am I2C-Bus melden. Erwartet werden 0x45 (Bridge) und
|
|
// 0x14 (GT9271-Touch); 0x18 waere der Audio-Baustein. Fehlt einer, sagt das mehr aus als
|
|
// jede Fehlermeldung des Treibers.
|
|
static void ws7_i2c_scan(const char *wann)
|
|
{
|
|
esp_rom_printf("[Panel] I2C-Bausteine (%s):", wann);
|
|
for (uint8_t addr = 1; addr < 127; addr++) {
|
|
if (i2c_master_probe(s_i2c_handle, addr, 50) == ESP_OK) {
|
|
esp_rom_printf(" 0x%02X", addr);
|
|
}
|
|
}
|
|
esp_rom_printf("\n");
|
|
}
|
|
#endif // JC_PANEL_TYPE == WS_PANEL_7H
|
|
|
|
// Klartext-Meldung auf der seriellen Konsole. Bewusst esp_rom_printf: das laeuft auch bei
|
|
// abgeschaltetem Core-Debug-Level und ohne Heap, also genau dann, wenn es darauf ankommt.
|
|
#define BSP_STEP(call, what) \
|
|
do { \
|
|
esp_err_t _err = (call); \
|
|
if (_err != ESP_OK) { \
|
|
esp_rom_printf("[Panel] FEHLER: %s -> %s\n", what, esp_err_to_name(_err)); \
|
|
} \
|
|
} while (0)
|
|
|
|
// Ohne PSRAM passt kein Framebuffer. Bei 1280x720 in RGB888 werden 3 x 2,7 MB gebraucht -
|
|
// bleibt der Panel-Treiber daran haengen, sieht man hier sofort, woran es liegt.
|
|
static void bsp_report_psram(size_t needed_bytes)
|
|
{
|
|
esp_rom_printf("[Panel] PSRAM gesamt %u KB, frei %u KB, groesster Block %u KB, benoetigt %u KB\n",
|
|
(unsigned)(heap_caps_get_total_size(MALLOC_CAP_SPIRAM) / 1024),
|
|
(unsigned)(heap_caps_get_free_size(MALLOC_CAP_SPIRAM) / 1024),
|
|
(unsigned)(heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM) / 1024),
|
|
(unsigned)(needed_bytes / 1024));
|
|
}
|
|
|
|
// =====================================================================================
|
|
#if JC_PANEL_TYPE == WS_PANEL_7H
|
|
// =====================================================================================
|
|
// Waveshare 7inch DSI LCD (H): Panel-Freigabe und Helligkeit haengen an einer Bridge
|
|
// auf I2C-Adresse 0x45 - es gibt weder einen Reset- noch einen Backlight-GPIO.
|
|
// Registerfolge wie im Linux-Treiber:
|
|
// Grundinitialisierung: C0=01, C2=01, AC=01 (vor dem DSI-Bringup)
|
|
// Freigabe/Helligkeit: AD=01, AB=255-Helligkeit, AA=01 (ERST wenn DSI laeuft,
|
|
// sonst bleibt das Bild dunkel bzw. flackert beim Start)
|
|
// =====================================================================================
|
|
static i2c_master_dev_handle_t s_bridge_handle = NULL;
|
|
|
|
static void ws7_bridge_write(uint8_t reg, uint8_t value)
|
|
{
|
|
if (s_bridge_handle == NULL) return;
|
|
uint8_t buf[2] = { reg, value };
|
|
esp_err_t err = i2c_master_transmit(s_bridge_handle, buf, sizeof(buf), 100);
|
|
if (err != ESP_OK) {
|
|
// Frueher lief das stillschweigend ins Leere. Schlaegt es fehl, wird die Bridge
|
|
// nie initialisiert und das Bild bleibt schwarz - ohne jeden Hinweis darauf.
|
|
esp_rom_printf("[Panel] Bridge-Register 0x%02X liess sich nicht schreiben: %s\n",
|
|
reg, esp_err_to_name(err));
|
|
}
|
|
}
|
|
|
|
// Grundeinstellung der Bridge. Bewusst mehrfach aufrufbar, siehe unten.
|
|
//
|
|
// Warum zweimal: Beim ersten Aufruf existiert die DSI-Strecke noch gar nicht - die Bridge
|
|
// bekommt ihre Einstellung also, bevor ein Signal anliegt. Beim ersten Einschalten reichte
|
|
// das nicht: Die Beleuchtung ging an (die haengt an der Bridge), ein Bild kam aber nicht.
|
|
// Erst ein Reset half - und dann nur deshalb, weil die Bridge ihre Einstellung aus dem
|
|
// vorigen Durchlauf noch hatte und diesmal ein DSI-Signal vorfand. Genau das holt der
|
|
// zweite Aufruf nach, ohne dass ein Reset noetig ist. Die Register sind wiederholbar.
|
|
static void ws7_bridge_core_init(const char *wann)
|
|
{
|
|
esp_rom_printf("[Panel] Bridge-Grundeinstellung %s\n", wann);
|
|
ws7_bridge_write(0xC0, 0x01);
|
|
ws7_bridge_write(0xC2, 0x01);
|
|
ws7_bridge_write(0xAC, 0x01);
|
|
}
|
|
|
|
// Bridge am I2C-Bus anmelden und erstmalig einstellen. Laeuft VOR dem DSI-Bringup;
|
|
// die Hintergrundbeleuchtung bleibt dabei noch aus.
|
|
static void ws7_bridge_pre_init(void)
|
|
{
|
|
i2c_device_config_t dev_cfg = {
|
|
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
|
|
.device_address = WS7_BRIDGE_I2C_ADDR,
|
|
.scl_speed_hz = 100000,
|
|
};
|
|
if (i2c_master_bus_add_device(s_i2c_handle, &dev_cfg, &s_bridge_handle) != ESP_OK) {
|
|
s_bridge_handle = NULL;
|
|
return;
|
|
}
|
|
ws7_bridge_core_init("vor dem DSI-Start");
|
|
}
|
|
|
|
void jc_backlight_init(void)
|
|
{
|
|
// Nichts zu tun - die Bridge uebernimmt das, siehe ws7_bridge_pre_init().
|
|
}
|
|
|
|
void jc_backlight_set(int percent)
|
|
{
|
|
if (percent > 100) percent = 100;
|
|
if (percent < 0) percent = 0;
|
|
uint8_t brightness = (uint8_t)((255 * percent) / 100);
|
|
ws7_bridge_write(0xAD, 0x01); // Panel-/Backlight-Freigabe
|
|
ws7_bridge_write(0xAB, (uint8_t)(255 - brightness));
|
|
ws7_bridge_write(0xAA, 0x01); // Helligkeit uebernehmen
|
|
}
|
|
|
|
// =====================================================================================
|
|
#else // JC-Panels: Hintergrundbeleuchtung als LEDC-PWM an einem GPIO
|
|
// =====================================================================================
|
|
void jc_backlight_init(void)
|
|
{
|
|
const ledc_channel_config_t ch = {
|
|
.gpio_num = BSP_LCD_BACKLIGHT,
|
|
.speed_mode = LEDC_LOW_SPEED_MODE,
|
|
.channel = LCD_LEDC_CH,
|
|
.intr_type = LEDC_INTR_DISABLE,
|
|
.timer_sel = 1,
|
|
.duty = 0,
|
|
.hpoint = 0
|
|
};
|
|
const ledc_timer_config_t tm = {
|
|
.speed_mode = LEDC_LOW_SPEED_MODE,
|
|
.duty_resolution = LEDC_TIMER_10_BIT,
|
|
.timer_num = 1,
|
|
.freq_hz = 5000,
|
|
.clk_cfg = LEDC_AUTO_CLK
|
|
};
|
|
ledc_timer_config(&tm);
|
|
ledc_channel_config(&ch);
|
|
}
|
|
|
|
void jc_backlight_set(int percent)
|
|
{
|
|
if (percent > 100) percent = 100;
|
|
if (percent < 0) percent = 0;
|
|
uint32_t duty = (1023 * percent) / 100;
|
|
ledc_set_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH, duty);
|
|
ledc_update_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH);
|
|
}
|
|
#endif // JC_PANEL_TYPE == WS_PANEL_7H
|
|
|
|
IRAM_ATTR static bool on_vsync(esp_lcd_panel_handle_t panel,
|
|
esp_lcd_dpi_panel_event_data_t *edata, void *ctx)
|
|
{
|
|
return lvgl_port_notify_lcd_vsync();
|
|
}
|
|
|
|
#if JC_PANEL_TYPE == WS_PANEL_7H
|
|
|
|
// ------------------------------------------------------------------------------------
|
|
// Aussetzer des GT9271 ueberbruecken
|
|
//
|
|
// Der Controller laesst gelegentlich einen Messzyklus aus und liefert ab und zu ein
|
|
// unplausibles Paket - die Werks-Testskizze hat solche Pakete ebenfalls verworfen. Der
|
|
// Hersteller-Treiber kann "gerade nichts Neues" nicht von "Finger weg" unterscheiden und
|
|
// meldet beides als losgelassen. Seit die Oberflaeche doppelt so oft abtastet, faellt das
|
|
// auf: Aus einem Tipp wird Druecken-Loslassen-Druecken-Loslassen, der Knopf loest also
|
|
// mehrfach aus.
|
|
//
|
|
// Deshalb liegt hier ein eigener get_xy-Aufsatz vor dem Treiber: Er verwirft unplausible
|
|
// Rohwerte und haelt den letzten gueltigen Punkt kurz fest. Losgelassen wird erst
|
|
// gemeldet, wenn WS7_TOUCH_HOLD_MS lang wirklich nichts mehr kam.
|
|
// ------------------------------------------------------------------------------------
|
|
static bool (*s_touch_get_xy_orig)(esp_lcd_touch_handle_t, uint16_t *, uint16_t *,
|
|
uint16_t *, uint8_t *, uint8_t) = NULL;
|
|
static uint16_t s_touch_last_x = 0;
|
|
static uint16_t s_touch_last_y = 0;
|
|
static int64_t s_touch_last_us = 0;
|
|
static bool s_touch_down = false;
|
|
|
|
static bool ws7_touch_get_xy(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y,
|
|
uint16_t *strength, uint8_t *point_num, uint8_t max_point_num)
|
|
{
|
|
bool got = s_touch_get_xy_orig(tp, x, y, strength, point_num, max_point_num);
|
|
int64_t now = esp_timer_get_time();
|
|
|
|
// Unplausible Rohwerte gelten als Aussetzer, nicht als Beruehrung. Wie viele davon
|
|
// anfallen, war bisher nirgends zu sehen - eine Beruehrung, deren Rohwerte lange
|
|
// verworfen werden, sieht von aussen wie ein Display aus, das nicht reagiert.
|
|
static uint32_t s_verworfen = 0;
|
|
if (got && *point_num > 0 &&
|
|
(x[0] > WS7_TOUCH_RAW_X_MAX || y[0] > WS7_TOUCH_RAW_Y_MAX)) {
|
|
if (s_verworfen == 0)
|
|
esp_rom_printf("[Touch] %u ms: Rohwert verworfen (x %u, y %u liegen ausserhalb)\n",
|
|
(unsigned)(now / 1000), (unsigned)x[0], (unsigned)y[0]);
|
|
s_verworfen++;
|
|
got = false;
|
|
*point_num = 0;
|
|
}
|
|
|
|
if (got && *point_num > 0) {
|
|
if (!s_touch_down)
|
|
esp_rom_printf("[Touch] %u ms: Beruehrung erkannt bei x %u, y %u; %u Rohwerte vorher verworfen\n",
|
|
(unsigned)(now / 1000), (unsigned)x[0], (unsigned)y[0],
|
|
(unsigned)s_verworfen);
|
|
s_verworfen = 0;
|
|
s_touch_last_x = x[0];
|
|
s_touch_last_y = y[0];
|
|
s_touch_last_us = now;
|
|
s_touch_down = true;
|
|
return true;
|
|
}
|
|
|
|
#if WS7_TOUCH_HOLD_MS > 0
|
|
if (s_touch_down && (now - s_touch_last_us) < ((int64_t)WS7_TOUCH_HOLD_MS * 1000)) {
|
|
x[0] = s_touch_last_x;
|
|
y[0] = s_touch_last_y;
|
|
if (strength) strength[0] = 0;
|
|
*point_num = 1;
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
s_touch_down = false;
|
|
return false;
|
|
}
|
|
|
|
// Der GT9271 des Waveshare-Panels meldet im Hochformat (Roh-X 0..720, Roh-Y 0..1280),
|
|
// das Bild laeuft aber im Querformat 1280x720. Achsentausch (und bei Bedarf Spiegelung)
|
|
// deshalb ueber den process_coordinates-Hook, der VOR den swap/mirror-Flags von
|
|
// esp_lcd_touch greift - deren Flags rechnen mit x_max/y_max des BILDES und wuerden auf
|
|
// den Rohwerten falsche Ergebnisse liefern.
|
|
static void ws7_touch_map(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y,
|
|
uint16_t *strength, uint8_t *point_num, uint8_t max_point_num)
|
|
{
|
|
(void)tp; (void)strength; (void)max_point_num;
|
|
if (point_num == NULL) return;
|
|
for (uint8_t i = 0; i < *point_num; i++) {
|
|
uint16_t raw_x = x[i];
|
|
uint16_t raw_y = y[i];
|
|
#if WS7_TOUCH_MIRROR_RAW_X
|
|
raw_x = (raw_x > WS7_TOUCH_RAW_X_MAX) ? 0 : (uint16_t)(WS7_TOUCH_RAW_X_MAX - raw_x);
|
|
#endif
|
|
#if WS7_TOUCH_MIRROR_RAW_Y
|
|
raw_y = (raw_y > WS7_TOUCH_RAW_Y_MAX) ? 0 : (uint16_t)(WS7_TOUCH_RAW_Y_MAX - raw_y);
|
|
#endif
|
|
// Achsentausch + Begrenzung auf die Bildflaeche (der GT9271 liefert vereinzelt
|
|
// Ausreisser, die sonst als Klick am Rand ankaemen).
|
|
uint16_t px = (raw_y >= LCD_H_RES) ? (LCD_H_RES - 1) : raw_y;
|
|
uint16_t py = (raw_x >= LCD_V_RES) ? (LCD_V_RES - 1) : raw_x;
|
|
#if WS7_ROTATE_180
|
|
// Bild wird um 180 Grad gedreht ausgegeben -> Touch muss mitdrehen.
|
|
px = (uint16_t)(LCD_H_RES - 1 - px);
|
|
py = (uint16_t)(LCD_V_RES - 1 - py);
|
|
#endif
|
|
#if WS7_TOUCH_DEBUG
|
|
esp_rom_printf("[Touch] roh X=%u Y=%u -> Bild X=%u Y=%u\n",
|
|
(unsigned)x[i], (unsigned)y[i], (unsigned)px, (unsigned)py);
|
|
#endif
|
|
x[i] = px;
|
|
y[i] = py;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if JC_PANEL_TYPE == JC_PANEL_70
|
|
// Der GT911 des 7,0"-Panels liefert Rohkoordinaten im Raster 800x480 (nicht 1024x600).
|
|
// Ohne Umrechnung waere nur das linke obere Rechteck erreichbar. Das Hersteller-Beispiel
|
|
// skaliert dafuer im Touch-Treiber; hier ueber den vorgesehenen process_coordinates-Hook,
|
|
// damit die Vendor-Datei esp_lcd_touch_gt911.c unveraendert bleibt.
|
|
#define JC_TOUCH_RAW_H_RES (800)
|
|
#define JC_TOUCH_RAW_V_RES (480)
|
|
|
|
static void jc_touch_scale(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y,
|
|
uint16_t *strength, uint8_t *point_num, uint8_t max_point_num)
|
|
{
|
|
(void)tp; (void)strength; (void)max_point_num;
|
|
if (point_num == NULL) return;
|
|
for (uint8_t i = 0; i < *point_num; i++) {
|
|
x[i] = (uint16_t)(((uint32_t)x[i] * LCD_H_RES) / JC_TOUCH_RAW_H_RES);
|
|
y[i] = (uint16_t)(((uint32_t)y[i] * LCD_V_RES) / JC_TOUCH_RAW_V_RES);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if JC_PANEL_TYPE == WS_PANEL_7H
|
|
// Panel wecken. Bewusst mehrfach, siehe unten.
|
|
//
|
|
// Ein reines DPI-Panel kennt keine reset-Funktion, esp_lcd_panel_reset() wuerde ins Leere
|
|
// greifen - deshalb nur die DCS-Befehle.
|
|
//
|
|
// Warum mehrfach: Verpasst der Panel-Controller diese Befehle, weil er nach dem
|
|
// Einschalten noch nicht bereit war, bleibt der Bildschirm dunkel, obwohl der P4 alles
|
|
// korrekt gesendet hat und jeder Schritt Erfolg meldet. Die Hintergrundbeleuchtung geht
|
|
// trotzdem an, weil sie an der Bridge haengt und nicht am Panel - genau dieses Bild.
|
|
// Die Befehle sind wiederholbar, ein zweiter Anlauf nach dem Start der Videoausgabe
|
|
// kostet nichts und faengt den Fall ab.
|
|
static void ws7_panel_wake(esp_lcd_panel_io_handle_t io, const char *wann)
|
|
{
|
|
uint8_t zero = 0x00;
|
|
uint8_t madctl = WS7_MADCTL;
|
|
esp_rom_printf("[Panel] Weckbefehle %s\n", wann);
|
|
BSP_STEP(esp_lcd_panel_io_tx_param(io, 0x36, &madctl, 1), "Panel-Befehl MADCTL");
|
|
BSP_STEP(esp_lcd_panel_io_tx_param(io, 0x11, &zero, 1), "Panel-Befehl Sleep Out");
|
|
vTaskDelay(pdMS_TO_TICKS(120));
|
|
BSP_STEP(esp_lcd_panel_io_tx_param(io, 0x29, &zero, 1), "Panel-Befehl Display On");
|
|
vTaskDelay(pdMS_TO_TICKS(20));
|
|
}
|
|
#endif
|
|
|
|
#if JC_PANEL_TYPE == WS_PANEL_7H
|
|
// Baut die komplette DSI-Strecke auf: Bus, Kommandokanal, Bildausgabe, und weckt das
|
|
// Panel. Bewusst als eigene Funktion, damit der ganze Vorgang wiederholbar ist.
|
|
static bool ws7_dsi_bringup(esp_lcd_dsi_bus_handle_t *out_bus,
|
|
esp_lcd_panel_io_handle_t *out_io,
|
|
esp_lcd_panel_handle_t *out_panel,
|
|
esp_lcd_dpi_panel_config_t *out_dpi_config)
|
|
{
|
|
esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL;
|
|
esp_lcd_panel_io_handle_t io = NULL;
|
|
esp_lcd_panel_handle_t disp_panel = NULL;
|
|
|
|
esp_lcd_dsi_bus_config_t bus_config = {
|
|
.bus_id = 0,
|
|
.num_data_lanes = WS7_DSI_LANES,
|
|
.phy_clk_src = MIPI_DSI_PHY_PLLREF_CLK_SRC_PLL_F20M,
|
|
.lane_bit_rate_mbps = WS7_DSI_LANE_MBPS,
|
|
};
|
|
BSP_STEP(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus), "MIPI-DSI-Bus");
|
|
|
|
esp_lcd_dbi_io_config_t dbi_config = {
|
|
.virtual_channel = 0,
|
|
.lcd_cmd_bits = 8,
|
|
.lcd_param_bits = 8,
|
|
};
|
|
BSP_STEP(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &io), "DBI-Kommandokanal");
|
|
|
|
esp_lcd_dpi_panel_config_t dpi_config = {
|
|
.virtual_channel = 0,
|
|
.dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT,
|
|
.dpi_clock_freq_mhz = WS7_DPI_CLOCK_MHZ,
|
|
#if WS7_COLOR_BITS == 24
|
|
.pixel_format = LCD_COLOR_PIXEL_FORMAT_RGB888,
|
|
#else
|
|
.pixel_format = LCD_COLOR_PIXEL_FORMAT_RGB565,
|
|
#endif
|
|
// Anzahl der Bildspeicher. Beim Waveshare-Panel genau einer: mit zweien kam nach
|
|
// dem Einschalten kein Bild (siehe WS7_SINGLE_FB in pins_config.h).
|
|
.num_fbs = LVGL_PORT_LCD_BUFFER_NUMS,
|
|
.video_timing = {
|
|
.h_size = BSP_LCD_H_RES,
|
|
.v_size = BSP_LCD_V_RES,
|
|
.hsync_back_porch = WS7_HSYNC_BACK,
|
|
.hsync_pulse_width = WS7_HSYNC_PULSE,
|
|
.hsync_front_porch = WS7_HSYNC_FRONT,
|
|
.vsync_back_porch = WS7_VSYNC_BACK,
|
|
.vsync_pulse_width = WS7_VSYNC_PULSE,
|
|
.vsync_front_porch = WS7_VSYNC_FRONT,
|
|
},
|
|
.flags = {
|
|
.use_dma2d = true,
|
|
},
|
|
};
|
|
BSP_STEP(esp_lcd_new_panel_dpi(mipi_dsi_bus, &dpi_config, &disp_panel),
|
|
"DPI-Panel (Framebuffer im PSRAM)");
|
|
if (disp_panel == NULL) {
|
|
esp_rom_printf("[Panel] Abbruch: Panel wurde nicht angelegt. Haeufigste Ursache ist zu "
|
|
"wenig PSRAM - PSRAM in den Board-Einstellungen aktivieren oder in "
|
|
"pins_config.h weniger Framebuffer waehlen.\n");
|
|
return false;
|
|
}
|
|
|
|
ws7_panel_wake(io, "vor dem Start der Videoausgabe");
|
|
|
|
BSP_STEP(esp_lcd_panel_init(disp_panel), "DPI-Videoausgabe starten");
|
|
|
|
// Zweiter Anlauf, jetzt bei laufender Videoausgabe: faengt den Fall ab, dass das
|
|
// Panel beim ersten Mal noch nicht aufnahmebereit war.
|
|
vTaskDelay(pdMS_TO_TICKS(WS7_PANEL_SETTLE_MS));
|
|
ws7_bridge_core_init("nach dem Start der Videoausgabe");
|
|
ws7_panel_wake(io, "nach dem Start der Videoausgabe");
|
|
|
|
*out_bus = mipi_dsi_bus;
|
|
*out_io = io;
|
|
*out_panel = disp_panel;
|
|
*out_dpi_config = dpi_config;
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
// Bringt Panel + Touch + LVGL-Port hoch (LVGL laeuft danach in eigenem Task).
|
|
// Rueckgabe false: Panel kam nicht hoch - der Aufrufer darf dann KEINE LVGL-Funktion
|
|
// benutzen, sonst folgt ein zweiter Absturz, der die eigentliche Ursache ueberdeckt.
|
|
bool jc_board_bringup(void)
|
|
{
|
|
#if JC_PANEL_TYPE == WS_PANEL_7H
|
|
// Anlaufzeit abwarten, BEVOR Bridge und Panel angesprochen werden.
|
|
//
|
|
// Das Displaymodul braucht nach dem Anlegen der Versorgung einen Moment, bis Bridge
|
|
// und Panel bereit sind. Faengt der P4 sofort an, laeuft der Hochlauf ins Leere: Die
|
|
// Bridge nimmt ihre Register nicht an oder das Panel zeigt trotz korrekt gesendeter
|
|
// DSI-Daten nichts - beides ohne Fehlermeldung, weil aus Sicht des P4 alles geklappt
|
|
// hat. Das Ergebnis war ein Display, das mal ansprang und mal nicht.
|
|
//
|
|
// Der Werks-Testsketch wartet aus demselben Grund 2 Sekunden am Anfang von setup().
|
|
// Dass es mit geoeffnetem seriellen Monitor fast immer klappte, hatte dieselbe
|
|
// Ursache: Dessen USB-Anmeldung verzoegert den Start und ersetzte damit die Wartezeit.
|
|
vTaskDelay(pdMS_TO_TICKS(WS7_PANEL_WARMUP_MS));
|
|
#endif
|
|
jc_backlight_init();
|
|
|
|
#if JC_PANEL_TYPE == WS_PANEL_7H
|
|
ws7_i2c_bus_recover(); // haengenden Bus vom letzten Reset befreien
|
|
#endif
|
|
|
|
i2c_master_bus_config_t i2c_bus_conf = {
|
|
.clk_source = I2C_CLK_SRC_DEFAULT,
|
|
.sda_io_num = BSP_I2C_SDA,
|
|
.scl_io_num = BSP_I2C_SCL,
|
|
.i2c_port = BSP_I2C_NUM,
|
|
#if JC_PANEL_TYPE == WS_PANEL_7H
|
|
// Stoerimpulsfilter und interne Abschlusswiderstaende - beides setzt auch das
|
|
// Waveshare-Beispiel. Ohne den Filter brechen Bridge und Touch weg, sobald die
|
|
// DSI-Ausgabe laeuft: Deren schnelle Signale stoeren auf das lange Flachbandkabel
|
|
// ein, und ungefilterte Stoerspitzen bringen die I2C-Uebertragung aus dem Tritt.
|
|
// Der Audio-Baustein auf der Platine bleibt dabei erreichbar, weil er nicht am
|
|
// Displaykabel haengt - genau dieses Muster war im Start-Log zu sehen.
|
|
.glitch_ignore_cnt = 7,
|
|
.flags.enable_internal_pullup = true,
|
|
#endif
|
|
};
|
|
i2c_new_master_bus(&i2c_bus_conf, &s_i2c_handle);
|
|
|
|
#if JC_PANEL_TYPE == WS_PANEL_7H
|
|
ws7_i2c_scan("vor dem Panel-Start");
|
|
// Bridge des Waveshare-Panels vorbereiten (noch ohne Hintergrundbeleuchtung).
|
|
ws7_bridge_pre_init();
|
|
#endif
|
|
|
|
// Reset-Grund mitschreiben: 1 = Kaltstart (Strom weg), alles andere ist ein Warmstart.
|
|
// Nur beim Warmstart behalten die Bausteine ihren alten Zustand - genau dann traten
|
|
// die schwarzen Bilder auf.
|
|
esp_rom_printf("[Panel] Startgrund: %d (1 = Kaltstart)\n", (int)esp_reset_reason());
|
|
|
|
static esp_ldo_channel_handle_t phy_pwr_chan = NULL;
|
|
esp_ldo_channel_config_t ldo_cfg = {
|
|
.chan_id = BSP_MIPI_DSI_PHY_PWR_LDO_CHAN,
|
|
.voltage_mv = BSP_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV,
|
|
};
|
|
BSP_STEP(esp_ldo_acquire_channel(&ldo_cfg, &phy_pwr_chan), "Stromversorgung MIPI-D-PHY");
|
|
|
|
esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL;
|
|
esp_lcd_panel_io_handle_t io = NULL;
|
|
esp_lcd_panel_handle_t disp_panel = NULL;
|
|
#if JC_PANEL_TYPE == WS_PANEL_7H
|
|
esp_lcd_dpi_panel_config_t dpi_config;
|
|
#endif
|
|
|
|
#if JC_PANEL_TYPE == WS_PANEL_7H
|
|
// ---------------- Waveshare 7inch DSI LCD (H) (1280x720) ----------------
|
|
// Kein Hersteller-Panel-Treiber: DSI-Bus, DBI-Kommandokanal und DPI-Panel werden
|
|
// direkt angelegt. Entscheidend ist die Referenztaktquelle PLL_F20M - mit der
|
|
// Standardquelle bleibt das Bild auf ESP32-P4 Rev. 1.3 / ECO2 schwarz.
|
|
bsp_report_psram((size_t)BSP_LCD_H_RES * BSP_LCD_V_RES * BSP_LCD_FB_BYTES_PER_PX
|
|
* LVGL_PORT_LCD_BUFFER_NUMS);
|
|
|
|
if (!ws7_dsi_bringup(&mipi_dsi_bus, &io, &disp_panel, &dpi_config)) {
|
|
return false;
|
|
}
|
|
|
|
#if WS7_DSI_RESTART
|
|
// Die Strecke einmal komplett ab- und wieder aufbauen.
|
|
//
|
|
// Beim ersten Einschalten kam trotz fehlerfreiem Hochlauf kein Bild; erst ein Reset
|
|
// half, und zwar zuverlaessig. Ein Reset unterscheidet sich an dieser Stelle nur in
|
|
// einem Punkt von unserem Ablauf: Die DSI-Verbindung geht einmal weg und neu auf,
|
|
// waehrend Bridge und Panel bereits versorgt und eingestellt sind. Weder die
|
|
// wiederholte Grundeinstellung der Bridge noch die wiederholten Weckbefehle ersetzen
|
|
// das. Genau dieser Ab- und Wiederaufbau wird hier nachgeholt - ohne Reset.
|
|
esp_rom_printf("[Panel] DSI-Strecke wird neu aufgebaut\n");
|
|
esp_lcd_panel_del(disp_panel);
|
|
esp_lcd_panel_io_del(io);
|
|
esp_lcd_del_dsi_bus(mipi_dsi_bus);
|
|
vTaskDelay(pdMS_TO_TICKS(WS7_DSI_RESTART_MS));
|
|
|
|
if (!ws7_dsi_bringup(&mipi_dsi_bus, &io, &disp_panel, &dpi_config)) {
|
|
return false;
|
|
}
|
|
#endif
|
|
#if WS7_TEST_PATTERN
|
|
// Prueffarbbalken: Sie entstehen im DSI-Baustein selbst und benutzen weder den
|
|
// Bildspeicher noch LVGL. Erscheinen sie, arbeitet die Anzeigekette bis zum Panel und
|
|
// der Fehler liegt weiter oben. Bleibt es schwarz, liegt es an DSI, Zeitbasis oder
|
|
// Panel.
|
|
//
|
|
// Der Hochlauf endet hier bewusst: Wuerde LVGL danach starten, schriebe es sofort in
|
|
// den Bildspeicher und das Pruefbild waere wieder weg - der Test saehe dann genauso
|
|
// aus wie der Fehler, den er finden soll.
|
|
jc_backlight_set(100); // Beleuchtung an, sonst ist nichts zu sehen
|
|
BSP_STEP(esp_lcd_dpi_panel_set_pattern(disp_panel, MIPI_DSI_PATTERN_BAR_VERTICAL),
|
|
"Prueffarbbalken");
|
|
esp_rom_printf("[Panel] Pruefbild steht - erwartet werden senkrechte Farbbalken.\n");
|
|
esp_rom_printf("[Panel] Hochlauf endet hier (WS7_TEST_PATTERN = 1, keine Oberflaeche).\n");
|
|
return false; // ohne LVGL: Anzeige bleibt beim Pruefbild
|
|
#endif
|
|
|
|
|
|
|
|
#elif JC_PANEL_TYPE == JC_PANEL_70
|
|
// ---------------- 7,0" JD9165 (1024x600) ----------------
|
|
esp_lcd_dsi_bus_config_t bus_config = JD9165_PANEL_BUS_DSI_2CH_CONFIG();
|
|
esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus);
|
|
|
|
esp_lcd_dbi_io_config_t dbi_config = JD9165_PANEL_IO_DBI_CONFIG();
|
|
esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &io);
|
|
|
|
esp_lcd_dpi_panel_config_t dpi_config = JD9165_1024_600_PANEL_60HZ_DPI_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB565);
|
|
dpi_config.num_fbs = LVGL_PORT_LCD_BUFFER_NUMS;
|
|
|
|
jd9165_vendor_config_t vendor_config = {
|
|
.mipi_config = {
|
|
.dsi_bus = mipi_dsi_bus,
|
|
.dpi_config = &dpi_config,
|
|
},
|
|
};
|
|
esp_lcd_panel_dev_config_t lcd_dev_config = {
|
|
.bits_per_pixel = 16,
|
|
.rgb_ele_order = ESP_LCD_COLOR_SPACE_RGB,
|
|
.reset_gpio_num = BSP_LCD_RST,
|
|
.vendor_config = &vendor_config,
|
|
};
|
|
esp_lcd_new_panel_jd9165(io, &lcd_dev_config, &disp_panel);
|
|
#else
|
|
// ---------------- 4,3" ST7701 (480x800) ----------------
|
|
esp_lcd_dsi_bus_config_t bus_config = ST7701_PANEL_BUS_DSI_2CH_CONFIG();
|
|
esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus);
|
|
|
|
esp_lcd_dbi_io_config_t dbi_config = ST7701_PANEL_IO_DBI_CONFIG();
|
|
esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &io);
|
|
|
|
esp_lcd_dpi_panel_config_t dpi_config = ST7701_480_360_PANEL_60HZ_DPI_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB565);
|
|
dpi_config.num_fbs = LVGL_PORT_LCD_BUFFER_NUMS;
|
|
|
|
st7701_vendor_config_t vendor_config = {
|
|
.mipi_config = {
|
|
.dsi_bus = mipi_dsi_bus,
|
|
.dpi_config = &dpi_config,
|
|
},
|
|
.flags = {
|
|
.use_mipi_interface = 1,
|
|
},
|
|
};
|
|
esp_lcd_panel_dev_config_t lcd_dev_config = {
|
|
.bits_per_pixel = 16,
|
|
.rgb_ele_order = ESP_LCD_COLOR_SPACE_RGB,
|
|
.reset_gpio_num = BSP_LCD_RST,
|
|
.vendor_config = &vendor_config,
|
|
};
|
|
esp_lcd_new_panel_st7701(io, &lcd_dev_config, &disp_panel);
|
|
#endif
|
|
#if JC_PANEL_TYPE != WS_PANEL_7H
|
|
esp_lcd_panel_reset(disp_panel);
|
|
esp_lcd_panel_init(disp_panel);
|
|
#endif
|
|
|
|
// Alle Framebuffer einmal auf Schwarz loeschen. Im Avoid-Tear-Direct-Mode (mehrere
|
|
// Buffer) kopiert LVGL pro Frame nur die geaenderten Bereiche in den naechsten Buffer;
|
|
// bis alle Buffer einmal vollstaendig bemalt sind, wuerde sonst uninitialisierter
|
|
// Speicher (weisses Rauschen) durchblitzen. Schwarz faellt gegen das dunkle Theme
|
|
// nicht auf. Cache-Writeback noetig, da die Buffer im PSRAM liegen und der DMA liest.
|
|
{
|
|
void *fbs[3] = { NULL, NULL, NULL };
|
|
size_t fb_size = (size_t)BSP_LCD_H_RES * BSP_LCD_V_RES * BSP_LCD_FB_BYTES_PER_PX;
|
|
#if LVGL_PORT_LCD_BUFFER_NUMS >= 3
|
|
esp_lcd_dpi_panel_get_frame_buffer(disp_panel, 3, &fbs[0], &fbs[1], &fbs[2]);
|
|
#elif LVGL_PORT_LCD_BUFFER_NUMS == 2
|
|
esp_lcd_dpi_panel_get_frame_buffer(disp_panel, 2, &fbs[0], &fbs[1]);
|
|
#else
|
|
esp_lcd_dpi_panel_get_frame_buffer(disp_panel, 1, &fbs[0]);
|
|
#endif
|
|
for (int i = 0; i < 3; i++) {
|
|
if (fbs[i]) {
|
|
memset(fbs[i], 0, fb_size);
|
|
esp_cache_msync(fbs[i], fb_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
|
|
}
|
|
}
|
|
}
|
|
|
|
esp_lcd_dpi_panel_event_callbacks_t cbs = {
|
|
#if LVGL_PORT_AVOID_TEAR_MODE
|
|
.on_refresh_done = on_vsync,
|
|
#else
|
|
.on_color_trans_done = on_vsync,
|
|
#endif
|
|
};
|
|
esp_lcd_dpi_panel_register_event_callbacks(disp_panel, &cbs, NULL);
|
|
|
|
#if JC_PANEL_TYPE == WS_PANEL_7H
|
|
// Bridge freigeben und Beleuchtung einschalten, BEVOR der Touch angesprochen wird.
|
|
// Der GT9271 antwortet erst danach: Die Freigabe der Bridge (Register 0xAD) versorgt
|
|
// offenbar auch ihn. In der Werks-Testskizze steht die Touch-Initialisierung aus
|
|
// demselben Grund hinter dem Einschalten der Beleuchtung.
|
|
jc_backlight_set(100);
|
|
vTaskDelay(pdMS_TO_TICKS(WS7_TOUCH_POWER_MS));
|
|
ws7_i2c_scan("nach dem Panel-Start");
|
|
#endif
|
|
|
|
esp_lcd_panel_io_handle_t tp_io_handle = NULL;
|
|
// MUSS vorbelegt sein: schlaegt die Touch-Initialisierung fehl, gibt der Treiber einen
|
|
// unbrauchbaren Zeiger zurueck. Ohne Vorbelegung landet Muell im LVGL-Port.
|
|
esp_lcd_touch_handle_t tp_handle = NULL;
|
|
esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG();
|
|
tp_io_config.scl_speed_hz = 100000;
|
|
#if JC_PANEL_TYPE == WS_PANEL_7H
|
|
// GT9271 des Waveshare-Panels: gleiche Registerbelegung wie der GT911, aber
|
|
// andere I2C-Adresse.
|
|
tp_io_config.dev_addr = WS7_TOUCH_I2C_ADDR;
|
|
#endif
|
|
esp_lcd_new_panel_io_i2c(s_i2c_handle, &tp_io_config, &tp_io_handle);
|
|
const esp_lcd_touch_config_t tp_cfg = {
|
|
.x_max = BSP_LCD_H_RES,
|
|
.y_max = BSP_LCD_V_RES,
|
|
.rst_gpio_num = BSP_LCD_TOUCH_RST,
|
|
.int_gpio_num = BSP_LCD_TOUCH_INT,
|
|
.levels = {
|
|
.reset = 0,
|
|
.interrupt = 0,
|
|
},
|
|
.flags = {
|
|
.swap_xy = 0,
|
|
.mirror_x = 0,
|
|
.mirror_y = 0,
|
|
},
|
|
#if JC_PANEL_TYPE == WS_PANEL_7H
|
|
.process_coordinates = ws7_touch_map,
|
|
#elif JC_PANEL_TYPE == JC_PANEL_70
|
|
.process_coordinates = jc_touch_scale,
|
|
#endif
|
|
};
|
|
if (esp_lcd_touch_new_i2c_gt911(tp_io_handle, &tp_cfg, &tp_handle) != ESP_OK) {
|
|
esp_rom_printf("[Panel] Touch-Controller nicht gefunden - Anzeige laeuft ohne Bedienung.\n");
|
|
tp_handle = NULL;
|
|
}
|
|
#if JC_PANEL_TYPE == WS_PANEL_7H
|
|
if (tp_handle) { // Aussetzer-Filter vor den Treiber haengen
|
|
s_touch_get_xy_orig = tp_handle->get_xy;
|
|
tp_handle->get_xy = ws7_touch_get_xy;
|
|
}
|
|
#endif
|
|
|
|
lvgl_port_interface_t interface =
|
|
(dpi_config.flags.use_dma2d) ? LVGL_PORT_INTERFACE_MIPI_DSI_DMA
|
|
: LVGL_PORT_INTERFACE_MIPI_DSI_NO_DMA;
|
|
esp_rom_printf("[Panel] Bringup fertig - LVGL wird gestartet\n");
|
|
lvgl_port_init(disp_panel, tp_handle, interface);
|
|
esp_rom_printf("[Panel] LVGL laeuft\n");
|
|
return true;
|
|
}
|
|
|
|
#endif // JC_USE_REAL_PANEL
|