第一次接触ST7735驱动的TFT屏时,最让人头疼的就是那一堆密密麻麻的引脚。我刚开始用STM32F103驱动1.44寸屏时,光是接线就折腾了半天。这里分享下我的实际接线经验:
ST7735通常有8个关键引脚需要连接,我用的是常见的7线SPI接法。背光控制(BLK) 这个引脚容易被忽略,其实直接接3.3V就能常亮,如果想省电可以接GPIO控制。RS引脚(也叫DC)特别重要,它决定发送的是命令还是数据,我习惯接在PC6,这个后面代码会体现。
最关键的SPI三兄弟:SCLK接PB13(时钟线)、MOSI接PB15(数据输出)、CS接PB12(片选)。注意STM32的SPI引脚是固定的,不能随便接。有次我把MOSI接到PA7,死活不工作,查了半天手册才发现SPI1和SPI2的引脚位置不同。
复位引脚(RST)建议接GPIO,不要直接接VCC。我遇到过屏幕初始化不稳定的情况,后来改成程序控制复位就解决了。具体接线可以参考这个表格:
| 屏幕引脚 | STM32连接 | 作用说明 |
|---|---|---|
| BLK | 3.3V | 背光控制 |
| RS(DC) | PC6 | 命令/数据切换 |
| RST | PC7 | 硬件复位 |
| CS | PB12 | 片选信号 |
| SCL | PB13 | SPI时钟 |
| SDA | PB15 | SPI数据输出 |
| GND | GND | 地线 |
| VCC | 3.3V | 电源 |
实际项目中,我建议先用杜邦线测试,稳定后再焊接。遇到过接触不良导致的花屏问题,用万用表量了半天才发现是CS引脚虚接。
配置SPI接口时,ST7735有几个参数特别关键。第一次使用时我直接复制了网上例程,结果屏幕显示全是乱码,后来发现是SPI模式没设对。CPOL=1 和 CPHA=1 这个组合(模式3)才是ST7735的正确打开方式。
这里有个坑:不同厂家的ST7735模块可能要求不同。我手上有两个不同批次的屏幕,一个用模式3工作正常,另一个却要用模式0。建议如果遇到显示异常,可以先尝试切换SPI模式:
c复制SPI_InitTypeDef SPI_InitStructure;
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; // 时钟极性
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; // 时钟相位
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; // 软件控制片选
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; // 18MHz
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_Init(SPI2, &SPI_InitStructure);
时钟分频我一般先用SPI_BaudRatePrescaler_2(18MHz),如果出现数据错误再降低。曾经在长线连接时遇到过干扰,降到SPI_BaudRatePrescaler_8才稳定。
特别注意软件NSS模式,硬件NSS我从来没成功过。还有数据位顺序必须是MSB先行,这个在ST7735手册里有明确要求。
第一次看到ST7735的初始化代码时,我被那一长串命令吓到了。后来发现其实可以分为几个关键部分:
电源配置(0xC0-0xC5)这部分设置内部电压参数,通常直接用厂家推荐值就行。有个技巧:如果屏幕亮度异常,可以调整0xC5命令的VCOM值(我一般用0x0E)。
帧率控制(0xB1-0xB3)这三个命令控制刷新率,参数组合影响屏幕响应速度。普通应用直接用0x01,0x2C,0x2D这个组合就行,追求流畅度可以尝试修改第一个参数。
最麻烦的是Gamma校正(0xE0,0xE1),这两个命令各有16个参数。我对比过不同厂家的初始化代码,发现Gamma值差异很大。后来在ST官方论坛找到一组通用参数:
c复制// 正极性Gamma校正
writeCmdData(0xe0, LCMD);
writeCmdData(0x0f, LDAT); writeCmdData(0x1a, LDAT);
writeCmdData(0x0f, LDAT); writeCmdData(0x18, LDAT);
...
// 负极性Gamma校正
writeCmdData(0xe1, LCMD);
writeCmdData(0x0f, LDAT); writeCmdData(0x1b, LDAT);
writeCmdData(0x0f, LDAT); writeCmdData(0x17, LDAT);
...
实际使用中,如果发现颜色显示不准,优先检查这部分。我曾经因为漏写一个参数导致红色显示成橙色。
ST7735手册说支持132x132分辨率,但我们用的屏幕是128x128,这就导致了经典的偏移问题。现象就是绘图时内容显示不全,或者出现错位。
经过多次测试,我发现最稳定的解决方案是在设置坐标时X轴加2,Y轴加3:
c复制void setPos(int sx, int ex, int sy, int ey) {
// X轴
writeCmdData(0x2a, LCMD);
writeCmdData(0x00, LDAT);
writeCmdData(sx+2, LDAT); // 关键偏移量
writeCmdData(0x00, LDAT);
writeCmdData(ex+2, LDAT);
// Y轴
writeCmdData(0x2b, LCMD);
writeCmdData(0x00, LDAT);
writeCmdData(sy+3, LDAT); // 关键偏移量
writeCmdData(0x00, LDAT);
writeCmdData(ey+3, LDAT);
writeCmdData(0x2c, LCMD); // 开始写入数据
}
这个偏移量不是固定的,不同批次屏幕可能需要微调。我开发过一个自动校准程序:在四个角显示标记点,通过按键调整偏移量直到显示正确。
还有个隐藏问题:内存对齐。STM32的SPI对非对齐访问很敏感,特别是在使用DMA时。建议像素数据按4字节对齐:
c复制__attribute__((aligned(4))) uint16_t frameBuffer[128*128];
最基本的像素填充函数writeAnColor虽然简单,但效率很低。通过示波器测量,发现纯色填充128x128屏幕要花费23ms。经过优化,我总结出几个提速技巧:
批量写入:ST7735支持连续写入,不必每次设置位置。修改后的函数先设置全屏区域,然后循环发送数据:
c复制void fillScreen(uint16_t color) {
setPos(0, 127, 0, 127);
uint8_t hi = color >> 8, lo = color & 0xFF;
LCS_CLR();
for(uint32_t i=0; i<16384; i++) {
while(!(SPI2->SR & SPI_I2S_FLAG_TXE));
SPI2->DR = hi;
while(!(SPI2->SR & SPI_I2S_FLAG_TXE));
SPI2->DR = lo;
}
LCS_SET();
}
DMA传输:终极提速方案。需要预先构造缓冲区,然后通过DMA发送:
c复制void DMA_Fill(uint16_t color) {
static uint16_t dmaBuf[128]; // 行缓冲区
for(int i=0; i<128; i++) dmaBuf[i] = color;
setPos(0, 127, 0, 127);
DMA1_Channel5->CCR &= ~DMA_CCR_EN;
DMA1_Channel5->CNDTR = 128*128;
DMA1_Channel5->CMAR = (uint32_t)dmaBuf;
DMA1_Channel5->CCR |= DMA_CCR_EN;
}
实测DMA方式仅需2.8ms就能完成全屏填充,比原始方法快8倍。但要注意缓冲区对齐和SPI FIFO设置。
ST7735使用RGB565格式,这和常见的RGB888不同。刚开始我直接用24位颜色值,结果显示异常。正确的颜色转换应该是:
c复制#define RGB888_TO_RGB565(r,g,b) (((r>>3)<<11)|((g>>2)<<5)|(b>>3))
实际使用中发现,不同屏幕对颜色的解析有差异。我手头的两个屏幕,一个显示红色为0xF800,另一个却要0xF810才准确。建议重要项目先做颜色校准。
还有个常见问题:颜色反相。这是因为有些模块出厂设置了反相显示,可以通过0x21命令切换:
c复制writeCmdData(0x21, LCMD); // 开启反相
writeCmdData(0x20, LCMD); // 关闭反相
做UI动画时,全屏刷新会导致闪烁。通过局部刷新可以大幅提升视觉效果:
c复制void updateArea(int x1, int y1, int x2, int y2, uint16_t* data) {
setPos(x1, x2, y1, y2);
LCS_CLR();
for(int i=0; i<(x2-x1+1)*(y2-y1+1); i++) {
write2Byte(data[i]);
}
LCS_SET();
}
配合双缓冲技术,可以完全消除闪烁。我的做法是:
对于简单动画,还可以用脏矩形算法,只更新发生变化的部分区域。
花屏问题:先检查电源是否稳定。我用示波器抓取到3.3V上有100mV的纹波,加了100μF电容后问题消失。其次检查SPI时钟相位,这是最常见的问题源。
显示偏移:除了前面说的+2/+3偏移量,还要注意屏幕的扫描方向。通过0x36命令可以控制显示方向:
c复制writeCmdData(0x36, LCMD);
writeCmdData(0xC8, LDAT); // 竖屏模式
触摸不灵敏:虽然和ST7735无关,但常出现在带触摸的模块上。建议检查触摸屏的采样率和滤波参数。
最后分享一个调试技巧:用逻辑分析仪抓取SPI波形。我通过这种方式发现CS信号有时会提前拉高,导致数据传输不完整。解决方法是在写操作间增加微小延时。