有了UART,为什么还有IIC和SPI?

大家好,我是良许。

在嵌入式开发中,我们经常会接触到UART、IIC(I2C)和SPI这三种通信协议。

很多初学者会有疑问:既然UART已经可以实现串行通信了,为什么还需要IIC和SPI呢?

今天我们就来深入聊聊这个话题,看看这三种通信协议各自的特点和应用场景。

1. 三种通信协议的基本特点

1.1 UART的特点与局限

UART(Universal Asynchronous Receiver/Transmitter,通用异步收发传输器)是我们最常接触的串行通信协议之一。

它的工作原理相对简单,只需要两根线就能实现全双工通信:一根TX(发送)、一根RX(接收),再加上一根GND(地线)。

UART的优点很明显:实现简单,使用方便,几乎所有的MCU都支持。

在调试阶段,我们经常用UART来打印日志信息,这也是为什么串口调试助手成为嵌入式工程师必备工具的原因。

但是,UART也有它的局限性。

首先,UART是点对点通信,一个UART接口只能连接一个设备。

如果你的STM32需要同时和多个传感器通信,就需要多个UART接口,这会占用大量的GPIO资源。

其次,UART没有时钟线,通信双方必须事先约定好波特率,如果波特率不匹配,通信就会出错。

最后,UART的传输速度相对较慢,常用的波特率是9600、115200等,对于需要高速传输的场景就显得力不从心了。

1.2 IIC的独特优势

IIC(Inter-Integrated Circuit)是由飞利浦公司开发的一种两线式串行总线协议。

它只需要两根线:SCL(时钟线)和SDA(数据线),就可以实现多主多从的通信架构。

IIC最大的优势在于它的多设备支持能力。

在同一条IIC总线上,你可以挂载多达127个设备(理论上),每个设备都有自己独特的7位地址(也支持10位地址)。

这意味着,用STM32的一个IIC接口,你就可以同时连接多个传感器、EEPROM、RTC等设备,大大节省了GPIO资源。

在实际项目中,我曾经用一个IIC总线同时连接了温湿度传感器、光照传感器、OLED显示屏和一个EEPROM芯片。

如果用UART实现同样的功能,至少需要4个UART接口,这在资源有限的MCU上是不现实的。

IIC的另一个优点是支持多主机模式。

虽然在实际应用中多主机场景不多见,但在某些复杂系统中,这个特性还是很有用的。

比如在汽车电子系统中,多个ECU可能需要共享同一条IIC总线来访问某些共享资源。

1.3 SPI的高速特性

SPI(Serial Peripheral Interface)是由摩托罗拉公司推出的一种高速全双工同步串行通信协议。

标准的SPI需要四根线:SCLK(时钟线)、MOSI(主机输出从机输入)、MISO(主机输入从机输出)和SS/CS(片选信号)。

SPI最突出的特点就是速度快。

因为它是同步通信,有独立的时钟线,所以不存在波特率不匹配的问题。

SPI的时钟频率可以达到几十MHz甚至上百MHz,这使得它非常适合需要高速数据传输的场景,比如SD卡、Flash存储器、高速ADC/DAC等。

在我之前做的一个汽车仪表项目中,需要驱动一块TFT彩屏显示复杂的图形界面。

我们选择了SPI接口的屏幕,时钟频率设置到了36MHz,这样才能保证画面刷新足够流畅。

如果用UART或者IIC,根本无法满足这个速度要求。

SPI也支持多从机模式,但和IIC不同的是,每增加一个从机设备,就需要额外占用一个GPIO作为片选信号。

这是SPI的一个小缺点,但考虑到它的高速特性,这点代价还是值得的。

2. 实际应用场景对比

2.1 何时选择UART

UART最适合的场景是点对点的中低速通信。比如:

调试输出是UART最常见的应用。

我们在开发STM32程序时,经常会用printf函数通过UART输出调试信息。

这种场景下,UART的简单易用性是其他协议无法比拟的。

下面是一个简单的HAL库UART输出示例:

// UART初始化(通常由CubeMX自动生成)
UART_HandleTypeDef huart1;

void MX_USART1_UART_Init(void)
{
    huart1.Instance = USART1;
    huart1.Init.BaudRate = 115200;
    huart1.Init.WordLength = UART_WORDLENGTH_8B;
    huart1.Init.StopBits = UART_STOPBITS_1;
    huart1.Init.Parity = UART_PARITY_NONE;
    huart1.Init.Mode = UART_MODE_TX_RX;
    huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    HAL_UART_Init(&huart1);
}

