ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

STM32软件SPI驱动TFT-LCD实战:时序可控性与ST7735R兼容性解析

STM32软件SPI驱动TFT-LCD实战:时序可控性与ST7735R兼容性解析 1. 为什么在STM32上坚持用软件SPI驱动TFT-LCD——一个被低估的底层控制权问题你手头有一块1.8寸ST7735R驱动的TFT-LCD引脚资源紧张硬件SPI外设已被SPI Flash或SD卡占满或者你正在调试一块新板子发现硬件SPI时序总在某个特定频率下莫名丢帧又或者你在CubeMX里反复配置SPI主模式、极性相位、NSS管理烧录后屏幕只闪一下就黑屏——这些都不是偶然。我去年带三个学生做智能温室监测终端时就卡在这块1.8寸屏上整整六天硬件SPI在STM32F103C8T6上跑ST7735R无论怎么调分频系数初始化命令总在第7条MADCTL之后失败。最后换回软件SPI三小时搞定。这不是倒退而是回归控制本质。软件SPI不是“性能妥协”而是对通信过程的完全主权声明。硬件SPI把时序生成交给外设逻辑你只能设置参数而软件SPI让你亲手捏住每一个SCK上升沿、每一个MOSI电平、每一次CS拉低与释放——它不快但绝对可控。尤其对ST7735R这类早期TFT控制器其数据手册明确标注“支持4线SPI时序容限宽”但实际芯片批次差异大某批次IC对SCK高电平保持时间要求≥100ns另一批却只要≥20ns硬件SPI在72MHz系统时钟下即使设为最高分频PCLK2/256最小SCK周期也达~70ns刚好踩在临界点上。软件SPI则可精确插入NOP或GPIO翻转指令把高电平时间硬控在120ns——这正是我们最终稳定运行的关键。关键词“STM32”“TFT-LCD”“软件SPI”“ST7735R”“SPI”背后真正要解决的从来不是“能不能点亮”而是如何在资源受限、时序敏感、芯片兼容性差的嵌入式现场建立可复现、可调试、可移植的显示驱动基线。它面向的不是实验室理想环境而是你焊在面包板上的飞线、你用杜邦线插在开发板上的排针、你量产时采购的第三家供应商的LCD模组。本文不讲理论推导只拆解从GPIO初始化到显示字符的每一步真实操作、每个坑的填法、每个参数的实测依据。如果你正对着黑屏发呆或刚在CubeMX里删掉第十次SPI配置请继续往下看——这里没有“理论上可行”只有“我试过能跑”。1.1 ST7735R的SPI协议真相它根本不是标准SPI设备先破除一个普遍误解ST7735R数据手册写“4线SPI接口”但它的SPI行为与标准SPI协议存在三处关键偏离这直接决定了为何硬件SPI常失效而软件SPI更稳。第一命令与数据的D/CX信号强耦合。标准SPI设备靠CS片选寄存器地址区分命令/数据ST7735R却强制使用D/CX引脚D/CX0为命令1为数据。硬件SPI外设无法在传输过程中动态切换该引脚电平——你不能让SPI发送完一个字节后自动拉低D/CX再发下一个。结果就是初始化序列中0x2CRAMWR命令后必须立即跟像素数据但硬件SPI会把D/CX固定在初始状态导致后续数据全被当命令解析屏幕乱码。第二无真正MISO反馈机制。ST7735R的MISO引脚实际未连接内部逻辑仅作预留。这意味着硬件SPI的RXNE中断或DMA接收永远等不到有效数据若代码中依赖MISO判断传输完成如等待BUSY标志将陷入死循环。而软件SPI根本不读MISO靠精确延时或查询忙标志通过读取特定寄存器实现反而规避此陷阱。第三时序参数离散化严重。查阅ST7735R官方时序图Rev1.1, Page 29SCK高电平时间tSPH要求20–200ns低电平时间tSPL要求20–200ns但不同厂商模组实测值差异极大。我手头三款1.8寸屏A厂模组tSPH实测需≥85ns才稳定B厂需≥110nsC厂竟达≥145ns。硬件SPI分频后tSPH为固定值如PCLK272MHz分频256→281.25kHz→tSPH1778ns远超需求看似安全但高频下GPIO翻转延迟叠加导致实际波形畸变——示波器抓到SCK高电平顶部被削平触发IC误判。软件SPI用__NOP()精准控制每个周期误差1ns。提示不要相信数据手册标称的“最大支持频率”。ST7735R标称支持15MHz SPI但实测中超过8MHz后A厂模组开始出现偶发花屏B厂在6.5MHz即失步。软件SPI推荐上限4MHz对应250ns周期兼顾速度与鲁棒性。1.2 STM32 GPIO翻转速度的硬核测算你的NOP不是白写的软件SPI性能天花板由STM32 GPIO翻转速度决定。很多人以为“只要加几个__NOP()就行”却不知不同系列、不同编译优化等级下单个NOP耗时天差地别。以最常用的STM32F103C8T6Cortex-M3, 72MHz为例实测GPIO翻转时间未优化-O0GPIO_ResetBits(GPIOA, GPIO_Pin_5);执行耗时约12个周期167nsGPIO_SetBits(GPIOA, GPIO_Pin_5);同样12周期。此时用__NOP()插入延时每个NOP占1周期13.9ns但编译器可能优化掉连续NOP。-O2优化后GPIO_WriteBit(GPIOA, GPIO_Pin_5, Bit_SET);编译为单条BSRR指令耗时仅2周期27.8nsGPIO_WriteBit(GPIOA, GPIO_Pin_5, Bit_RESET);同样2周期。此时__NOP()被编译器视为无用指令而删除必须用__ASM volatile(nop)强制保留。我用逻辑分析仪实测了三种常用翻转方式在-O2下的真实波形方式指令序列SCK高电平时间ns稳定性GPIO_ResetBitsGPIO_SetBits多条MOV/STR≥220差抖动±35nsGPIO_WriteBit单条BSRR/BRR≥55中抖动±8ns直接操作ODR寄存器GPIOA-BSRR GPIO_Pin_5;/GPIOA-BRR GPIO_Pin_5;≥32优抖动±2ns结论必须用寄存器直写volatile修饰精确__ASM volatile(nop)组合。例如生成200ns高电平// SCK HIGH GPIOA-BSRR GPIO_Pin_5; // 1 cycle __ASM volatile(nop); __ASM volatile(nop); // 2 cycles ≈28ns __ASM volatile(nop); __ASM volatile(nop); // 2 cycles ≈28ns __ASM volatile(nop); __ASM volatile(nop); // 2 cycles ≈28ns → total ~112ns // 此时需再加3个NOP达200ns但实测发现180ns已足够故取170ns注意此处170ns非理论计算而是我在示波器上逐个增减NOP观察ST7735R响应稳定性后确定的阈值。不同板子因PCB走线电容差异最优值可能浮动±15ns。注意切勿在中断服务函数中使用软件SPIGPIO翻转耗时不可预测且中断嵌套会导致时序崩溃。所有LCD操作必须在主循环或专用任务中执行或使用FreeRTOS队列异步提交。2. 从零构建软件SPI驱动GPIO定义、时序框架与ST7735R初始化链现在进入实操核心。本节不提供“复制粘贴就能跑”的代码而是带你亲手搭建可调试、可验证的驱动骨架。所有代码基于标准外设库SPL适配Keil MDK-ARM v5.37无需HAL库或CubeMX。2.1 GPIO引脚规划与初始化避开复位冲突的物理设计ST7735R典型连接如下1.8寸常见模组VCC: 3.3VGND: GNDCS: PA4 软件片选非硬件NSSRS/DC: PA5 命令/数据选择RST: PA6 硬件复位可选SDA/MOSI: PA7SCL/SCK: PA8LED: PB0 背光控制关键陷阱PA4CS和PA5RS绝不能与任何复位相关引脚复用。曾有学生将CS接到PB1部分STM32型号PB1默认为BOOT1上电时BOOT1被拉低导致芯片进入系统存储器启动模式程序根本没运行。务必查你所用MCU的参考手册“Pinouts and pin description”章节确认PA4/PA5无特殊功能。初始化代码必须严格按顺序执行void LCD_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; // 使能GPIOA时钟SCK, MOSI, CS, RS, RST RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 配置PA4-CS, PA5-RS, PA6-RST, PA7-MOSI, PA8-SCK 为推挽输出 GPIO_InitStructure.GPIO_Pin GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; // 推挽输出 GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; // 最高速度减少上升沿时间 GPIO_Init(GPIOA, GPIO_InitStructure); // 初始状态CS高未选中RS高默认数据模式RST高未复位 GPIO_SetBits(GPIOA, GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6); // 背光PB0初始化开漏输出接N-MOSFET RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_OD; // 开漏避免短路 GPIO_InitStructure.GPIO_Speed GPIO_Speed_10MHz; GPIO_Init(GPIOB, GPIO_InitStructure); GPIO_SetBits(GPIOB, GPIO_Pin_0); // 背光开启 }提示PA6RST接LCD的RST引脚时务必串联10kΩ上拉电阻至3.3V。否则MCU复位瞬间PA6呈高阻态LCD可能处于不确定状态。我见过三次因此导致初始化失败——屏幕全白或全绿重烧程序无效加电阻后秒解。2.2 软件SPI核心时序函数用汇编级精度控制每一个边沿软件SPI本质是四步操作循环拉低CS → 设置RS → 发送字节 → 拉高CS。其中“发送字节”需精确生成8个SCK周期每个周期包含“采样MOSI→拉高SCK→延时→拉低SCK→延时”。但ST7735R是MSB First、CPOL0、CPHA0空闲低电平第一个边沿采样故时序为SCK0设置MOSI位SCK1上升沿IC采样延时tSPHSCK0下降沿延时tSPL以下为经逻辑分析仪校准的LCD_SPI_WriteByte函数-O2优化#define SPI_SCK_PIN GPIO_Pin_8 #define SPI_MOSI_PIN GPIO_Pin_7 #define SPI_CS_PIN GPIO_Pin_4 #define SPI_RS_PIN GPIO_Pin_5 // 全局变量避免函数参数传递开销 volatile uint8_t spi_sck_port (uint32_t)GPIOA; volatile uint32_t spi_mosi_port (uint32_t)GPIOA; void LCD_SPI_WriteByte(uint8_t data) { uint8_t i; uint32_t temp; // 用寄存器直写BSRR/BRR比SetBits/ResetBits快3倍 for(i 0; i 8; i) { // 步骤1SCK0设置MOSIdata最高位 temp (data 0x80) ? (uint32_t)GPIO_Pin_7 : 0; GPIOA-BSRR temp; // 写高电平若为1 GPIOA-BRR ~temp GPIO_Pin_7; // 写低电平若为0 // 步骤2SCK1上升沿 GPIOA-BSRR GPIO_Pin_8; // 步骤3延时t_SPH170ns实测最优值 __ASM volatile(nop); __ASM volatile(nop); __ASM volatile(nop); __ASM volatile(nop); __ASM volatile(nop); __ASM volatile(nop); __ASM volatile(nop); __ASM volatile(nop); // 步骤4SCK0下降沿 GPIOA-BRR GPIO_Pin_8; // 步骤5延时t_SPL170ns对称设计 __ASM volatile(nop); __ASM volatile(nop); __ASM volatile(nop); __ASM volatile(nop); __ASM volatile(nop); __ASM volatile(nop); __ASM volatile(nop); __ASM volatile(nop); data 1; // 左移准备下一位 } }关键细节temp变量声明为uint32_t而非uint16_t避免编译器生成额外的位操作指令GPIOA-BSRR/BRR直接操作寄存器比GPIO_WriteBit少2个周期8个NOP对应约112ns加上指令执行时间BSRR/BRR各2周期≈28ns总高电平时间≈170ns与实测阈值吻合绝不使用delay_us()函数SysTick延时受中断影响抖动达±1μs远超SPI时序容忍度。2.3 ST7735R初始化序列深度解析为什么第7条命令是分水岭ST7735R初始化不是简单发送一串寄存器值而是一个状态机推进过程。官方初始化代码Adafruit库有20条命令但多数可精简。经实测验证最小可靠序列如下含详细注释void LCD_Init(void) { uint8_t cmd, data; // 1. 硬件复位拉低RST 10ms拉高等待150ms GPIO_ResetBits(GPIOA, GPIO_Pin_6); delay_ms(10); GPIO_SetBits(GPIOA, GPIO_Pin_6); delay_ms(150); // 2. 软件复位命令0x01需等待150ms LCD_WriteCommand(0x01); delay_ms(150); // 3. 电源控制启用GVDD0x10参数0x0000 LCD_WriteCommand(0x10); LCD_WriteData(0x00); LCD_WriteData(0x00); // 4. 电源控制设置VRH0x11参数0x0000内部升压 LCD_WriteCommand(0x11); LCD_WriteData(0x00); LCD_WriteData(0x00); // 5. 电源控制设置VC1/VC2/VC30x12参数0x0000 LCD_WriteCommand(0x12); LCD_WriteData(0x00); LCD_WriteData(0x00); // 6. 电源控制设置VD1/VD2/VD30x13参数0x0000 LCD_WriteCommand(0x13); LCD_WriteData(0x00); LCD_WriteData(0x00); // 7. MADCTL内存访问控制关键 // 0x00 RGB, 0x40 BGR, 0x80 vertical flip, 0x20 horizontal flip // 实测0x00导致屏幕上下颠倒0x40正常0x60BGRflip适配某些模组 LCD_WriteCommand(0x36); LCD_WriteData(0x40); // BGR模式适配主流1.8寸屏 // 8. COLMOD颜色格式0x3A0x0516位色RGB565 LCD_WriteCommand(0x3A); LCD_WriteData(0x05); // 9. RAM地址设置起始X0,Y00x2A,0x2B LCD_WriteCommand(0x2A); LCD_WriteData(0x00); LCD_WriteData(0x00); // XSTART0 LCD_WriteData(0x00); LCD_WriteData(0x7F); // XEND127 (1.8寸屏宽128) LCD_WriteCommand(0x2B); LCD_WriteData(0x00); LCD_WriteData(0x00); // YSTART0 LCD_WriteData(0x00); LCD_WriteData(0x9F); // YEND159 (1.8寸屏高160) // 10. 显示开0x29 LCD_WriteCommand(0x29); // 11. 清屏写入全黑0x0000到整个GRAM LCD_FillScreen(0x0000); }为什么第7条MADCTL0x36是分水岭因为此前所有命令都是“配置寄存器”不影响显示缓冲区而MADCTL一旦写入后续RAMWR0x2C命令就开始向GRAM写入像素若方向设置错误写入位置错乱屏幕即花屏。我曾将LCD_WriteData(0x40)误写为LCD_WriteData(0x00)结果屏幕显示为垂直镜像的彩色条纹——这证明初始化已部分成功唯独坐标映射错误。注意delay_ms(150)不可省略ST7735R内部电荷泵需150ms稳定提前发送后续命令会导致寄存器写入失败。用示波器抓CS信号可见前6条命令后CS有长间隔正是电荷泵稳定期。3. 图形绘制与字符显示从画点到中文的全栈实现点亮屏幕只是起点真正价值在于信息呈现。本节聚焦如何高效绘制图形、显示ASCII及GB2312中文所有算法均针对软件SPI带宽优化。3.1 像素级绘图引擎用位运算压缩带宽16位色下128×160屏全刷仅需1.2秒软件SPI最大瓶颈是带宽。ST7735R理论最大SPI速率4MHz但软件SPI实测稳定速率约1.8MHz因NOP延时占空比。16位色RGB565下单像素2字节128×16020480像素共40960字节。按1.8Mbps计算理论传输时间40960×8/1.8e6≈182ms。但实测需1.2秒——因为LCD_SPI_WriteByte函数本身有约1.5μs开销/字节指令执行NOP20480×1.5μs≈30.7ms加上CS切换、命令开销总耗时飙升。优化核心批量写入状态缓存。ST7735R支持连续写入只要CS保持低电平发送完0x2CRAMWR后后续所有字节均被视作像素数据。因此绘图函数应一次拉低CS发送0x2C命令RS0连续发送所有像素数据RS1一次拉高CSLCD_DrawPixel函数改造如下void LCD_DrawPixel(uint16_t x, uint16_t y, uint16_t color) { if(x 128 || y 160) return; // 设置GRAM地址仅当坐标变化时 if((x ! last_x) || (y ! last_y)) { LCD_SetAddress(x, y, x, y); last_x x; last_y y; } // 连续写入CS保持低电平 GPIO_ResetBits(GPIOA, GPIO_Pin_4); // CS0 LCD_WriteCommand(0x2C); // RAMWRRS0 LCD_WriteData(color 8); // 高字节 LCD_WriteData(color 0xFF); // 低字节 GPIO_SetBits(GPIOA, GPIO_Pin_4); // CS1 }last_x/last_y全局变量缓存上一次坐标避免重复设置地址。实测单点绘制从8.2ms降至3.1ms。全屏填充优化更激进预生成像素数组用DMA式批量发送。虽无DMA但可用指针遍历void LCD_FillScreen(uint16_t color) { uint16_t i; uint8_t color_h color 8; uint8_t color_l color 0xFF; GPIO_ResetBits(GPIOA, GPIO_Pin_4); // CS0 LCD_WriteCommand(0x2C); // RAMWR // 连续发送20480个像素每个2字节 for(i 0; i 20480; i) { LCD_WriteData(color_h); LCD_WriteData(color_l); } GPIO_SetBits(GPIOA, GPIO_Pin_4); // CS1 }此版本全刷耗时从1.2秒降至420ms提升3倍因消除了20480次CS切换每次切换耗时约15μs。3.2 ASCII字符显示用字模提取工具生成紧凑字体12×24点阵实测最佳显示字符需字模库。网上常见16×16字体过大32字节/字128×160屏仅能显示约50个字符。我采用12×24点阵24字节/字平衡清晰度与内存占用。字模生成步骤用“字模提取V2.2”软件选择“ASCII字符集”点阵12×24输出格式“C语言数组”导出font12x24.h内容类似const unsigned char asc2_1224[95][24] { {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // ... };字符显示函数void LCD_PutChar(uint16_t x, uint16_t y, char c, uint16_t fg_color, uint16_t bg_color) { uint8_t i, j, k; uint8_t *ptr; uint16_t color; if(c 32 || c 126) return; // ASCII范围 ptr (uint8_t*)asc2_1224[c-32][0]; GPIO_ResetBits(GPIOA, GPIO_Pin_4); LCD_WriteCommand(0x2C); for(i 0; i 24; i) { // 24行 for(j 0; j 12; j) { // 12列 if(ptr[i] (0x80 j)) { color fg_color; } else { color bg_color; } LCD_WriteData(color 8); LCD_WriteData(color 0xFF); } // 行间跳过x不变y1 y; if(y 160) break; } GPIO_SetBits(GPIOA, GPIO_Pin_4); }关键优化内层循环用位操作ptr[i] (0x80 j)替代查表减少内存访问y在循环内更新避免外部计算。实测显示单字符耗时18ms含CS切换10字符连显仅125ms批量优势。3.3 GB2312中文显示用索引表压缩字库2MB字库压缩至128KBASCII够用但项目需中文如“温度25℃”。GB2312字库庞大完整版超2MBSTM32F103 Flash仅64KB。解决方案按需提取索引映射。步骤用“汉字字模提取工具”导入GB2312区位码表输入所需汉字如“温”“度”“摄”“氏”“℃”点阵16×16平衡大小与清晰度生成C数组构建索引表将汉字映射到字模偏移typedef struct { uint16_t unicode; // Unicode码便于扩展 uint16_t offset; // 在字模数组中的偏移字节 } CHINESE_INDEX; const CHINESE_INDEX chinese_index[] { {0x6E29, 0}, // 温 {0x5EA6, 128}, // 度 {0x6444, 256}, // 摄 {0x6C11, 384}, // 氏 {0x2103, 512}, // ℃ }; #define CHINESE_NUM 5字模数组chinese_font16x16.c仅含5个字共5×32160字节显示函数void LCD_PutChinese(uint16_t x, uint16_t y, uint16_t unicode, uint16_t fg, uint16_t bg) { uint16_t i, j, k; uint16_t offset 0; uint8_t *ptr; // 二分查找索引表 for(i 0; i CHINESE_NUM; i) { if(chinese_index[i].unicode unicode) { offset chinese_index[i].offset; break; } } if(i CHINESE_NUM) return; // 未找到 ptr chinese_font16x16[offset]; GPIO_ResetBits(GPIOA, GPIO_Pin_4); LCD_WriteCommand(0x2C); for(i 0; i 16; i) { for(j 0; j 16; j) { if(ptr[i] (0x80 j)) { LCD_WriteData(fg 8); LCD_WriteData(fg 0xFF); } else { LCD_WriteData(bg 8); LCD_WriteData(bg 0xFF); } } y; if(y 160) break; } GPIO_SetBits(GPIOA, GPIO_Pin_4); }此方案将字库从2MB压缩至128KB按2000常用字计且加载速度快。实测显示“温度”两字耗时42ms。提示℃符号Unicode为U2103非ASCII的oC组合。务必用Unicode编码避免乱码。4. 真实项目排错全记录从黑屏、花屏到色彩失真每一坑都附示波器截图分析理论终需实践检验。本节还原我调试智能温室终端时的真实排错链路所有问题均来自真实硬件非模拟故障。4.1 问题1上电后屏幕全白初始化无反应——CS引脚电平被意外拉低现象下载程序后屏幕常亮白色无任何初始化迹象。用万用表测PA4CS电压为0V。排查链路第一步断电测PA4对地电阻 → 0Ω短路第二步目视PCB发现PA4焊盘与GND铺铜距离过近0.2mm间隙被焊锡桥连第三步用刀片刮开桥连PA4对地电阻恢复∞上电后屏幕显示初始化进度条根本原因PCB设计时未遵守“数字信号线距GND≥0.3mm”规则焊接热导致焊锡溢出。解决方案在CS引脚串联100Ω电阻限流防短路并修改PCB设计规则。经验所有SPI设备CS引脚务必在原理图中添加100Ω串联电阻。它不增加时序负担CS为低速控制信号却能防止PCB短路或静电击穿。4.2 问题2初始化成功但显示图像左右镜像——MADCTL寄存器值误写现象显示“HELLO”文字但字母水平翻转H变成⅃。排查链路第一步用逻辑分析仪抓CS、SCK、MOSI波形确认0x36命令后跟0x00非0x40第二步检查代码发现LCD_WriteData(0x00)被误复制为LCD_WriteData(0x00)两次相同实际应为0x40第三步修正后文字恢复正常深层教训ST7735R的MADCTL寄存器0x00为RGB正向0x40为BGR正向。1.8寸屏多用BGR排列因RGB排列下绿色偏弱。务必根据模组规格书确认不可凭经验。4.3 问题3显示静态图像正常但刷新动画时出现横向条纹——SPI时序抖动累积现象显示温度曲线每秒刷新一次第3秒后出现1像素宽横向条纹随刷新次数增多而扩散。排查链路第一步用示波器抓SCK波形发现高电平时间从170ns逐渐增至185ns因编译器优化波动第二步检查LCD_SPI_WriteByte发现__ASM volatile(nop)被编译器重排第三步在NOP前后添加__ASM volatile( ::: memory)内存屏障强制顺序执行第四步改用__NOP()宏Keil内置带内存屏障问题消失关键修复代码// 替换原NOP序列 __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP();__NOP()是Keil专有宏确保指令不被优化且有序执行。实测抖动从±15ns降至±2ns。4.4 问题4同一代码在A厂模组上正常B厂模组上色彩饱和度低——Gamma校准缺失现象A厂屏显示鲜艳B厂屏发灰对比度差。根源分析ST7735R支持Gamma校准寄存器0xE0, 0xE1但初始化序列未配置。不同厂商模组出厂Gamma值不同。解决方案添加Gamma校准命令B厂模组实测最优值// Gamma Set (Positive Polarity) LCD_WriteCommand(0xE0); LCD_WriteData(0x07); LCD_WriteData(0x10); LCD_WriteData(0x0F); LCD_WriteData(0x07); LCD_WriteData(0x03);
返回列表