外观
2.4 TFT LCD 显示屏
这节要搞懂:把一块屏幕点亮,显示文字和数字。 需要基础:2.3(SPI 部分)
一、什么是 TFT LCD
TFT LCD(Thin Film Transistor Liquid Crystal Display,薄膜晶体管液晶显示屏)是一种彩色液晶屏。每个像素由一个薄膜晶体管独立控制,因此响应快、色彩好。
一块屏幕模组由三部分组成:
| 部分 | 作用 |
|---|---|
| 液晶面板 | 真正显示的那一层 |
| 驱动 IC | 一颗小芯片,负责接收数据并驱动面板 |
| 背光 | 后面的灯,不亮就什么都看不见 |
你的代码打交道的对象是驱动 IC,常见型号:ST7789、ILI9341、GC9A01。
2.8 寸屏有两种驱动 IC,别买错
同样是 2.8 寸 240×320 的模块,市面上有两种芯片:
| 驱动 IC | 说明 |
|---|---|
| ST7789 | 本课程用这个,ESP-IDF 自带驱动 |
| ILI9341 | 也很常见,驱动源码要换成对应型号的 |
二、买哪块屏
项目使用 2.8 寸 ST7789,240×320 分辨率,SPI 接口。
三、屏幕用了哪些引脚
屏幕是焊在板子上的,不用自己接线。要记住的是每根信号线走的是哪个 GPIO —— 代码里的宏就是按这张表填的。
| 屏幕上的丝印 | ESP32-S3 引脚 | 作用 |
|---|---|---|
| VCC | 3.3V | 供电 |
| GND | GND | 地 |
| SCL / SCK | GPIO12 | SPI 时钟(FSPI 默认脚) |
| SDA / MOSI | GPIO11 | SPI 数据(FSPI 默认脚) |
| CS | GPIO10 | 片选(FSPI 默认脚) |
| DC / RS | GPIO5 | ⭐ 关键脚,见下 |
| RES / RST | GPIO4 | 复位 |
| BLK / LED | GPIO17(经三极管驱动) | 背光 |
DC 这根线是干嘛的
SPI 传过去的都是一串数字,屏幕怎么知道这是命令("清屏!")还是数据("这个点是红色")?
就靠 DC 这根线的电平:
- DC = 低电平 → 接下来传的是命令
- DC = 高电平 → 接下来传的是数据
这是 SPI 屏幕的标配,代码里 LCD_DC_Clr() / LCD_DC_Set() 干的就是这件事。
四、不用自己写驱动
ST7789 的初始化要发几十条命令,每条是什么意思都写在几百页的手册里。这部分不用自己啃。
本课程用的是一套移植好的驱动,加上两个数据文件:
| 文件 | 管什么 |
|---|---|
lcd_init.c / lcd_init.h | 底层:配置引脚、发命令发数据、开机时的初始化序列、设置绘图窗口 |
lcd.c / lcd.h | 上层:画点画线、填色块、显示字符、显示汉字、显示图片 |
lcdfont.h | 字库:汉字和 ASCII 字符的点阵数据 |
pic.h | 图片:图片取完模之后生成的数组 |
后两个是取模软件生成的,不用手写,下面的操作过程会讲怎么做。
平时写业务代码,只需要调 lcd.h 里的那些函数:
c
LCD_Init(); // 初始化屏幕
LCD_Fill(0, 0, LCD_W, LCD_H, WHITE); // 填一块颜色
LCD_ShowString(0, 40, (const u8 *)"hello", RED, WHITE, 16, 0); // 显示字符串
LCD_ShowIntNum(48, 40, 240, 3, RED, WHITE, 16); // 显示数字
LCD_ShowPicture(0, 160, 223, 73, gImage_test2); // 显示图片lcd_init.c 里那几十条初始化命令,看不懂也没关系,它只在开机时跑一次。
底层其实只有三个动作
整个驱动翻来覆去就在干三件事:
| 函数 | 干什么 |
|---|---|
LCD_WR_REG(cmd) | 发命令:把 DC 拉低,送一个字节,再把 DC 拉回高 |
LCD_WR_DATA(color) | 发数据:DC 保持高,送两个字节(一个 RGB565 像素) |
LCD_Address_Set(x1,y1,x2,y2) | 划定范围:告诉屏幕"接下来的数据往这个矩形里填" |
画图就是:先 LCD_Address_Set 圈一块地,然后一个劲往里 LCD_WR_DATA,屏幕会自动从左上角往右下角按顺序填。
想换屏幕方向改这里
lcd_init.h 里的 USE_HORIZONTAL 控制屏幕方向(横屏 / 竖屏 / 翻转),它改的是 ST7789 的 0x36 寄存器。
改了这个,LCD_W 和 LCD_H 也要跟着对调。
五、什么是 RGB565
RGB565 是一种颜色编码格式:用 16 位(2 字节) 表示一个像素,其中红色占 5 位、绿色占 6 位、蓝色占 5 位。
绿色多一位,是因为人眼对绿色最敏感。
不用自己算,用这个宏:
c
#define RGB565(r, g, b) ((((r) & 0xF8) << 8) | (((g) & 0xFC) << 3) | ((b) >> 3))
uint16_t red = RGB565(255, 0, 0);
uint16_t white = RGB565(255, 255, 255);
uint16_t black = RGB565(0, 0, 0);六、这套驱动用的是"软件模拟 SPI" ⭐
打开 lcd_init.c,发数据的函数长这样:
c
void LCD_Writ_Bus(u8 dat)
{
u8 i;
LCD_CS_Clr(); // 片选拉低:我要跟你说话了
for (i = 0; i < 8; i++) {
LCD_SCLK_Clr(); // 时钟拉低
if (dat & 0x80) { // 取出最高位
LCD_MOSI_Set(); // 是 1 → 数据线拉高
} else {
LCD_MOSI_Clr(); // 是 0 → 数据线拉低
}
LCD_SCLK_Set(); // 时钟拉高 ← 屏幕在这一刻读走这一位
dat <<= 1; // 左移一位,准备发下一位
}
LCD_CS_Set(); // 片选拉高:说完了
}这里面没有用到芯片的 SPI 外设,全是在用 gpio_set_level() 手动翻转引脚 —— 这叫软件模拟 SPI(也叫 bit-bang,"一位一位地敲")。
这正好把 2.3 讲的波形变成了代码
对照 2.3 的 SPI 波形 看这段循环:
LCD_SCLK_Clr()→ 时钟低电平的时候把数据准备好LCD_SCLK_Set()→ 时钟上升沿,接收方在这一刻采样
这就是 2.3 里说的 Mode 0:上升沿采样。别人画的波形图,在这里就是这七行代码。
软件模拟的代价是慢,好处是:
| 软件模拟 SPI | 硬件 SPI 外设 | |
|---|---|---|
| 速度 | 慢(每一位都要 CPU 亲自翻) | 快(外设自己发,CPU 可以去干别的) |
| 引脚 | 任意 GPIO 都行 | 走特定引脚才能跑满速 |
| 移植 | 换芯片只要改几个宏 | 要重写整套配置 |
| 代码 | 一眼看懂 | 要配总线、配设备、配 DMA |
对时钟、天气这类画面变化不频繁的项目,软件模拟完全够用,而且学习价值更高 —— 你能亲眼看到协议是怎么一位一位拼出来的。
所以串口打印的是普通 GPIO 日志
烧录后串口会打印 gpio: GPIO[11]| InputEn: 0| OutputEn: 1| ...,这几个脚是被当成普通输出口配置的,不是 SPI 外设的脚。看到这个别以为配错了 —— 这套驱动本来就不用 SPI 外设。
七、刷屏会卡怎么办
先算一笔账。屏幕是 240×320,一个像素 16 位:
整屏像素数:240 × 320 = 76,800 个
每个像素要发 16 位,每一位要翻一次时钟
整屏时钟翻转次数:76,800 × 16 = 1,228,800 次一百多万次 GPIO 翻转全靠 CPU 一次次执行,整屏刷一次是几百毫秒级别的事 —— 慢到你能用眼睛看见它从上往下刷。
解决办法:别刷整屏,只刷变化的地方。
时钟界面上其实只有"秒"在变:
c
// ❌ 每秒整屏重画一次 —— 肉眼可见地闪
LCD_Fill(0, 0, LCD_W, LCD_H, WHITE);
LCD_ShowIntNum(100, 50, second, 2, RED, WHITE, 16);
// ✅ 只把秒数那两个字符的位置重画一遍 —— 瞬间完成
LCD_ShowIntNum(100, 50, second, 2, RED, WHITE, 16);两个字符是 16 × 16 × 2 = 512 个像素,只有整屏的万分之七。
驱动里为什么到处塞 vTaskDelay(1)
翻开 lcd.c,LCD_Fill、LCD_ShowChar、LCD_ShowPicture 的循环里都有一句:
c
vTaskDelay(pdMS_TO_TICKS(1));这是在主动让出 CPU。整屏刷新要占用 CPU 好几百毫秒,如果一口气跑完不撒手:
- 别的任务(读传感器、连 WiFi)全都轮不上
- 看门狗会认为系统卡死,直接把板子重启
每画完一行让一下,这些问题就都没有了。这就是第一章 1.6 调度器 讲的"协作"—— 长任务要主动给别人留出机会。
代价是变慢了:LCD_Fill 刷整屏 320 行,光这些 delay 就是 320 毫秒起步。所以更要遵守"只刷变化区域"。
本节要点
- TFT LCD = 面板 + 驱动 IC + 背光,代码对接的是驱动 IC
- 本课程用 2.8 寸 240×320、ST7789、SPI 接口的屏
- ⭐ DC 脚区分命令和数据,别接错
- 驱动不用自己写,把现成的
lcd_init.c/lcd.c拷进工程,在CMakeLists.txt里登记一下就能用 - RGB565 = 16 位表示一个像素
- ⭐ 本课程的驱动是软件模拟 SPI,用 GPIO 一位一位翻出来的,没用 SPI 外设
- 卡顿靠只刷变化区域解决
操作过程
目标:在屏幕上显示汉字、英文和数字、以及一张图片。
这一节要用到两个取模软件,先下载好:
- PCtoLCD2002 —— 汉字/字符取模(约 0.9 MB)
- Img2Lcd —— 图片取模(约 0.2 MB)
两个都是免安装的小工具,解压出来直接双击运行。
第 1 步:建工程
建工程流程见 2.2 第 2 步
Project Name 填 SPI(叫什么都行),Target 选 esp32s3。
第 2 步:把驱动源码放进工程
ST7789 的驱动(lcd_init.c / lcd_init.h / lcd.c / lcd.h)不用自己写,拷现成的。
先在工程根目录下新建一个 driver 文件夹,把这四个文件放进去:

