
1. 为什么UART值得每一个嵌入式从业者吃透UART这四个字母几乎是每个搞硬件和嵌入式的人入行接触的第一个通信协议。它简单到两根线就能跑起来又重要到几乎所有的芯片、模组、开发板都会保留至少一路串口。你拿到的每一块STM32开发板、每一个4G DTU模块、每一台路由器的主板上几乎都能找到UART的身影。它不像I2C需要时钟线同步也不像CAN总线那样有复杂的仲裁机制UART就是老老实实的异步收发你发一个字节我收一个字节简单直接。但简单不等于没有坑。我见过太多人在调试串口时被波特率偏差、电平不匹配、地线没共地这些问题卡住半天。也见过有人把TTL电平直接怼到RS232接口上结果芯片冒烟的。还有人在Linux下写串口程序open了设备节点却死活收不到数据最后发现是termios配置里漏了一个标志位。这些问题看起来都是小问题但每一个都能让你在调试台上耗掉一整个下午。这篇内容我会从UART的底层原理讲起把波特率、数据帧、电平标准这些核心概念掰开揉碎然后落到实际操作用讲清楚TTL、RS232、RS485这几种常见物理层怎么选、怎么接、怎么防坑。再往上走一层聊聊STM32的HAL库怎么配置UART、Linux下串口编程的关键步骤、以及用USB转TTL模块刷固件和调试设备时的实战经验。最后我会整理一份常见问题速查表把那些年我踩过的坑和排查思路都列出来。不管你是刚接触单片机的学生还是做了几年硬件想回头把基础打牢的工程师这篇内容都能让你对UART有一个从里到外的完整认知。我不会只讲理论每个知识点都会配上实际场景和可操作的步骤让你看完就能上手。2. UART协议核心原理拆解2.1 异步串行通信的本质没有时钟线怎么对齐数据UART的全称是Universal Asynchronous Receiver/Transmitter翻译过来就是通用异步收发器。注意“异步”这两个字这是理解UART的关键。I2C和SPI都是同步通信它们有一根专门的时钟线SCL或SCK数据在时钟的上升沿或下降沿被采样收发双方靠时钟信号来对齐每一位数据。UART没有时钟线那它怎么知道每一位数据什么时候该采样呢答案就是靠预先约定的波特率。收发双方在通信之前必须约定好同一个波特率比如9600、115200。发送方按照这个速率一位一位地把数据放到线上接收方按照同样的速率去采样。这就像两个人约好了每隔一秒钟说一个字虽然没有节拍器但双方心里都在默数只要节奏一致就能听懂对方在说什么。但这里有一个隐含的问题接收方怎么知道一帧数据从哪里开始如果线上一直是高电平空闲状态突然来了一个下降沿接收方就知道可能有数据来了。这个下降沿就是起始位Start Bit。起始位的作用就是告诉接收方“注意我要开始发数据了你准备按约定的波特率采样。”一个完整的UART数据帧由以下几部分组成起始位1位逻辑0低电平标志一帧数据的开始数据位5到9位通常是8位低位先发LSB First校验位0或1位可选奇校验、偶校验、无校验停止位1位、1.5位或2位逻辑1高电平标志一帧数据的结束以最常见的配置“9600-8-N-1”为例它的含义是波特率9600数据位8位无校验None停止位1位。一帧数据总共占10位1起始8数据1停止。如果波特率是9600那么每一位的时间是1/9600≈104.17微秒一帧10位就是约1.04毫秒。也就是说在这种配置下每秒最多能传输约960个字节。2.2 波特率的计算与误差容忍度波特率Baud Rate的定义是每秒传输的符号数。在UART中每个符号就是一位所以波特率就等于每秒传输的位数bps。常见的波特率有9600、19200、38400、57600、115200、230400、460800、921600等。为什么是这些看起来不太规整的数字因为它们都是早期电话调制解调器时代留下来的标准后来被沿用下来了。波特率的精度直接决定了通信能否成功。接收方在每个位的中间时刻进行采样如果收发双方的波特率有偏差采样点就会逐渐偏离位的中心。偏差累积到一定程度采样点就会落到相邻位上导致误码。一般来说UART能容忍的波特率误差在2%到3%左右超过这个范围通信就会不稳定甚至完全失败。误差是怎么来的大多数MCU的UART波特率是由系统时钟分频得到的。比如STM32F103的系统时钟是72MHz要得到115200的波特率分频系数是72000000/115200625。这个值是整数所以误差为0。但如果系统时钟是8MHz要得到115200分频系数是8000000/115200≈69.44不是整数实际分频只能是69或70对应的实际波特率就是8000000/69≈115942或8000000/70≈114286误差分别是0.64%和-0.79%。这个误差在容忍范围内通信可以正常进行。但如果用内部RC振荡器作为时钟源情况就不一样了。RC振荡器的精度通常在±1%到±2%之间再加上分频误差总误差可能超过3%这时候通信就会出问题。所以我在实际项目中凡是涉及UART通信的一律用外部晶振不用内部RC。多花几毛钱的晶振钱省下的是几小时的调试时间。注意如果你在用STM32的HAL库配置UARTHAL库会自动计算BRR寄存器的值你只需要填入目标波特率即可。但如果你用的是寄存器操作或者自己写底层驱动一定要根据实际时钟频率计算分频值并验证误差是否在可接受范围内。2.3 数据帧格式的灵活配置与校验位的作用数据位长度可以是5到9位但最常用的是8位因为一个ASCII字符正好是8位实际7位加1位校验或扩展。有些协议会用9位数据位第9位作为地址位或命令/数据标志位比如Modbus RTU在某些变种中就会用到。校验位是用来检测传输错误的。奇校验Odd Parity要求数据位加校验位中“1”的个数为奇数偶校验Even Parity要求“1”的个数为偶数。接收方收到数据后重新计算校验如果和收到的校验位不符就知道传输过程中有误码。但校验位只能检测单比特错误不能纠错也不能检测双比特错误。所以在噪声较大的环境中校验位的作用有限更可靠的做法是在应用层加CRC校验。停止位可以是1位、1.5位或2位。1位是最常用的2位通常用于低速通信或噪声较大的环境给接收方更多的时间来处理上一帧数据。1.5位只在某些特定场景下使用实际项目中很少见。我在实际项目中遇到过一个问题两个设备单独通信都正常但接在一起就偶尔丢数据。后来发现是其中一个设备的停止位配置成了2位另一个是1位。虽然大多数情况下1位停止位的接收方能正确识别2位停止位的发送方因为停止位是高电平和空闲状态一样但在连续发送时额外的停止位会导致帧间隔变化接收方的状态机可能会误判。所以通信双方的帧格式必须完全一致这是铁律。3. 物理层电平标准TTL、RS232、RS485怎么选怎么接3.1 TTL电平板级通信的默认选择TTLTransistor-Transistor Logic电平是芯片引脚直接输出的电平标准。对于3.3V供电的MCUTTL高电平是3.3V低电平是0V对于5V供电的MCU高电平是5V低电平是0V。TTL电平的传输距离很短通常不超过几十厘米适合板内芯片之间的通信。TTL串口的接线很简单只需要三根线TX、RX、GND。TX是发送RX是接收GND是地线。注意TX和RX要交叉连接A的TX接B的RXA的RX接B的TX。GND必须共地否则电平没有参考点通信会乱码或完全失败。我见过很多新手在调试时忘记接GND只接了TX和RX然后纳闷为什么收不到数据。没有共地接收方无法判断发送方的电平高低因为“高”和“低”是相对于各自的GND而言的。如果两个设备的GND没有连在一起它们的电平参考点就不同接收方看到的可能一直是高电平或者一直是低电平。提示TTL串口在板内通信时非常可靠但如果你要把信号引出到板外比如用杜邦线连接到另一个设备线长最好不要超过30厘米。线越长分布电容越大信号边沿越缓高速波特率下容易误码。如果必须长距离传输考虑降低波特率或者改用RS232/RS485。3.2 RS232负逻辑与长距离传输RS232是早期为电话线和调制解调器设计的串行通信标准。它的电平标准和TTL完全不同RS232使用负逻辑高电平是-3V到-15V低电平是3V到15V。也就是说逻辑1对应负电压逻辑0对应正电压。这和TTL正好相反。RS232的接收器通常能容忍±25V的输入所以它的抗干扰能力比TTL强得多传输距离可以达到15米甚至更长取决于波特率和线缆质量。但RS232是单端信号每个信号线都需要一根独立的回地线所以线缆比较粗成本也高。在实际项目中MCU的TTL串口不能直接连接到RS232接口中间必须加电平转换芯片比如MAX232、SP3232、ADM3202等。这些芯片内部有电荷泵能把3.3V或5V的电源转换成RS232需要的正负电压。典型电路是MCU的TX接转换芯片的T1IN转换芯片的T1OUT接DB9接口的Pin3TXDMCU的RX接转换芯片的R1OUT转换芯片的R1IN接DB9的Pin2RXDGND接Pin5。DB9接口的引脚定义是Pin2是RXDPin3是TXDPin5是GND。注意这里的RXD和TXD是相对于DTE设备比如电脑而言的。如果你用电脑的串口连接另一个DTE设备需要用到交叉线Null Modem把Pin2和Pin3交叉。如果连接的是DCE设备比如早期的调制解调器则用直连线。3.3 RS485差分信号与多点组网RS485使用差分信号传输两根线A和B信号是两根线上的电压差。差分信号的最大优势是抗共模干扰能力强因为干扰通常会同时耦合到两根线上接收端做减法时干扰就被抵消了。RS485的传输距离可以达到1200米速率最高10Mbps短距离下是工业现场最常用的串行通信标准之一。RS485是半双工通信同一时刻只能有一个设备发送。所以需要一个方向控制信号DE/RE来切换收发模式。在MCU端通常用一个GPIO控制RS485收发器的DE和RE引脚。发送前把DE拉高发送完成后拉低切换到接收模式。RS485总线上可以挂多个设备典型的是32个节点使用更高阻抗的收发器可以扩展到128个甚至256个。每个设备在总线上的位置和终端电阻的配置会影响信号质量。在长距离或高速率下总线两端需要各接一个120Ω的终端电阻来匹配阻抗消除反射。我在一个工业项目中用过RS485组网总线上挂了16个传感器节点最远的节点距离主控约300米。波特率用的是9600线缆用的是屏蔽双绞线。调试时发现最远的节点偶尔丢包后来在总线两端加了120Ω终端电阻问题就解决了。终端电阻的作用是吸收信号到达线缆末端时的反射如果不加反射信号会和原始信号叠加导致接收端误判。特性TTLRS232RS485电平标准0V/3.3V或5V±3V到±15V差分±1.5V到±6V逻辑正逻辑负逻辑差分传输距离30cm15m1200m通信方式全双工全双工半双工组网能力点对点点对点多点最多32-256节点抗干扰能力弱中等强典型应用板内芯片通信电脑串口、老式设备工业现场、传感器网络3.4 电平转换与隔离什么时候需要光耦和ESD保护在实际项目中TTL、RS232、RS485之间的转换是家常便饭。TTL转RS232用MAX232之类的芯片TTL转RS485用MAX485、SP3485之类的收发器。这些芯片的选型主要看供电电压、速率、节点数、隔离需求。如果通信双方的地电位不同或者现场有强电磁干扰就需要考虑隔离。隔离的方式有光耦隔离和磁隔离。光耦隔离的优点是成本低、技术成熟缺点是速率受限普通光耦通常只能到几十kbps到几百kbps功耗也较高。磁隔离比如ADI的ADuM系列速率高、功耗低但成本也高。关于“9600波特率用什么光耦隔离最合适”这个问题9600波特率对光耦的速度要求不高常见的PC817、EL817、TLP521都能胜任。但要注意光耦的电流传输比CTR会随时间和温度衰减设计电路时要留足够的余量。我通常会在光耦的LED侧串联一个限流电阻把电流设在5mA到10mA之间这样既能保证足够的驱动能力又不会加速光耦老化。ESD保护也是工业现场必须考虑的问题。RS232接口暴露在外很容易受到静电放电的冲击。常见的做法是在信号线上加TVS二极管或ESD保护阵列比如SM712专门为RS485设计的、PESD系列。选型时要注意钳位电压要低于收发器芯片的绝对最大额定值否则保护不了芯片。注意光耦隔离和ESD保护是两回事。光耦隔离解决的是地电位差和共模干扰问题ESD保护解决的是静电放电问题。在恶劣的工业环境中两者往往需要同时使用。4. 实战STM32与Linux下的UART配置与编程4.1 STM32 HAL库UART初始化与收发实战STM32的HAL库把UART的初始化封装得很简单但简单背后有一些细节如果不注意就会踩坑。下面我以STM32F103C8T6为例讲一下用HAL库配置UART的完整流程。首先在CubeMX里配置USART1模式选Asynchronous波特率115200数据位8位无校验停止位1位。GPIO方面PA9自动配置为USART1_TXPA10配置为USART1_RX。NVIC里使能USART1全局中断。生成代码后HAL库会自动生成MX_USART1_UART_Init函数。发送数据用HAL_UART_Transmit函数它有三个参数UART句柄、数据缓冲区指针、数据长度、超时时间。比如发送一个字符串uint8_t msg[] Hello UART\r\n; HAL_UART_Transmit(huart1, msg, sizeof(msg)-1, 1000);接收数据有两种方式阻塞接收和中断接收。阻塞接收用HAL_UART_Receive它会一直等到收到指定长度的数据或超时。中断接收用HAL_UART_Receive_IT它启动接收后立即返回收到数据后在中断回调函数HAL_UART_RxCpltCallback里处理。我推荐用中断接收因为阻塞接收会占用CPU影响其他任务的执行。但中断接收有一个坑HAL_UART_Receive_IT每次只能接收指定长度的数据收完后需要重新调用才能继续接收。如果你要连续接收不定长数据更好的方式是使用空闲中断IDLE Interrupt配合DMA。空闲中断的原理是当UART总线在一个字节传输时间后没有新的数据到来硬件就会触发空闲中断。在空闲中断里你可以读取DMA已经接收到的数据长度然后处理这一帧数据。这种方式不需要知道对方要发多少字节非常适合处理不定长协议帧。配置步骤是使能USART1的DMA接收通道在CubeMX里添加DMA请求模式选Circular或Normal。然后在代码里使能空闲中断__HAL_UART_ENABLE_IT(huart1, UART_IT_IDLE); HAL_UART_Receive_DMA(huart1, rx_buffer, BUFFER_SIZE);在USART1_IRQHandler里判断空闲中断标志清除标志后计算接收到的数据长度void USART1_IRQHandler(void) { if (__HAL_UART_GET_FLAG(huart1, UART_FLAG_IDLE)) { __HAL_UART_CLEAR_IDLEFLAG(huart1); uint16_t len BUFFER_SIZE - __HAL_DMA_GET_COUNTER(hdma_usart1_rx); // 处理rx_buffer里的len个字节 } HAL_UART_IRQHandler(huart1); }这套方案我在多个项目中用过接收不定长数据非常稳定。唯一需要注意的是DMA缓冲区的长度要足够大至少要能容纳一帧最长数据。4.2 Linux串口编程termios配置与读写操作在Linux下操作串口本质上是操作/dev/ttyS或/dev/ttyUSB设备文件。打开串口用open函数配置串口用termios结构体。下面是一个完整的配置示例#include termios.h #include fcntl.h #include unistd.h int fd open(/dev/ttyUSB0, O_RDWR | O_NOCTTY | O_NDELAY); if (fd 0) { perror(open); return -1; } struct termios options; tcgetattr(fd, options); cfsetispeed(options, B115200); cfsetospeed(options, B115200); options.c_cflag | (CLOCAL | CREAD); options.c_cflag ~CSIZE; options.c_cflag | CS8; options.c_cflag ~PARENB; options.c_cflag ~CSTOPB; options.c_cflag ~CRTSCTS; options.c_lflag ~(ICANON | ECHO | ECHOE | ISIG); options.c_iflag ~(IXON | IXOFF | IXANY); options.c_oflag ~OPOST; options.c_cc[VMIN] 0; options.c_cc[VTIME] 10; tcsetattr(fd, TCSANOW, options);这段代码里几个关键点CLOCAL忽略调制解调器控制线CREAD使能接收CS8设置8位数据位PARENB关闭校验CSTOPB关闭2位停止位CRTSCTS关闭硬件流控。c_lflag里关闭ICANON和ECHO让串口工作在原始模式否则输入会被行缓冲和回显。c_cc[VMIN]和c_cc[VTIME]控制read函数的阻塞行为VMIN0且VTIME10表示最多等待1秒。读写操作直接用read和write函数char buf[256]; int n read(fd, buf, sizeof(buf)); if (n 0) { // 处理收到的n个字节 } write(fd, AT\r\n, 4);我在Linux下调试串口时遇到过一个典型问题open了设备节点write也成功了但read一直返回0。排查后发现是termios配置里没有清除ICANON标志导致read在等待换行符。另一个常见问题是权限不足普通用户没有权限打开/dev/ttyUSB0需要把用户加入dialout组或者用sudo运行。提示在Linux下调试串口可以用stty命令快速查看和设置串口参数。比如stty -F /dev/ttyUSB0 -a查看当前配置stty -F /dev/ttyUSB0 115200 cs8 -cstopb -parenb设置波特率和帧格式。用cat /dev/ttyUSB0可以实时查看串口收到的数据用echo test /dev/ttyUSB0可以发送数据。4.3 USB转TTL模块选型与刷机实战USB转TTL模块是每个嵌入式工程师必备的工具。常见的芯片有CH340、CP2102、FT232、PL2303等。CH340便宜但驱动兼容性一般在某些Linux发行版上需要手动编译驱动。CP2102和FT232稳定性好驱动完善但价格稍高。FT231X是FT232的升级版支持更高的波特率和更低的功耗在Linux内核里自带驱动插上就能用。选型时主要看三点一是支持的波特率范围二是驱动兼容性三是是否支持3.3V和5V电平切换。很多模块上有跳线帽可以选择VCC输出3.3V还是5V但TX和RX的电平通常跟随VCC。如果你用3.3V的MCU一定要把跳线帽调到3.3V否则5V的TX信号可能会损坏MCU的RX引脚。用USB转TTL模块刷固件是常见操作。以路由器刷Breed为例步骤大致是拆开路由器外壳找到主板上的UART焊盘通常标有TX、RX、GND有时还有VCC用杜邦线或飞线连接到USB转TTL模块。注意TX接RXRX接TXGND接GND。VCC一般不要接因为路由器主板通常有自己的供电接VCC可能导致电源冲突。连接好后打开串口终端Windows下用PuTTY或SecureCRTLinux下用minicom或picocom波特率通常设为115200。给路由器上电终端上应该能看到启动日志。在启动过程中按特定按键比如数字键9或字母键t进入U-Boot命令行然后通过TFTP或串口协议上传固件。我刷过一台AX6路由器用的是CH340模块波特率115200。第一次连接时终端全是乱码换了几个波特率都不行。后来发现是CH340模块的驱动有问题换了一个CP2102模块就正常了。所以如果你遇到乱码先排除驱动问题再检查波特率和线序。对于海思Hi3798系列机顶盒TTL刷机的操作类似。Hi3798M100的U-Boot通常会在启动时等待几秒钟按CtrlC或特定快捷键可以进入命令行。进入后用loady或tftp命令加载固件然后写入eMMC。需要注意的是Hi3798的UART引脚通常是1.8V电平不是3.3V。如果你用3.3V的USB转TTL模块直接连接可能会损坏芯片。这种情况下需要加一个电平转换电路或者用支持1.8V的专用模块。5. 常见问题与排查技巧实录5.1 串口通信故障排查速查表串口通信出问题原因无非那么几类接线问题、配置问题、电平问题、干扰问题。下面这张表是我多年调试经验的总结按现象分类方便你快速定位。现象可能原因排查方法解决方案完全无数据TX/RX接反交换TX和RX试试交叉连接完全无数据未共地测量两设备GND间电压连接GND完全无数据波特率不匹配尝试常见波特率统一波特率乱码波特率偏差大检查时钟源和分频用外部晶振重新计算分频乱码电平不匹配测量TX空闲电平加电平转换偶尔丢数据停止位/校验位不一致核对双方帧格式统一帧格式偶尔丢数据线缆过长缩短线缆或降波特率加驱动或改RS485偶尔丢数据无终端电阻检查RS485总线加120Ω终端电阻接收一段时间后死机中断未清除检查中断标志清除中断标志接收一段时间后死机DMA缓冲区溢出检查DMA计数增大缓冲区加溢出处理5.2 那些年我踩过的UART坑第一个坑是TTL电平直接接RS232。刚入行的时候我以为串口就是串口把单片机的TX直接接到电脑串口的RXD上结果单片机引脚瞬间冒烟。RS232的±12V电平对TTL引脚来说是致命的。后来每次连接前都会先确认电平标准TTL接TTLRS232接RS232中间该加转换芯片就加。第二个坑是波特率误差累积。有一次用内部RC振荡器跑115200短数据还能勉强通信一帧超过20字节就必丢。后来用示波器量了TX波形发现位宽偏差很大最后一帧的停止位已经偏了将近半个位。换成外部晶振后问题消失。所以高速波特率下时钟源精度至关重要。第三个坑是RS485方向切换延迟。用GPIO控制RS485收发器的DE引脚时发送完成后要等最后一个字节完全移出移位寄存器才能拉低DE否则最后一个字节会被截断。我一开始是调用完发送函数就立即拉低DE结果对方收到的数据总是少最后一个字节。后来改成在发送完成中断里拉低DE问题解决。第四个坑是Linux串口权限。在Ubuntu下用普通用户打开/dev/ttyUSB0提示Permission denied。用sudo可以解决但每次都要输密码很麻烦。正确做法是把用户加入dialout组sudo usermod -aG dialout $USER然后重新登录。这个坑我踩了好几次每次换新电脑都要重新设置。第五个坑是USB转TTL模块的VCCIO电压。有些模块的TX/RX电平是5V有些是3.3V但模块上没有任何标注。我买过一批便宜的CH340模块TX空闲电平是5V接到3.3V的STM32上虽然大部分时候能工作但长期运行后STM32的RX引脚就损坏了。后来我养成了一个习惯拿到新模块先用万用表量一下TX空闲电平确认是3.3V再接到3.3V的MCU上。5.3 提高串口通信可靠性的几个实用技巧第一个技巧是在应用层加帧头和校验。UART本身没有帧同步机制只靠起始位和停止位。如果受到干扰接收方可能把噪声当成起始位收到一帧错误数据。在应用层加一个固定的帧头比如0xAA 0x55和CRC校验可以过滤掉大部分错误帧。第二个技巧是使用DMA空闲中断接收不定长数据。前面已经讲过这种方式不需要知道对方发多少字节也不占用CPU是STM32上处理串口接收的最佳实践。第三个技巧是在RS485总线上加偏置电阻。当总线上所有设备都不发送时A和B线处于浮空状态容易受到干扰产生误触发。在A线上加一个上拉电阻到VCCB线上加一个下拉电阻到GND可以让总线在空闲时保持确定的状态。偏置电阻的阻值通常在560Ω到1kΩ之间。第四个技巧是用示波器看波形。串口通信出问题时万用表只能量静态电平看不到动态波形。示波器可以看波特率是否准确、信号边沿是否陡峭、是否有过冲和振铃。我调试RS485时用示波器发现信号在末端有明显的反射加了终端电阻后波形就干净了。第五个技巧是保留调试串口。在产品开发阶段尽量保留一路UART作为调试输出打印日志和状态信息。等产品稳定后再决定是否关闭。很多问题在实验室发现不了到了现场才暴露有调试串口就能快速定位。6. 从UART延伸到更广阔的通信世界UART是通信协议的起点但绝不是终点。理解了UART的异步收发、波特率、帧格式这些概念再去看其他协议就会容易很多。比如I2C的同步时钟、SPI的全双工四线制、CAN总线的差分仲裁、Modbus的应用层协议都是在UART或类似物理层基础上发展出来的。如果你已经掌握了UART下一步可以研究Modbus RTU。Modbus RTU就是跑在UART之上的应用层协议定义了功能码、寄存器地址、CRC校验等规则。工业现场大量的PLC、传感器、仪表都用Modbus RTU通信。学会Modbus你就能和大部分工业设备对话。再往上走可以了解RS485与Modbus的组合、CAN总线在汽车电子中的应用、以及以太网和TCP/IP协议栈。通信协议的学习路径是层层递进的UART是地基地基打牢了上面的楼才能盖得高。我在实际项目中最大的体会是通信协议的问题90%出在物理层和配置层只有10%出在协议本身。线接错了、电平不匹配、波特率不一致、地线没共地这些问题比协议逻辑错误常见得多。所以每次调试通信我都会先从物理层查起线接对了吗电平匹配吗共地了吗然后再查配置波特率、数据位、停止位、校验位一致吗最后才去查协议逻辑。这个排查顺序能帮你省下大量时间。最后分享一个小技巧如果你手头没有示波器可以用一个USB转TTL模块配合串口助手来“听”总线上的数据。把USB转TTL的RX接到总线的TX上GND共地打开串口助手设置好波特率就能看到总线上传输的数据。虽然不能看波形但至少能确认有没有数据、数据对不对。这个方法在現場调试时特别管用。