ST7789驱动实战:从SPI时序到RGB565显存映射的完整解析
1. ST7789液晶屏驱动入门指南
第一次拿到ST7789液晶屏和STM32开发板时,我完全不知道从何下手。这块240x240分辨率的彩色屏幕看起来简单,但数据手册里密密麻麻的时序图和寄存器说明让人望而生畏。经过几天的摸索,我发现只要抓住几个关键点,就能让这块屏幕亮起来。
ST7789是一款采用SPI接口的TFT液晶驱动芯片,支持最高18位色深(RGB666),但最常用的是16位RGB565模式。它的核心功能可以概括为:通过SPI接收控制命令和图像数据,将数据存入内部GRAM(图形存储器),然后自动刷新到屏幕上显示。对于嵌入式开发者来说,重点需要关注三个部分:SPI通信时序、显示控制命令和GRAM操作。
我使用的硬件平台是STM32F103C8T6最小系统板,通过4线SPI(SCK、MOSI、DC、CS)连接ST7789。选择这个组合是因为STM32的硬件SPI性能稳定,而4线模式比3线模式操作更简单。在实际接线时,除了SPI信号线,别忘了连接RESET和背光控制引脚,这些在初始化阶段都很重要。
2. SPI时序配置详解
2.1 解读数据手册的SPI时序
打开ST7789数据手册的第36页,可以看到4线SPI接口的时序图。这里有几个关键参数需要注意:
- CPOL(时钟极性)= 0:空闲时SCK保持低电平
- CPHA(时钟相位)= 0:数据在时钟第一个边沿采样
- 数据位顺序:MSB(高位)先传输
- 最大SPI时钟频率:62.5MHz(实际使用建议不超过30MHz)
在STM32CubeMX中配置SPI时,这些参数对应以下设置:
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2; // 36MHz/2=18MHz2.2 DC引脚的关键作用
ST7789的DC(Data/Command)引脚决定了传输的是命令还是数据:
- DC=0:当前字节是命令(如设置显示模式)
- DC=1:当前字节是数据(如像素颜色值)
这个引脚需要配合SPI时序精确控制。我通常这样实现发送函数:
void ST7789_SendCommand(uint8_t cmd) { HAL_GPIO_WritePin(DC_GPIO_Port, DC_Pin, GPIO_PIN_RESET); HAL_SPI_Transmit(&hspi1, &cmd, 1, HAL_MAX_DELAY); } void ST7789_SendData(uint8_t data) { HAL_GPIO_WritePin(DC_GPIO_Port, DC_Pin, GPIO_PIN_SET); HAL_SPI_Transmit(&hspi1, &data, 1, HAL_MAX_DELAY); }3. 关键寄存器配置实战
3.1 显示方向控制(0x36命令)
数据手册第156页的0x36命令控制显示方向,这个寄存器特别重要:
/* * BIT7: MY - 行地址顺序 * BIT6: MX - 列地址顺序 * BIT5: MV - 行列交换 * BIT4: ML - 垂直刷新顺序 * BIT3: RGB - RGB/BGR顺序 * BIT2: MH - 水平刷新顺序 */ #define MADCTL_MY 0x80 #define MADCTL_MX 0x40 #define MADCTL_MV 0x20 #define MADCTL_ML 0x10 #define MADCTL_RGB 0x08 #define MADCTL_MH 0x04 // 常用配置:正常方向+RGB顺序 uint8_t madctl = MADCTL_RGB; ST7789_SendCommand(0x36); ST7789_SendData(madctl);3.2 颜色模式设置(0x3A命令)
ST7789支持多种颜色格式,我们选择RGB565:
// 0x55: 16位/pixel (RGB565) // 0x66: 18位/pixel (RGB666) ST7789_SendCommand(0x3A); ST7789_SendData(0x55);4. GRAM操作与图像显示
4.1 设置显示窗口
在写入像素数据前,必须设置显示窗口(行列地址):
void ST7789_SetWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { // 设置列地址 ST7789_SendCommand(0x2A); ST7789_SendData(x0 >> 8); ST7789_SendData(x0 & 0xFF); ST7789_SendData(x1 >> 8); ST7789_SendData(x1 & 0xFF); // 设置行地址 ST7789_SendCommand(0x2B); ST7789_SendData(y0 >> 8); ST7789_SendData(y0 & 0xFF); ST7789_SendData(y1 >> 8); ST7789_SendData(y1 & 0xFF); // 准备写入GRAM ST7789_SendCommand(0x2C); }4.2 实现清屏函数
基于窗口设置,可以写出高效的清屏函数:
void ST7789_FillScreen(uint16_t color) { ST7789_SetWindow(0, 0, 239, 239); uint8_t data[2] = {color >> 8, color & 0xFF}; HAL_GPIO_WritePin(DC_GPIO_Port, DC_Pin, GPIO_PIN_SET); for(uint32_t i=0; i<240*240; i++) { HAL_SPI_Transmit(&hspi1, data, 2, HAL_MAX_DELAY); } } // 常用颜色定义 #define ST7789_BLACK 0x0000 #define ST7789_BLUE 0x001F #define ST7789_RED 0xF800 #define ST7789_GREEN 0x07E0 #define ST7789_WHITE 0xFFFF5. 完整初始化流程
5.1 硬件复位序列
正确的上电时序对屏幕稳定工作至关重要:
void ST7789_Reset(void) { // 拉低复位引脚至少10us HAL_GPIO_WritePin(RESET_GPIO_Port, RESET_Pin, GPIO_PIN_RESET); HAL_Delay(20); HAL_GPIO_WritePin(RESET_GPIO_Port, RESET_Pin, GPIO_PIN_SET); HAL_Delay(120); // 等待120ms复位完成 }5.2 完整的初始化代码
结合所有关键步骤的初始化函数:
void ST7789_Init(void) { ST7789_Reset(); // 退出睡眠模式 ST7789_SendCommand(0x11); HAL_Delay(120); // 颜色接口设置 ST7789_SendCommand(0x3A); ST7789_SendData(0x55); // RGB565 // 显示方向设置 ST7789_SendCommand(0x36); ST7789_SendData(0x00); // 正常方向 // 关闭反显 ST7789_SendCommand(0x21); // 显示开 ST7789_SendCommand(0x29); // 清屏为蓝色 ST7789_FillScreen(ST7789_BLUE); }6. 性能优化技巧
6.1 使用DMA加速数据传输
当刷新全屏时,SPI传输可能成为瓶颈。使用DMA可以显著提升性能:
// 在CubeMX中启用SPI TX DMA void ST7789_FillScreen_DMA(uint16_t color) { ST7789_SetWindow(0, 0, 239, 239); static uint16_t buffer[240]; // 一行像素缓冲区 for(int i=0; i<240; i++) buffer[i] = color; HAL_GPIO_WritePin(DC_GPIO_Port, DC_Pin, GPIO_PIN_SET); for(int y=0; y<240; y++) { HAL_SPI_Transmit_DMA(&hspi1, (uint8_t*)buffer, 480); while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY); } }6.2 部分刷新优化
只刷新变化区域可以大幅减少数据传输量:
void ST7789_UpdateRegion(uint16_t x0, uint16_t y0, uint16_t width, uint16_t height, uint16_t *pixels) { ST7789_SetWindow(x0, y0, x0+width-1, y0+height-1); HAL_GPIO_WritePin(DC_GPIO_Port, DC_Pin, GPIO_PIN_SET); HAL_SPI_Transmit(&hspi1, (uint8_t*)pixels, width*height*2, HAL_MAX_DELAY); }7. 常见问题排查
7.1 屏幕显示异常
如果屏幕出现花屏、颜色错乱等问题,可以按以下步骤排查:
- 检查SPI时钟极性(CPOL)和相位(CPHA)设置
- 确认DC引脚电平切换时机正确
- 验证RGB颜色顺序设置(0x36命令的RGB位)
- 检查电源稳定性,ST7789需要稳定的3.3V供电
7.2 数据传输速度问题
当屏幕刷新率低时:
- 提高SPI时钟频率(但不要超过芯片规格)
- 使用DMA传输代替轮询模式
- 减少每次传输的数据量(如只刷新变化区域)
- 优化代码结构,减少不必要的延迟
我在实际项目中遇到过屏幕偶尔闪屏的问题,后来发现是电源线过长导致电压不稳。缩短电源线并增加一个100μF的电容后问题解决。另一个常见问题是SPI时钟速度过高导致数据错误,特别是在面包板连接时,建议初期使用较低时钟频率(如10MHz),稳定后再逐步提高。
