SPI LCD 显示:文字输出与全屏刷色
源码路径:
example/HC32F460JEUA/7.spi LCD/LCD 1.14inch/source/main.c驱动库:midware/ST7789 1.14inch/tftlcd.c/tftlcd.h
1. 功能简介
这个例子让单片机在彩屏上「说话」,分两部分:
开机时显示四行文字信息(相当于一个开机画面):
|
|
然后进入主循环,做两件事循环往复:
- 三颗 LED 一起翻转
- 全屏刷成黑色 → 等 500ms → 全屏刷成青色,不断交替
全屏刷色看起来简单,其实是个很有用的性能测试:屏幕能多快刷完一整屏,直接决定了你以后做动画、做实时曲线会不会卡。240×240 个像素 = 57600 个像素 × 2 字节 = 115KB 数据,全靠 SPI 一个字节一个字节送过去。
学会这个例子,你就有了「显示」这个能力——之后所有的数据、菜单、曲线都能画到屏上。
2. 硬件准备
需要一块 ST7789 驱动的 1.14 寸 TFT 彩屏(240×135 分辨率),共 8 根线。
|
|
接线要点:
| 线 | 类型 | 注意 |
|---|---|---|
| SCK (PB6) | SPI3 复用 | 时钟 |
| MOSI (PB7) | SPI3 复用 | 只发不收,所以不用接 MISO |
| DC (PB8) | 普通 GPIO | 接错必花屏,最关键的一根 |
| RST (PB9) | 普通 GPIO | 不接屏可能初始化不了 |
| PWR/BLK (PB3) | 普通 GPIO | 不接屏是黑的,你会以为程序坏了 |
| VCC | 电源 | 必须 3.3V!接 5V 会烧屏 |
| GND | 地 | 必须共地 |
⚠️ 三个必踩的坑
- VCC 一定接 3.3V,绝不能接 5V。这类小屏没有电平转换和稳压,5V 直接烧。
- 背光(BLK/PWR)没接或没点亮 → 屏一片漆黑。很多人以为程序没跑起来,其实内容画上去了只是看不见。
- 杜邦线要短。SPI 跑得快,线长了信号劣化会花屏。10cm 以内为宜。
板载 LED(PA0/PA1/PA2)用来指示程序在运行。
3. 完整代码
|
|
4. 代码分段讲解
4.1 LCD_Init() 一句话干了一大堆事
|
|
这一行是驱动库封装的成果。它内部依次完成了:
- 开 SPI3 外设时钟:
PWC_Fcg1PeriphClockCmd(PWC_FCG1_PERIPH_SPI3, Enable) - 配置 SCK/MOSI 引脚复用为
Func_Spi3_Sck/Func_Spi3_Mosi - 配置 DC/RST/PWR 三个引脚为普通 GPIO 输出
- 初始化 SPI3:三线模式、主机模式、时钟极性/相位、速度
- 硬件复位屏幕:
LCD_RST(0)→ 延时 →LCD_RST(1)→ 延时 - 发送一长串 ST7789 初始化命令:设置像素格式(RGB565)、显示方向、伽马校正、电压参数、退出睡眠、开显示……
- 打开背光:
LCD_PWR(1)
第 6 步那一串命令有几十行,都是 ST7789 数据手册里规定的固定套路。这也是为什么大家都用现成驱动库——这些寄存器值抄手册/例程就行,自己推没有意义。
想看细节就去读 midware/ST7789 1.14inch/tftlcd.c。
4.2 颜色变量:一次设置,全局生效
|
|
这是两个全局变量,在 tftlcd.h 里 extern 声明。
LCD_ShowString、LCD_ShowChar、LCD_Draw_Point、LCD_DrawLine 这些函数内部会读这两个变量来决定用什么颜色。所以你不用给每个函数传颜色参数——设一次,后面画的东西都是这个色。
想换颜色就直接改:
|
|
BACK_COLOR 的作用是文字的背景——显示字符时,字的笔画用 POINT_COLOR,笔画之外的空白填 BACK_COLOR。设成和屏幕底色一致,字看起来就是「透明背景」的。
4.3 LCD_ShowString 的五个参数
|
|
坐标原点 (0,0) 在屏幕左上角,x 向右增大,y 向下增大。这和数学里的坐标系 y 轴方向相反,是所有显示设备的惯例。
四行文字的 y 坐标是 0、15、30、45,间隔 15 像素——正好比 12 号字高一点,行与行之间有 3 像素间隙,不会挤在一起。这就是「行距」的手工计算。
字体大小可选 12 / 16 / 24,取决于驱动库里存了哪几套字模。字号越大越清晰但占屏幕越多。
4.4 LCD_Fill 全屏刷色
|
|
把一个矩形区域填成纯色。这里给的是 (0,0) 到 (240,240),也就是整屏。
LCD_Fill 的内部实现值得了解:
|
|
关键在第①步:ST7789 有个窗口机制。你设好窗口后,连续发像素数据,屏会自动从窗口左上角开始逐行填,填满自动换行,不需要你每个像素都指定坐标。这让批量刷屏效率很高。
为什么全屏刷色是性能测试? 240×240 = 57600 个像素,每个 2 字节,共 115200 字节要通过 SPI 送出去。如果 SPI 跑 20MHz,理论上要 115200×8/20000000 ≈ 46ms。你能明显看到刷屏的过程(尤其从上往下扫的感觉),这就是刷新率的直观体现。
想让它快起来,就要用 DMA——驱动库已经预留了 DMA 配置(M4_DMA1、DmaCh1),开启后 CPU 只需要启动传输,剩下的搬运硬件自己干。
4.5 注意:文字会被覆盖
|
|
主循环第一次 LCD_Fill 就把开机文字全覆盖了。所以你只能在上电瞬间看到那四行字,之后就是纯粹的黑青交替。
如果想让文字一直留着,把 LCD_ShowString 也搬进循环里,在每次 Fill 之后重画:
|
|
顺便提一个显示编程的经典问题:闪烁(Flicker)。 「全屏擦掉 → 重画」这种做法,如果刷新频繁,人眼会看到明显闪烁。 专业做法是只重画变化的区域(比如只擦掉数字那一小块再重写),或者用双缓冲(在内存里画好整帧再一次性送屏)。做仪表、做动画时这点很重要。
4.6 只刷了一次却看到交替?
细看代码会发现:黑色刷完后 Ddl_Delay1ms(500),但青色刷完后没有延时,直接进入下一轮循环刷黑。
所以实际节奏是:黑色停留 500ms,青色只停留一瞬间(刷屏本身耗时 + 循环开销)。视觉上会是「黑得久、青色一闪」。
想让两个颜色停留时间一样,在青色后面也补一句延时:
|
|
这属于原示例的小瑕疵,正好可以拿来当练手的第一个改动。
5. 实验现象
- 上电瞬间:屏幕清黑,随后左上角出现四行青色文字:
1 2 3 4Boring_TECH TFTLCD TEST 240*135 HC32F460 2022/6/20 - 紧接着进入主循环,文字被覆盖,屏幕开始黑 ↔ 青交替刷新(黑色停留约 0.5 秒,青色一闪而过)。
- 三颗 LED 同步闪烁。
如果你想仔细看那四行字,可以在 while(1) 前面加一句 Ddl_Delay1ms(3000); 让它多停 3 秒。
6. 常见问题
Q1:屏幕全黑,什么都没有? 按顺序排查(这是最常见的问题):
- 背光接了吗?亮了吗? PWR/BLK 引脚(PB3)没接或没拉高,屏就是黑的。先确认背光亮不亮——用手机摄像头对着屏看,或者在暗处看有没有微光。背光亮但没内容,是另一类问题。
- VCC 是 3.3V 吗? 电压不对屏不工作(接 5V 还可能烧掉)。
- GND 共地了吗?
Q2:背光亮了,但屏幕白屏 / 花屏 / 显示乱码条纹?
- DC 脚(PB8)接对了吗? 这是头号原因。DC 错了,屏会把数据当命令执行,结果完全混乱。
- RST 脚(PB9)接了吗? 没复位,屏内部寄存器状态不确定。
- SPI 时钟太快? 试着在
tftlcd.c里降低 SPI 分频,速度慢下来往往就好了。 - 杜邦线太长? 换短线(10cm 内),或者插紧一点。SPI 高速信号对连线质量很敏感。
Q3:显示的内容是「躺着」的 / 上下颠倒 / 左右镜像?
- 改
tftlcd.h里的USE_HORIZONTAL宏。0、1 是竖屏,2、3 是横屏,四个值对应四个旋转方向,一个个试,总有一个对。
Q4:文字显示不全,或者跑到屏幕外面了?
- 检查坐标和屏幕分辨率。1.14 寸屏横屏是 240×135,y 坐标超过 135 就看不见了。
LCD_ShowString的「宽度」参数决定什么时候换行,给太小会提前换行导致文字断开。
Q5:屏幕边缘有几像素的杂色/偏移?
- ST7789 屏有「显示偏移」参数需要按具体模块调整。不同厂家的 1.14 寸模块偏移量可能不同(比如需要 x 偏移 40、y 偏移 53)。在
tftlcd.c的LCD_Address_Set里加上偏移量修正。
Q6:刷屏太慢,做动画很卡?
- 提高 SPI 时钟(在信号质量允许的范围内)。
- 用 DMA:驱动库预留了 DMA 配置,开启后刷屏速度能提升数倍。
- 只刷变化区域,不要整屏重画。比如显示时间只需要重画那几个数字所占的小矩形。
Q7:想显示中文怎么办?
- 驱动库自带的字模只有 ASCII 字符。显示中文需要汉字字库。两个方案:
- 用取模软件把要用的几个汉字做成数组存进程序(适合固定文字,如「温度」「湿度」)。
- 把完整 GB2312 字库(约 260KB)存到外部 QSPI Flash 或 SD 卡里,需要时按区位码算出偏移读点阵数据(适合要显示任意汉字的场合)。这就是 QSPI/SD 卡章节和本章的联动应用。
7. 小结
LCD_Init()一句话完成 SPI 配置、屏复位、寄存器初始化、开背光。- DC 脚区分命令和数据,是驱动 LCD 的核心;接错必花屏。
- 背光不开屏就是黑的,排查显示问题先看背光。
- VCC 必须 3.3V,接 5V 烧屏。
POINT_COLOR/BACK_COLOR是全局颜色变量,设一次全局生效。- 坐标原点在左上角,y 向下增大。
USE_HORIZONTAL宏控制屏幕旋转方向。- 全屏刷色要传 115KB 数据,想快就上 DMA;想不闪就只刷变化区域。
8. 拓展应用
有了屏幕,你的项目一下就从「玩具」变成「产品」了:
-
数字仪表盘 显示温度、湿度、电压、电流的实时数值。配合
LCD_ShowNum和大号字体,做一个电子温湿度计、万用表显示屏。 -
实时波形 / 曲线图 每采一个数据点就
LCD_Draw_ColorPoint画一个点,横轴不断右移——就是简易示波器、心率曲线、温度趋势图。配合LCD_DrawLine连线更平滑。 -
菜单界面 + 按键导航 结合按键中断章节:上下键移动光标(用
LCD_Fill画高亮条),确认键进入子菜单。这就是所有带屏设备的操作逻辑,工业仪表、3D 打印机面板都是这么做的。 -
图片 / 开机 Logo 显示 用取模软件把 BMP/PNG 转成 RGB565 数组,
LCD_Show_Image显示出来。图片数据大的话存到 QSPI Flash(映射地址直接读)或 SD 卡里,不占程序空间。 -
中文界面 GB2312 字库存外部 Flash / SD 卡,实现任意汉字显示。这是做中文产品界面的必经之路。
-
游戏 贪吃蛇、俄罗斯方块、打砖块——240×240 的分辨率玩这些完全够。屏 + 按键就是一台掌机。
-
时钟 / 天气站 RTC 提供时间、传感器提供温湿度、WiFi 模块(串口通信)抓天气数据,全显示在屏上。这是最经典的综合练手项目,能把前面所有章节串起来。
-
上移到 GUI 框架 掌握了底层画点画线,下一步可以移植 LVGL(轻量级图形库)。它能做出带动画、按钮、滑块、图表的现代化界面。LVGL 只需要你提供一个「把一块缓冲区刷到屏上」的回调函数——也就是本章的
LCD_Fill/LCD_Address_Set能力。本章学的就是 LVGL 的地基。
综合项目建议:把七章内容串成一个「数据记录仪」—— 按键启动/停止记录 → Timer0 定时触发采样 → 数据存到 SD 卡 CSV 文件 → LCD 实时显示曲线和状态 → 串口 输出调试日志 → 字库放 QSPI Flash → GPIO LED 指示运行状态。 做完这个,你就是一个能独立开发嵌入式产品的工程师了。