ST77903+STM32H743 QSPI驱动实战:RT-Thread与LVGL移植全记录

发布时间:2026/10/16 3:39:13
ST77903+STM32H743 QSPI驱动实战:RT-Thread与LVGL移植全记录 简介面向穿戴设备显示应用开发者提供基于STM32H743与RT-Thread、LVGL的ST77903驱动参考DEMO。该LCD驱动IC采用QSPI接口且内部不带RAM需主机端连续传输例程通过专用刷屏线程保障显示刷新适合刚接触QSPI屏或RT-Thread多线程显示任务的工程师。7z压缩包共2591个文件、13.66MB以C/H源码、汇编、Sconscript构建脚本、ICF链接配置和TXT说明为主包含LVGL基础工程与完整RT-Thread组件另有部分PNG/GIF表盘资源及PDF文档。目前已有2585人学习三个手表表盘UI可直接迁移参考能帮助理解无显存QSPI屏的刷新机制、RT-Thread线程调度与LVGL对接流程缩短项目原型开发时间。1. ST77903的板子不难找难的是QSPI和LVGL之间那层窗户纸把 ST77903、STM32H743、RT-Thread、LVGL、QSPI 这几个词放在一起搜出来的结果大多是开发板商品页和屏厂 DEMO 截图真正能照着跑通的笔记反而少。ST77903 是一颗非常常见的小尺寸 LCD 驱动 IC很多圆屏、方屏模组都在用接口通常走 QSPI 或 MIPI DSI而 H743 又是自带 QUADSPI 外设、内存带宽也够用的主控所以这对组合成了不少人做彩屏交互的第一选择。可现实是板子和屏连接起来简单点亮却是另一回事QSPI 初始化序列里一个寄存器写错就是白屏LVGL 的 flush 回调没等传输完成就返回就是花屏RT-Thread 的 BSP 版本和 LVGL 包版本不对付编译都过不去。这篇文章就是记录我从拿到模组到跑通 DEMO 的完整过程适合手头正好卡在 ST77903 调试上的开发者也适合打算用 H743 做彩屏项目、想先看别人踩过哪些坑的人。2. 环境选型H743、RT-Thread、LVGL 的版本别乱搭2.1 版本清单先对齐再动手ST77903 的 DEMO 工程看着是一个整体实际上由四层东西拼起来主控驱动库、RTOS、图形库、屏驱动代码。这四层的版本只要有一层和另外三层不匹配工程就处在一种编译能过但点不亮或者编译直接报错的状态里。我这次用的组合是经过几轮调整才稳定下来的先列出来给你对照。表格里写的是我实际跑通的版本线不是官方强制要求。H743 的 HAL 库版本尽量新一些因为 ST77903 的 QSPI 时序对库里的 QUADSPI 驱动有依赖太老的 HAL 库在间接模式下的超时处理写得不够干脆。组件版本选择说明主控STM32H743VIT6480MHz 主频2MB Flash1MB RAMHAL 库1.11.0 及以上H7 系列的 QUADSPI 驱动更新比较频繁RT-Thread4.1.1 或 5.0.x4.1.1 最稳5.0.x 的 BSP 结构有调整LVGL8.3.x8.2 到 8.3 之间 API 变动小DEMO 兼容性最好ST77903 驱动模组厂商提供的初始化序列每一批模组的 gamma 参数可能不同LVGL 版本是这里面最容易被忽略的。有的开发者习惯直接拉最新版的 LVGL 9.x结果发现 DEMO 里用的lv_disp_drv_t结构体成员名变了lv_disp_flush_ready的调用方式也改了整个移植工作从调屏变成了调 API。RT-Thread 的软件包仓库里带的 LVGL 包版本号以你拉取当天的为准我建议在menuconfig里手动锁定 8.3.x不要用 latest。2.2 两个通行方案RT-Thread Studio 和传统 MDKRT-Thread 工程的打开方式有两种各有各的适用场景。第一种是用 RT-Thread Studio它自带 BSP 向导和软件包管理menuconfig配置完直接生成工程LVGL 包也能在图形界面里勾选对新手最友好。第二种是传统的 Keil MDK 工程适合你手里的 DEMO 本身就是 MDK 格式或者团队协作时大家都用 MDK。我这次是先用的 RT-Thread Studio 导入后来因为调试 QSPI 时序需要频繁改寄存器又切回 MDK。切的过程中发现一个关键点RT-Thread Studio 生成的工程里board.c的时钟树配置和 MDK 工程里的system_clock_config()是两套逻辑切换后必须重新检查SystemClock_Config()里的 PLL 配置否则 QUADSPI 的时钟源不对屏的刷新率会变得很怪。# RT-Thread Studio 导入已有 BSP 后强制重新生成工程文件 # 在 BSP 目录下执行 scons --targetmdk5 -s scons --targeteclipse -s上面两条命令的作用是分别生成 MDK5 和 Eclipse 格式的工程文件。如果你拿到的是别人打包好的 DEMO里面大概率只带着一种工程格式用scons --target重新生成可以省去手动添加源码组的功夫。执行完后检查project.uvprojx或.cproject的生成时间确认覆盖成功。2.3 板级配置先从 board.h 里找屏的引脚H743 的引脚功能复用很灵活同一组 QUADSPI 信号可以映射到好几组引脚上。DEMO 工程里的board.h或lcd_port.h一般会定义一个引脚宏块这部分是移植最早要改的地方。/* 以某款模组的引脚定义为例实际以你的板子原理图为准 */ #define LCD_CS_PIN GPIO_PIN_10 #define LCD_CS_GPIO_PORT GPIOI #define LCD_SCK_PIN GPIO_PIN_6 #define LCD_SCK_GPIO_PORT GPIOB #define LCD_IO0_PIN GPIO_PIN_2 #define LCD_IO0_GPIO_PORT GPIOE这段代码在做什么把 QUADSPI 的四根数据线对应的 GPIO 引脚定义集中起来。不同开发板上的引脚差异很大有的板子把 CS 接在 PI10有的接在 PB6你在移植时不能直接照抄 DEMO 里的定义必须打开自己板子的原理图逐个核对。核对的标准很简单把芯片手册里的引脚复用表拉出来确认这几个引脚在 AF9QUADSPI 复用功能下能正常工作。3. 拿到 DEMO 先读工程QSPI 驱动的骨架长这样3.1 目录里先翻这四个文件一份 ST77903 的 DEMO 工程代码量看着不多但文件堆放位置没有统一标准经常是驱动文件、应用文件、配置文件混在一起。我建议拿到工程后不要急着编译先按功能把下面四类文件找出来这能省掉后面一多半的排查时间。文件类型常见文件名作用屏驱动初始化st77903.c / lcd_init.c存放初始化序列数组和时序参数QSPI 底层驱动qspi_if.c / bsp_qspi.c封装 QUADSPI 外设的读写操作LVGL 桥接lv_port_disp.c / disp_flush.c实现disp_flush回调板级引脚配置board.c / lcd_port.h定义引脚、时钟和背光控制我一般会先用全局搜索找0x11和0x29这两个值。0x11是 LCD 驱动的 Sleep Out 命令0x29是 Display On 命令它们一定出现在初始化序列数组里。找到这个数组就找到了屏驱动文件整个 DEMO 的技术核心在那一刻就暴露了。剩下的 QSPI 底层和 LVGL 桥接基本是围绕这个数组在转。3.2 QSPI 初始化入口从哪里开始执行H743 上跑 RT-Thread代码的执行顺序是复位向量 →SystemInit→entry→rtthread_startup→ 板级初始化 → 应用初始化。屏驱动挂在哪个阶段取决于 DEMO 作者的工程习惯。常见的有两种一种是在rt_hw_board_init里直接调qspi_init让屏最早亮起来另一种是在main函数里通过INIT_BOARD_EXPORT或INIT_APP_EXPORT宏挂载。/* RT-Thread 自动初始化机制放在 main.c 或 app.c 中 */ static int rt_hw_lcd_init(void) { qspi_lcd_init(); /* 完成 QUADSPI 外设和 GPIO 配置 */ st77903_init(); /* 发送初始化序列给屏 */ lv_init(); /* LVGL 只有当屏能亮之后才初始化 */ return 0; } INIT_APP_EXPORT(rt_hw_lcd_init);INIT_APP_EXPORT是 RT-Thread 的自动初始化宏被它修饰的函数会在系统启动后的应用初始化阶段自动执行不需要你在main里显式调用。这么做的好处是模块之间解耦坏处是排错时不容易找到入口。如果你发现屏没亮先在qspi_lcd_init()第一行加个打印确认为什么没有执行到。3.3 ST77903 初始化序列一长串数组怎么读打开st77903.c你会看到一段结构体数组这就是整块屏的黑匣子开关。数组每个元素包含命令号、参数个数和参数内容。下面这个片段是典型的写法实际值以你的模组规格书为准。const lcd_cmd_t st77903_init_sequence[] { {0x11, 0, NULL}, /* Sleep Out退出睡眠 */ {0x36, 1, (uint8_t*)\x48}, /* MADCTL控制扫描方向 */ {0x3A, 1, (uint8_t*)\x55}, /* 像素格式设为 RGB565 */ {0xB0, 2, (uint8_t*)\x00\x00}, /* 厂商保留命令模组差异点 */ {0x29, 0, NULL}, /* Display On开显示 */ };这里的关键不是逐条背命令而是理解三个区间。0x11之前是软复位和电源设置0x11到0x29之间是扫描方向、像素格式、gamma 校正这一类影响画质的参数0x29之后是亮度、局部显示这类运行期命令。排错时先确认0x29是否被发送屏只要收到了 Display On至少能亮起来。4. 配置 QSPI 和 ST77903时钟分频、DC 线和 LVGL 桥接4.1 引脚和时钟先把这张表对齐QUADSPI 在 H743 上有两组可选引脚不同板子用的映射不一样。我把 DEMO 里的默认映射和常见替代映射整理成一张表移植时的第一优先级就是核对这张表。信号名DEMO 默认引脚常见替代引脚备注QUADSPI_CLKPB6PB2时钟线频率由分频系数决定QUADSPI_BK1_CSPI10PB10片选低电平有效QUADSPI_BK1_IO0PE2PB1数据线 0QUADSPI_BK1_IO1PE3PD7数据线 1QUADSPI_BK1_IO2PE4PD6数据线 2QUADSPI_BK1_IO3PE5PD5数据线 3时钟配置是这里最容易翻车的地方。ST77903 的 QSPI 模式对时钟频率有上限要求模组规格书里一般会写一个典型值。我手头这块模组在 40MHz 左右跑得很稳超过之后偶发花屏。在代码里调节的方式是通过HAL_RCC_ExGetClockConfig查看当前 QSPI 时钟源再调整分频系数。/* 设置 QUADSPI 时钟分频系数越大频率越低 */ hqspi.Init.ClockPrescaler 6; /* 由 QSPI 时钟源频率除以 (ClockPrescaler 1) */ hqspi.Init.FifoThreshold 32; /* FIFO 阈值影响传输效率 */ hqspi.Init.SampleShifting QSPI_SAMPLE_SHIFTING_HALFCLK; /* 数据采样点 */ClockPrescaler的计算逻辑是实际 QSPI 时钟等于外设时钟源除以分频系数加一。H743 的 QUADSPI 时钟源一般来自 AHB 域如果你不确定当前值不要在代码里猜直接在调试器里看RCC寄存器的值。SampleShifting这个参数值得多说一句它控制数据采样点相对时钟沿的位置花屏时把它从HALFCLK改成QUARTERCLK往往有奇效这是我在调另一块 SPI 屏时学到的招。4.2 QUADSPI 的两种模式间接模式发命令内存映射刷帧ST77903 走 QSPI 时命令阶段其实只用单线 SPI 协议数据阶段才切到四线。H743 的 QUADSPI 外设天然支持这种用法关键是要配置好指令格式和地址格式。/* 发送单个命令字的配置 */ sCommand.InstructionMode QSPI_INSTRUCTION_1_LINE; /* 命令按单线发送 */ sCommand.AddressMode QSPI_ADDRESS_NONE; /* 小屏无需地址 */ sCommand.DataMode QSPI_DATA_1_LINE; /* 参数按单线发送 */ sCommand.DummyCycles 0; sCommand.NbData len; sCommand.Instruction cmd; HAL_QSPI_Command(hqspi, sCommand, HAL_QSPI_TIMEOUT_DEFAULT_VALUE);这段配置的含义是向屏发送一个带参数的命令命令和参数都按单线 SPI 协议走。之所以要区分单线和四线是因为 ST77903 的指令集本身是按标准 SPI 定义的命令阶段如果直接用四线发送屏会当作乱码丢弃。等到后面刷图像数据时再把DataMode改成QSPI_DATA_4_LINES传输效率直接翻四倍。/* 刷一帧图像数据四线输出模式 */ sCommand.InstructionMode QSPI_INSTRUCTION_1_LINE; sCommand.AddressMode QSPI_ADDRESS_1_LINE; sCommand.AddressSize QSPI_ADDRESS_16_BITS; /* 16 位地址对应屏的显存窗口 */ sCommand.Address start_pixel_addr; /* 像素起始地址 */ sCommand.DataMode QSPI_DATA_4_LINES; /* 数据走四线 */ sCommand.DummyCycles 2; /* 地址和数据之间的等待周期 */ HAL_QSPI_Command(hqspi, sCommand, HAL_QSPI_TIMEOUT_DEFAULT_VALUE); HAL_QSPI_Transmit(hqspi, pixel_data, buffer_size, HAL_QSPI_TIMEOUT_DEFAULT_VALUE);DummyCycles是四线数据模式下必须调对的参数不同厂家的模组要求不一样一般是 0 到 4 之间的某个偶数。调这个参数没有捷径只能对照模组手册的时序图或者用示波器看 MISO 上的数据。我一般先从 0 开始试花屏就把值往上加。4.3 LVGL 对接flush 回调和你绕不开的等待LVGL 要显示内容靠的是底层disp_flush回调把显存里的像素数据送到屏上。这个回调写得好不好直接决定画面是否撕裂、掉帧。/* LVGL 显示回调header 里是当前要刷的像素区域 */ static void disp_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) { uint32_t addr area-x1 area-y1 * lcd_width; qspi_lcd_set_window(area-x1, area-y1, area-x2, area-y2); qspi_lcd_send_pixels(addr, (uint8_t *)color_p, (area-x2 - area-x1 1) * (area-y2 - area-y1 1) * 2); /* 必须在传输完成后再通知 LVGL */ while (HAL_QSPI_GetState(hqspi) ! HAL_QSPI_STATE_READY) { } lv_disp_flush_ready(disp_drv); }lv_disp_flush_ready这个函数特别关键。LVGL 的规则是你不调用它它就不会往这个区域继续输出内容所以很多人第一版代码忘了加结果是界面卡在启动画面一动不动。另外要注意调用完HAL_QSPI_Transmit后外设可能还在忙直接返回会导致上一帧没传完下一帧就开始写出现水平撕裂线。我这里的 while 循环等待是一种简单粗暴的保险更高效的做法是开 DMA 传输完成中断在中断回调里调lv_disp_flush_ready。5. 常见问题和排查白屏、花屏、卡死三板斧5.1 白屏先分清是 QSPI 没通还是 LVGL 没刷现象背光亮了但整个屏幕是均匀的白色或透明色没有任何内容。最常见的原因是0x29Display On没有被执行到或者执行前屏还在睡眠状态。做法是分两步排查。第一步在初始化序列数组后面加一个打印确认整个数组都被遍历完毕并且最后一次 HAL 状态是HAL_QSPI_STATE_READY。第二步用调试器在disp_flush里打断点看这个回调到底有没有被 LVGL 调用。如果回调没触发问题出在上层检查 LVGL 有没有lv_init定时器有没有跑。如果回调触发了但屏还是白屏问题出在底层检查 QUADSPI 的引脚映射和时钟分频有没有配错。5.2 花屏时钟分频和模式切换的锅现象屏幕能显示内容但颜色一团糟或者图像有规律性的错位、斜条纹。这类问题八成和 QSPI 时钟有关。先看ClockPrescaler把它往大了调让时钟频率降下来看花屏是否减轻。我手头这块模组从分频 3 改成 6 之后斜条纹完全消失。如果降频没用就把注意力放到DummyCycles上。这个参数在四线数据模式下错一个值接收到的像素数据就会整体移位表现为颜色对不上。每次改完都重新刷一帧纯色画面测试红色变蓝色、绿色变红色的情况大多是这个参数的问题。5.3 卡死等待标志位的常见误区现象程序启动后跑一段时间就卡死或者初始化阶段直接进了Error_Handler。我在调这块屏时遇到一个特别典型的例子HAL_QSPI_Transmit返回超时程序死在 HAL 库里出不来。原因是我在初始化 QUADSPI 之前没有把相关的 GPIO 复用模式全部配好导致 CS 引脚一直处于不确定状态屏根本不响应任何命令。排查办法是初始化 QSPI 前先单独把所有的引脚电平打印一遍。另一个常见原因是中断优先级如果 QUADSPI 中断优先级和 SysTick 冲突在多线程环境下会导致 RT-Thread 调度异常表现为随机卡死。检查中断分组设置确保 QUADSPI 的中断优先级不低于 SysTick。6. 装完怎么验证跑通 widgets demo 并校准颜色代码全部移植完成后验证工作从一个小 demo 开始。先关闭所有业务代码只保留屏驱动和 LVGL加载自带的lv_demo_widgets。/* main 函数里加载官方 demo */ lv_demo_widgets(); /* 该 demo 会生成按钮、滑块、仪表盘等控件 */这个 demo 包含大量不同颜色的圆角控件和阴影效果只要屏幕上能看到颜色过渡自然、圆角边缘没有明显锯齿、文字没有拖影QSPI 传输就算基本稳定。接下来做两个交叉验证第一让界面连续跑 30 分钟观察有没有偶发花屏第二用示波器量 QSPI_CLK 引脚确认频率和代码里的预期一致。验证通过后最后一个步骤是校准颜色。ST77903 的默认 gamma 参数是从厂商 DEMO 里抄来的同一批屏可能存在个体差异。你可以在初始化序列的最后追加几条亮度调整命令通过改变寄存器值观察屏幕色温变化找到最适合自己产品的值。从那以后我每次拿到一套新的屏驱动 DEMO都会先强制自己走一遍先读数组、再查引脚表、最后调时钟的顺序不再直接编译烧录。刷横向条纹测试图永远是第一步纯色画面第二步之后再上 LVGL 的动画 demo。这套流程替我挡掉过很多次改错寄存器导致的返工也希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询