// 重定向printf到UART
int fputc(int ch, FILE *f)
{
    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
    return ch;
}

// 使用示例
int main(void)
{
    HAL_Init();
    MX_USART1_UART_Init();
    
    printf("System initialized!\r\n");
    printf("Temperature: %d degrees\r\n", temperature);
    
    while(1)
    {
        // 主循环
    }
}

除了调试,UART还常用于和PC通信、GPS模块通信、蓝牙模块通信等场景。

这些场景的共同特点是:设备数量少(通常就一个),对速度要求不高,但需要可靠的数据传输。

2.2 何时选择IIC

IIC最适合连接多个低速外设的场景。

在我做过的项目中,IIC的典型应用包括:

传感器网络是IIC的主战场。比如在一个环境监测系统中,你可能需要同时读取温湿度传感器(如SHT30)、气压传感器(如BMP280)、光照传感器(如BH1750)等。

这些传感器的数据更新频率不高,但数量多,用IIC连接最合适。

// IIC读取SHT30温湿度传感器示例
I2C_HandleTypeDef hi2c1;

#define SHT30_ADDR 0x44 << 1  // 7位地址左移1位

void SHT30_ReadData(float *temperature, float *humidity)
{
    uint8_t cmd[2] = {0x2C, 0x06};  // 高重复性测量命令
    uint8_t data[6];
    
    // 发送测量命令
    HAL_I2C_Master_Transmit(&hi2c1, SHT30_ADDR, cmd, 2, HAL_MAX_DELAY);
    HAL_Delay(20);  // 等待测量完成
    
    // 读取数据
    HAL_I2C_Master_Receive(&hi2c1, SHT30_ADDR, data, 6, HAL_MAX_DELAY);
    
    // 计算温湿度
    uint16_t temp_raw = (data[0] << 8) | data[1];
    uint16_t humi_raw = (data[3] << 8) | data[4];
    
    *temperature = -45 + 175 * ((float)temp_raw / 65535.0);
    *humidity = 100 * ((float)humi_raw / 65535.0);
}

// 在同一总线上读取多个传感器
void ReadAllSensors(void)
{
    float temp, humi;
    uint16_t light;
    
    // 读取温湿度
    SHT30_ReadData(&temp, &humi);
    printf("Temperature: %.2f C, Humidity: %.2f %%\r\n", temp, humi);
    
    // 读取光照(假设BH1750地址为0x23)
    // ... BH1750读取代码
    
    // 所有传感器共用同一个IIC总线
}

EEPROM存储也是IIC的常见应用。

很多小容量的EEPROM芯片(如AT24C02、AT24C256)都使用IIC接口。

在需要保存系统配置参数、校准数据等场景下,IIC EEPROM是很好的选择。

2.3 何时选择SPI

SPI最适合需要高速数据传输的场景。典型应用包括:

Flash存储器是SPI的重要应用领域。

在我参与的一个数据记录仪项目中,需要存储大量的传感器数据,我们选择了SPI接口的NOR Flash芯片(如W25Q128)。

SPI的高速特性保证了数据能够快速写入和读取。

// SPI Flash写入示例(W25Q128)
SPI_HandleTypeDef hspi1;

#define W25Q128_CS_LOW()  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET)
#define W25Q128_CS_HIGH() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET)

void W25Q128_WritePage(uint32_t addr, uint8_t *data, uint16_t len)
{
    uint8_t cmd[4];
    
    // 写使能
    W25Q128_CS_LOW();
    cmd[0] = 0x06;  // Write Enable命令
    HAL_SPI_Transmit(&hspi1, cmd, 1, HAL_MAX_DELAY);
    W25Q128_CS_HIGH();
    
    // 页编程
    W25Q128_CS_LOW();
    cmd[0] = 0x02;  // Page Program命令
    cmd[1] = (addr >> 16) & 0xFF;
    cmd[2] = (addr >> 8) & 0xFF;
    cmd[3] = addr & 0xFF;
    HAL_SPI_Transmit(&hspi1, cmd, 4, HAL_MAX_DELAY);
    HAL_SPI_Transmit(&hspi1, data, len, HAL_MAX_DELAY);
    W25Q128_CS_HIGH();
    
    // 等待写入完成
    HAL_Delay(5);
}

显示屏驱动也经常使用SPI。

TFT彩屏、OLED屏等都有SPI接口版本。

由于显示屏需要传输大量的像素数据,SPI的高速特性能够保证画面流畅显示。

高速ADC/DAC也是SPI的典型应用。

在音频处理、高速数据采集等场景下,SPI能够提供足够的带宽来传输采样数据。

