我们每天都在用 WiFi、蓝牙、USB——但你知道吗?在这些“高级”技术的背后,所有芯片互相“聊天”用的,其实是同一种最基础的“方言”。它只有两种音调:0V 和 5V(或 3.3V 等其他电平标准)。它的名字叫 TTL。
从单片机到 USB 转串口芯片,再到芯片与芯片之间的板级互联,TTL 电平几乎就是它们的“母语”。今天这篇文章不讲复杂的协议栈,只用一台示波器和几个小实验,带你亲眼看看芯片之间到底是怎么“说话”的。
写在前面:本文的演示目的是把抽象的理论知识变成肉眼可见的波形,因此会尽量避免使用过于晦涩的专业名词,方便初学者理解。如有疏漏之处,也欢迎各位大佬补充指正。
一、两个最基本的概念:模拟信号与数字信号
要理解芯片怎么通信,首先要知道两个最基本的概念:模拟信号和数字信号。
模拟信号是连续变化的。 我们以一条正弦波为例:横轴 X(时间)每走一点,纵轴 Y(电压)就跟着变一点,Y 可以取无限多个值——1.001V、1.002V、1.0021V……在任意两个电压值之间,永远还能找出第三个值。这就是模拟信号,现实世界中的声音、温度、光线,本质上都是模拟量。

数字信号则只有两种状态:0 或者 1。 还是那条正弦波,我们在中间画一条线作为判决门限,规定:高于这条线的是 1,低于这条线的是 0。原本平滑的波浪,就这样被“切”成了一串方方正正的 0 和 1。简单来讲,Y 只能是“有”或者“无”,没有中间态。这就是数字信号。

但是这里有一个问题:如果信号正好卡在门限线上,电路该怎么办? 它既不算高也不算低,输出就可能在 0 和 1 之间来回乱跳,造成误判。
所以实际电路中,我们会设置两个门限:
- 信号从高往低掉,要跌破下限(低电平门限)才算 0;
- 信号从低往高爬,要突破上限(高电平门限)才算 1。
中间那段模糊地带呢?电路直接“装没看见”,保持原来的状态不翻转。这样一来,即使信号在这个区间里受到噪声干扰、小幅晃荡,输出依然是稳定的。

这种“双门限、带记忆”的设计,在电路上有个专门的名字叫迟滞比较(施密特触发),它是数字电路抗干扰的第一道防线。以经典的 5V TTL 电平标准为例:输出低电平不高于 0.4V,输出高电平不低于 2.4V;接收端则把 0.8V 以下认作 0,把 2.0V 以上认作 1。中间留出的这段差距,就是专门用来“吃掉”噪声的噪声容限。
二、编码:给 0 和 1 一本“密码本”
知道了 0 和 1 怎么表示,接下来的问题是:两个设备怎么知道对方“说”了什么?
设备能发送 1 或者 0,但一长串 01001000 本身并没有含义。所以我们需要一套统一的规则,把 0 和 1 的排列组合“翻译”成具体内容——这就是编码。你可以把它理解成一本双方事先约定好的“密码本”。
最经典的密码本就是 ASCII 码。它规定了大写字母【H】对应二进制 0100 1000(十六进制 0x48),【e】对应 0110 0101,以此类推,每个字符都有唯一对应的 0/1 组合。
但要注意,UART 串口有个重要特点——低位先发(LSB First)。也就是说,实际线路上传输的顺序是反过来的:【H】在导线上真实出现的比特流是 0001 0010,而不是书写习惯的 0100 1000。这一点在后面读示波器波形时非常关键。

三、起始位与停止位:话从哪开始,到哪结束
我们已经知道了如何表达内容,但两个芯片又该如何知道对方什么时候开始说话、什么时候说完了呢?
试想一下:如果线路空闲时是 0,那么发送方连续发三个 0,接收方根本分不清这是“三个 0”还是“根本没说话”。所以必须有一套统一的“报幕”规则:
- 平时不说话时,线路保持高电平(1),这是双方约定好的“安静状态”;
- 要开始说话时,先把线路拉低变成 0,这个下降沿就是发令枪,叫起始位——接收方一检测到这个从高到低的跳变,就知道“对方开口了”,立刻进入接收状态;
- 说完约定的位数后,再拉回高电平,叫停止位,表示“这句话说完了”。
这样,无论数据内容是什么,接收方都能准确知道“话从哪开始、到哪结束”。这种不需要额外时钟线、靠电平跳变自行对齐节奏的方式,正是异步串口名字的由来。
四、一帧数据的完整结构:以发送“Hello”为例
把前面的规则组合起来:按照低位先发的原则,发送【Hello】时,每个字母都独立打包成一“帧”,在线路上实际长这样:

