
简介面向STM32嵌入式开发者的4.3英寸TFT LCD触摸屏驱动例程包定位清晰适合学习STM32F103ZET6控制彩色液晶屏与触摸交互的入门用户也适合需要快速从KEIL迁移至STM32CubeIDE的工程师。例程基于NT35510控制器通过16位8080并口驱动屏幕触摸芯片GT968/GT1151采用I2C总线通信代码由正点原子KEIL工程完整移植到STM32CubeIDE环境可直接用HAL库打开编译。压缩包整体约73.02MB上游未提供文件总数及类型明细结合资源描述可知例程内容包含基于CubeIDE的完整工程源码及屏幕/触摸驱动实现可配合CSDN博文《STM32 驱动4.3寸TFT LCD 触摸屏》使用。目前已有956人学习下载。通过该例程能快速掌握LCD初始化、画点画线、字符显示、触摸坐标读取等流程深入理解8080并口时序与I2C触摸芯片配置方法大幅缩短自行移植和调试时间。 拿到“STM32F103ZET6-TFTLCD-TOUCH-4.3INCH.rar”这个工程压缩包的时候很多人第一反应是解压、打开工程、点编译然后烧进去看效果。但实际折腾过的人都知道这套组合——STM32F103ZET6主控配4.3寸TFTLCD加触摸——说大不大说小也绝对不小。你要是没搞明白驱动原理和硬件映射关系轻则白屏点不亮重则把屏烧了或者MCU引脚冲突到怀疑人生。这期就以这个经典的rar工程为引子把背后涉及到的FSMC并口驱动、电阻/电容触摸坐标换算、引脚重映射和PWM背光调节这些核心点一层层拆开来讲清楚。不管你是刚入手STM32的新手还是准备把LCD触摸屏塞进智能小车、仪表盘项目的进阶玩家这篇都能给你点实在的参考。1. 解压后先别急着编译工程结构与板卡连线速览1.1 工程包里通常藏着什么这类以芯片型号外设名命名的rar包一般不是某个官方SDK而是前人已经调通的一套完整工程模板。解压后你大概率会看到几个固定目录USER存放main.c、stm32f10x_it.c、系统配置文件、CORE存放启动文件startup_stm32f10x_hd.s和内核相关头文件、SYSTEM正点原子风格的delay、sys、usart底层、HARDWARElcd、touch、key、led等外设驱动以及FWLIB或STM32F10x_FWLib标准外设库的源码。这种目录结构本身就是一套很好的学习范本它把硬件相关代码和业务逻辑分开了后期你要移植到智能车或者做项目改造只改HARDWARE和USER里的东西就够了。打开main.c之前我建议你先做两件事第一看HARDWARE/lcd.h里的屏幕分辨率宏定义确认是不是480x272第二看lcd.c里的LCD控制器型号比如LCD_SSD1963、LCD_R61509V之类。4.3寸屏的控制器型号比较杂有SSD1963、R61509V、ILI9806等不同控制器的初始化寄存器序列不一样如果工程里的型号和实物对不上屏幕是死活点不亮的。1.2 4.3寸屏到底怎么接到STM32上4.3寸TFTLCD模块几乎没有用SPI接口的基本都是16位8080并口触摸部分要么是四线电阻屏通过XPT2046或ADS7843这类ADC芯片转SPI要么是电容屏通过FT5x06、GT9147等I2C接口芯片。所以你会在模块背面看到一大排排针包括FSMC_D0~D15、FSMC_Ax、LCD_RS、LCD_WR、LCD_RD、LCD_RST、LCD_CS、T_CS、T_CLK、T_DIN、T_DOUT、T_IRQ等。这里很多人第一次会懵为什么LCD的RS寄存器/数据选择要接到FSMC地址线上为什么数据线叫FSMC_D而不是GPIO原因正是这一节要讲的核心——FSMC外设的硬件自动读写机制。简单讲STM32内部把外部LCD当作一块静态存储器来访问你往某个地址写数据FSMC会自动把数据总线和控制线按8080时序拉高拉低不需要CPU一条一条指令去翻转GPIO。所以先把模块和开发板的连线理清楚哪个引脚对应哪个功能再去看代码你会觉得逻辑顺很多。1.3 三分钟确认工程和你的板子匹配拿到工程先把HARDWARE/lcd.h和HARDWARE/touch.h打开对照里面宏定义的引脚号和你手上的开发板原理图。检查顺序记住三样片选CS、复位RST、背光BL。背光引脚在很多板子上直接接3.3V或者由某个GPIO控制如果工程里把背光接到了某个PWM引脚比如PB1那说明作者可能做了背光亮度调节功能你要确认这个引脚没被其他外设占用。我的习惯是先在纸上画一遍引脚映射表花五分钟做这件事能省掉后面三小时查线的时间。2. FSMC这条高速公路4.3寸屏并口驱动为什么非得这么干2.1 为什么4.3寸屏偏爱FSMC而不是普通GPIO很多人一开始不理解TFTLCD不能像OLEDSSD1306那样用I2C或者SPI吗答案是屏幕分辨率越高需要的像素数据量越大低速串口根本喂不饱。480x272像素、16位色深一帧全屏数据是480x272x2约等于261KB就算只在局部刷新用18MHz的SPI刷图也会明显看到撕裂和拖影而16位并口在FSMC的50MHz时钟下刷起来就流畅得多。FSMC是“灵活的静态存储控制器”它本来是为了扩展外部SRAM、NOR Flash而设计的但8080并口的LCD时序跟SRAM读写时序非常接近——都有片选、读使能、写使能、地址/数据总线所以直接拿来驱动LCD属于“杀鸡用牛刀但牛刀确实更好使”。这就是为什么绝大多数32位MCU方案驱动大屏都默认走FSMC而不是GPIO模拟时序。GPIO模拟并非不行但每个像素都要多条指令翻转IOCPU占用率直接拉满系统干不了别的事。2.2 8080时序在FSMC里是怎么“翻译”的8080并口通信的关键在于写命令和写数据要分两个阶段靠RS引脚区分——RS拉低表示当前总线上是命令/寄存器地址RS拉高表示当前总线上是要写入或读出的数据。在FSMC驱动方案里RS引脚直接连到一根FSMC地址线比如FSMC_A0。这样CPU访问地址0x60000000时A00对应命令周期访问0x60000002时A01对应数据周期。代码里通常会出现这两句宏定义#define LCD_CMD_ADDR *(volatile uint16_t *)0x60000000 #define LCD_DATA_ADDR *(volatile uint16_t *)0x60000002往LCD_CMD_ADDR写值FSMC就会自动完成片选拉低、地址建立、写使能脉冲这套动作然后总线上的数据被LCD当作命令接收往LCD_DATA_ADDR写值就被当作要显示的数据或要写入的寄存器值。这个机制理解透之后你再看LCD_WriteReg、LCD_WriteData这些函数本质上就是不同地址的指针赋值而已。2.3 关键初始化代码逐行拆解FSMC驱动LCD的初始化分三步使能GPIO时钟和FSMC时钟、配置FSMC的NOR/SRAM Bank1时序参数、把LCD控制器内部寄存器配置成正确的显示模式。GPIO部分要特别注意FSMC_D0~D15复用在GPIOD和GPIOE上控制线A0、NE1、NOE、NWE分别复用在不同GPIO上这些引脚必须配置为GPIO_Mode_AF_PP复用推挽输出。接下来是FSMC时序结构体FSMC_NORSRAMInitStructure.FSMC_AddressSetupTime 0x01; // 地址建立时间 FSMC_NORSRAMInitStructure.FSMC_DataSetupTime 0x05; // 数据建立时间 FSMC_NORSRAMInitStructure.FSMC_MemoryType FSMC_MemoryType_SRAM; FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth FSMC_MemoryDataWidth_16b; FSMC_NORSRAMInitStructure.FSMC_Bank FSMC_Bank1_NORSRAM1; // NE1片选这几个时间参数是调屏的重点设得太短高速读写下数据建立不够容易花屏设得太长速度变慢刷新率上不去。经验值上4.3寸屏SSD1963/R61509V用AddressSetupTime1、DataSetupTime5起步如果出现花屏或颜色错乱优先把DataSetupTime调到8~10试试。很多人看到初始化代码里还有一大段LCD_Init里面是一连串寄存器写值操作那些其实不需要背关键是知道它是让LCD控制器处于RGB565、480x272、逐行扫描等正确状态即可。3. 触摸坐标的换算从ADC裸值到屏幕像素的“变形记”3.1 触摸IC是怎么把“按压”变成数字的4.3寸模块的常见配置是四线电阻屏配XPT2046触摸IC这个IC通过SPI接口和MCU通信。它的工作原理不复杂电阻屏的上下两层导电膜按压时在触点位置短接XPT2046通过切换X、X-、Y、Y-的供电方向分别在两个方向上测出分压值这个电压经过内部ADC转成12位数字量就是“原始坐标”。用SPI读取XPT2046时控制字节里关键位包括起始位、通道选择A2~A0和分辨率SER/DFR。比如要读X方向坐标控制字通常设为0xD0也就是通道Y用于测量X分压读Y方向坐标用0x90。代码里的TP_Read_AD函数核心就几条语句但有一个细节很多人容易忽略连续读取时需要给一点延时或者做多次采样取平均值。触摸屏的ADC值在按压抖动时变化非常大不做滤波的话你画的线会是毛刺状的。3.2 两点校准的数学原理直接读出的X/Y原始ADC值跟LCD像素坐标存在一个线性关系像素坐标 原始值 × 比例因子 偏移量。这就是为什么工程里touch.h会有TOUCH_AD_LEFT、TOUCH_AD_RIGHT、TOUCH_AD_TOP、TOUCH_AD_BOTTOM这些宏定义——它们就是用两点校准法标定出来的边界值。两点校准的操作方式屏幕上先显示左上角和右下角两个十字标记让你用笔分别点击记录下这两点的ADC值然后反推出映射关系。比如屏幕分辨率480x272左上角触点的ADC值是(X1, Y1)右下角是(X2, Y2)那么:X方向比例因子 480 / (X2 - X1)Y方向比例因子 272 / (Y2 - Y1)X偏移量 -X1 × 比例因子Y偏移量 -Y1 × 比例因子拿到这四个参数之后运行时每个触摸点都做一次线性变换就能把ADC值变成屏幕像素坐标。这个换算不是“可选项”是必选项。不校准直接用的后果就是你点屏幕左边的按钮系统以为你点的是右边十个新手有八个会在这上面卡住。3.3 校准参数怎么用宏定义和运行时修正工程里的校准参数有两种存在形式一种直接写死在头文件宏定义里适合屏和触摸屏固定、没有批量生产的场景另一种是开机进校准模式把参数存到EEPROM或Flash的特定扇区适合需要换屏或者现场调试的项目。如果你只是想跑通例程直接用头文件里的宏定义即可但如果你发现点按位置总偏不要急着改代码先用工程自带的“触摸校准”例程重新采一次值。另外触摸屏有一个特性叫“负载效应”——就是按压位置不同ADC值会有轻微非线性变化这在四线电阻屏上尤其明显。廉价4.3寸电阻屏的边角位置偏差个十几二十个像素很正常所以如果你要做按键密集的UI建议按键做大一点或者做“长按确认”逻辑来防误触。我自己的项目里一般会把触摸判定区域比视觉按键区域外扩10像素体验会好很多。4. 显示中文、呼吸灯和GPIO重映射把例程改成自己的东西4.1 让屏幕显示中文字库和编码绕不开的三件事TFTLCD例程里默认只带英文ASCII字库想显示中文你得先解决三件事字库、取模格式、编码转换。常用的做法是把16x16点阵中文字库比如标配的GB2312字库烧到外部SPI Flash里需要显示某个汉字时先算出它在字库文件中的偏移地址读出点阵数据再交给LCD驱动去描点。汉字在GB2312里的定位公式是很多人的坎。一块16x16点阵字库文件里每个汉字占用32字节。假设要显示“嵌”字首先要拿到它的GBK机内码在字符串里实际存储的两个字节然后查区位码表计算出它在字库文件中的偏移。工程里常见的一个取字模函数会做这样的运算uint32_t offset ((GBKH - 0xA1) * 94 (GBKL - 0xA1)) * 32;这段代码的含义是GB2312编码的汉字按区94区和位94位排列减去0xA1得到区位索引再加上字库文件的偏移基数。理解了这个公式你换任意大小的字库比如24x24、32x32都能举一反三——只是点阵字节数从32变成72或128。如果不做编码转换直接从UTF-8编码的字符串里截取汉字显示就会出现乱码甚至数组越界。常见做法是在工程里用C标准库或自带的编码转换函数或者干脆在源码里用**开头的GBK编码字符串常量把编辑器编码设置为GB2312。4.2 TIM3呼吸灯从引脚重映射到PWM占空比调节工程跑通LCD之后很多人会顺手做个小实验——让屏幕背光或者LED“呼吸”起来。这就要用到TIM3的PWM输出了。但这里藏着一个比较容易忽略的点TIM3的PWM通道默认映射在PA6、PA7、PB0、PB1上但如果你用的开发板把PA6/PA7已经连到别的东西上了比如SPI1或ADC你就需要用到引脚重映射功能把TIM3_CH1重映射到另一个引脚上。引脚重映射的操作本身很简单但要先打开AFIO时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); // 开启AFIO时钟 GPIO_PinRemapConfig(GPIO_Remap_TIM3, ENABLE); // 全重映射这之后TIM3_CH1会从PA6跑到PC6CH2从PA7跑到PC7等等。很多人忘了开AFIO时钟结果重映射函数一调用就进HardFault。PWM调光亮度的核心是改占空比而呼吸灯无非就是让CCR寄存器值随时间从0慢慢加到ARR再降回来。比如ARR设为999代表PWM周期是1000个计数单位那CCR500时背光就是半亮。用一个简单的三角波算法或者sin曲线算法去更新CCR就能实现顺滑的呼吸效果。注意一点更新CCR值尽量放在PWM更新中断或者主循环里定时执行不要在PWM输出中断里做浮点运算否则会影响时序稳定性。4.3 把LCD触摸模块拆出来塞进智能车项目到了这个阶段你可以把整个工程里跟自己需求无关的东西砍掉只保留LCD驱动、触摸驱动、延时函数再做成一个模块化的库。比如做智能小车的时候在LCD上显示速度曲线、电池电压、转向角度用触摸屏做“动画菜单”选择各种模式——这些都可以复用之前调通的代码。我的建议是把LCD和触摸屏的驱动文件从例程中单独拿出来放进你自己工程的一个BSP目录同时把所有硬件相关的宏定义集中到bsp_lcd.h和bsp_touch.h。这样当你换屏或者换主控比如从F103换到F407时只需要改这两个头文件里的引脚映射和FSMC时序参数显示层和业务层的代码完全不用动。这一步做得好后续项目迭代的效率会提升一个档次。5. 实测翻车现场白屏、漂移、引脚打架的排查实录5.1 白屏先查背光再查FSMC时序白屏是这类工程最常见的故障但白屏也分“纯白”和“有背光但无显示”两种。纯白说明背光亮了但LCD没有收到正常的数据/命令如果连背光都不亮问题九成出在硬件供电或背光控制引脚上。先用万用表量一下LCD模块的VCC是不是3.3V或5V视模块要求再查背光引脚有没有电压。背光正常的情况下白屏排查顺序确认LCD_CS是否接在FSMC_NE1上代码里使用的Bank是否和硬件一致确认LCD_RS用的是FSMC地址线还是普通GPIO两者对应完全不同的驱动方式确认FSMC时序参数是否过短特别是FSMC_DataSetupTime最后检查LCD控制器型号是否匹配。我遇到过一次特别隐蔽的白屏工程用的是ILI9806控制器驱动但实际屏是R61509V一个功能完全一样但寄存器初始化序列不同的控制器导致整个屏幕只有背光亮别的什么都没显示。这个教训是——不要只看屏幕尺寸和分辨率一定要核实控制器型号。5.2 触摸漂移先怀疑电源再怀疑算法触摸屏出现“点不准”“越来越飘”的问题多数不是算法写错了而是参考电压不稳定。XPT2046的分压测量完全依赖于VCC的稳定度如果给触摸IC供电的引脚上还挂了背光LED或者电机驱动电流一波动ADC参考电压跟着抖坐标自然飘。解决办法很简单触摸IC的VCC单独走线或者在靠近触摸IC的电源引脚处加一颗10uF钽电容和0.1uF陶瓷电容去耦。另外如果电阻屏用了很久屏幕表面有污渍或者导电膜老化校准数据也会慢慢失效这种情况下重新校准只能缓解换屏才是根本。软件层面还有一个坑读取触摸坐标时没有做“有效判断”就上报。实际按压时触点接触会有几十毫秒的抖动这期间读到的坐标是跳变的。我习惯在读取函数里加一个简单的“连续两次采样差值小于阈值才算有效”的滤波逻辑实测能消除大部分触摸漂移的观感问题。5.3 引脚冲突重映射和JTAG这两个老冤家用STM32F103ZET6这种144脚的大芯片引脚资源一般不会太紧张但依然要小心两种冲突。第一种是FSMC引脚和JTAG引脚打架STM32F103的JTAG默认占用PB3、PB4、PA15等引脚而这些引脚有时候会被复用到FSMC地址线或者TIM3重映射通道上。不能用JTAG调试。第二种是TIM3重映射后跑到PC6~PC9而这几个引脚可能和SDIO或FSMC的数据线共用。我的习惯是每次做引脚分配时先打开芯片的数据手册在引脚功能表上把占用情况标一遍特别是要对照GPIO_Remap表格确认重映射后的引脚没有和别的外设冲突。最后说一个关于电源的重要提醒4.3寸TFTLCD的背光电流不小峰值能到200mA以上。如果你用开发板上的3.3V LDO给屏幕和MCU同时供电亮屏瞬间压降很容易导致MCU复位表现就是“一上电就白屏重启循环不断”。这种问题不是代码能解决的要么给屏幕单独供电要么换一个输出电流足够的电源方案。我自己后来做带屏项目都是直接上5V输入、一路给屏供电、一路给MCU供电再共用GND从此再没遇到“一亮屏就重启”的毛病。这套STM32F103ZET6 4.3寸TFTLCD 触摸的组合包看着只是一个rar压缩包但能把里面的FSMC时序、触摸校准、引脚复用这几块真正吃透你对整个STM32的硬件抽象和外设协作能力会上一个很大的台阶。别急着把工程跑通就完事试着把里面的LCD驱动改成你自己项目的模块试着调一调呼吸灯和触摸按钮的联动你会发现自己对“嵌入式系统”这四个字的理解变得具体多了。本文还有配套的精品资源点击获取