1. 从一个“点亮屏幕”的小目标说起很多人拿到ESP32开发板之后第一个想做的交互项目往往不是点灯而是让一块屏幕显示出字来。原因很简单灯只能告诉你“有电”而屏幕能告诉你“系统活着而且听懂了你的话”。串口屏就是实现这个目标最省事的路径之一。它把复杂的图形绘制、字库管理、触摸响应都封装在屏幕背后的控制芯片里主控只需要通过串口发几条指令屏幕就会乖乖把文字画出来。ESP32负责逻辑串口屏负责显示分工明确各干各的活。这篇内容围绕“咸鱼ESP32实例—串口屏文字显示”这个主题展开面向的是刚接触ESP32、手里有一块串口屏、但不知道从哪下手的朋友。我会把硬件接线、串口参数、指令格式、字库选择、常见翻车点全部拆开讲清楚让你看完就能自己动手把文字显示出来。关键词包括ESP32、串口屏、UART通信、文字显示、字库、指令集、波特率、TTL电平。这些词后面都会反复出现因为它们是这条链路里绕不开的核心。先明确一个概念串口屏不是普通SPI或I2C屏幕。它内部通常有一颗独立的MCU或专用显示控制芯片负责驱动液晶面板、管理字库、处理触摸事件。你发给它的不是像素数据而是“在坐标(x,y)用字号z显示字符串abc”这样的命令。这种模式的好处是主控压力极小ESP32只需要占用一个UART外设剩下的计算和显存都交给屏幕自己。代价是刷新率和动画能力有限但对于显示传感器数据、菜单、状态提示这类场景完全够用。我见过不少人一上来就买SPI TFT屏结果被驱动库、字库取模、显存分配折腾得够呛最后连“Hello World”都没显示出来。串口屏的入门门槛低得多只要你会用串口发数据就能让屏幕出字。这也是为什么我建议新手从串口屏开始先把“主控与显示设备通信”这件事跑通再去挑战更底层的屏幕方案。2. 硬件选型与接线别让电平问题毁掉一晚上2.1 串口屏的常见类型与挑选思路市面上的串口屏大致分两类一类是“指令屏”你发什么指令它就执行什么屏幕本身不存逻辑另一类是“组态屏”配套上位机软件做界面工程屏幕里跑一套预设的页面逻辑主控只负责改变量。对于“文字显示”这个需求指令屏更直接学习成本也更低。挑选时重点看三个参数供电电压、通信电平、默认波特率。供电电压常见有5V和12V两种。5V的可以直接从ESP32开发板的5V引脚取电但要注意电流是否足够屏幕背光全开时可能吃掉几百毫安。12V的就需要额外电源不能直接从开发板取。通信电平方面绝大多数串口屏的UART是TTL电平但电压域可能是5V也可能是3.3V。ESP32的IO是3.3V电平如果屏幕的RX接受5V输入长期看可能损伤ESP32的TX引脚反过来屏幕的TX输出5V直接进ESP32的RX也会有问题。注意在接线之前务必确认屏幕的通信电平。如果不确定先用万用表量一下屏幕TX引脚在空闲状态下的电压接近3.3V就直连接近5V就需要电平转换。2.2 ESP32与串口屏的接线方案ESP32一般有三个UARTUART0用于下载和调试UART1和UART2可以自由映射到任意GPIO。我习惯用UART2来连串口屏把UART0留给串口监视器这样调试信息不会和屏幕指令混在一起。典型接线如下ESP32引脚串口屏引脚说明GPIO17 (TX2)RXESP32发送指令给屏幕GPIO16 (RX2)TX屏幕返回数据给ESP325VVCC供电确认屏幕工作电压GNDGND共地必须接这里有个容易忽略的点TX和RX必须交叉连接。ESP32的TX接屏幕的RXESP32的RX接屏幕的TX。我见过有人直连TX到TX然后纳闷为什么屏幕一点反应都没有。另外GND一定要接不共地的话信号没有参考电平通信必然失败。如果你用的是UART1默认引脚是GPIO9和GPIO10但这两个引脚在某些ESP32模组上连接了内部Flash使用时会出问题。所以更稳妥的做法是用UART2或者在代码里把UART1重映射到其他空闲GPIO。2.3 供电与背光的实际问题串口屏的背光电流不容小觑。一块3.5寸屏背光全开可能达到200mA以上加上屏幕控制芯片本身的功耗整体可能超过300mA。ESP32开发板上的5V引脚通常来自USB供电USB口能提供500mA左右勉强够用但如果同时接了其他外设就可能出现屏幕闪烁或ESP32复位。我的做法是给屏幕单独供5V只把GND和ESP32共起来。这样屏幕的电流不经过开发板的稳压器系统更稳定。如果屏幕支持背光调节指令也可以在初始化时把背光亮度调低一些既能省电又能延长背光寿命。3. 串口参数与指令格式让屏幕听懂你的话3.1 波特率、数据位、停止位的匹配串口通信的第一道坎就是参数匹配。串口屏出厂时通常有一个默认波特率常见的是9600、115200、或者57600。你必须在ESP32代码里把UART配置成完全一样的参数否则收到的就是乱码或者根本没反应。以115200为例ESP32端的配置如下Serial2.begin(115200, SERIAL_8N1, 16, 17);这行代码的意思是使用UART2波特率1152008位数据位无校验1位停止位RX接GPIO16TX接GPIO17。SERIAL_8N1是绝大多数串口屏的默认格式如果你不确定先按这个来不通再查屏幕手册。有个细节值得注意有些串口屏在启动时会打印一串开机信息如果你在ESP32的setup里立刻发指令可能会和开机信息冲突。我通常会在setup里先delay(500)到1000毫秒等屏幕初始化完成后再发第一条指令。3.2 指令帧结构拆解串口屏的指令通常是十六进制帧不同厂家的协议不一样但结构上大同小异。以常见的“文本显示”指令为例一帧可能包含以下部分帧头固定字节比如0xAA或0x5A用来标识一帧的开始指令类型比如0x01表示显示文本坐标X和Y各占两个字节高字节在前或低字节在前要看协议字号一个字节对应屏幕内部预设的字库大小颜色两个字节RGB565格式文本内容字符串的ASCII或GBK编码帧尾固定字节比如0xBB或者校验和具体到某个屏幕你必须拿到它的指令手册。没有手册的话可以尝试用串口助手发一些常见帧格式观察屏幕反应。但更靠谱的办法是找卖家要资料或者在网上搜同型号的开源例程。3.3 用代码封装指令发送函数直接在主循环里拼十六进制数组会让代码很难维护。我的习惯是写一个发送函数把坐标、字号、颜色、文本作为参数传进去函数内部负责拼帧和发送。void sendText(uint16_t x, uint16_t y, uint8_t font, uint16_t color, const char* text) { uint8_t frame[128]; uint8_t idx 0; frame[idx] 0xAA; // 帧头 frame[idx] 0x01; // 指令类型显示文本 frame[idx] x 8; frame[idx] x 0xFF; frame[idx] y 8; frame[idx] y 0xFF; frame[idx] font; frame[idx] color 8; frame[idx] color 0xFF; while (*text) { frame[idx] *text; } frame[idx] 0xBB; // 帧尾 Serial2.write(frame, idx); }这段代码是示意性的实际帧格式要根据你的屏幕手册调整。重点在于把拼帧逻辑封装起来主逻辑里只需要调用sendText(10, 20, 16, 0xFFFF, Hello)代码可读性会好很多。提示如果屏幕支持GBK编码显示中文时字符串要用GBK编码的字节数组不能直接用UTF-8。Arduino IDE默认源文件是UTF-8直接传中文会乱码。解决办法是用取模软件生成GBK字节数组或者把源文件保存为GBK编码。4. 字库与编码中文显示的真正门槛4.1 屏幕内置字库与外部字库的区别很多串口屏内置了ASCII字库和一级汉字字库这意味着你发ASCII字符或者常用汉字的GBK编码屏幕就能直接显示。但内置字库的字号通常是固定的几档比如16×16、24×24、32×32不能任意缩放。如果你需要特殊字体或者更大字号就需要用外部字库文件通过屏幕的SD卡接口或者Flash存储来加载。内置字库的好处是省事发指令就能显示。坏处是字体样式单一而且有些屏幕的内置汉字字库只覆盖常用字生僻字显示不出来。外部字库灵活但需要额外的取模和下载步骤。对于“文字显示”这个入门需求先用内置字库跑通等有特殊需求再折腾外部字库。4.2 ASCII与GBK编码的实操处理ASCII字符的编码很简单一个字节对应一个字符。中文就麻烦一些GBK编码里一个汉字占两个字节而且这两个字节的值都可能大于0x7F。在C语言里如果直接用char数组存GBK字符串要注意符号扩展问题。更稳妥的做法是用uint8_t数组明确按字节处理。// GBK编码的“你好”字节序列 uint8_t text[] {0xC4, 0xE3, 0xBA, 0xC3, 0x00}; Serial2.write(text, 4);如果你在Arduino代码里写中文字符串比如sendText(10, 10, 16, 0xFFFF, 你好)编译器会按源文件编码来存这个字符串。如果源文件是UTF-8那“你好”就是6个字节屏幕按GBK解析就会乱码。解决办法有两个一是把源文件另存为GBK编码二是用取模软件生成GBK字节数组直接嵌入代码。我个人的习惯是用取模软件生成字节数组虽然看起来不够优雅但胜在可控。每次要显示新的中文内容就用软件生成对应的GBK字节序列复制到代码里。这样不管IDE是什么编码显示都不会出问题。4.3 字库大小与显示效果的权衡字号越大显示越清晰但占用的屏幕空间也越大。16×16的汉字在3.5寸屏上已经能看清24×24更适合远距离观看32×32则适合标题或者大数字显示。选择字号时要考虑屏幕分辨率和观看距离。如果是手持设备16或24就够了如果是固定在墙上的显示面板32甚至更大才看得清。另外不同字号的字库文件大小差异很大。一级汉字3755个16×16字库大约120KB24×24大约270KB32×32大约480KB。如果屏幕的Flash空间有限就要权衡字库覆盖范围和字号。有些屏幕支持按需加载字库用哪个字就取哪个字的模这样可以节省空间但实现起来更复杂。5. 从零跑通第一个显示例程5.1 最小验证先让屏幕回个话在写显示代码之前我建议先做一个最小验证ESP32发一条简单指令看屏幕有没有反应。最简单的指令通常是“清屏”或者“设置背光”。如果屏幕有返回数据的功能也可以发一条查询指令看ESP32能不能收到回复。这个步骤的目的是把通信链路先打通。如果连清屏都做不到那问题一定在接线、波特率、或者电平上跟指令格式无关。把问题范围缩小排查起来会快很多。void setup() { Serial.begin(115200); // 调试串口 Serial2.begin(115200, SERIAL_8N1, 16, 17); // 屏幕串口 delay(1000); // 发送清屏指令示例具体字节看手册 uint8_t clearCmd[] {0xAA, 0x02, 0xBB}; Serial2.write(clearCmd, sizeof(clearCmd)); Serial.println(Clear command sent); } void loop() { // 空循环先验证通信 }烧录后打开串口监视器看是否打印了“Clear command sent”。如果屏幕有变化比如闪一下或者变黑说明通信链路是通的。如果没反应先检查接线和波特率。5.2 显示第一行文字通信打通之后就可以发文本显示指令了。先用ASCII字符测试因为ASCII编码简单不容易出错。比如显示“ESP32 OK”void loop() { sendText(10, 10, 16, 0xFFFF, ESP32 OK); delay(2000); }如果屏幕上出现了白色的“ESP32 OK”恭喜你核心链路已经跑通了。接下来可以尝试改坐标、改颜色、改字号观察屏幕的变化。这个过程能帮你快速理解指令里各个参数的作用。5.3 动态刷新与数据展示静态文字显示只是第一步实际项目里更常见的是动态刷新比如显示传感器读数、倒计时、状态变化。动态刷新的关键是“局部刷新”只更新变化的区域而不是整屏重绘。整屏重绘会导致闪烁而且刷新率上不去。局部刷新的做法是先用背景色在文字区域画一个填充矩形把旧文字盖掉再在新位置或者用新内容显示文字。有些屏幕支持“透明背景”模式文字直接覆盖不需要先填充背景。具体用哪种方式要看屏幕指令集的支持情况。void updateValue(int value) { // 先用背景色清除旧值区域 fillRect(100, 50, 80, 24, 0x0000); // 黑色背景 // 再显示新值 char buf[16]; sprintf(buf, %d, value); sendText(100, 50, 24, 0x07E0, buf); // 绿色文字 }这段代码里fillRect是假设屏幕支持填充矩形指令。如果不支持可以用显示空格字符串的方式来覆盖旧内容。动态刷新时要注意刷新频率太快了屏幕反应不过来太慢了数据更新不及时。一般100到200毫秒刷新一次比较合适。6. 那些让我熬夜的坑与排查思路6.1 屏幕完全没反应从供电查到指令屏幕没反应是最常见的问题排查顺序应该是供电→接线→波特率→指令格式。先量屏幕的VCC和GND之间有没有电压再看TX和RX有没有接反然后确认ESP32和屏幕的波特率是否一致最后才怀疑指令格式。我遇到过一次屏幕没反应查了半天发现是杜邦线内部断了。万用表量通断才发现外表看不出来。所以接线排查不能只看插没插上要量通断。6.2 显示乱码编码问题占九成乱码的原因几乎都是编码不匹配。ASCII字符乱码通常是波特率不对中文乱码通常是UTF-8和GBK搞混了。排查方法是先只显示ASCII如果ASCII正常说明通信参数没问题问题在中文编码上。如果ASCII也乱码那就是波特率或者数据位配置错了。还有一种乱码是“部分正常部分乱码”比如英文字母正常数字乱码。这种情况通常是字库覆盖范围的问题屏幕内置字库可能不包含某些符号。解决办法是换字号或者换字库。6.3 屏幕闪烁或ESP32复位电源在报警屏幕闪烁、ESP32频繁复位十有八九是供电不足。屏幕背光启动瞬间电流很大如果和ESP32共用USB供电电压会被拉低导致ESP32欠压复位。解决办法是给屏幕单独供电或者在背光引脚上加一个大电容缓冲。我试过在屏幕VCC和GND之间并一个470μF的电解电容闪烁问题明显改善。电容的作用是在电流突变时提供缓冲相当于给屏幕配了一个小水库。6.4 指令发出去了但屏幕只执行一半有时候屏幕会“吃掉”一部分指令比如坐标对了但文字没显示或者文字显示了但颜色不对。这通常是帧结构的问题帧头帧尾不对、字节序不对、或者校验和算错了。逐字节对照手册用串口助手手动发一遍看屏幕反应能快速定位问题。还有一种可能是发送速度太快屏幕来不及处理。在两条指令之间加一个delay(10)到delay(50)给屏幕一点处理时间。特别是连续发多条指令时这个延时很关键。7. 进阶思路让文字显示更实用7.1 用变量和格式化输出组织内容实际项目里显示的内容往往是动态的。用sprintf把数值格式化成字符串再发给屏幕是最常用的做法。注意缓冲区要足够大避免溢出。char buf[32]; float temp 25.6; sprintf(buf, Temp: %.1f C, temp); sendText(10, 100, 16, 0xFFFF, buf);浮点数格式化在ESP32上默认可能不支持需要在Arduino IDE里开启“Enable full printf”选项或者用dtostrf函数替代。7.2 多页面与状态切换如果项目需要显示多个页面比如主界面、设置界面、数据界面可以用一个变量记录当前页面根据页面值决定显示哪些内容。切换页面时先清屏再绘制新页面的内容。清屏指令通常比逐区域清除更快但会闪一下。如果屏幕支持双缓冲或者页面切换指令体验会更好。7.3 与传感器结合的实际案例串口屏文字显示最常见的应用就是配合传感器做数据展示。比如接一个温湿度传感器每隔几秒读一次数据格式化后显示在屏幕上。这个过程中ESP32负责读取传感器、处理数据、发送显示指令串口屏负责呈现。整个链路清晰调试也方便。我在一个环境监测的小项目里就是这么做的ESP32通过I2C读温湿度通过UART发给串口屏屏幕上分三行显示温度、湿度、更新时间。代码量不大但实用性很强。后来加了按键切换页面可以看历史最大值最小值整个交互就完整了。7.4 性能与稳定性的长期观察长时间运行后可能会遇到屏幕花屏、通信卡死等问题。我的经验是加一个看门狗定期检查通信状态。如果连续几次发送失败就重新初始化UART和屏幕。另外避免在中断里发送串口数据中断里发数据容易导致时序问题。把要发送的内容放到队列里在主循环里统一发送稳定性会好很多。还有一点串口屏的指令响应需要时间不要在主循环里无延时地连续发送。加一个状态机等上一条指令的响应如果有或者固定延时后再发下一条。这样虽然刷新率低一点但系统更稳。8. 我个人的几条实操心得第一买屏幕的时候一定要找卖家要指令手册和例程。没有手册的屏幕除非你愿意花大量时间逆向否则不要碰。手册里的指令格式、波特率、字库说明能帮你省下无数个夜晚。第二先用USB转TTL模块在电脑上把屏幕调通再接到ESP32上。电脑上用串口助手发指令屏幕能正常显示说明屏幕本身没问题。然后再把同样的指令通过ESP32发这样能把问题范围缩小到ESP32端。第三中文显示不要硬编码UTF-8字符串用取模软件生成GBK字节数组。虽然代码看起来不够优雅但省去了编码转换的麻烦显示效果也稳定。第四供电一定要留余量。屏幕背光、ESP32、传感器加起来电流可能超过USB口的输出能力。给屏幕单独供电或者在电源端加足够的滤波电容能避免很多玄学问题。第五调试信息走UART0屏幕指令走UART2。不要把调试信息和屏幕指令混在同一个串口上否则你会在串口监视器里看到一堆十六进制乱码根本没法调试。第六指令之间加延时。屏幕不是电脑处理速度有限。连续发指令时每条之间加10到50毫秒延时能显著降低丢指令的概率。第七先跑通ASCII再折腾中文。ASCII显示正常了说明通信链路、波特率、指令格式都没问题。这时候再处理中文编码问题范围就小很多。第八保留一个“最小可运行例程”。每次改代码改出问题了就回到最小例程确认硬件没问题再逐步加功能。这样能快速定位是哪一步引入的问题。这些经验都是我在实际项目中踩坑踩出来的希望对你有用。串口屏文字显示本身不复杂但细节很多把细节处理好了后面做更复杂的界面就轻松了。