364 lines
9.8 KiB
C
364 lines
9.8 KiB
C
#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;
|
||
}
|
||
/* TAP [x y]:缺省 120,80;换行后解析,避免坐标未收齐就触发 */
|
||
if (strncmp(s_cmd_buf, LCD_DUMP_TAP_CMD, 3) == 0) {
|
||
unsigned tx = 120U;
|
||
unsigned ty = 80U;
|
||
if (s_cmd_buf[3] == ' ' || s_cmd_buf[3] == '\t') {
|
||
char *p1 = s_cmd_buf + 4;
|
||
char *p2;
|
||
tx = (unsigned)strtoul(p1, &p2, 10);
|
||
if (p2 != p1) {
|
||
while (*p2 == ' ' || *p2 == '\t') p2++;
|
||
ty = (unsigned)strtoul(p2, NULL, 10);
|
||
}
|
||
}
|
||
if (tx > 239U) tx = 239U;
|
||
if (ty > 319U) ty = 319U;
|
||
MainTask_Sendmsg(MSG_ID_TOUCH, 1, (uint16_t)tx, (uint16_t)ty);
|
||
SEGGER_RTT_WriteString(0, "TAP inject\n");
|
||
s_cmd_len = 0U;
|
||
s_cmd_buf[0] = '\0';
|
||
continue;
|
||
}
|
||
if (strcmp(s_cmd_buf, LCD_DUMP_RESET_CMD) == 0) {
|
||
(void)app_log_try_command(LCD_DUMP_RESET_CMD);
|
||
s_cmd_len = 0U;
|
||
s_cmd_buf[0] = '\0';
|
||
continue;
|
||
}
|
||
if (strcmp(s_cmd_buf, LCD_DUMP_CMD) == 0) {
|
||
s_cmd_len = 0U;
|
||
s_cmd_buf[0] = '\0';
|
||
return 1U;
|
||
}
|
||
}
|
||
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';
|
||
|
||
if (c == 0x01) {
|
||
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 */
|