为什么不直接丢进 main 文件夹
丢进 main 也能编译。单独放一个 driver 文件夹,是为了把"别人写的驱动"和"自己写的业务代码"分开,以后换屏、换芯片时一眼就知道该动哪些文件。
第 3 步:告诉编译器去哪找这些文件
新建的文件夹编译器并不知道,要在 main/CMakeLists.txt 里登记一下。

改成这样:

cmake
idf_component_register(SRCS "main.c" "../driver/lcd.c" "../driver/lcd_init.c"
INCLUDE_DIRS "." "../driver")两处各管一件事:
| 写在哪 | 管什么 | 不写会怎样 |
|---|---|---|
SRCS | 要编译哪些 .c 文件 | 报 undefined reference to LCD_Init 之类的链接错误 |
INCLUDE_DIRS | 去哪些文件夹里找 .h | 报 lcd.h: No such file or directory |
.. 表示上一级目录 —— 因为这个 CMakeLists.txt 在 main/ 里面,要往上退一层才能看到 driver/。
第 4 步:用 PCtoLCD2002 取汉字字模
屏幕不认识"周"这个字,它只认识一堆点亮/不亮的点。取模就是把汉字翻译成这样一串点阵数据。
打开 PCtoLCD2002,先点**「模式」→「字符模式」,再点「选项」**,按下图设置:

