1. 一次串口调试失败让我重新理解了通信事情是这样的两块STM32板子用UART对接一个发一个收电平逻辑看着都对代码也是标准库例程改的结果上电之后接收端死活收不到数据。折腾了半个下午最后发现是两块板子没有共地——虽然都用USB供电但一个插在台式机前面板一个插在笔记本上地电位差了好几伏数据线上的电平识别自然全乱套。这件事让我意识到一个特别容易被忽略的事实通信这件事从来不是“把线连上”就完事儿。它背后是一整套关于信号怎么产生、怎么传输、怎么被正确解读的约定。我们把这一整套约定统称为通信基本原理。这篇是【通信观系列】的第二篇我会从最底层的物理信号讲起一直聊到大家平时经常接触的串口、SPI、I2C、CAN、以太网、无线通信以及软件层面的Socket和进程通信。你会发现不管技术名词多花哨背后的原理其实就那几样东西信源、信道、编码、调制、同步、协议。把这些吃透了以后遇到任何通信问题都会有一种“庖丁解牛”的感觉——至少不会两眼一抹黑。适合谁看做嵌入式、单片机开发常年跟UART、SPI、CAN打交道的工程师写后端、搞分布式系统需要理解网络通信和进程通信的开发者还有刚入行想建立完整通信知识体系的朋友。这篇文章尽量做到不堆公式用大白话和实际案例把原理讲透。2. 通信铁三角信息、信道和约定2.1 香农模型剥开来看通信就三件事信息论里有个经典模型信源 - 编码 - 信道 - 解码 - 信宿中间还夹着一个“噪声”。很多人一看这些术语就头疼但用寄快递来类比就很好懂。信源你要寄出去的东西比如一盒饼干信息本身。编码把饼干装进快递箱填上收件人地址和联系方式把信息变成可传输的信号格式。信道快递运输的路径比如公路、航空传输介质可能是铜线、光纤、空气。噪声运输途中可能发生的意外比如箱子被压扁、地址条被雨淋湿干扰信号的东西。解码收件人打开包裹检查饼干是否完好从信号中还原信息。信宿最终收到饼干的人。所以通信的本质就是三件事发什么怎么表达信息、怎么发用什么介质和方式传、怎么确认发对了有没有反馈和纠错机制。不管你是用两根线传串口数据还是用5G基站传高清视频都逃不出这个框架。2.2 模拟和数字信号世界的两种表达早期的电话是模拟通信说话的声音大小直接转换成电压高低的变化信号长啥样信息就长啥样。优点是实现简单缺点是抗干扰能力差——电压只要被噪声带偏一点声音就失真了。数字通信则把信息变成0和1的序列用离散的电平、频率或相位来表示。它的优势是抗噪能力强只要噪声没把1变成0、把0变成1信息就能完整恢复。现代通信系统几乎全是数字的哪怕你的声带是模拟的手机也会先采样量化成数字流再发出去。2.3 协议通信双方必须共同遵守的“行规”有了信号载体还不够。A设备发一串高低电平B设备怎么知道从哪里开始读、读几位、解析成什么含义这就必须靠协议。协议规定了电平格式、帧格式、波特率、校验方式、应答机制等等。就好比两个人打电话A说“你好”B如果听不懂中文这通电话就白打了。协议就是通信双方共用的“语言”而且是包括语法、单词、标点的完整语言体系。注意通信里90%的“疑难杂症”本质上都是收发双方对协议的某一条理解不一致。要么波特率不匹配要么帧格式不对要么校验方式不同。所以排查通信问题的第一步永远是确认协议参数。3. 从电平到协议UART、SPI、I2C、CAN这些总线到底在解决什么问题3.1 为什么不能只有一种通信总线如果世界只有一种总线那大家闭着眼睛连总没错。但现实中不同的应用场景对通信的要求天差地别有的要省线比如温度传感器塞在狭小空间里有的要高速比如摄像头图像传输有的要长距离抗干扰比如工厂车间里的设备互联有的要能挂一堆设备比如传感器网络。于是就有了各种总线方案。我整理了一张对比表大家先有个整体认知总线类型线数最少通信方式典型速率最大距离拓扑结构典型应用UART2TX/RX异步串行最高几Mbps常用115200bps十几米RS232更短点对点调试串口、蓝牙模块、GPS模块SPI3SCK/MOSI/MISO片选同步串行几十Mbps以上几米一主多从Flash存储、显示屏、SD卡I2C2SCL/SDA同步串行半双工最高3.4Mbps常用400kbps几米一主多从靠地址区分传感器、EEPROM、RTCCAN2CANH/CANL异步串行差分最高1Mbps典型500kbps几十米到几千米多主总线带仲裁汽车电子、工业控制RS4852A/B异步串行差分最高10Mbps常用9600bps1200米以上一主多从多点工业现场、Modbus为什么会有这么多差异核心就是两个变量成本和复杂度。UART最简单但只能点对点SPI快但线多I2C线少但速率一般CAN既能多主又抗干扰但需要控制器和收发器RS485距离远但本身只是物理层标准还得搭配Modbus等协议使用。3.2 UART最朴素的通信方式坑却最多UART是异步串行通信意思是没有独立的时钟线收发双方各自用自己的时钟来采样。它靠什么同步靠起始位。平时线路空闲是高电平发送方拉低一个位时间表示“开始”然后依次发8个数据位最后发停止位。接收方检测到下降沿后按约定的波特率采样。这里“波特率”指的是每秒传送的码元个数。双方的波特率必须一致误差一般不能超过3%。但即使标称一致如果晶振精度低或分频参数设置不对时间一长就会累积误差导致最后一个位采样错位。实际调试中UART最容易被忽视的三个坑共地。TX和RX必须共用一个参考地否则电平判断没有基准。开头我被卡半天的就是这个问题。TX/RX交叉。A的TX接B的RXA的RX接B的TX很多人第一次连反了。波特率偏差。如果你用了非标准的波特率比如123456bps接收方很难精确匹配最好用115200、9600这些标准值。3.3 SPI与I2C同步时钟省了“对齐”的麻烦SPI和I2C都有专门的时钟线数据线在时钟的驱动下逐位传输所以收发双方不需要靠起始位猜节奏同步问题比UART简单很多。SPI是主从模式主机产生时钟通信开始时拉低片选CS选中从机。它有4根线SCK时钟、MOSI主出从入、MISO主入从出、CS片选。好处是速率快、全双工坏处是从机数量多的时候片选线占地方。I2C则只靠两根线SCL时钟和SDA数据所有设备都挂在同一条总线上每个设备有唯一的7位或10位地址。主机发送起始条件SDA在SCL高电平期间拉低然后发送地址和读/写位被寻址的从机回一个ACK。因为线是开漏的所以必须接上拉电阻阻值一般在1k~10k之间具体看总线上挂了多少设备和通信速率。上拉电阻太小灌电流太大太大则边沿变缓高速通信容易出错。个人经验调试I2C时用逻辑分析仪抓SDA和SCL的波形看起始条件、地址、ACK位是否正常比看任何日志都直观。SPI同理重点抓片选信号有没有正常拉低时钟极性CPOL和相位CPHA是否和从机匹配。3.4 CAN通信波形好坏一眼就能看出来CAN在汽车和工业领域用得非常多它也是“通信热词”里的熟面孔。CAN之所以抗干扰能力强核心在于差分传输。CANH和CANL上的电压互为相反接收端只关心两者的差值所以共模噪声会被抵消。CAN总线的逻辑电平有两种显性电平对应逻辑0和隐性电平对应逻辑1。显性时CANH≈3.5VCANL≈1.5V差值是2V隐性时两者都约2.5V差值是0V。总线上只要有一个节点发送显性位整条总线就是显性——这正是CAN总线仲裁的基础多个节点同时发送时ID小的显性位多的自动获胜。判断CAN通信好不好拿示波器看波形最关键。我列几个要点差分幅值显性电平差分电压应该在1.5V~3V之间如果低于1.2V可能线缆过长、终端电阻异常或节点驱动能力不足。隐性电平理想情况CANH和CANL都接近2.5V相对各自地如果整体上移或下移可能共地不良。边沿情况正常的位跳变应该很干净上升沿和下降沿没有明显的过冲和振铃。如果波形边沿有“毛刺”或回勾大概率是终端电阻没匹配好。位时间用示波器测量一个位的实际长度和波特率计算出的理论位时间对比。比如500kbps的位时间是2μs测出来差太多就说明某个节点的时钟有问题。CAN总线两端必须各接一个120Ω的终端电阻用来匹配阻抗、防止信号反射。很多人只在其中一个节点接波形就会容易出现振铃。3.5 RS485同样是差分但走得更远RS485也是一种差分总线但它不像CAN那样自带复杂协议它只规定了电气特性。RS485用一对双绞线A/B通过A-B之间的电压差表示逻辑0和1。它的最大优势是距离远、速率和距离可折中9600bps时能传1200米以上适合工厂里的变频器和PLC通信。和CAN类似RS485也需要终端电阻120Ω匹配而且是不带供电的、半双工的同一时刻只能有一个节点发送。如果总线上有两个节点同时讲就会冲突。所以RS485通常搭配Modbus RTU这类主从协议由主机统一调度从机只在被点名时才回数据。4. 网络通信与进程通信分层思想才是通信的“终极解法”4.1 为什么要有分层单块芯片上的总线通信你可以把所有规则揉在一张电路图里搞清楚。但一旦到了互联网这种全球规模的通信如果不分层任何一个人想改一个环节都得把整个系统推倒重来。分层的基本思路是把通信过程拆成若干层每一层只干自己的事并为上层提供服务。上层不需要关心下层怎么传输下层也不需要理解上层的业务含义。这就像寄快递你只负责把包裹给快递员快递员负责运到另一个城市至于包裹是汽车还是飞机运的、走哪条路你完全不用操心。最常见的分层模型是OSI七层模型和TCP/IP四层模型。实际工程中用得最多的还是TCP/IP模型它把通信分成四层层次作用典型协议/技术应用层定义业务数据的语义HTTP、MQTT、Modbus TCP、WebSocket传输层端到端的可靠传输与流量控制TCP、UDP网络层寻址和路由找到目标设备IP、ICMP网络接口层物理信号的收发和帧的封装以太网、WiFi、PPP4.2 封装与解封装通信的“套娃”过程假设你写了一个HTTP请求要发给云端服务器。数据从应用层往下走每一层都会在数据前面加一个“头”里面写着这一层的控制信息。传输层加TCP头里面有源端口和目的端口端口是干什么的就是标识这台设备上的哪个应用程序。相当于一栋楼里的房间号。网络层加IP头里面有源IP和目的IPIP是机器的“门牌号”负责找到你的设备在哪栋楼。网络接口层加以太网头里面有源MAC和目的MAC。MAC是网卡的“身份证号”负责在同一个局域网里把数据帧从一台设备交到另一台设备。接收端再从底层往上逐层剥掉头部最终把应用数据交给对应端口的进程。这个过程叫解封装。一个很常见的误区IP地址负责跨网络寻址MAC地址负责局域网内的物理传输。路由器就是根据IP地址决定下一个应该转发给谁但真正在网线上跑的时候还是要靠MAC地址一层一层“接力”。所以你在抓包软件里会看到一个IP报文在每一跳的MAC帧里源MAC和目的MAC是不断变化的但IP地址始终不变。4.3 进程间通信IPC与组件通信通信原理在软件里的翻版硬件电路板之间通信靠电平、总线、协议软件里不同进程、不同组件之间的通信其实也遵循同样的基本原理。比如管道Pipe就是一条临时的“串口”数据从一端流入从另一端流出单向的。相当于UART的点对点。消息队列Message Queue相当于带缓冲的串口进程往队列里放消息另一个进程取消息解耦了收发双方。共享内存Shared Memory一块内存区域被多个进程映射到自己的地址空间读写同一块物理内存通信速率最快但需要同步机制。这相当于多个设备直接挂在同一个内存总线上谁都能读写但得防止冲突。Socket是最常见的一套跨设备进程通信接口底层就是TCP/IP协议栈。Socket把传输层封装的细节藏起来你只需要绑定IP和端口然后read/write跟操作文件一样。前端里讲的“组件通信父传子、子传父”本质也是数据在“组件”这个节点之间传递只不过它的信道变成了JavaScript的函数调用和事件协议变成了框架定义的生命周期接口。但核心思想没变一份数据从发送方按特定格式传出来接收方按对应规则解析中间还要考虑时序和异常处理。“跨核通信”“D2D通信”这些概念要么是消息传递要么是共享内存都是从通信基本原理里衍生出来的换汤不换药。5. 无线通信看不见的信道更考验基本原理5.1 电磁波、频率和调制有线的传输介质是导线无线的传输介质是电磁波。电磁波有一个核心属性频率。不同频率的电磁波传播特性和应用场景完全不同。低频如几百kHz穿透力强但带宽小适合远距离低速传输比如AM广播。高频如2.4GHz、5GHz带宽大、速率快但穿透力差、衰减快适合WiFi和蓝牙。超高频毫米波带宽巨大但距离极短多用于5G高频和点对点微波。但二进制数据不能直接用0/1的序列去驱动天线因为纯方波信号频率成分太杂、辐射效率也太低。所以要把数字信号“搬到”一个适合传输的高频载波上进行传输这就是调制。调制的三种基本方式调幅AM用数据改变载波的幅度。调频FM用数据改变载波的频率。调相PM/PSK用数据改变载波的相位。WiFi、LTE、LoRa这些系统都在用更复杂的调制方式比如正交频分复用OFDM、扩频调制LoRa的CSS但底层逻辑都是把比特映射成某种可区分的信号特征。5.2 双工方式怎么让两边都能说话无线通信上收发双方不可能都用相同的频率同时说因为会互相干扰。于是设计了几种双工方式单工只能一方发一方收比如遥控器。半双工两边都能发但同一时间只能一方发。比如对讲机PTT按键按下才能说话。全双工两边同时收发。比如手机打电话技术上通过频分双工FDD或时分双工TDD实现。LoRa、蓝牙、WiFi这类常见的近场无线通信很多都是半双工的。LoRa的发送和接收切换还需要时间所以协议里会规定发送前先监听信道、发送后等待ACK这跟RS485的半双工总线很像——只是信道从双绞线换成了空气。5.3 蜂窝通信里的BBU和D2D移动通信里的“BBU”是基带处理单元通俗讲就是负责把语音、视频这些业务数据“打包”成适合无线传输的信号再通过RRU和天线发出去。它和数据中心里的“网关”、路由器的角色类似都是做“协议转换数据调度”的工作。“D2D通信”是Device to Device的缩写也就是设备之间不经过基站直接通信就像局域网里两台电脑不经过路由器直接网线互联一样。它能降低时延、提高频谱利用率但需要解决发现、同步、资源分配这些问题。这些问题的本质还是通信基本原理里的“如何共享信道”“如何约定时序”。量子通信是另一个话题它利用量子态来传递信息具有天然防窃听的特性。但不管怎么“量子”通信的基本问题依然是编码、传输和测量只是载体从电磁波换成了量子态。6. 通信问题排查用基本原理定位故障的四板斧6.1 第一步分清楚是哪一层出了问题通信系统是分层设计的所以排查也得分层。我的经验是先从物理层入手再到数据链路层最后看应用层。物理层就是连线、电平、波形数据链路层是帧格式、时序、地址应用层才是你业务数据的语义。拿FT232H通信异常来说FT232H是USB转UART/FIFO的桥接芯片很多人遇到“连不上”“乱码”“超时”的问题。这时候不要急着改代码先查三层物理层USB是否被识别驱动是否装好设备管理器里波特率是否设置正确用示波器或万用表测TX/RX引脚有没有电平翻转数据链路层用串口助手自发自收确认发送的数据能原样回来。能回来说明链路通不能回来就回到物理层。应用层检查你对接收数据的解析逻辑。比如是否设了超时、是否按长度截取、是否忽略了起始符。很多人在第一步都没确认的情况下就一头扎进应用代码里结果调了半天发现是线接反了。6.2 第二步示波器看波形逻辑分析仪看时序“如何通过CAN总线波形判断通信的好坏”这类问题答案就是抓波形。示波器能看到模拟信号质量幅值、边沿、振铃、噪声。逻辑分析仪则更侧重数字逻辑哪些位是0是1、时序关系对不对。排查I2C总线时逻辑分析仪直接解码出地址和数据的十六进制值对照芯片手册就知道是不是读到了期望的值。排查SPI时重点看CS片选拉低的时间窗口、CLK的极性和相位、DIN/DOUT数据是否有偏移。排查UART乱码示波器抓一帧波形手动量一下起始位宽度就能算出实际波特率是多少和配置的对比一下就知道误差出在哪。6.3 第三步看日志要带着“协议思维”应用层的日志也不是随便看的。我见过很多同事把收到的原始数据直接打成人眼可读的字符串结果遇到二进制协议就全成了乱码。正确做法是先把原始字节以十六进制打印出来再按照协议字段逐一解析。判断通信好坏不能只看“有没有数据”要看“数据对不对”。比如Modbus RTU帧头、功能码、数据、CRC校验任何一位错位都会导致解析失败。这时候日志里记录的十六进制帧配合协议文档通常一眼就能看出是CRC算错了还是从机地址配错了。6.4 第四步用最小系统复现再做加法如果整个系统一堆设备通信异常时到底是谁的问题我的习惯是先让两个节点通信其余全部断开确认链路基本可用后再一个个挂设备。比如RS485总线上先把主机和一个从机直接对接两端加终端电阻能通了再加第二个从机再排查地址和接线。这个“逐层剥离逐步增加”的方法虽然笨但是最可靠。它利用了通信基本原理里的一条铁律每一层的问题只能在本层解决。如果你在物理层还有终端电阻没接对那应用层写再多重传机制都白搭。7. 写在最后的一些体会这几年做嵌入式、做上位机、做网络通信我越来越觉得通信基本原理不是一门需要背公式的课而是一套“思维方式”。从电平到协议从总线到网络从硬件到软件本质上都在回答同一个问题A如何让B准确理解自己的意图我最常跟新人说的一句话是遇到通信问题先别急着换代码先问自己三个问题——信号是否可靠地到达了接收方是否按约定解析了解析之后是否给出了正确的回应把这三个问题想透了大部分问题都能定位到具体环节。比如你发一帧数据对方没回应那就先看对方引脚是否收到了这帧信号的边沿再看收到的比特组合是不是你发的那一串最后看它有没有按协议回包。每一步都有对应的工具和检查方法而不是靠猜。如果你准备深入某个具体领域比如CAN总线开发、I2C传感器驱动、或者Socket网络编程请务必先回来把这篇“基本原理”吃透。基础打牢了后面遇到再花哨的技术也只是一层窗户纸。下一篇文章我打算聊点更贴近实战的东西比如“从波形到数据用逻辑分析仪调通第一路I2C传感器”。如果你也有类似的通信调试经历欢迎在评论区聊聊你踩过的坑。