Display UI: ST7735 Treiber + Frame-Scroller + Speicheranzeige
- ST7735 SPI-Treiber für 0.96 Zoll Display (80x160 Pixel) - ASCII-Font 6x8 mit Text-Ausgabe - Frame-Counter (gross, rot) - SD-Speicher-Balken (grün/gelb/rot) - Frame-Scroller: rollende Liste der letzten 7 Frames - RSSI-Farbbalken (rot/gelb/grün) - Message-Type (B=BSM, C=CAM, D=DENM) - BSSID Hex + Kanal/RSSI - Display UI Task aktualisiert alle 2 Sekunden - Frame Writer sendet Frames an Scroller Queue - Channel Hopper aktualisiert g_v2x_channel_idx
This commit is contained in:
@@ -0,0 +1,258 @@
|
||||
/**
|
||||
* 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 <stdio.h>
|
||||
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user