3. 三种协议的技术细节对比

3.1 硬件资源占用

从硬件资源占用的角度来看,三种协议各有特点。

UART每个接口需要2个GPIO(TX和RX),如果需要硬件流控,还需要额外的RTS和CTS引脚。

重要的是,每增加一个UART设备,就需要一个完整的UART外设模块。

对于GPIO资源紧张的MCU来说,这是个不小的负担。

IIC只需要2个GPIO(SCL和SDA),无论连接多少个设备,都只占用这两个引脚。

这是IIC最大的优势。

但需要注意的是,IIC总线需要上拉电阻(通常是4.7K或10K),这在设计PCB时需要考虑进去。

SPI需要3个共享的GPIO(SCLK、MOSI、MISO),加上每个从设备一个独立的片选信号。

如果你要连接5个SPI设备,就需要3+5=8个GPIO。

虽然比UART好一些,但还是比IIC占用更多的引脚资源。

3.2 传输速度对比

传输速度是选择通信协议时的重要考量因素。

UART的速度通常在几Kbps到几Mbps之间。

常用的波特率有9600、115200、460800、921600等。在实际应用中,115200bps是最常用的速度,因为它在可靠性和速度之间取得了很好的平衡。

更高的波特率虽然理论上可行,但容易受到线缆长度、电磁干扰等因素的影响。

IIC的标准速度有三种:标准模式(100Kbps)、快速模式(400Kbps)和高速模式(3.4Mbps)。

在实际应用中,大多数IIC设备工作在100Kbps或400Kbps。

虽然速度不如SPI,但对于传感器、EEPROM等低速设备来说已经足够了。

SPI的速度可以达到几十MHz甚至上百MHz。

在STM32中,SPI的时钟频率通常可以设置到APB总线频率的一半。

比如如果APB2总线是72MHz,SPI就可以工作在36MHz。

这个速度是UART和IIC无法企及的。

3.3 可靠性与抗干扰能力

在可靠性方面,三种协议各有千秋。

UART采用异步通信,没有时钟线,因此对时钟精度要求较高。

如果收发双方的时钟偏差太大,就会导致数据错误。

但UART通常有奇偶校验位和停止位,可以在一定程度上检测传输错误。

在工业现场,UART常常配合RS485或RS232电平转换芯片使用,以提高抗干扰能力和传输距离。

IIC采用开漏输出加上拉电阻的方式,这种设计天然支持多主机仲裁和时钟同步。

IIC协议本身包含了应答机制,每传输一个字节,接收方都要发送ACK或NACK信号,这提高了通信的可靠性。

但IIC对上拉电阻的阻值比较敏感,如果选择不当,可能会导致通信不稳定。

SPI采用推挽输出,信号边沿陡峭,抗干扰能力较强。由于有独立的时钟线,不存在时钟同步问题。

但SPI协议本身没有应答机制,如果需要确认数据是否正确接收,需要在应用层实现。

在高速应用中,SPI的信号完整性需要特别注意,可能需要考虑阻抗匹配、走线长度等因素。

4. 总结与选择建议

回到最初的问题:有了UART,为什么还需要IIC和SPI?答案很简单:因为它们各有所长,适用于不同的应用场景。

UART适合点对点、中低速、需要简单可靠通信的场景。

它的优势在于实现简单、使用方便、几乎所有MCU都支持。

如果你只需要连接一两个设备,不需要很高的速度,UART是最好的选择。

IIC适合连接多个低速外设的场景。

它的最大优势是节省GPIO资源,一条总线可以挂载多个设备。

如果你的项目需要连接多个传感器、EEPROM等低速设备,IIC是不二之选。

SPI适合需要高速数据传输的场景。

它的速度是三者中最快的,适合Flash存储器、显示屏、高速ADC/DAC等应用。

如果你的项目对速度有较高要求,SPI是最佳选择。

在实际项目中,这三种协议往往是配合使用的。

比如在我做过的一个智能家居项目中,STM32通过UART连接WiFi模块与云端通信,通过IIC连接多个环境传感器采集数据,通过SPI连接Flash存储历史数据。

这样的设计充分发挥了每种协议的优势,实现了最优的系统架构。

选择通信协议时,需要综合考虑设备数量、传输速度、GPIO资源、成本等多个因素。没有最好的协议,只有最合适的协议。

理解每种协议的特点和适用场景,才能在项目中做出正确的选择。

希望这篇文章能帮助大家更好地理解UART、IIC和SPI这三种常用的通信协议。

posted on 2026-01-15 10:06  良许Linux  阅读(3)  评论(0)    收藏  举报