四个关键项,必须和驱动对上,否则显示出来是乱的:
| 选项 | 设成 | 意思 |
|---|---|---|
| 点阵格式 | 阴码 | 1 表示这个点要亮 |
| 取模方式 | 逐列式 | 一列一列地扫 |
| 取模走向 | 逆向(低位在前) | 每个字节里,最低位对应最上面那个点 |
| 自定义格式 | C51 格式 | 生成的数据直接能粘进 C 代码 |
设好之后点确定回到主界面,再调三个地方,然后输入汉字、点**「生成字模」**:

| 位置 | 设成 | 说明 |
|---|---|---|
| 字体 | 宋体 | 笔画规整,小尺寸下最清楚 |
| 字宽 / 字高 | 16 / 16 | 要和代码里 LCD_ShowChinese(..., 16, 0) 的 16 对上 |
| 等比缩放 | 勾上 | 保证字不变形 |
生成的数据就在最下面那个框里,把它拷进驱动的字库文件 lcdfont.h 里。
字宽字高必须和代码里的字号一致
驱动是按字号去不同的字库数组里找的:
c
if (sizey == 12) LCD_ShowChinese12x12(...); // 查 tfont12
else if (sizey == 16) LCD_ShowChinese16x16(...); // 查 tfont16
else if (sizey == 24) LCD_ShowChinese24x24(...); // 查 tfont24
else if (sizey == 32) LCD_ShowChinese32x32(...); // 查 tfont32取模时设 16×16,数据就得放进 tfont16,代码里也得写 16。放错数组或者字号写错,结果都是什么都不显示。
生成出来的字库长这样,每个汉字是一条记录:
c
typedef struct {
unsigned char Index[2]; // 索引:这个字的编码(2 字节)
unsigned char Msk[32]; // 点阵:16×16 的点,哪些亮哪些不亮
} typFNT_GB16;
const typFNT_GB16 tfont16[] = {
{ {0xD6,0xDC}, {0x00,0x00,0xFC,0x3F, ...} }, // 周
{ {0xD2,0xBB}, {0x00,0x00,0x00,0x00, ...} }, // 一
...
};为什么代码里写 0xD6, 0xDC,不直接写"周" ⭐
这是这一节最容易卡住的地方,值得单独讲清楚。
先看驱动是怎么显示一个汉字的(lcd.c 里的 LCD_ShowChinese16x16):
c
for (k = 0; k < HZnum; k++) { // 把字库从头翻到尾
if ((tfont16[k].Index[0] == *(s)) && // 比对第 1 个字节
(tfont16[k].Index[1] == *(s + 1))) { // 比对第 2 个字节
... // 对上了 → 把 Msk 里的点画出来
}
}它干的事就是"拿编码当钥匙,去字库里对号入座":你传进去两个字节,它挨个比对字库里每条记录的 Index,哪条对上了就画哪条的点阵。对不上就什么都不画。
所以问题变成了:这把"钥匙"该用哪种编码?
答案是 GBK,原因有三条:
① 字库里的 Index 就是 GBK 码
PCtoLCD2002 是 2002 年的老软件,跑在简体中文 Windows 上,它输出的索引字节就是 GBK 机内码(GBK 是 GB2312 的扩展)。字库是它生成的,钥匙当然得用同一套编码。
② GBK 一个汉字固定 2 字节,结构上正好对得上
看驱动里那几个数字:
| 代码 | 说明 |
|---|---|
unsigned char Index[2] | 索引只留了 2 个字节 |
tfont16[k].Index[0]、Index[1] | 只比对 2 个字节 |
s += 2;(LCD_ShowChinese 里) | 每显示完一个字,指针往后跳 2 个字节 |
整套驱动都是按"一个汉字 = 2 字节"写死的。 GBK 正好是定长 2 字节,严丝合缝。
③ UTF-8 装不进去
UTF-8 里一个汉字是 3 个字节:
"周" GBK → D6 DC (2 字节)
"周" UTF-8 → E5 91 A8 (3 字节)3 个字节塞不进 Index[2];就算塞得下,s += 2 也会每个字错位一个字节,越往后越乱。
所以千万别直接写中文字符串
c
// ❌ 这样写,屏幕上什么都不会出现
LCD_ShowChinese(0, 0, (u8 *)"周一天气", RED, WHITE, 16, 0);为什么:ESP-IDF 的 .c 文件是按 UTF-8 保存的,编译器会把 "周" 编译成 E5 91 A8 三个字节。驱动拿 E5 91 这两个字节去字库里找,而字库里存的是 D6 DC —— 永远对不上,循环走完什么都没画,屏幕一片空白。
不会报错,不会崩溃,就是不显示。所以必须手写十六进制:
c
// ✅ 直接写 GBK 编码,用注释标明是哪个字
static u8 weather_text[] = {
0xD6, 0xDC, /* zhou 周 */
0xD2, 0xBB, /* yi 一 */
0xCC, 0xEC, /* tian 天 */
0xC6, 0xF8, /* qi 气 */
0x00
};编码用在线工具查就行,比如 GBK 编码查询器:

