1. 通信协议基础认知
从事嵌入式开发这些年,我经常遇到新手工程师分不清SPI、UART和IIC这三种基础通信协议的区别。这三种协议就像电子设备之间的"方言",各自适用于不同的场景。记得我第一次调试传感器时,就因为选错了通信方式,导致整块电路板无法正常工作。今天我们就来彻底搞懂它们的差异,让你在项目选型时不再纠结。
这三种协议都属于串行通信(Serial Communication)的范畴,与并行通信相比,它们只需要少数几根线就能完成数据传输,特别适合引脚资源有限的单片机系统。但它们在硬件连接、通信方式、传输速率等方面存在显著差异,这些差异直接决定了它们各自的应用场景。
提示:选择通信协议时,首先要考虑外设支持的协议类型,其次要考虑系统对速度、可靠性和硬件复杂度的要求。
2. 物理层特性对比
2.1 接线方式差异
SPI(Serial Peripheral Interface)采用四线制连接:
- SCLK:时钟信号线(主机控制)
- MOSI:主机输出从机输入
- MISO:主机输入从机输出
- SS/CS:片选信号(每个从机单独一根)
UART(Universal Asynchronous Receiver/Transmitter)只需要两根线:
- TX:发送数据线
- RX:接收数据线
IIC(Inter-Integrated Circuit)采用两线制:
- SDA:双向数据线
- SCL:时钟信号线
我在实际布线时发现,SPI虽然线多但布线简单,IIC虽然省线但要注意上拉电阻的取值(通常4.7kΩ)。UART最省事,但只能点对点连接。
2.2 电气特性对比
| 特性 | SPI | UART | IIC |
|---|---|---|---|
| 电压电平 | 取决于器件 | 通常TTL/RS232 | 开漏输出需上拉 |
| 最大速率 | 50Mbps+ | 115200bps常见 | 400kbps(标准模式) |
| 传输距离 | <1m | 可达15m(RS232) | <1m |
| 功耗 | 较高 | 中等 | 最低 |
实测中发现,SPI在高速传输时容易产生信号完整性问题,需要严格控制走线长度。IIC的时钟拉伸(clock stretching)特性在低速设备混用时需要特别注意。
3. 协议层深度解析
3.1 通信机制差异
SPI是全双工同步通信协议,主机通过SCLK提供时钟信号,数据在时钟边沿被采样。它的优势在于:
- 数据传输没有帧格式限制
- 时钟速率可灵活调整
- 支持多从机架构(通过片选信号)
我曾用STM32的SPI接口驱动TFT屏幕,设置时钟相位(CPHA)和极性(CPOL)参数时踩过坑:
c复制// 正确的SPI初始化配置示例
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; // 时钟极性
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; // 时钟相位
UART是异步通信协议,双方需要预先约定相同的波特率。它的特点包括:
- 必须配置起始位、停止位
- 常见8N1格式(8数据位、无校验、1停止位)
- 需要精确的时钟源
IIC是半双工同步协议,具有以下独特机制:
- 设备地址寻址(7位或10位)
- 多主机仲裁
- 应答机制(ACK/NACK)
3.2 典型通信时序
SPI的时序最为简单,以Mode 0为例:
- 拉低片选信号
- 在SCLK下降沿输出数据
- 在SCLK上升沿采样数据
- 传输完成后拉高片选
UART的帧结构包含:
- 起始位(逻辑低)
- 5-9位数据位(LSB先发)
- 可选校验位
- 停止位(逻辑高)
IIC的典型传输流程:
- 起始条件(SDA下降沿时SCL高)
- 发送7位地址+R/W位
- 接收应答
- 数据传输(每字节后跟应答)
- 停止条件(SDA上升沿时SCL高)
4. 应用场景选择指南
4.1 何时选择SPI
SPI适合以下场景:
- 高速数据传输(Flash存储器、显示屏)
- 需要全双工通信
- 系统中有多个同类型外设
- 对布线复杂度不敏感
我在智能手表项目中使用SPI连接加速度计,实现了10MHz的采样率。但要注意SPI没有流控机制,连续传输大数据时需要做好缓冲。
4.2 何时选择UART
UART的典型应用包括:
- 设备调试接口(连接USB转TTL)
- 远距离通信(配合RS232/RS485)
- 简单点对点连接
- 不同电压域设备间通信
一个实用的技巧:当波特率误差超过2%时,通信可能失败。我曾用下面公式计算实际误差:
code复制误差% = |(实际波特率 - 理论波特率)| / 理论波特率 × 100%
4.3 何时选择IIC
IIC在以下场景表现优异:
- 连接多个低速传感器(温湿度、气压等)
- 引脚资源极度受限
- 需要热插拔支持
- 系统中有多个主设备
在物联网网关设计中,我通过IIC同时连接了0x68(RTC)、0x76(气压计)和0x44(温湿度)三个设备。记得地址冲突时可以通过硬件跳线修改地址。
5. 常见问题排查实录
5.1 SPI通信失败排查
-
时钟无输出:
- 检查SPI外设时钟使能
- 确认GPIO模式设置为复用推挽输出
- 示波器观察SCLK信号
-
数据错位:
- 确认CPHA/CPOL设置与从设备一致
- 检查时钟频率是否超过从设备限制
- 尝试降低时钟速率测试
5.2 UART数据乱码
-
波特率不匹配:
- 双方使用相同波特率
- 检查系统时钟和分频设置
- 用示波器测量实际波特率
-
帧格式错误:
- 确认数据位、停止位、校验位设置
- 检查是否启用硬件流控(RTS/CTS)
5.3 IIC总线锁死
-
总线死锁:
- 检查SCL是否被意外拉低
- 尝试发送多个时钟脉冲恢复
- 必要时重新上电复位
-
无应答:
- 确认设备地址正确
- 检查上拉电阻值(通常4.7kΩ)
- 测量总线电压是否符合要求
6. 进阶使用技巧
6.1 SPI的DMA应用
对于高速数据传输,使用DMA可以大幅降低CPU负载。配置要点:
- 设置正确的数据宽度(8/16位)
- 配置DMA传输完成中断
- 注意缓存对齐问题
c复制// STM32 SPI DMA配置示例
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
6.2 UART的环形缓冲区
实现稳定的UART通信需要良好的缓冲机制。我常用的设计:
- 双缓冲区结构(接收/发送)
- 使用读写指针管理
- 加入溢出保护
6.3 IIC的软件实现
当硬件IIC不稳定时,可以用GPIO模拟:
- 严格遵循时序要求
- 处理时钟拉伸
- 加入超时机制
c复制void I2C_Delay(void) {
for(uint8_t i=0; i<10; i++) __NOP();
}
void I2C_Start(void) {
SDA_HIGH();
SCL_HIGH();
I2C_Delay();
SDA_LOW();
I2C_Delay();
SCL_LOW();
}
7. 性能优化实践
7.1 SPI速度提升技巧
- 使用IO翻转速率高的GPIO
- 减小PCB走线长度
- 适当提高驱动能力
- 使用DMA减少中断开销
实测发现,将SPI时钟从1MHz提升到8MHz时,TPS61165 LED驱动器的刷新率从30fps提升到了240fps。
7.2 UART可靠性增强
- 添加硬件流控(CTS/RTS)
- 使用奇偶校验
- 实现软件校验和
- 采用差分传输(RS422/485)
在工业环境中,我通过改用RS485接口,将UART通信距离从3米延长到了1200米。
7.3 IIC总线扩展方案
当需要连接大量IIC设备时:
- 使用IIC多路复用器(如PCA9548)
- 分时复用总线
- 考虑改用SPI总线
在多传感器数据采集系统中,我通过PCA9548成功扩展出8条IIC总线,连接了24个环境传感器。
