knxdisplay/main/hello_world_main.c
2026-01-14 19:34:20 +01:00

273 lines
8.6 KiB
C

/*
* SPDX-FileCopyrightText: 2026 User
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "driver/gpio.h"
#include "driver/i2c_master.h"
#include "esp_log.h"
#include "esp_heap_caps.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_mipi_dsi.h"
#include "esp_ldo_regulator.h"
#include "esp_lcd_touch_gt911.h"
#include "lvgl.h"
#include "esp_lcd_jd9365_10_1.h"
#define TAG "JD9365_LVGL"
// Display config
#define LCD_H_RES 800
#define LCD_V_RES 1280
#define LCD_BIT_PER_PIXEL 24
#define LCD_RST_GPIO 27
#define LCD_BK_GPIO 26
#define LCD_BK_ON_LEVEL 1
// MIPI DSI config
#define MIPI_DSI_LANE_NUM 2
#define MIPI_DSI_PHY_LDO_CHAN 3
#define MIPI_DSI_PHY_LDO_VOLTAGE_MV 2500
// Touch config (GT911)
#define TOUCH_I2C_SDA 7
#define TOUCH_I2C_SCL 8
#define TOUCH_INT_GPIO 3
#define TOUCH_RST_GPIO 2
#define TOUCH_I2C_FREQ_HZ 400000
static esp_lcd_panel_handle_t panel_handle = NULL;
static esp_lcd_touch_handle_t touch_handle = NULL;
static esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL;
static esp_lcd_panel_io_handle_t mipi_dbi_io = NULL;
static SemaphoreHandle_t refresh_done_sem = NULL;
// ================= Color Transfer Done Callback ===================
static bool on_color_trans_done(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *event_data, void *user_ctx)
{
BaseType_t high_task_awoken = pdFALSE;
if (refresh_done_sem) {
xSemaphoreGiveFromISR(refresh_done_sem, &high_task_awoken);
}
return high_task_awoken == pdTRUE;
}
// ================= Flush Callback ===================
static void lvgl_flush_cb(lv_display_t *disp, const lv_area_t *area, uint8_t *color_p)
{
xSemaphoreTake(refresh_done_sem, portMAX_DELAY);
esp_lcd_panel_draw_bitmap(panel_handle, area->x1, area->y1,
area->x2 + 1, area->y2 + 1, color_p);
lv_display_flush_ready(disp);
}
// ================= MIPI DSI / JD9365 init ===================
static void lcd_init(void)
{
// Backlight GPIO
gpio_config_t bk_gpio = {
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = 1ULL << LCD_BK_GPIO
};
gpio_config(&bk_gpio);
gpio_set_level(LCD_BK_GPIO, LCD_BK_ON_LEVEL);
// LDO für MIPI PHY
esp_ldo_channel_handle_t ldo_mipi = NULL;
esp_ldo_channel_config_t ldo_cfg = {
.chan_id = MIPI_DSI_PHY_LDO_CHAN,
.voltage_mv = MIPI_DSI_PHY_LDO_VOLTAGE_MV
};
esp_ldo_acquire_channel(&ldo_cfg, &ldo_mipi);
// MIPI DSI Bus
esp_lcd_dsi_bus_config_t bus_cfg = JD9365_PANEL_BUS_DSI_2CH_CONFIG();
esp_lcd_new_dsi_bus(&bus_cfg, &mipi_dsi_bus);
// Panel IO
esp_lcd_dbi_io_config_t io_cfg = JD9365_PANEL_IO_DBI_CONFIG();
esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &io_cfg, &mipi_dbi_io);
// Panel Driver
esp_lcd_dpi_panel_config_t dpi_cfg = JD9365_800_1280_PANEL_60HZ_DPI_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB888);
jd9365_vendor_config_t vendor_cfg = {
.flags = { .use_mipi_interface = 1 },
.mipi_config = { .dsi_bus = mipi_dsi_bus, .dpi_config = &dpi_cfg, .lane_num = MIPI_DSI_LANE_NUM }
};
esp_lcd_panel_dev_config_t panel_cfg = {
.reset_gpio_num = LCD_RST_GPIO,
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
.bits_per_pixel = LCD_BIT_PER_PIXEL,
.vendor_config = &vendor_cfg
};
esp_lcd_new_panel_jd9365(mipi_dbi_io, &panel_cfg, &panel_handle);
// Initialize panel first
esp_lcd_panel_reset(panel_handle);
esp_lcd_panel_init(panel_handle);
esp_lcd_panel_swap_xy(panel_handle, true);
esp_lcd_panel_disp_on_off(panel_handle, true);
// Now set up synchronization after panel is ready
refresh_done_sem = xSemaphoreCreateBinary();
xSemaphoreGive(refresh_done_sem); // Allow first draw to proceed
// Register color transfer done callback (signals when draw_bitmap completes)
esp_lcd_dpi_panel_event_callbacks_t cbs = {
.on_color_trans_done = on_color_trans_done,
};
esp_lcd_dpi_panel_register_event_callbacks(panel_handle, &cbs, NULL);
}
// ================= Touch init ===================
static void touch_init(void)
{
// I2C Bus initialisieren
i2c_master_bus_config_t i2c_bus_cfg = {
.i2c_port = I2C_NUM_0,
.sda_io_num = TOUCH_I2C_SDA,
.scl_io_num = TOUCH_I2C_SCL,
.clk_source = I2C_CLK_SRC_DEFAULT,
.glitch_ignore_cnt = 7,
.flags.enable_internal_pullup = true, // Board hat externe Pullups
};
i2c_master_bus_handle_t i2c_bus = NULL;
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &i2c_bus));
// Touch Panel IO
esp_lcd_panel_io_handle_t touch_io = NULL;
esp_lcd_panel_io_i2c_config_t touch_io_cfg = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG();
touch_io_cfg.scl_speed_hz = TOUCH_I2C_FREQ_HZ;
ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c(i2c_bus, &touch_io_cfg, &touch_io));
// GT911 Touch Controller (with rotation for landscape)
esp_lcd_touch_config_t touch_cfg = {
.x_max = LCD_V_RES, // Swapped for landscape
.y_max = LCD_H_RES,
.rst_gpio_num = TOUCH_RST_GPIO,
.int_gpio_num = TOUCH_INT_GPIO,
.levels = {
.reset = 0,
.interrupt = 0,
},
.flags = {
.swap_xy = 1, // Swap X/Y for 90° rotation
.mirror_x = 1, // Mirror X for correct orientation
.mirror_y = 0,
},
};
ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_gt911(touch_io, &touch_cfg, &touch_handle));
ESP_LOGI(TAG, "Touch GT911 initialized");
}
// ================= LVGL Touch Callback ===================
static void lvgl_touch_cb(lv_indev_t *indev, lv_indev_data_t *data)
{
uint16_t x[1];
uint16_t y[1];
uint8_t touch_cnt = 0;
esp_lcd_touch_read_data(touch_handle);
bool touched = esp_lcd_touch_get_coordinates(touch_handle, x, y, NULL, &touch_cnt, 1);
if (touched && touch_cnt > 0) {
data->point.x = x[0];
data->point.y = y[0];
data->state = LV_INDEV_STATE_PRESSED;
} else {
data->state = LV_INDEV_STATE_RELEASED;
}
}
// ================= LVGL init ===================
static void lvgl_init(void)
{
lv_init();
// Display erstellen
lv_display_t* display1 = lv_display_create(LCD_H_RES, LCD_V_RES);
// Set color format to match panel (RGB888 = 24-bit)
lv_display_set_color_format(display1, LV_COLOR_FORMAT_RGB888);
// Rotate to landscape
lv_display_set_rotation(display1, LV_DISPLAY_ROTATION_90);
// Allocate buffers in PSRAM with proper alignment for DMA
size_t buf_size = LCD_H_RES * 100 * 3; // 100 lines at a time, RGB888
uint8_t *buf1 = heap_caps_aligned_alloc(64, buf_size,
MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA);
uint8_t *buf2 = heap_caps_aligned_alloc(64, buf_size,
MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA);
if (!buf1 || !buf2) {
ESP_LOGE(TAG, "Failed to allocate display buffers!");
return;
}
lv_display_set_buffers(display1, buf1, buf2, buf_size, LV_DISPLAY_RENDER_MODE_PARTIAL);
// Flush-Callback setzen
lv_display_set_flush_cb(display1, lvgl_flush_cb);
// Touch Input Device registrieren
lv_indev_t *indev = lv_indev_create();
lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
lv_indev_set_read_cb(indev, lvgl_touch_cb);
lv_indev_set_display(indev, display1);
lv_display_set_default(display1);
}
// ================= UI erstellen ===================
static void create_ui(void)
{
// Tileview für Swipe-Pages
lv_obj_t *tileview = lv_tileview_create(lv_screen_active());
// Seite 1 - Blau
lv_obj_t *tile1 = lv_tileview_add_tile(tileview, 0, 0, LV_DIR_RIGHT);
lv_obj_set_style_bg_color(tile1, lv_color_hex(0x2196F3), 0);
lv_obj_t *label1 = lv_label_create(tile1);
lv_label_set_text(label1, "Seite 1\n\nSwipe nach links ->");
lv_obj_set_style_text_color(label1, lv_color_white(), 0);
lv_obj_set_style_text_font(label1, &lv_font_montserrat_28, 0);
lv_obj_center(label1);
// Seite 2 - Grün
lv_obj_t *tile2 = lv_tileview_add_tile(tileview, 1, 0, LV_DIR_LEFT);
lv_obj_set_style_bg_color(tile2, lv_color_hex(0x4CAF50), 0);
lv_obj_t *label2 = lv_label_create(tile2);
lv_label_set_text(label2, "Seite 2\n\n<- Swipe nach rechts");
lv_obj_set_style_text_color(label2, lv_color_white(), 0);
lv_obj_set_style_text_font(label2, &lv_font_montserrat_28, 0);
lv_obj_center(label2);
}
// ================= Main ===================
void app_main(void)
{
ESP_LOGI(TAG, "Starting JD9365 + LVGL 9");
lcd_init();
touch_init();
lvgl_init();
create_ui();
while (1) {
lv_tick_inc(10);
lv_task_handler();
vTaskDelay(pdMS_TO_TICKS(10));
}
}