在泰山派(RK3566)上给ST7789屏幕写SPI驱动,我踩过的那些设备树和DMA的坑

张开发
2026/4/17 23:57:24 15 分钟阅读

分享文章

在泰山派(RK3566)上给ST7789屏幕写SPI驱动,我踩过的那些设备树和DMA的坑
在RK3566平台为ST7789屏幕开发SPI驱动的实战避坑指南当一块ST7789 SPI屏幕遇上Rockchip RK3566芯片看似简单的驱动开发背后隐藏着无数细节陷阱。本文将带你深入设备树配置、DMA优化和SPI时序调校的实战现场还原从零搭建显示系统的完整思考路径。1. 设备树配置从寄存器映射到引脚复用的精确控制设备树作为硬件描述的核心载体其正确性直接决定驱动能否正常识别设备。在RK3566平台上SPI控制器的设备树节点继承自RK3568设计但需要特别注意以下几点1.1 SPI控制器基础配置解析Rockchip SPI控制器在设备树中的典型定义如下spi3: spife640000 { compatible rockchip,rk3066-spi; reg 0x0 0xfe640000 0x0 0x1000; interrupts GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH; clocks cru CLK_SPI3, cru PCLK_SPI3; clock-names spiclk, apb_pclk; dmas dmac0 26, dmac0 27; dma-names tx, rx; pinctrl-names default, high_speed; pinctrl-0 spi3m0_cs0 spi3m0_pins; pinctrl-1 spi3m0_cs0 spi3m0_pins_hs; num-cs 2; status disabled; };关键参数说明reg物理地址映射范围必须与芯片手册完全一致clock-names主时钟和APB总线时钟的命名约定不可更改dmasDMA通道分配需要核对芯片资源占用情况1.2 外设节点定义常见陷阱为ST7789添加子节点时开发者常犯的三个典型错误spi3 { status okay; pinctrl-names default; pinctrl-0 spi3m1_cs0 spi3m1_pins; spi_lcd0 { compatible sitronix,st7789v; reg 0; // CS0线选择 spi-max-frequency 32000000; // 必须明确指定的参数 dc-gpios gpio3 RK_PC1 GPIO_ACTIVE_HIGH; reset-gpios gpio3 RK_PC0 GPIO_ACTIVE_LOW; // 显示方向配置 rotation 90; }; };注意ST7789的dc-gpios数据/命令选择引脚必须正确定义否则无法正常发送命令和数据。2. SPI通信协议层的精细调校2.1 时钟极性与时序匹配ST7789对SPI模式有严格时序要求设备树中需明确配置spi_lcd0 { spi-cpol; // 时钟极性 spi-cpha; // 时钟相位 spi-3wire; // 3线模式节省IO };实测发现当时钟频率超过40MHz时需要启用Rockchip特有的高速模式pinctrl-1 spi3m1_cs0 spi3m1_pins_hs;2.2 数据传输优化技巧通过对比不同传输方式的效率传输方式240x240帧率CPU占用率单字节传输8 FPS95%DMA传输32 FPS15%打包传输(16KB)45 FPS20%实现高效传输的核心代码static void st7789_bulk_write(uint8_t *buf, size_t len) { struct spi_transfer xfer { .tx_buf buf, .len len, .bits_per_word 8, }; spi_sync_transfer(spi_dev, xfer, 1); }3. DMA配置的深水区3.1 内存对齐与缓存一致性DMA传输必须保证缓冲区物理地址对齐#define DMA_ALIGN 32 uint8_t *fb_buf kmalloc(BUF_SIZE, GFP_DMA | GFP_KERNEL); if (!fb_buf) { fb_buf dma_alloc_coherent(spi_dev-dev, BUF_SIZE, dma_handle, GFP_KERNEL); }3.2 中断风暴预防当DMA传输完成中断过于频繁时需要调整阈值dmac0 { rockchip,burst-length 4; rockchip,burst-threshold 0; };4. 与LVGL框架的协同优化4.1 双缓冲机制实现static struct { uint16_t *front_buf; uint16_t *back_buf; struct mutex lock; } display; void lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color) { mutex_lock(display.lock); memcpy(display.back_buf, color, area-x2 * area-y2 * 2); schedule_work(flush_work); mutex_unlock(display.lock); }4.2 垂直同步信号模拟通过GPIO模拟VSYNC可显著减少撕裂static void vsync_thread(void *data) { while (!kthread_should_stop()) { gpio_set_value(vsync_gpio, 1); udelay(1); gpio_set_value(vsync_gpio, 0); msleep(16); // 60Hz刷新率 } }5. 调试技巧与性能分析5.1 逻辑分析仪抓包要点配置示波器触发条件SPI时钟边沿触发CS下降沿作为触发源捕获深度至少4K samples5.2 内核trace工具链echo 1 /sys/kernel/debug/tracing/events/spi/enable cat /sys/kernel/debug/tracing/trace_pipe典型问题诊断流程检查SPI时钟是否正常输出验证CS信号切换时机确认DC线电平变化符合协议测量数据传输实际速率在完成所有调试后最终实现的显示系统可以达到60FPS全屏刷新低于5ms的输入延迟内存占用控制在8MB以内

更多文章