每一帧的结构都是:起始位(0)+ 8 位数据 + 停止位(1),一共 10 个比特。
为什么数据位偏偏是 8 位?因为 ASCII 码一个字符正好可以用 8 位(一个字节)来表示,这样一次发一个字母,收发双方处理起来都最方便。当然,数据位并不是固定的,常见的配置还有 7 位、6 位甚至 5 位——这一点我们会在后面的实验中亲手验证。
五、波特率:双方约定好的“节拍”
但光有帧结构还不够。考虑一个棘手的情况:如果数据是连续的 1 或者 0,比如 11110000,线路上的波形就是一段长时间的高电平加一段长时间的低电平。接收方怎么知道这是 4 个 1 还是 3 个 1?
所以我们还必须约定每个 0 或 1 持续多长时间——这就是波特率(Baud Rate)。
规则很简单:相同的时间长度只表示一个比特。比如 1 秒钟传 1 个比特,波特率就是 1;1 秒钟传 9600 个比特,波特率就是 9600。接收方照着事先约定好的时间“节拍”去数,一个格子对应一个比特,就不会数错。
反过来说,如果两边的波特率约定不一致——发送方按 9600 的节拍说,接收方按 4800 的节拍听——那每个比特都会被数成两个,收到的必然是乱码。这就是为什么串口通信前,双方必须先统一参数。
波特率的倒数就是位宽(每位持续时间):波特率 50 对应每位 20ms,波特率 9600 对应每位约 104μs。这个换算关系,马上就会用到。
六、实验验证:用示波器亲眼看一次“Hello”
理论讲完了,接下来我们动手验证。
实验器材:USB 转 TTL 转换器、示波器、电脑、杜邦线若干。
接线方法:找到转换器上的对应接口——TXD(发送端)和 GND(地),分别连接到示波器的探头信号端和接地夹。注意,示波器测量的是两点之间的电压差,所以 GND 一定要共地,否则测到的波形没有意义。接线完成后,用 USB 线把转换器接入电脑,即可开始测试。

参数设置:打开串口工具,设置波特率为 50(故意设得很低,方便肉眼观察),数据位 8 位,停止位 1 位——这就是教科书上最常见的“8N1”格式(N 表示无校验位)。

设置完成后,发送【Hello】,观察示波器。
可以看到,示波器上立刻出现了一串高低跳变的脉冲。我们把它展开,逐位来看。

第一步,核对节拍。 这串数据是在波特率 50 下发送的,意味着每个 0 或 1(bit)持续 20 毫秒。目前示波器的时间档位为每格 200 毫秒,一个位宽应该占 1/10 格。我们看示波器上最窄的那段电平,宽度正好就是 20 毫秒——说明理论和实测的节拍完全对上了。
第二步,逐位读取。 从波形上第一个“1”变成“0”的位置(也就是下降沿、起始位)开始,沿着时间轴向右,每 20 毫秒读一个电平:
- 先根据帧格式,把每帧的 8 位数据部分整理出来(去掉起始位和停止位);
- 由于 UART 是低位先发,把读到的 8 位反转过来,就得到了书写顺序的二进制;
- 拿第一帧来说,反转后显然就是我们期望的字母【H】的 ASCII 码
0100 1000; - 用同样的方法整理其余四帧,依次得到【e】【l】【l】【o】——正是我们发送的【Hello】。

至此,理论推导和实测波形严丝合缝。
七、改变参数再验证:波特率、数据位、停止位到底意味着什么
只测一组参数还不够说明问题。现在我们更改参数后再试一次:将波特率提到 100,数据位改为 7 位,停止位改为 2 位,再发一次【Hello】,然后对比两个波形。

先看波特率。 两次测量的时间档位不同(一次 200ms,一次 100ms),但两个波形上最短的一个间隔在屏幕上的长度差不多——别忘了时间档位差了一倍,这说明波特率 100 时,一个 0 或 1 的实际持续时间确实缩短了一半(20ms → 10ms)。波特率提高,每个比特的时间就缩短,单位时间内能传输的信息量也就更大。
再看数据位。 逐位读一遍波特率 100 的这组波形,可以发现数据部分从 8 位变成了 7 位。这 7 位是怎么来的?其实发送的时候,直接把每个数据最高位的那个 0 给“丢掉”了,只发低 7 位。
最后看停止位。 停止位从一个 1 变成了两个 1,也就是高电平的收尾部分变成了两个单位长度。停止位的本质就是帧与帧之间的最小间隔,设成 2 位,等于给接收方多留了一个比特的处理时间。

