#ifndef __ILI9341_H__ #define __ILI9341_H__ #ifdef __cplusplus extern "C" { #endif #include "main.h" #include "spi.h" #include #include /* ============================================================ * ILI9341 TFT-LCD 驱动 (STM32H750 + SPI1 + DMA1_Stream2) * * 设计要点(详见 SystemDrivers/README_ILI9341.md): * 1. 硬件旋转:方向完全由 MADCTL 决定,软件不做转置, * LVGL 坐标与面板坐标 1:1 对应,从根本上消除移动物体 * 残留/拖影问题。 * 2. 全异步 DMA:ILI9341_FlushArea_DMA() 启动 DMA 后立刻返回, * 传输完成由 SPI 中断回调收尾并触发用户回调(=lv_disp_flush_ready)。 * 刷新任务不再阻塞,SPI/DMA 时序自同步,不依赖任何 RTOS 信号量。 * 3. DMA 源缓冲必须位于 DMA 可访问内存(AXI SRAM 0x24000000), * 绝不能放在 DTCM(0x20000000)——DMA1 无法访问 DTCM。 * LVGL 绘制缓冲用 LCD_RAM_ATTR 强制放入 .lcd_ram 段。 * 4. 看门狗 + 出错自恢复:任何一次传输异常都会被恢复, * 不会级联导致长时间白屏。 * ============================================================ */ /* ============================================================ * 诊断/调试开关 * ILI9341_USE_DMA : 1 = 用 DMA 搬运像素(+任务内同步等待) * 0 = 纯阻塞 SPI(完全不用 DMA,CPU 直推) * 两种方式共用同一入口 ILI9341_FlushArea_DMA(), * 切换后对比日志即可判断“错位/残留”是否与 DMA 相关。 * ILI9341_LOG_EN : 1 = 每次 flush 打印一行日志(走 UART4 Debug_Printf) * ILI9341_LOG_EVERY: 每多少次 flush 打印一次(1=每次;调大可减轻串口负担) * ============================================================ */ #ifndef ILI9341_USE_DMA #define ILI9341_USE_DMA 1 #endif #ifndef ILI9341_LOG_EN #define ILI9341_LOG_EN 1 #endif #ifndef ILI9341_LOG_EVERY #define ILI9341_LOG_EVERY 1U #endif /* 面板原生(未旋转)分辨率 */ #define ILI9341_NATIVE_WIDTH 240U #define ILI9341_NATIVE_HEIGHT 320U /* 当前工作方向下的分辨率(默认横屏 320x240,见 ILI9341_SetRotation) */ #define ILI9341_WIDTH 320U #define ILI9341_HEIGHT 240U /* ------------------------------------------------------------ * LCD 控制引脚(与 main.h / gpio.c 保持一致,PB12=CS 已被配置为输出) * ------------------------------------------------------------ */ #define LCD_CS_PORT GPIOB #define LCD_CS_PIN GPIO_PIN_12 #define LCD_DC_PORT GPIOB #define LCD_DC_PIN GPIO_PIN_7 #define LCD_RST_PORT GPIOB #define LCD_RST_PIN GPIO_PIN_8 #define LCD_BL_PORT GPIOB #define LCD_BL_PIN GPIO_PIN_9 /* RGB565 常用颜色(大端字节序由发送时处理) */ #define ILI9341_BLACK 0x0000 #define ILI9341_WHITE 0xFFFF #define ILI9341_RED 0xF800 #define ILI9341_GREEN 0x07E0 #define ILI9341_BLUE 0x001F #define ILI9341_YELLOW 0xFFE0 #define ILI9341_CYAN 0x07FF #define ILI9341_MAGENTA 0xF81F #define ILI9341_GRAY 0x8410 /* ------------------------------------------------------------ * DMA 可访问内存属性。 * 所有交给 ILI9341_FlushArea_DMA() 的缓冲区都必须用它修饰, * 链接脚本(motor_ili9341_lcd.sct)会把 .lcd_ram 段放到 AXI SRAM。 * 32 字节对齐是为了配合 D-Cache 的 clean-by-address 粒度。 * ------------------------------------------------------------ */ #if defined(__GNUC__) || defined(__clang__) #define LCD_RAM_ATTR __attribute__((section(".lcd_ram"), aligned(32))) #else #define LCD_RAM_ATTR #warning "未识别的工具链:请自行确保 LCD DMA 缓冲位于 DMA 可访问内存并 32 字节对齐" #endif /* 传输完成回调(在 SPI 中断上下文里被调用,务必只做极短的事) */ typedef void (*ILI9341_FlushDoneCb)(void *user_data); /* ---- 初始化 / 方向 ---- */ void ILI9341_Init(void); void ILI9341_SetRotation(uint8_t rotation); /* 0/1/2/3,默认 1=横屏 */ /* ---- 阻塞式基础绘图(仅用于初始化清屏/自检,非 LVGL 热路径) ---- */ void ILI9341_FillScreen(uint16_t color); void ILI9341_FillRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color); uint16_t ILI9341_Color565(uint8_t r, uint8_t g, uint8_t b); /* ---- LVGL 异步刷新入口 ---- * data 必须已按面板发送顺序排列(配合 lv_conf.h 的 LV_COLOR_16_SWAP=1, * LVGL 直接产出正确字节序,无需再拷贝/交换)。 * 函数启动 DMA 后立即返回;传输完成后在中断里调用 done_cb(user_data)。 * 若参数非法或启动失败,会同步调用 done_cb 以避免 LVGL 卡死。 */ void ILI9341_FlushArea_DMA(uint16_t x, uint16_t y, uint16_t w, uint16_t h, const uint16_t *data, ILI9341_FlushDoneCb done_cb, void *user_data); /* ---- 传输是否进行中(供上层判断) ---- */ uint8_t ILI9341_IsBusy(void); /* ---- 看门狗:从任务上下文周期性调用(例如 lv_timer_handler 之后)。 * 绝不能从中断里调用。若一次 DMA 传输超过 timeout_ms 仍未完成, * 强制中止、重建 SPI、恢复 CS 并触发完成回调,避免永久白屏。 * timeout_ms 建议 150~200ms。 */ void ILI9341_ServiceWatchdog(uint32_t timeout_ms); #ifdef __cplusplus } #endif #endif /* __ILI9341_H__ */