3、通信协议基础:UART、I2C、SPI 协议详解与对比、波特率/时钟配置要点
做模组联调,说白了就是让两个或多个芯片“说同一种语言”。
我见过不少新手,上来就怼代码,结果调了三天发现是协议配置错了。嗯,这章咱们就把嵌入式里最常用的三种总线——UART、I2C、SPI——彻底捋一遍。你想想看,搞懂了它们,联调时至少能少走一半弯路。
核心观点:选对协议,联调就成功了一半。选错协议,后面全是坑。
3.1 UART:最基础的“串口”
UART,通用异步收发器。说白了就是两根线(TX、RX)来回传数据。没有时钟线,全靠双方约定好“波特率”来同步。
我个人习惯把UART比作两个人打电话——一个人说,一个人听,说完一句换一下。但问题是,没有时钟线,你怎么知道对方什么时候开始说话?
这就是波特率的作用。波特率就是每秒传输的符号数。比如9600波特,意味着每秒钟传9600个符号。每个符号通常是一个bit。所以9600波特 ≈ 9600bps。
我的经验:联调时最常犯的错误就是波特率不匹配。我曾经花了一整天查一个GPS模组,死活收不到数据,最后发现是开发板默认115200,模组是9600。嗯,从那以后我每次联调第一件事就是确认波特率。
UART 帧格式
一个UART数据帧长这样:
起始位(1bit) + 数据位(5~8bit) + 校验位(可选) + 停止位(1~2bit)
举个例子,8N1就是:1个起始位 + 8个数据位 + 无校验 + 1个停止位。这是最常用的配置。
波特率配置要点
- 双方必须一致:发送方和接收方的波特率误差最好在±2%以内。超过这个范围,数据就会错位。
- 常用波特率:9600、19200、38400、115200、921600。我一般调试用115200,稳定又够快。
- 时钟源精度:MCU的主时钟精度直接影响波特率。用内部RC振荡器时,误差可能达到3%~5%,这时候建议用外部晶振。
注意:有些模组(比如蓝牙模块)默认波特率是9600,但支持自动波特率检测。如果你用115200去连,它可能能自适应,也可能直接不响应。我建议先看数据手册,别赌。
3.2 I2C:多设备“总线”
I2C只有两根线:SDA(数据线)和SCL(时钟线)。但它能挂几十个设备,每个设备有唯一的地址。你想想看,这比UART的点对点灵活多了。
I2C是同步协议,时钟由主机产生。主机发送起始条件后,从机在地址匹配时回应ACK。嗯,这里有个坑——I2C是开漏输出,所以必须加上拉电阻。
I2C 时序要点
起始条件:SCL高电平时,SDA从高变低
停止条件:SCL高电平时,SDA从低变高
数据传输:SCL低电平时改变SDA,SCL高电平时采样SDA
应答:每个字节后接收方拉低SDA表示ACK
时钟配置要点
- 标准模式:100kHz。最通用,几乎所有I2C设备都支持。
- 快速模式:400kHz。大部分现代传感器都支持。
- 高速模式:3.4MHz。需要特殊硬件支持,我很少用。
- 上拉电阻:一般4.7kΩ~10kΩ。总线电容大时用更小的电阻,但功耗会增大。
避坑指南:我曾经调试一个温湿度传感器,I2C总是读到0xFF。查了半天发现是上拉电阻焊错了,用了100kΩ。信号上升沿太慢,SCL时钟都跑完了SDA还没稳定。换成4.7kΩ后一切正常。
3.3 SPI:高速“全双工”
SPI是速度之王。它用4根线:MOSI(主机输出从机输入)、MISO(主机输入从机输出)、SCLK(时钟)、CS(片选)。
SPI是全双工的——主机发数据的同时也能收数据。这一点比I2C和UART都强。我调LCD屏、SD卡、射频模组时,首选SPI。
SPI 四种模式
SPI有4种模式,由时钟极性(CPOL)和时钟相位(CPHA)决定:
| 模式 | CPOL | CPHA | 时钟空闲电平 | 数据采样边沿 |
|---|---|---|---|---|
| 模式0 | 0 | 0 | 低电平 | 上升沿 |
| 模式1 | 0 | 1 | 低电平 | 下降沿 |
| 模式2 | 1 | 0 | 高电平 | 下降沿 |
| 模式3 | 1 | 1 | 高电平 | 上升沿 |
重要:SPI主从双方的模式必须一致。我见过有人把从机设成模式0,主机设成模式3,结果数据全是乱的。读数据手册时,一定要确认从机支持的SPI模式。
时钟配置要点
- 最大时钟频率:取决于从机规格和PCB走线长度。一般10MHz以内很安全,超过20MHz就要注意信号完整性了。
- 分频系数:MCU主时钟通过分频得到SPI时钟。比如主频80MHz,分频系数8,得到10MHz SPI时钟。
- 片选时序:CS在传输前拉低,传输后拉高。有些从机要求CS拉低后等几个时钟周期才能开始传数据。
3.4 三大协议对比总结
我把它们放在一起对比,你一眼就能看出区别:
| 特性 | UART | I2C | SPI |
|---|---|---|---|
| 信号线数量 | 2(TX/RX) | 2(SDA/SCL) | 4(MOSI/MISO/SCLK/CS) |
| 通信方式 | 异步 | 同步 | 同步 |
| 传输方向 | 全双工 | 半双工 | 全双工 |
| 最大从机数 | 1对1 | 理论127个 | 理论无限(靠CS) |
| 典型速度 | 115200bps~1Mbps | 100kHz~3.4MHz | 10MHz~50MHz |
| 硬件复杂度 | 低 | 中(需上拉电阻) | 中高 |
| 典型应用 | 调试口、GPS、蓝牙 | 传感器、EEPROM | LCD、SD卡、射频模组 |
3.5 联调时的协议选择策略
我个人有个简单的选择原则:
- 速度要求不高,设备少:用UART。简单可靠,调试方便。
- 设备多,线要少:用I2C。两根线挂一堆传感器,省IO口。
- 要高速,传大量数据:用SPI。LCD刷屏、SD卡读写,非它莫属。
最后说一句:不管用哪种协议,联调前先拿逻辑分析仪或示波器看波形。我每次联调新模组,第一件事就是抓波形确认时序对不对。这比看代码靠谱多了。