简介:面向使用STM32F103C8T6驱动1.8寸ST7735彩屏的嵌入式初学者与项目开发者,这份程序包针对商家例程可读性不足、中文显示支持欠缺的问题,重写了大部分驱动与显示逻辑,重点实现基于字库的中英文混合显示,并保留较清晰的移植提示,便于迁移到相近的MCU或屏幕模组。压缩包共101个文件,约412KB,以C源文件与H头文件为主体,分别承载外设驱动、字库数据、显示接口定义与标准外设库,另含可直接烧录验证的HEX文件、Keil工程及选项配置、启动汇编和辅助脚本,目录结构兼顾编译组织与快速上手。已有4887人学习下载,说明其在STM32小屏显示场景中具有一定参考价值。读者可从中获得ST7735初始化时序、颜色格式设置、字符取模与坐标绘制等可复用代码,也能对照工程配置完成烧录验证,并借助文件内提示排查显示异常与移植适配问题。
1. 手头只有一块 C8T6 和 1.8 寸屏,这个工程能省掉不少试错
手头只有一块 STM32F103C8T6 最小系统板和一块 1.8 寸 ST7735 彩屏,串口调试信息又不想一直挂着电脑,这种场景最适合拿这个工程开刀。它把 ST7735 的 SPI 驱动、RGB565 填色和一套 16x16 中文点阵字库揉在一起,上电就能在 1.8 寸屏上显示中文参数。
很多人以为中文显示难在字库,实际先翻车的是 SPI 时序、开窗偏移和源文件编码;字库反而是最后才该动的东西。这份工程适合手上有 C8T6 最小系统、想给小型设备加本地中文界面的开发者,也适合刚学 SPI 想找一个完整显示链路练手的人。下面从接线、目录、驱动、字库一路拆到二次开发,照着能把屏先点亮,再改成自己的界面。
2. 硬件连线与工程骨架:从 C8T6 到 ST7735 的引脚分配和目录结构
2.1 引脚与 SPI 模式:哪些脚必须接,哪些脚可以省
1.8 寸 ST7735 模块常见引出脚是 SCL、SDA、CS、DC、RES、VCC、GND、BLK。这里的 SCL/SDA 不是 I2C,而是 SPI 的时钟和数据,别被丝印带偏。STM32F103C8T6 的 SPI1 固定在 PA5/PA7,接起来最顺,CS、DC、RES 可以挂任意普通 GPIO,背光 BLK 直接接 3.3V 或者留一个 GPIO 做 PWM 调光。屏和 C8T6 都是 3.3V 逻辑,VCC 不要接 5V,背光限流电阻一般模块已经带上,裸屏才需要自己算。
| ST7735 引脚 | C8T6 引脚 | 说明 |
|---|---|---|
| SCL | PA5 | SPI1_SCK,时钟 |
| SDA | PA7 | SPI1_MOSI,单向写屏 |
| CS | PA4 | 片选,低有效 |
| DC | PA3 | 命令/数据选择,低命令高数据 |
| RES | PA2 | 复位,低脉冲有效 |
| VCC | 3.3V | 不要接 5V |
| GND | GND | 共地 |
| BLK | 3.3V 或 PA1 | 背光,PWM 可调亮度 |
SPI 模式先用 Mode 0,8 位数据,MSB 先出,速率不要一上来就拉满。C8T6 的 SPI1 挂在 APB2 上,72MHz 主频下分频 8 是 9MHz,分频 4 是 18MHz。1.8 寸屏短线飞线跑 9MHz 先点亮,稳定后再往 18MHz 提;线长超过十厘米、杜邦线质量一般的时候,高速下花屏是玄学也是必然,先降速比改字库有用。
void ST7735_SPI_Init(void) { GPIO_InitTypeDef gpio; SPI_InitTypeDef spi; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE); /* PA5=SCK, PA7=MOSI,复用推挽 */ gpio.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7; gpio.GPIO_Mode = GPIO_Mode_AF_PP; gpio.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &gpio); /* PA2=RES, PA3=DC, PA4=CS,普通推挽输出 */ gpio.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4; gpio.GPIO_Mode = GPIO_Mode_Out_PP; gpio.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &gpio); SPI_I2S_DeInit(SPI1); spi.SPI_Direction = SPI_Direction_1Line_Tx; /* 只写屏,不需要 MISO */ spi.SPI_Mode = SPI_Mode_Master; spi.SPI_DataSize = SPI_DataSize_8b; spi.SPI_CPOL = SPI_CPOL_Low; /* Mode 0 */ spi.SPI_CPHA = SPI_CPHA_1Edge; spi.SPI_NSS = SPI_NSS_Soft; spi.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; /* 72M/8=9MHz */ spi.SPI_FirstBit = SPI_FirstBit_MSB; spi.SPI_CRCPolynomial = 7; SPI_Init(SPI1, &spi); SPI_Cmd(SPI1, ENABLE); }这段初始化只做两件事:把 PA5/PA7 交给 SPI1 外设,把 PA2/PA3/PA4 留成普通 IO 给复位、命令选择和片选。SPI_Direction_1Line_Tx是只发不收,接屏只需要 MOSI,MISO 可以不接;如果工程里用的是全双工配置也能跑,只是读回的数据丢掉。SPI_BaudRatePrescaler_8对应 9MHz,先把屏点亮再改小分频。有人把 CPOL/CPHA 配成 Mode 3 也能亮,但 Mode 0 更常见,改之前先确认屏厂初始化表里写的模式。
注意:有些 1.8 寸模块把 CS 直接下拉到地,只留 DC 和 RES,这种屏少一根片选线也能跑;但工程里如果定义了 CS 宏,还是把它接到 PA4,避免后面换屏时改代码。
2.2 目录结构:驱动层、字库层、应用层怎么分
这类 C8T6 显示工程的文件名可能不完全一样,但分层一般跑不出四层:启动和时钟、屏驱动、字库、界面。标准外设库版本会在Drivers下放StdPeriph_Driver,HAL 版本会换成STM32F1xx_HAL_Driver,核心分层不变。把驱动和字库拆开的好处是换屏只改Hardware,换字库只改Font,界面层不用跟着重写。
Project/ ├─ Core/ │ ├─ Inc/ │ └─ Src/ │ ├─ main.c // 系统时钟、初始化、主循环 │ ├─ stm32f1xx_it.c // 中断 │ └─ system_stm32f1xx.c ├─ Drivers/ │ ├─ CMSIS/ │ └─ StdPeriph_Driver/ // 或 HAL_Driver ├─ Hardware/ │ ├─ st7735.c // 写命令、初始化、开窗、填色、画点 │ ├─ st7735.h │ └─ spi_bus.c // SPI 收发和 GPIO 宏 ├─ Font/ │ ├─ font_ascii_8x16.c // 半角字符 │ └─ font_gb2312_16x16.c // 工程用到的汉字点阵 └─ User/ ├─ ui.c // 菜单、参数页、状态页 └─ ui.hst7735.c只关心“命令怎么发、窗口怎么开、像素怎么填”,不关心显示的是汉字还是数字;font_gb2312_16x16.c只提供点阵数组和查表函数,不碰 SPI;ui.c负责坐标、颜色、刷新逻辑。这样改界面时不用翻初始化序列,换 1.8 寸屏时也不用重排菜单。实际工程里可能把spi_bus.c合进st7735.c,或者把字库放在User下,只要调用关系是单向的,后面就好维护。
我一般会先看main.c里初始化顺序,再看st7735.c里的命令表,最后才看字库。顺序反了容易把“屏不亮”误判成“字库不对”,浪费半天。屏驱动的黑匣子主要在初始化和开窗,字库的问题通常只在显示阶段暴露,先让全屏填色正常,中文才有意义。
2.3 上电第一屏:编译下载前先确认这三件事
第一件事是源文件编码。Keil 里如果源文件存成 UTF-8,"中文"到单片机里是三个字节,而 GB2312 字库按两个字节查表,显示一定乱。工程里如果直接写中文字符串,把文件编码设成 GB2312/GBK;更稳的做法是把界面文字存成uint8_t数组,或者用取模软件生成的字模索引,不依赖编辑器编码。
第二件事是坐标偏移宏。ST7735 的显存常见是 132x162,实际视口 128x160,不同模块的可见区起点不一样,有的偏 2/1,有的偏 0/24。工程里一般会有ST7735_X_OFFSET和ST7735_Y_OFFSET,编译前先看这两个值是不是和屏厂给的例程一致,不一致就先填 0 跑全屏,再看边界差多少。
第三件事是验证顺序:先全屏填红、绿、蓝,确认颜色和方向;再画一个 16x16 实心方块,确认开窗和坐标;最后显示一个“中”字,确认字库和编码。main.c里可以写成这样,下载后一眼能看出卡在哪一步:
int main(void) { Delay_Init(); ST7735_SPI_Init(); ST7735_Init(); ST7735_FillScreen(ST7735_RED); /* 第一步:全屏红 */ Delay_ms(500); ST7735_FillScreen(ST7735_GREEN); /* 第二步:全屏绿 */ Delay_ms(500); ST7735_FillScreen(ST7735_BLUE); /* 第三步:全屏蓝 */ Delay_ms(500); ST7735_FillScreen(ST7735_BLACK); ST7735_ShowString(0, 0, "STM32F103C8", ST7735_WHITE, ST7735_BLACK); ST7735_ShowString(0, 18, "1.8寸 ST7735", ST7735_YELLOW, ST7735_BLACK); ST7735_ShowString(0, 36, "显示中文", ST7735_CYAN, ST7735_BLACK); while (1) { } }这段main不追求界面好看,只做分层验证。全屏红绿蓝能过,说明 SPI、复位、初始化序列和颜色格式基本对了;16x16 方块能过,说明开窗和坐标偏移对了;中文能过,才轮到字库。如果全屏颜色就不对,不要往下查字库,先回到MADCTL和COLMOD。
3. ST7735 驱动拆解:写命令、开窗口、刷 RGB565 的完整链路
3.1 底层读写:SPI 收发与 DC/CS/RST 时序
ST7735 的并行接口和 SPI 接口都存在,1.8 寸模块最常见是 SPI。SPI 写屏靠三根控制线分工:CS 拉低选中屏,DC 低表示接下来是命令、高表示数据,RES 低脉冲复位。命令字节走 SPI 发出去,数据字节跟在命令后面。CS 可以在一次连续像素流里一直保持低,不要每发一个字节就拉高,否则部分屏会把一帧切断,表现就是花屏或者颜色错位。
#define ST7735_CS_LOW() GPIO_ResetBits(GPIOA, GPIO_Pin_4) #define ST7735_CS_HIGH() GPIO_SetBits(GPIOA, GPIO_Pin_4) #define ST7735_DC_LOW() GPIO_ResetBits(GPIOA, GPIO_Pin_3) #define ST7735_DC_HIGH() GPIO_SetBits(GPIOA, GPIO_Pin_3) #define ST <p> <a href="https://download.csdn.net/download/zhouml_msn/12326266" style="color:#ec7500;font-size:14px;"> 本文还有配套的精品资源,点击获取 </a> <img alt="menu-r.4af5f7ec.gif" src="https://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif" style="width:16px;margin-left:4px;vertical-align:text-bottom;cursor:text;"> </p>