到这里我们小结一下:
- 数据位:每次发送或接收时,一帧中有效数据的实际长度;
- 停止位:可以理解成连续发送数据时,帧与帧之间的最小间隔时间;
- 波特率:一个 0 或一个 1 所占的单位时间长度,也就是通信的“节拍”。
这样一来,TTL 在实际通信中到底是怎么一步步实现的,就非常清楚了。
八、两个思考题
接下来我们思考两个问题:
- 如果把数据位设置成 6 位,采用 ASCII 编码还能正常通信吗?
- 如果两个设备通信时,停止位和数据位配置不一样,是否还能够通信?
问题一:数据位 6 位,还能用 ASCII 吗?
理论上,如果字符编码的高位有 1,而数据位只有 6 位,那高位的信息就会被截掉,必然出错。但标准 ASCII 码有个特点:它实际只定义了 0~127 共 128 个字符,最高两位恰好都是 0。也就是说,哪怕只发低 6 位,截掉的两位本来就是 0,不影响内容。
是骡子是马,拉出来遛遛。我们用实验来验证:直接把转换器的 TX 和 RX 短接(自己发给自己,这叫回环测试),用任意一个串口工具把数据位改成 6 位——由于收发两端是同一个设备,波特率和停止位天然一致,不会对结果产生影响。
先发送 3+3=9,可以看到通信完全正常——因为这些字符的编码都在 0~63 之间,低 6 位就足够表示了。再尝试发送任意大写字母,收到的消息就变成了乱码——因为大写字母的 ASCII 码在第 7 位上有 1,被截掉了,接收方只能根据剩下的 6 位“瞎猜”。

这就是数据位的具体含义:它决定了每个字符能携带多少信息,也决定了你能用多大的“密码本”。
问题二:两边参数配置不一样,还能通信吗?
这个问题大家不妨自己先琢磨琢磨。我可以先把实验现象告诉你们,然后你们根据这些现象自己推一推,看能不能找出原因。
现象是这样的——双方都用 ASCII 编码通信:
情况一:数据位 8 的一端,给数据位 7 的一端发消息,能正常通信;而且大部分情况下,不管停止位设成多少,都没问题。
情况二:反过来,数据位 7 的一端,给数据位 8 的一端发,就不行了;同样,停止位怎么改都不管用。
情况三:如果双方数据位相同,只是停止位不一样,那绝大部分情况下,两边能双向正常通信。

这三种现象的具体测试结果已经摆在这里了。想一想:为什么会出现这样的差异?提示一下——回忆一下数据位在帧结构里的位置,以及接收方是“从哪一位开始数、数几位”的。欢迎在评论区留下你的推理过程。
九、TTL 的局限与改进
到这里,我们已经弄清楚了什么是波特率、停止位、数据位——这就是最基础的 TTL 串口通信。很多嵌入式设备内部的模块间“对话”,采用的都是这种方式。
但 TTL 的缺点也非常明显:
- 传输距离短。 如果通信距离过长,导线的电阻就不能忽略,分压会导致到达终端的电压不足,接收端可能就识别不了高低电平了。一般来说,TTL 电平适合板级、厘米级到米级的通信。
- 抗干扰能力差。 TTL 是单端信号——用一根信号线相对于地的绝对电压来表示 0 和 1,0 和 1 之间的电压差只有几伏。外部电磁干扰很容易叠加在这几伏上,造成误判。速率越高,每个比特的时间越短,留给判决的余量越小;距离越远,干扰和衰减越严重。两个问题叠加,错误率直线上升。
为了解决这些问题,人们做了各种改进:
- RS232:把电压摆幅大幅提高,逻辑 1 用 -3
-15V 表示,逻辑 0 用 +3+15V 表示(典型 ±12V)。电压差拉大到二十多伏,抗干扰能力自然增强,传输距离也比 TTL 远得多(可达十几米)。 - RS422:改用差分信号,用两根线传输一对相位相反的信号,接收端只看两线之间的电压差值。外部干扰往往同时作用在两根线上(共模干扰),做差之后正好抵消——抗干扰能力大幅增强,可以实现千米级的超长距离和更高速率。
在后面我们还会讲到 RS485、CAN 等更多总线——它们在差分的基础上,又解决了多设备组网、总线仲裁等更复杂的问题。感兴趣的话,记得关注。
结语
从 0V 和 5V 两种电平出发,我们用门限把连续的电压切成 0 和 1,用编码给 0 和 1 赋予含义,用起始位和停止位划定一句话的边界,用波特率统一双方的节拍——一套完整的通信规则就这样搭建起来了,并且被示波器上的真实波形一一验证。
到这里,我们就真正了解了最基础的 TTL 电平通信。它是几乎所有现代通信技术的地基——看懂了它,再去看 RS485、CAN、I2C、SPI,甚至 USB 和网络,都会容易得多。
我们下期再见。
网页版串口工具
USB转串口设备
USB转串口设备(全功能RS-232)
本文首发于知乎:原文链接