ARTICLE DETAIL

资讯详情

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

UART串口通信详解:从电平标准到STM32配置与调试

UART串口通信详解:从电平标准到STM32配置与调试 简介UART串口通信的Verilog实现资料包面向FPGA学习者和嵌入式系统开发者解决设备间全双工串行通信的设计与验证问题适合有一定数字逻辑基础的读者参考。包内共89个文件以v格式的RTL源码uart.v、fifo.v、clock_divider.v等为核心辅以xdc约束文件、dcp综合结果、bit配置文件、Vivado工程文件及Modelsim仿真脚本整体约642KB目录按srcs、runs、sim等模块划分便于定位设计、约束与验证文件。已有3005人学习下载。资料覆盖UART帧格式、起始位/停止位/校验位、波特率分频及FIFO缓冲等关键点清晰呈现串并转换与时钟控制的Verilog实现思路借助testbench.v可开展仿真测试结合综合报告和布局布线结果能完整走通从RTL设计到FPGA上板验证的流程适合课程设计、毕业设计或项目初版搭建时直接借鉴。1. 串口通信到底在解决什么问题做嵌入式开发的人几乎没有一个能绕开UART串口通信。不管是刚接触单片机的学生还是干了多年的老工程师调试板子的第一步往往就是先把串口打通——日志能出来世界就亮了。UART全称是Universal Asynchronous Receiver/Transmitter通用异步收发器说人话就是一根线发数据一根线收数据不用时钟线两边约定好速度就能通信。很多初学者会问现在USB、以太网、Wi-Fi都这么普及了为什么还在用串口答案很简单串口是所有通信方式里最省资源、最容易调试、最通用的。一个MCU可能没有以太网控制器可能没有USB控制器但几乎不可能没有UART外设。而且串口调试只需要一个USB转TTL工具一根杜邦线打开串口助手就能看到数据这种直观性是其他通信方式比不了的。适合用UART的场景也很明确短距离低速率通信、设备调试日志输出、与蓝牙模块/GPS模块/4G模组对接、传感器数据读取以及两个MCU之间的简单数据交换。如果你做的是高速大吞吐量的传输那是USB或者以太网的活别为难UART。但如果你需要快速验证一块新板子能不能跑起来UART永远是最优先打通的接口。这篇文章我就从硬件电气层、USB转串口芯片、STM32/ESP32/GD32这类主流MCU的配置、以及UART协议本身的细节这几个角度把串口通信这件事完整梳理一遍。新手可以把它当入门指南老手也能在驱动安装、奇偶校验配置、Verilog实现这些模块里找到值得对照的坑。2. 硬件层细节电平、引脚和接线2.1 TTL电平、RS232电平、RS485千万别搞混很多第一次做串口通信的人手里拿着一个USB转TTL工具想连一个老式的RS232设备结果怎么都收不到数据甚至烧坏引脚。原因就是电平标准不匹配。UART在MCU内部走的是TTL电平高电平代表空闲低电平代表起始位逻辑1是3.3V或者5V逻辑0是0V。而RS232是负逻辑电平逻辑1是-3V到-15V逻辑0是3V到15V两者完全相反。如果你直接把TTL电平的设备接到RS232接口上轻则通信失败重则烧毁IO口。RS485则是差分信号A、B两根线之间的电压差来表达逻辑状态抗干扰能力强适合长距离和工业现场。RS485用的还是UART协议只是物理层换成了差分收发器。选型建议板子之间短距离通信用TTL电平直连连接老式工控设备、PC的DB9接口就用RS232转接工业现场长距离传输选RS485。常见的USB转串口工具比如FT232R、CP2102这些芯片的方案输出的是TTL电平不能直接接RS232接口中间需要加MAX3232这类电平转换芯片。2.2 接线顺序和地线的坑UART通信只需要三根线TXD发送、RXD接收、GND地线。最容易犯的错误是TXD和RXD接反。两个设备之间要交叉连接——A设备的TXD接B设备的RXDA设备的RXD接B设备的TXD。很多新手按“同名相连”的习惯去接结果TXD接TXD两边都在发没人收自然是黑屏。地线不接这个问题更隐蔽。有些时候你不接地线短距离通信也能工作因为两边可能通过其他路径共地了。但一旦距离拉长或者两边电源不共地地电位差就会导致通信异常时好时坏、乱码、偶尔丢字节。所以不管距离多近GND一定要接这是我在实际项目里踩过最多的坑之一。还有一点要注意供电电平。3.3V的MCU和5V的MCU之间通信TXD电平不能直接互怼。5V设备往3.3V设备发送时高电平接近5V可能损坏3.3V的IO口。稳妥的做法是加电平转换芯片或者用电阻分压。现在很多STM32的IO口标称容忍5V但你不能盲目相信所有引脚都支持看数据手册确认一下更保险。3. USB转串口芯片FT232R、CP2102这些驱动到底要装什么3.1 常见芯片对应的驱动选择电脑没有原生串口所以要靠USB转串口芯片来虚拟出一个COM口。市面上最常见的芯片型号是FTDI的FT232R/FT232RL、Silicon Labs的CP2102/CP2102N以及CH340、CH341。这些芯片的驱动选择是每个嵌入式工程师入门时都要过的一关。FT232R在Windows下通常会被识别为“USB Serial Port”对应的驱动是FTDI VCPVirtual COM Port驱动。你去FTDI官网或者用Windows Update自动搜索一般都能装上。注意FT232R的老版本驱动偶尔会被Windows签名问题拦下来需要进入高级启动选项禁用驱动签名强制或者直接用最新版的驱动安装包覆盖。CP2102和CP2102N的驱动是Silicon Labs提供的CP210x VCP Driver。CP2102是比较老的型号CP2102N是新版驱动有通用包。很多国产开发板上印着CP2102插上电脑后设备管理器里出现一个带问号的设备就是因为驱动没装好。下载CP210x Universal Windows Driver装完即可。CH340是南京沁恒的方案价格便宜国产开发板用得非常多。它的驱动在官网有Windows 10/11有时能自动识别识别不了就手动装。CH340这个芯片有个特点某些Linux发行版内核自带驱动模块ch341.ko插上就能用反而比Windows还省事。驱动安装成功的标志是在设备管理器里能看到COM口编号比如COM3、COM5。如果你看到的是“USB Serial Port (COM3)”这种字样说明驱动正常。如果看到一个未知设备或者带黄色感叹号的设备先卸载再重装驱动换USB口也值得一试。3.2 驱动装不上的排查思路驱动装不上几乎是每个新手都会遇到的状况。我的排查顺序是这样的第一换USB线。有些USB线只能充电不能传数据线芯只有电源没有数据这种线插上去设备根本不会被识别。判断方法很简单用这根线去连一个U盘或者手机看电脑能不能识别。第二检查设备管理器。如果系统完全没反应可能是USB口供电有问题换一个电脑的USB口试试最好直接用主板背面的USB口而不是前置面板的。第三确认芯片型号。很多开发板的USB转串口芯片丝印被磨掉或者很小看不清如果不知道型号就没法对应驱动。可以看PCB上的丝印、电路结构或者用芯片上的标识来猜。实在不确定可以把PCB拍照放大看。第四装驱动时右键以管理员身份运行安装程序。有时候权限不够会导致驱动安装失败没有任何提示就静默结束了。芯片型号常见驱动名称备注FT232R/FT232RLFTDI VCP Driver老牌方案稳定性好CP2102/CP2102NCP210x VCP Driver笔记本厂商常用CH340/CH341CH340 Driver国产方案价格低MAX232/MAX3232无电平转换芯片不涉及USB驱动4. 主流MCU的UART实操配置4.1 STM32的HAL库串口配置STM32是学习UART最经典的平台因为它的串口外设丰富文档资料也多。HAL库的串口配置流程在STM32CubeMX里很直观选择你要用的UART外设配置模式为Asynchronous异步设置波特率、数据位、停止位、校验位生成代码后在main函数里调用发送和接收函数就行了。CubeMX里默认的波特率是115200这个值配合8个数据位、1个停止位、无校验8N1是最通用的配置。HAL库的串口发送函数是HAL_UART_Transmit()阻塞式发送适合偶尔发一条日志。接收函数HAL_UART_Receive()可以阻塞接收也可以配合中断使用HAL_UART_Receive_IT()。必须提醒的是HAL_UART_Receive_IT()是一次性的接收完指定长度的数据后中断就关闭了想要持续接收必须在回调函数里重新调用一次这个函数。这个“重新使能”的步骤是新手最容易漏的漏了之后就会发生“第一次能收到第二次就断了”的现象。另外HAL_UART_Transmit()的阻塞特性在中断上下文里不能随便调用否则可能卡死。如果要在中断里发数据建议用HAL_UART_Transmit_IT()或者直接把要发的数据放进队列在主循环里再发送。很多人从标准库转到HAL库时觉得串口变复杂了其实HAL的抽象层次更高处理错误和超时的逻辑更完善。C8T6这种小容量芯片的串口配置用标准库和HAL库都能跑但维护代码的时候HAL的强类型结构体更不易出错。4.2 ESP32和GD32的串口配置ESP32的UART很有意思它不像STM32那样把串口引脚固定死而是可以用GPIO矩阵把UART信号映射到几乎任意引脚上。使用Arduino框架时Serial1.begin(115200, SERIAL_8N1, RX_PIN, TX_PIN)就能完成映射和初始化。ESP32有三个硬件UART外设串口0默认用作日志输出串口1和2可以自由安排。ESP32在使用官方IDF框架时uart_driver_install()函数会初始化驱动然后通过uart_write_bytes()和uart_read_bytes()收发数据。要注意的是ESP32的默认日志输出会占用UART0如果你要用UART0做其他事情先重新配置或者直接换用其他UART。GD32是国产MCU里对标STM32的一个系列串口外设和STM32很相似但细节上有差异。GD32的UART支持的数据位有8位和9位9位模式下可以配合奇偶校验使用。算下来支持的字长配置包括7位数据加校验、8位数据加校验、8位无校验、9位数据等模式。如果你用标准库配置串口时发现数据位和校验位的组合不对多半是寄存器里字长位和校验位的联动关系没搞清楚——在GD32里配置校验位之后数据长度要相应调整这一点和STM32几乎一致。4.3 HAL库串口配置的完整步骤用STM32CubeMX配置HAL库串口看起来简单但有几个容易忽略的选项第一DMA选项。如果通信数据量大建议在UART设置页里打开DMA的发送和接收通道。DMA可以做到接收不定长数据配合空闲中断IDLE Line Interrupt来实现“不知道对方发多少字节也能完整接收”的效果。这个方案比固定长度接收要实用得多。第二中断优先级。UART中断的优先级不要和系统滴答定时器冲突。用FreeRTOS时UART中断优先级要低于可管理的最高优先级否则可能导致任务调度异常。第三波特率误差。STM32的USART波特率是由外设时钟分频计算的当你用了非标准波特率比如1000000虽然标准但不在常用表里时实际波特率和设定的波特率之间可能有偏差。双方波特率误差超过2%就可能出现误码。所以两边尽量用同一个标准波特率表9600、19200、38400、57600、115200、230400这些。5. UART协议本身数据位、波特率、奇偶校验5.1 波特率的计算与误差波特率就是每秒传输多少个符号bitUART的波特率通常等于比特率因为一个符号就是一个bit。常用波特率115200表示每秒传输115200个bit算上起始位、停止位和可能的校验位实际有效数据速率要打折扣。在8N1模式下传输1字节需要10个bit1个起始位8个数据位1个停止位所以115200波特率每秒最多传输11520字节。STM32里波特率寄存器的值是BRR 外设时钟频率 / 波特率。如果你用的是72MHz的APB2总线时钟配置115200波特率时分频系数是72000000 / 115200 625刚好是整数误差为0。但如果APB1的36MHz时钟配置28800波特率36000000 / 28800 1250也是整数。看起来数值都合适但当你遇到非整数分频时就会有误差。比如500000波特率配72MHz时钟72000000 / 500000 144还是整数。真正出现误差的是类似921600这种波特率在72MHz下分频系数是78.125取整后误差约0.16%这个量级在短时间内不影响通信但累计多了就丢帧。所以实操建议是能选整数分频的波特率就选整数分频的两个设备通信时优先用115200、57600这些在主流MCU上误差接近0的波特率。5.2 数据位、停止位、校验位的组合UART的帧格式是空闲高电平→ 起始位低电平→ 数据位LSB在前→ 校验位可选→ 停止位高电平。最常见的配置是8N1即8个数据位、无校验、1个停止位。奇偶校验的作用是检错分奇校验和偶校验。奇校验要求包括校验位在内的所有“1”的个数为奇数偶校验要求为偶数。但要注意奇偶校验只能检错不能纠错而且只能检测单比特翻转错误。如果两个比特同时翻转校验仍然通过。在UART这种近距离低干扰场景里奇偶校验的意义没那么大大部分应用直接关闭即可。数据位除了8位之外还有7位、9位等模式。7位模式通常用于传ASCII码最高位可以用来传其他信息。9位模式在MODBUS等协议里用作地址帧和指令帧的区分标志。GD32和STM32的数据位配置里9位模式和校验位会相互影响使用9位数据时可以把第9位当作奇偶校验位来用这种模式在一些工业通信协议里很常见。用Verilog实现UART时最需要注意的就是状态机的时序。发送器的状态机要生成起始位、数据位LSB到MSB按顺序移位、校验位如果要和停止位。计数逻辑写错一个周期波形就会有毛刺。接收器更麻烦因为它没有独立的时钟只能靠波特率时钟过采样。常规做法是16倍过采样在数据位中间采样这样可以有效避开数据边沿附近的翻转不稳定区域。5.3 用Verilog实现UART的常见坑FPGA爱好者或者做逻辑设计的人很多时候需要在Verilog里自己写UART模块。这里有几个容易出错的地方第一接收端的采样时机。如果你的系统时钟是50MHz波特率是115200那么每比特的时间是50MHz / 115200 ≈ 434个时钟周期。你可以在起始位下降沿触发开始然后延时约217个周期即半比特时间去采样第一个数据位之后每434个周期采样一次。这个“半比特采样”是为了确保采样点落在数据位中间避免采到边沿的抖动电平。第二起始位的确认。接收状态下要监测RX线从高到低的跳变但干扰信号也会造成短暂的低电平。稳妥的做法是检测到下降沿后延时半比特时间再看一次RX线是否仍为低电平如果依然为低才算是一个有效起始位。这个“二次确认”能过滤掉大部分毛刺。第三发送器的空闲状态。发送端的TX线在空闲时必须保持高电平这是UART协议的基础。如果复位之后TX线默认输出低电平对端会认为一直在收到起始位通信必然失败。所以复位后发送器的初始输出一定要置1。第四跨时钟域处理。如果你的UART系统时钟和波特率时钟不是同一个时钟域接收端的异步信号需要先打两拍同步否则可能产生亚稳态。这个在FPGA上做UART时尤其关键。6. 常见问题与排查技巧实录6.1 收不到数据、乱码、偶发丢帧我把调试UART时最常见的症状和对应原因整理成一个速查表遇到问题直接对着查症状可能原因排查方法完全收不到数据TX/RX接反检查交叉接线完全收不到数据波特率不匹配确认两边波特率一致完全收不到数据GND没接接上共地线收到乱码波特率误差过大改为标准波特率收到乱码电平不匹配确认TTL/RS232/RS485偶发丢帧中断优先级问题调整UART中断优先级偶发丢帧接收缓冲区溢出加长接收缓冲或启用流控一个字节都收不到但发正常对方芯片没供电检查目标板电源插上USB转串口没反应USB线只有充电功能换数据线乱码这个问题最让人头疼。有一次我调试一块板子115200波特率下乱码换成9600就正常了一开始以为是波特率误差问题结果查到最后发现是板上的晶振不对——标称12MHz的晶振实际装成了11.0592MHz。这种硬件问题很难查排查效率最高的方式就是用示波器或者逻辑分析仪看波形。没有仪器的话可以用串口助手的“波形显示”功能或者利用MCU的定时器测量一个已知延时的电平跳变来反推实际波特率。偶发丢帧还有一个容易忽略的原因发送端和接收端使用了不同的停止位配置一端的停止位是1位另一端配置成了2位两边也能通信但时序余量会变小长报文传输时容易出错。通用性最好的配置还是8N1两边保持一致。6.2 调试工具和技巧调试UART工具选对了事半功倍。硬件上一个USB转TTL工具是标配预算允许的话买带过流保护的防止误接电源烧坏电脑USB口。示波器是排查电平问题的利器能看到波形幅度、毛刺、波特率误差没有示波器的话逻辑分析仪性价比更高几十块钱的8通道逻辑分析仪配合PulseView就能把UART波形解出来。软件方面串口助手推荐用支持定时发送、波形显示、HEX和ASCII切换的。我习惯先用HEX模式发送因为可以精确控制每一个字节然后切换到ASCII模式看可读文本。数据分析时发送端可以在每个数据包头部加上固定的帧头比如0xAA 0x55接收端检测到帧头再开始解析数据这样能有效对抗粘包问题。还有一个很多人不知道的技巧用串口回环测试来定位问题。把TXD和RXD直接短接用串口助手发送数据如果收不到自己发的数据说明问题出在USB转串口工具或者驱动这一侧如果能收到说明发送链路是通的问题在目标板那一侧。这个排障思路能帮你快速缩小问题范围。调试记录也是个好习惯。我每次调试串口都会记录下用的波特率、数据位配置、线序连接和异常现象这样下次遇到类似问题时可以直接翻出来对照。串口通信的问题往往不是单一原因而是好几个因素叠加在一起的结果记录越详细定位越快。7. 最后再分享一点经验做了这么多年嵌入式开发我越来越觉得UART串口通信是整个嵌入式调试体系的基石。UART本身很简单但围绕它的电平转换、驱动安装、MCU配置、协议实现这四层内容每一个都有细碎的坑。把这些细节吃透不仅仅是为了让串口跑起来更是为了建立起整个系统调试的思维框架。如果你正在做第一个串口项目我建议你别急着上复杂功能先把最简单的回环测试跑通再实现MCU和PC之间的数据收发然后逐步增加中断、DMA、协议解析。这个过程走完一遍你对UART的理解会比只看资料深刻得多。如果这篇文章里提到的某个问题正好戳中了你的痛点不用客气直接拿着方案去试。串口通信这个领域没有玄学所有的异常都有对应的物理或逻辑解释耐心排查总能找到答案。本文还有配套的精品资源点击获取
返回列表