网上的 STM32 例程直接写中文,为什么它能显示 ⭐
因为源码文件的保存编码不一样,跟芯片没关系。
这套驱动最早是从 STM32 移植过来的。Keil 工程在简体中文 Windows 上默认按 ANSI(也就是 GBK) 保存,所以:
| 源码文件编码 | "周" 编译进固件后是 | 能否匹配字库的 D6 DC | |
|---|---|---|---|
| STM32 / Keil | GBK | D6 DC | ✅ 对得上 |
| ESP-IDF / VS Code | UTF-8 | E5 91 A8 | ❌ 对不上 |
编译器不会做转换,源文件里是什么字节,字符串里就是什么字节。所以同一段代码换个编辑器保存一下,显示就坏了 —— 这也是它难查的原因。
结论:别去依赖源码文件的编码。 汉字直接写成 GBK 字节数组,字库的索引也一样:
c
{ {0xD6,0xDC}, {...点阵...} } // ✅ 写字节,跟文件编码无关
{ "周", {...点阵...} } // ❌ 写汉字,换个编码保存就废了这样不管文件存成 UTF-8 还是 GBK,都能正常显示。
也可以让编译器帮你转(不推荐)
GCC 有两个参数可以指定"源文件是什么编码"和"字符串在固件里按什么编码存":
-finput-charset=UTF-8 -fexec-charset=GBK加上之后,UTF-8 源文件里的 "周" 会被编译器转成 D6 DC,就能直接写中文了。
但本课程不用这个办法,因为它是整个工程全局生效的:所有字符串都会变成 GBK,包括 printf 打到串口的日志 —— 而串口助手一般按 UTF-8 解码,中文日志立刻全变乱码。
为了少打几个十六进制,把日志搞乱,不划算。
为什么英文和数字就不用管这些
c
LCD_ShowString(0, 40, (const u8 *)"LCD_W:", RED, WHITE, 16, 0); // 直接写就行因为英文走的是另一条路。LCD_ShowChar 里是这么算的:
c
num = num - ' '; // 用 ASCII 码减去空格(0x20),直接算出下标
temp = ascii_1608[num][i]; // 按下标取点阵,不用查找ASCII 字符在 GBK 和 UTF-8 里是同一个字节(都是 0x00~0x7F 原样保留),所以不管文件存成什么编码,'L' 永远是 0x4C。
这也是为什么英文显示又快又不出错 —— 不用遍历字库,直接算下标。汉字太多没法这么干,只能老老实实查表。
一个字占多少空间
一个 16×16 的汉字,点阵是 16 × 16 ÷ 8 = 32 字节,加上 2 字节索引,一个字 34 字节。
汉字字库是编译进固件的,所以:用到哪几个字就取哪几个,别把整个 GB2312 字库(六千多字,二十多万字节)塞进去。
这也是为什么示例里只取了"周一天气"这几个字。
第 5 步:用 Img2Lcd 取图片模
图片同理,也要先变成数组。打开 Image2Lcd,把图片拖进去,按下图设置:

