ILI9341: 刷新改为 DMA+任务内同步等待,消除移动残留竞态

- 中断只置完成标志,等待/收尾/回调全部移到任务上下文
- 去掉 ISR 内 lv_disp_flush_ready、ISR 内 Abort、双重收尾等竞态
- flush 返回时该块像素必已完整写入,LVGL 不会提前复用缓冲
- 拉高 CS 前等待 SPI 移位排空,避免尾部像素被截断
- 传输 50ms 超时后重建 SPI 自恢复
This commit is contained in:
zhy200308
2026-07-08 11:16:57 +08:00
parent e4d61b19f0
commit 1273a14bf2
2 changed files with 76 additions and 73 deletions
+11 -9
View File
@@ -12,8 +12,8 @@ STM32H750 · SPI1 · DMA1_Stream2 · LVGL v8
| 旧版问题 | 根因 | 新版做法 |
|---|---|---|
| 移动方块留下残影/线条 | `disp_flush()` 里做软件 90° 转置,坐标/尺寸与 LVGL 失效区域不严格一致,且多一层 `rot_buf→dma_buf` 拷贝 | **硬件旋转**:面板 MADCTL 直接设横屏,LVGL 坐标 = 面板坐标,刷新区与失效区严格 1:1,转置整段删除 |
| 长时间运行白屏 | ① DMA 源缓冲可能被链接器放进 **DTCM**,而 DMA1 无法访问 DTCM;② 阻塞式信号量 + 出错 `Abort`,SPI 状态一乱就级联白屏 | ① 缓冲用 `.lcd_ram` 段**强制放入 AXI SRAM**;② 全异步 DMA,去掉信号量;出错回调 + 看门狗自恢复,单次异常不级联 |
| 刷新与 CPU 抢时间 | flush_cb 阻塞等待传输完成 | flush_cb 启动 DMA 后**立即返回**,双缓冲边渲染边传,完成中断里 `lv_disp_flush_ready()` |
| 长时间运行白屏 | ① DMA 源缓冲可能被链接器放进 **DTCM**,而 DMA1 无法访问 DTCM;② 阻塞式信号量 + 出错 `Abort`,SPI 状态一乱就级联白屏 | ① 缓冲用 `.lcd_ram` 段**强制放入 AXI SRAM**;② 传输超时后重建 SPI 自恢复,单次异常不级联 |
| 刷新时序/竞态 | 中断里 `lv_disp_flush_ready`、ISR 内 `Abort`、完成被收尾两次等竞态,会丢失“擦除”写入 → 移动物体残留 | **DMA 加速 + 任务内同步等待**:中断只置完成标志,等待/收尾/回调全在任务里做;flush 返回时该块像素一定已完整写入,杜绝提前复用缓冲/擦除未写完 |
| 字节序靠 CPU 逐像素交换 | `LV_COLOR_16_SWAP=0`,驱动里手工打包 | `LV_COLOR_16_SWAP=1`,LVGL 直接产出面板字节序,DMA 直接搬运 |
> 备注:旧 `ILI9341.h` 里关于 “CS=PB12 未配置为输出” 的警告是**过时的**——
@@ -26,15 +26,17 @@ STM32H750 · SPI1 · DMA1_Stream2 · LVGL v8
```
LVGL 渲染 → buf_1/buf_2 (.lcd_ram, AXI SRAM, 32B 对齐)
→ disp_flush() : 裁剪坐标,直接调 ILI9341_FlushArea_DMA()
→ FlushArea_DMA : 设窗口(2A/2B/2C) → clean D-Cache → CS 低 → 启动 SPI DMA,立即返回
→ [DMA 完成] SPI1 中断 → HAL_SPI_TxCpltCallback → CS 高 → lv_disp_flush_ready()
→ [出错] HAL_SPI_ErrorCallback → Abort → 收尾 → flush_ready(不卡死)
→ [兜底] 任务里 ILI9341_ServiceWatchdog(180) 超时中止+重建 SPI
→ FlushArea_DMA : 设窗口(2A/2B/2C) → clean D-Cache → CS 低 → 启动 SPI DMA
→ 任务内轮询等待 lcd_xfer_done1~2ms50ms 超时兜底)
→ 等 SPI 移位排空 → CS 高 → done_cb(=lv_disp_flush_ready)
→ [DMA 完成] SPI1 中断 → HAL_SPI_TxCpltCallback → 仅置 lcd_xfer_done=1
→ [出错] HAL_SPI_ErrorCallback → 记录错误 + 置 lcd_xfer_done=1,任务侧恢复
→ [兜底] 超时(或 ServiceWatchdog) → Abort + 重建 SPI
```
关键不变量:任意时刻**至多一次 DMA 在途**(LVGL 在 `flush_ready` 前不会再次
调用 `flush_cb`)。收尾函数 `LCD_FinishTransfer()` 在关中断临界区内“取回调+复位
状态”,保证 `done_cb` 只被调用一次,杜绝 ISR 与看门狗竞争
关键不变量:`flush_cb` **返回时该块像素一定已完整写入面板**LVGL 不会提前复用
缓冲,“擦除”写入不会丢失。中断只做“置标志”一件事,不在中断里调用 LVGL、不在
中断里 `Abort`,从根上消除竞态
---