/** * V2X-Display Driver - ST7735 SPI (T-Dongle-C5) * * Display: ST7735, 80x160 Pixel * SPI-Pins: CS=GPIO10, RST=GPIO1, SDA(MOSI)=GPIO2, SCK=GPIO6 * LCD-Backlight: GPIO0 * * Statusanzeige: * - Oben: SD-Speicher in %, Frame-Counter * - Unten: rollende Liste der letzten X Frames (RSSI-Balken + msg_type) */ #include "esp_log.h" #include "v2x_display.h" #include "esp_lcd_panel_ops.h" #include "esp_lcd_panel_vendor.h" #include "esp_lvgl_port.h" #include "driver/gpio.h" #include static const char *TAG = "v2x-display"; /* ST7735 Kommandos */ #define ST7735_NOP 0x00 #define ST7735_SWRESET 0x01 #define ST7735_SLPIN 0x10 #define ST7735_SLPOUT 0x11 #define ST7735_PTLON 0x12 #define ST7735_NORON 0x13 #define ST7735_INVOFF 0x20 #define ST7735_INVON 0x21 #define ST7735_DIS5520 0x29 #define ST7735_MADCTL 0x36 #define ST7735_COLMOD 0x3A #define ST7735_CASET 0x2A #define ST7735_RASET 0x2B #define ST7735_RAMWR 0x2C static esp_lcd_panel_handle_t s_panel = NULL; static bool s_initialized = false; /* ST7735 Init-Sequenz (TFT 0.96 Zoll 80x160, SPI, ROTG) */ static const esp_lcd_panel_cmd_init_t st7735_init[] = { {0x11, (uint8_t[]){0}, 0, 120}, /* SWRESET */ {0x3A, (uint8_t[]){0x55}, 1, 0}, /* COLMOD=16bit */ {0x36, (uint8_t[]){0x08}, 1, 0}, /* MADCTL (portrait) */ {0x21, (uint8_t[]){}, 0, 0}, /* INVOFF */ {0x29, (uint8_t[]){}, 0, 0}, /* DISON */ }; esp_err_t v2x_display_init(void) { esp_err_t ret; ESP_LOGI(TAG, "Initialisiere ST7735 Display..."); /* Backlight an */ gpio_config_t bl_conf = { .pin_bit_mask = (1ULL << DISP_GPIO_BL), .mode = GPIO_MODE_OUTPUT, .pull_down_en = GPIO_PULLDOWN_DISABLE, .pull_up_en = GPIO_PULLUP_DISABLE, }; gpio_config(&bl_conf); gpio_set_level(DISP_GPIO_BL, 1); /* Reset-Pin */ gpio_config_t rst_conf = { .pin_bit_mask = (1ULL << DISP_GPIO_RST), .mode = GPIO_MODE_OUTPUT, }; gpio_config(&rst_conf); /* SPI für LCD */ spi_bus_config_t bus_cfg = { .mosi_io_num = DISP_GPIO_SDA, /* GPIO2 */ .miso_io_num = -1, /* LCD braucht kein MISO */ .sclk_io_num = DISP_GPIO_SCK, /* GPIO6 */ .quadwp_io_num = -1, .quadhd_io_num = -1, .max_transfer_sz = DISP_W * DISP_H * sizeof(uint16_t), }; ret = spi_bus_initialize(SPI3_HOST, &bus_cfg, 1); if (ret != ESP_OK) { ESP_LOGE(TAG, "spi_bus_init fehlgeschlagen"); return ret; } /* LCD-Panel konfigurieren */ esp_lcd_panel_dev_config_t panel_cfg = { .reset_gpio_num = DISP_GPIO_RST, .color_space = ESP_LCD_COLOR_SPACE_RGB, .bits_per_pixel = 16, }; ret = esp_lcd_new_panel_st7735(NULL, &panel_cfg, &s_panel); if (ret != ESP_OK) { ESP_LOGE(TAG, "ST7735 Panel konnte nicht erstellt werden"); return ret; } /* Panel initialisieren */ ret = esp_lcd_panel_reset(s_panel); if (ret != ESP_OK) goto err; ret = esp_lcd_panel_init(s_panel); if (ret != ESP_OK) goto err; ret = esp_lcd_panel_disp_on_off(s_panel, true); if (ret != ESP_OK) goto err; /* Komplette Init-Sequenz (falls ST7735 nicht korrekt init) */ ret = esp_lcd_panel_cmd_init(s_panel, st7735_init, sizeof(st7735_init)/sizeof(st7735_init[0])); if (ret != ESP_OK) { ESP_LOGW(TAG, "ST7735 Init-Kommandos fehlgeschlagen: %s", esp_err_to_name(ret)); } s_initialized = true; g_disp_initialized = true; g_disp_panel = s_panel; ESP_LOGI(TAG, "ST7735 Display bereit (80x160, SPI)"); return ESP_OK; err: ESP_LOGE(TAG, "Display-Init fehlgeschlagen"); return ret; } esp_err_t v2x_display_clear(uint16_t color) { if (!s_initialized) return ESP_ERR_INVALID_STATE; esp_lcd_panel_draw_bitmap(s_panel, 0, 0, DISP_W, DISP_H, &color); return ESP_OK; } esp_err_t v2x_display_rect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color, bool filled) { if (!s_initialized) return ESP_ERR_INVALID_STATE; uint8_t *buf = malloc(w * h * sizeof(uint16_t)); if (!buf) return ESP_ERR_NO_MEM; for (int i = 0; i < w * h; i++) { ((uint16_t *)buf)[i] = color; } esp_lcd_panel_draw_bitmap(s_panel, x, y, x + w, y + h, buf); free(buf); return ESP_OK; } esp_err_t v2x_display_line(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color) { /* Einfacher Bresenham-Linienzeiger für 1-Pixel Linien */ if (!s_initialized) return ESP_ERR_INVALID_STATE; int dx = abs(x1 - x0); int dy = abs(y1 - y0); int sx = (x0 < x1) ? 1 : -1; int sy = (y0 < y1) ? 1 : -1; int err = dx - dy; while (true) { esp_lcd_panel_draw_bitmap(s_panel, x0, y0, x0 + 1, y0 + 1, &color); if (x0 == x1 && y0 == y1) break; int e2 = 2 * err; if (e2 > -dy) { err -= dy; x0 += sx; } if (e2 < dx) { err += dx; y0 += sy; } } return ESP_OK; } esp_err_t v2x_display_off(void) { if (!s_initialized) return ESP_ERR_INVALID_STATE; gpio_set_level(DISP_GPIO_BL, 0); if (s_panel) esp_lcd_panel_disp_on_off(s_panel, false); return ESP_OK; } /* Fortschrittsbalken: progress 0.0 - 1.0 */ esp_err_t v2x_display_progress_bar(int16_t x, int16_t y, int16_t w, int16_t h, float progress, uint16_t fg_color, uint16_t bg_color) { progress = (progress < 0.0f) ? 0.0f : (progress > 1.0f) ? 1.0f : progress; /* Hintergrund */ v2x_display_rect(x, y, w, h, bg_color, true); /* Vordergrund */ int16_t fw = (int16_t)(w * progress); if (fw > 0) { v2x_display_rect(x, y, fw, h, fg_color, true); } /* Rahmen */ for (int i = 0; i < w; i++) { v2x_display_line(x + i, y, x + i, y + h, DISP_COLOR_BLACK); } for (int i = 0; i < h; i++) { v2x_display_line(x, y + i, x + w, y + i, DISP_COLOR_BLACK); } return ESP_OK; } /* RSSI-Balken: -100 bis -20 dBm → 0-100% */ esp_err_t v2x_display_rssi_gauge(int8_t rssi) { if (rssi < -100) rssi = -100; if (rssi > -20) rssi = -20; float pct = (float)(rssi + 100) / 80.0f; /* Farbe: rot → gelb → grün */ uint16_t color = DISP_COLOR_RED; if (pct > 0.6f) color = DISP_COLOR_GREEN; else if (pct > 0.3f) color = DISP_COLOR_YELLOW; return v2x_display_progress_bar(0, 0, DISP_W, 3, pct, color, DISP_COLOR_BLACK); } /* Frame-Info als Zeile rendern */ esp_err_t v2x_display_render_frame_line(int16_t x, int16_t y, const disp_frame_info_t *info, uint16_t bg_color) { /* Background */ v2x_display_rect(x, y, DISP_W, 12, bg_color, true); /* RSSI-Farbring links (5 Pixel breit) */ if (info->rssi != 0) { int8_t r = info->rssi; if (r < -100) r = -100; if (r > -20) r = -20; float pct = (float)(r + 100) / 80.0f; uint16_t bar_color = DISP_COLOR_RED; if (pct > 0.6f) bar_color = DISP_COLOR_GREEN; else if (pct > 0.3f) bar_color = DISP_COLOR_YELLOW; v2x_display_rect(x, y + 1, 5, 10, bar_color, true); } /* Message-Typ-Icon */ char msg_char = '?'; uint16_t msg_color = DISP_COLOR_GRAY; switch (info->msg_type) { case 1: msg_char = 'B'; msg_color = DISP_COLOR_GREEN; break; /* BSM */ case 2: msg_char = 'C'; msg_color = DISP_COLOR_CYAN; break; /* CAM */ case 3: msg_char = 'D'; msg_color = DISP_COLOR_RED; break; /* DENM */ } /* Zeichen: msg_char */ /* (Wird in Schritt 2: Font-Ausgabe implementiert) */ (void)msg_color; (void)x; (void)y; return ESP_OK; }