#include "Drv_ILI9341_Lcd_Dump.h" #if DEBUG_LCD_DUMP #include "SEGGER_RTT.h" #define LCD_DUMP_RTT_UP_CH 1 #define LCD_DUMP_RTT_DOWN_CH 0 #define LCD_DUMP_CMD "DUMP" #define LCD_DUMP_TAP_CMD "TAP" #define LCD_DUMP_RESET_CMD "sys reset" #define LCD_DUMP_MAGIC "SCRN" #define LCD_DUMP_FMT_RGB565 1 #define LCD_DUMP_CMD_BUF_SIZE 32 #pragma pack(1) typedef struct { char magic[4]; uint16_t width; uint16_t height; uint16_t format; uint16_t reserved; } lcd_dump_header_t; #pragma pack() static uint8_t s_dump_busy; static uint8_t s_rtt_ready; static char s_rtt_up_buf[1024]; static char s_cmd_buf[LCD_DUMP_CMD_BUF_SIZE]; static uint8_t s_cmd_len; static uint8_t s_row565[LCD_W * 2U]; static struct rt_thread s_dump_thread; ALIGN(RT_ALIGN_SIZE) static rt_uint8_t s_dump_stack[1024]; static uint8_t LCD_SPI_Transceive(uint8_t tx) { uint32_t timeout = 200000U; while (!(SPI2->sts & SPI_I2S_TDBE_FLAG) && --timeout) {} spi_i2s_data_transmit(SPI2, tx); timeout = 200000U; while (!(SPI2->sts & SPI_I2S_RDBF_FLAG) && --timeout) {} if (timeout == 0U) { return 0xFF; } return (uint8_t)spi_i2s_data_receive(SPI2); } static void LCD_SPI_FlushRx(void) { while (SPI2->sts & SPI_I2S_RDBF_FLAG) { (void)spi_i2s_data_receive(SPI2); } } static void LCD_SPI_EnableMisoMux(void) { gpio_init_type gpio_init_struct; gpio_default_para_init(&gpio_init_struct); gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_MODERATE; gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; gpio_init_struct.gpio_mode = GPIO_MODE_MUX; gpio_init_struct.gpio_pins = GPIO_PINS_14; gpio_init_struct.gpio_pull = GPIO_PULL_NONE; gpio_init(GPIOB, &gpio_init_struct); } static void LCD_SPI_CsGpioLow(void) { gpio_init_type gpio_init_struct; gpio_default_para_init(&gpio_init_struct); gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_MODERATE; gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT; gpio_init_struct.gpio_pins = GPIO_PINS_12; gpio_init_struct.gpio_pull = GPIO_PULL_NONE; gpio_init(GPIOB, &gpio_init_struct); gpio_bits_reset(GPIOB, GPIO_PINS_12); } static void LCD_SPI_CsHwRestore(void) { gpio_init_type gpio_init_struct; gpio_bits_set(GPIOB, GPIO_PINS_12); gpio_default_para_init(&gpio_init_struct); gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_MODERATE; gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; gpio_init_struct.gpio_mode = GPIO_MODE_MUX; gpio_init_struct.gpio_pins = GPIO_PINS_12; gpio_init_struct.gpio_pull = GPIO_PULL_NONE; gpio_init(GPIOB, &gpio_init_struct); } static void LCD_SPI_SetBaud(spi_mclk_freq_div_type div) { spi_enable(SPI2, FALSE); SPI2->ctrl1 &= ~(0x7U << 3); /* Standard div 2..256 live in CTRL1[5:3]; AT32 maps enum 0..7 there. */ SPI2->ctrl1 |= (uint16_t)(((uint16_t)div & 0x7U) << 3); spi_enable(SPI2, TRUE); } static uint16_t LCD_RGB888To565(uint8_t r, uint8_t g, uint8_t b) { return (uint16_t)(((uint16_t)(r & 0xF8) << 8) | ((uint16_t)(g & 0xFC) << 3) | (b >> 3)); } static void LCD_RTT_WriteBlocking(const void *data, uint32_t len) { const uint8_t *p = (const uint8_t *)data; uint32_t sent = 0U; uint32_t spin = 0U; while (sent < len) { unsigned wrote = SEGGER_RTT_Write(LCD_DUMP_RTT_UP_CH, p + sent, len - sent); if (wrote == 0U) { if ((++spin & 0x3FU) == 0U) { rt_thread_mdelay(1); } continue; } spin = 0U; sent += wrote; } } /* Soft-CS byte helpers (CS already held low by caller). */ static void LCD_Soft_WR_REG(uint8_t reg) { LCD_DC_Clr(); (void)LCD_SPI_Transceive(reg); } static void LCD_Soft_WR_DATA16(uint16_t data) { LCD_DC_Set(); (void)LCD_SPI_Transceive((uint8_t)(data >> 8)); (void)LCD_SPI_Transceive((uint8_t)(data & 0xFF)); } /* Set CASET/RASET only — do NOT send 0x2C (memory write). */ static void LCD_Soft_SetAddr(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) { LCD_Soft_WR_REG(0x2A); LCD_Soft_WR_DATA16(x1); LCD_Soft_WR_DATA16(x2); LCD_Soft_WR_REG(0x2B); LCD_Soft_WR_DATA16(y1); LCD_Soft_WR_DATA16(y2); } static void LCD_ReadFrameRGB565(void) { uint16_t x, y; register rt_base_t level; LCD_SPI_FlushRx(); LCD_Soft_SetAddr(0, 0, (uint16_t)(LCD_W - 1), (uint16_t)(LCD_H - 1)); LCD_Soft_WR_REG(0x2E); LCD_DC_Set(); (void)LCD_SPI_Transceive(0xFF); /* dummy */ for (y = 0; y < LCD_H; y++) { level = rt_hw_interrupt_disable(); for (x = 0; x < LCD_W; x++) { uint8_t r = LCD_SPI_Transceive(0xFF); uint8_t g = LCD_SPI_Transceive(0xFF); uint8_t b = LCD_SPI_Transceive(0xFF); uint16_t color = LCD_RGB888To565(r, g, b); s_row565[(x * 2U)] = (uint8_t)(color >> 8); s_row565[(x * 2U) + 1] = (uint8_t)(color & 0xFF); } rt_hw_interrupt_enable(level); LCD_RTT_WriteBlocking(s_row565, (uint32_t)(LCD_W * 2U)); } } static uint8_t LCD_Dump_CommandPending(void) { char rx[8]; unsigned read_len; unsigned i; /* 烧录外部 UI 资源时占用 RTT0 down,禁止此处吞字节 */ if (app_log_flash_busy()) { return 0U; } read_len = SEGGER_RTT_Read(LCD_DUMP_RTT_DOWN_CH, rx, sizeof(rx)); if (read_len == 0U) { return 0U; } for (i = 0; i < read_len; i++) { char c = rx[i]; if (c == '\r' || c == '\n' || c == '\0') { if (s_cmd_len > 0U) { s_cmd_buf[s_cmd_len] = '\0'; if (app_log_try_command(s_cmd_buf)) { s_cmd_len = 0U; s_cmd_buf[0] = '\0'; continue; } } s_cmd_len = 0U; s_cmd_buf[0] = '\0'; continue; } if (s_cmd_len + 1U >= LCD_DUMP_CMD_BUF_SIZE) { s_cmd_len = 0U; } s_cmd_buf[s_cmd_len++] = c; s_cmd_buf[s_cmd_len] = '\0'; /* 合成点击:验证 UI 命中路径(点万能按钮中心) */ if (strstr(s_cmd_buf, LCD_DUMP_TAP_CMD) != NULL) { s_cmd_len = 0U; s_cmd_buf[0] = '\0'; MainTask_Sendmsg(MSG_ID_TOUCH, 1, 120, 80); SEGGER_RTT_WriteString(0, "TAP inject 120,80\n"); continue; } if (strstr(s_cmd_buf, LCD_DUMP_RESET_CMD) != NULL) { s_cmd_len = 0U; s_cmd_buf[0] = '\0'; (void)app_log_try_command(LCD_DUMP_RESET_CMD); continue; } if (c == 0x01 || strstr(s_cmd_buf, LCD_DUMP_CMD) != NULL) { s_cmd_len = 0U; s_cmd_buf[0] = '\0'; return 1U; } } return 0U; } static void LCD_Dump_ThreadEntry(void *parameter) { (void)parameter; SEGGER_RTT_WriteString(0, "LCDDump ready\n"); while (1) { LCD_Dump_Poll(); rt_thread_mdelay(5); } } void LCD_Dump_Init(void) { if (s_rtt_ready) { return; } SEGGER_RTT_ConfigUpBuffer(LCD_DUMP_RTT_UP_CH, "LCDDump", s_rtt_up_buf, (uint32_t)sizeof(s_rtt_up_buf), SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL); s_rtt_ready = 1; } void LCD_Dump_StartPoller(void) { static uint8_t started; if (!s_rtt_ready) { LCD_Dump_Init(); } if (started) { return; } started = 1; rt_thread_init(&s_dump_thread, "lcd_dump", LCD_Dump_ThreadEntry, RT_NULL, s_dump_stack, sizeof(s_dump_stack), 8, 10); rt_thread_startup(&s_dump_thread); } void LCD_DumpScreen_RTT(void) { lcd_dump_header_t header; if (!s_rtt_ready) { LCD_Dump_Init(); } SEGGER_RTT_WriteString(0, "LCDDump start\n"); LCD_SPI_EnableMisoMux(); header.magic[0] = LCD_DUMP_MAGIC[0]; header.magic[1] = LCD_DUMP_MAGIC[1]; header.magic[2] = LCD_DUMP_MAGIC[2]; header.magic[3] = LCD_DUMP_MAGIC[3]; header.width = LCD_W; header.height = LCD_H; header.format = LCD_DUMP_FMT_RGB565; header.reserved = 0; LCD_RTT_WriteBlocking(&header, (uint32_t)sizeof(header)); /* Slow clock; hold CS for one full-frame RAMRD burst. */ LCD_SPI_SetBaud(SPI_MCLK_DIV_64); LCD_SPI_CsGpioLow(); LCD_ReadFrameRGB565(); LCD_SPI_CsHwRestore(); LCD_SPI_SetBaud(SPI_MCLK_DIV_4); SEGGER_RTT_WriteString(LCD_DUMP_RTT_UP_CH, "DONE"); SEGGER_RTT_WriteString(0, "LCDDump done\n"); } void LCD_Dump_Poll(void) { if (s_dump_busy) { return; } if (!LCD_Dump_CommandPending()) { return; } s_dump_busy = 1; LCD_DumpScreen_RTT(); s_dump_busy = 0; } #endif /* DEBUG_LCD_DUMP */