SPI、I2C、UART 详解
SPI、I2C 和 UART 是三种常见的串行通信协议,广泛应用于嵌入式系统、传感器、外设通信等领域。它们各有特点,适用于不同的场景。以下是对这三种协议的详细解释和比较:
1. SPI(Serial Peripheral Interface)
1.1 基本特性
- 全双工通信:可以同时发送和接收数据。
- 高速传输:通常支持几 Mbps 到几十 Mbps 的传输速率。
- 主从架构:一个主设备可以连接多个从设备,通过片选信号(SS)选择通信的从设备。
- 同步通信:使用时钟信号(SCLK)同步数据传输。
- 硬件简单:需要较多的引脚(至少4根:SCLK、MOSI、MISO、SS)。
1.2 信号线
- SCLK(Serial Clock):时钟信号,由主设备生成。
- MOSI(Master Out Slave In):主设备发送数据,从设备接收数据。
- MISO(Master In Slave Out):从设备发送数据,主设备接收数据。
- SS(Slave Select):片选信号,用于选择从设备。
1.3 通信过程
- 主设备拉低目标从设备的 SS 信号。
- 主设备生成时钟信号(SCLK)。
- 主设备通过 MOSI 发送数据,同时通过 MISO 接收数据。
- 通信结束后,主设备拉高 SS 信号。
1.4 优点
- 高速传输。
- 全双工通信。
- 硬件实现简单。
1.5 缺点
- 需要较多的引脚。
- 不支持多主设备。
1.6 应用场景
- 高速数据传输(如存储器、显示屏、传感器)。
- 需要全双工通信的场景。
2. I2C(Inter-Integrated Circuit)
2.1 基本特性
- 半双工通信:同一时间只能发送或接收数据。
- 低速传输:通常支持 100kbps(标准模式)、400kbps(快速模式)和 3.4Mbps(高速模式)。
- 多主多从架构:支持多个主设备和多个从设备。
- 同步通信:使用时钟信号(SCL)同步数据传输。
- 硬件简单:只需要两根信号线(SDA、SCL)。
2.2 信号线
- SDA(Serial Data):数据线,用于发送和接收数据。
- SCL(Serial Clock):时钟信号,由主设备生成。
2.3 通信过程
- 主设备发送起始条件(START)。
- 主设备发送从设备地址和读写位。
- 从设备发送应答信号(ACK)。
- 主设备发送或接收数据。
- 通信结束后,主设备发送停止条件(STOP)。
2.4 优点
- 硬件简单,只需要两根信号线。
- 支持多主设备和多从设备。
- 地址机制灵活,支持多个设备。
2.5 缺点
- 传输速率较低。
- 半双工通信。
2.6 应用场景
- 低速数据传输(如传感器、EEPROM)。
- 需要连接多个设备的场景。
3. UART(Universal Asynchronous Receiver/Transmitter)
3.1 基本特性
- 异步通信:不需要时钟信号,通过起始位和停止位同步数据传输。
- 全双工通信:可以同时发送和接收数据。
- 点对点通信:通常用于两个设备之间的通信。
- 硬件简单:只需要两根信号线(TX、RX)。
3.2 信号线
- TX(Transmit):发送数据。
- RX(Receive):接收数据。
3.3 通信参数
- 波特率:数据传输速率,如9600、19200、115200等。
- 数据位:每个数据包的位数,通常为5、6、7或8位。
- 停止位:每个数据包结束的标志,通常为1或2位。
- 校验位:用于错误检测,可以是无校验、奇校验或偶校验。
3.4 通信过程
- 发送设备在空闲状态下保持高电平。
- 发送设备拉低电平(起始位)表示数据传输开始。
- 发送设备发送数据位。
- 发送设备发送校验位(可选)。
- 发送设备拉高电平(停止位)表示数据传输结束。
3.5 优点
- 硬件简单,只需要两根信号线。
- 全双工通信。
- 异步通信,不需要时钟信号。
3.6 缺点
- 传输速率较低。
- 点对点通信,不支持多设备。
3.7 应用场景
- 计算机与外部设备通信(如调试接口、GPS模块)。
- 需要简单点对点通信的场景。
4. SPI、I2C、UART 的比较
特性 | SPI | I2C | UART |
---|---|---|---|
通信方式 | 同步 | 同步 | 异步 |
传输速率 | 高速(几 Mbps 到几十 Mbps) | 低速(100kbps 到 3.4Mbps) | 低速(通常 115200bps) |
信号线 | 4根(SCLK、MOSI、MISO、SS) | 2根(SDA、SCL) | 2根(TX、RX) |
通信模式 | 全双工 | 半双工 | 全双工 |
设备连接 | 主从架构,支持多从设备 | 多主多从架构 | 点对点 |
硬件复杂度 | 较高 | 较低 | 最低 |
应用场景 | 高速数据传输 | 低速多设备通信 | 简单点对点通信 |
5. 选择指南
-
选择 SPI:
- 需要高速数据传输。
- 需要全双工通信。
- 设备数量较少,且引脚资源充足。
-
选择 I2C:
- 需要连接多个设备。
- 引脚资源有限。
- 数据传输速率要求不高。
-
选择 UART:
- 需要简单的点对点通信。
- 不需要时钟信号。
- 数据传输速率要求不高。
6. 编程示例
6.1 SPI 示例(基于 Linux)
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>
int main() {
int fd;
unsigned char tx_buffer[] = {0x01, 0x02, 0x03};
unsigned char rx_buffer[3];
// 打开 SPI 设备
fd = open("/dev/spidev0.0", O_RDWR);
if (fd < 0) {
perror("open");
return -1;
}
// 发送和接收数据
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx_buffer,
.rx_buf = (unsigned long)rx_buffer,
.len = sizeof(tx_buffer),
};
ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
// 打印接收到的数据
for (int i = 0; i < sizeof(rx_buffer); i++) {
printf("Received: 0x%02X\n", rx_buffer[i]);
}
// 关闭 SPI 设备
close(fd);
return 0;
}
6.2 I2C 示例(基于 Linux)
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
int main() {
int fd;
unsigned char buffer[2];
// 打开 I2C 设备
fd = open("/dev/i2c-1", O_RDWR);
if (fd < 0) {
perror("open");
return -1;
}
// 设置从设备地址
int addr = 0x50;
if (ioctl(fd, I2C_SLAVE, addr) < 0) {
perror("ioctl");
return -1;
}
// 发送数据
buffer[0] = 0x00; // 寄存器地址
buffer[1] = 0x55; // 数据
write(fd, buffer, 2);
// 接收数据
read(fd, buffer, 1);
printf("Received: 0x%02X\n", buffer[0]);
// 关闭 I2C 设备
close(fd);
return 0;
}
6.3 UART 示例(基于 Linux)
#include <stdio.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <string.h>
int main() {
int fd;
struct termios options;
// 打开串口
fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1) {
perror("open");
return -1;
}
// 配置串口
tcgetattr(fd, &options);
cfsetispeed(&options, B9600); // 设置波特率
cfsetospeed(&options, B9600);
options.c_cflag |= (CLOCAL | CREAD); // 启用接收
options.c_cflag &= ~PARENB; // 无校验
options.c_cflag &= ~CSTOPB; // 1位停止位
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8; // 8位数据位
tcsetattr(fd, TCSANOW, &options);
// 发送数据
char tx_buffer[] = "Hello UART!";
write(fd, tx_buffer, strlen(tx_buffer));
// 接收数据
char rx_buffer[256];
int n = read(fd, rx_buffer, sizeof(rx_buffer));
if (n > 0) {
rx_buffer[n] = '\0';
printf("Received: %s\n", rx_buffer);
}
// 关闭串口
close(fd);
return 0;
}
总结
- SPI:适合高速、全双工通信,但需要较多引脚。
- I2C:适合低速、多设备通信,硬件简单。
- UART:适合简单的点对点通信,无需时钟信号。
根据具体需求选择合适的通信协议,可以更好地满足项目要求。