| 选项 | 设成 | 为什么 |
|---|---|---|
| 输出数据类型 | C 语言数组(*.c) | 生成的是能直接用的 .c 文件 |
| 扫描模式 | 水平扫描 | 从左到右、从上到下,和屏幕刷新顺序一致 |
| 输出灰度 | 16 位真彩色 | 就是 第五节 讲的 RGB565 |
| 颜色位数 | R:5bits, G:6bits, B:5bits | 同上,别选成 R5G5B5 |
| 最大宽度和高度 | 填你的图片实际尺寸 | 超过屏幕尺寸会显示不全 |
| 包含图像头数据 | ⭐ 不要勾 | 见下面 |
"包含图像头数据"必须取消勾选
勾上之后,Image2Lcd 会在数组开头塞几个字节,记录图片的宽、高、颜色格式。
但驱动里的 LCD_ShowPicture 是从 pic[0] 就当像素数据用的:
c
LCD_Address_Set(x, y, x + length - 1, y + width - 1);
for (...) {
LCD_WR_DATA8(pic[k * 2]); // ← 直接从第 0 个字节开始读
LCD_WR_DATA8(pic[k * 2 + 1]);
k++;
}它不会跳过头部,宽高也是你在调用时手动传进去的(223, 73),根本不看头部里写了什么。
所以头部如果存在,会被当成前几个像素画出来,整张图往后错位。
点**「保存」**生成 .c 文件,里面是一个 gImage_xxx 数组。把它放进工程(比如做成 pic.h),代码里 #include 就能用。
图片别太大
一张 240×320 的全屏图,按 RGB565 算就是 240 × 320 × 2 = 153600 字节 ≈ 150KB。
这些数据是编译进固件里的,图放多了固件会迅速变大。只放必要的小图标。
第 6 步:写代码
c
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "lcd_init.h"
#include "lcd.h"
#include "pic.h"
// "周一天气"四个字的 GBK 机内码,一个汉字两字节
static u8 weather_text[] = {
0xD6, 0xDC, /* zhou */
0xD2, 0xBB, /* yi */
0xCC, 0xEC, /* tian */
0xC6, 0xF8, /* qi */
0x00
};
// 这次要显示上面数组里的第几个字(下标从 0 开始)
static u8 show_zhou_tian[] = {0, 1, 2};
void app_main(void)
{
LCD_Init(); // 初始化屏幕
LCD_Fill(0, 0, LCD_W, LCD_H, WHITE); // 整屏刷白,相当于清屏
// 显示汉字:从 (0,0) 开始,按 show_zhou_tian 挑 2 个字,红字白底,16 号字
LCD_ShowChineseByIndexes(0, 0, weather_text, show_zhou_tian, 2, RED, WHITE, 16, 0);
// 显示英文和数字:把屏幕的宽高打在屏上
LCD_ShowString(0, 40, (const u8 *)"LCD_W:", RED, WHITE, 16, 0);
LCD_ShowIntNum(48, 40, LCD_W, 3, RED, WHITE, 16);
LCD_ShowString(80, 40, (const u8 *)"LCD_H:", RED, WHITE, 16, 0);
LCD_ShowIntNum(128, 40, LCD_H, 3, RED, WHITE, 16);
LCD_ShowString(0, 70, (const u8 *)"Increaseing Nun:", RED, WHITE, 16, 0);
// 显示图片:左上角 (0,160),宽 223、高 73
LCD_ShowPicture(0, 160, 223, 73, gImage_test2);
}几个常用函数的参数:
| 函数 | 参数含义 |
|---|---|
LCD_Fill(x1, y1, x2, y2, color) | 把这个矩形填成某个颜色 |
LCD_ShowString(x, y, str, 字色, 背景色, 字号, 是否叠加) | 显示一串英文/数字 |
LCD_ShowIntNum(x, y, num, 位数, 字色, 背景色, 字号) | 显示一个整数 |
LCD_ShowFloatNum1(x, y, num, 位数, 字色, 背景色, 字号) | 显示一个小数(保留两位) |
LCD_ShowPicture(x, y, 宽, 高, 数组) | 在 (x,y) 画一张取好模的图 |
LCD_DrawLine / LCD_DrawRectangle / Draw_Circle | 画线、画矩形框、画圆 |
"是否叠加"那个参数(mode)
最后那个 0 / 1 决定背景怎么处理:
0= 不叠加:把字的背景整块刷成背景色,会盖掉原来的内容1= 叠加:只画笔画的那些点,背景保持原样
在纯色背景上写字用 0(快,而且能自动擦掉上一次的内容);想把字压在图片上用 1。
LCD_ShowChineseByIndexes 是怎么挑字的
c
static u8 weather_text[] = {0xD6,0xDC, 0xD2,0xBB, 0xCC,0xEC, 0xC6,0xF8, 0x00};
static u8 show_zhou_tian[] = {0, 1, 2};
LCD_ShowChineseByIndexes(0, 0, weather_text, show_zhou_tian, 2, RED, WHITE, 16, 0);weather_text 是字池(周、一、天、气,下标 0~3),show_zhou_tian 是这次要显示哪几个,2 是显示几个。
所以这一句显示的是下标 0 和 1,也就是"周一"。数组里第三个元素 2 因为 count 只写了 2,这次没用上 —— 想显示"周一天",把 count 改成 3 就行。
这么设计的好处是:字库和字池只存一份,想显示什么组合就换一组下标。天气时钟上"晴 / 阴 / 雨 / 雪"就是这么切换的。
坐标原点在左上角
(0, 0) 是屏幕左上角,x 往右增大、y 往下增大 —— 和数学课上的坐标系是上下颠倒的。
所以 LCD_ShowPicture(0, 160, ...) 是把图画在屏幕偏下的位置。
第 7 步:编译烧录
第 8 步:应该看到什么
屏幕(白底):
- 左上角红色汉字
- 下面一行
LCD_W:240 LCD_H:320 - 再下面
Increaseing Nun: - 屏幕中下部一张图片
串口会打印出被配置成输出的那几个引脚,正好对应上面那张引脚表:
gpio: GPIO[4]| InputEn: 0| OutputEn: 1| ... ← RST
gpio: GPIO[5]| InputEn: 0| OutputEn: 1| ... ← DC
gpio: GPIO[10]| InputEn: 0| OutputEn: 1| ... ← CS
gpio: GPIO[11]| InputEn: 0| OutputEn: 1| ... ← SDA(MOSI)
gpio: GPIO[12]| InputEn: 0| OutputEn: 1| ... ← SCL
gpio: GPIO[17]| InputEn: 0| OutputEn: 1| ... ← 背光屏幕一片白、什么都没有就别往下走
说明 LCD_Fill 生效了但后面的显示没出来,先确认字库和图片数组有没有正确加进工程。
动手试试
- 把屏幕点亮,全屏刷成红色。
- 依次刷红、绿、蓝,各停 1 秒。
- 只刷屏幕中间一个小方块,对比一下速度差多少。