317 lines
7.9 KiB
C
317 lines
7.9 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_MAGIC "SCRN"
|
|
#define LCD_DUMP_FMT_RGB565 1
|
|
#define LCD_DUMP_CMD_BUF_SIZE 16
|
|
|
|
#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;
|
|
|
|
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') {
|
|
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 || 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 */
|