引言:UART/SPI/I2C是嵌入式芯片外设扩展的核心接口
嵌入式系统设计中,芯片自身资源难以满足实际需求,需通过扩展外设实现功能升级。UART、SPI、I2C作为嵌入式最通用的三类串行通信接口,是芯片与外设数据传输的核心桥梁。
各类嵌入式芯片均集成这三类接口,应用覆盖全嵌入式领域。掌握其原理、硬件设计与驱动开发,是开发者从"会用芯片"到"会设计系统"的关键。本文将从多维度全面解析,助力开发者掌握外设扩展核心能力。
一、UART串行通信的核心原理、协议规范与硬件设计
1.1 核心原理
UART是异步串行通信接口,无需时钟同步,通过TX(发送)、RX(接收)两根数据线实现全双工双向数据传输,核心是完成芯片并行数据与串行数据的双向转换。
通信双方必须约定一致的通信参数,否则会导致数据传输错误。
1.2 协议规范
UART协议核心是通信参数和数据帧格式,核心参数需双方严格一致:
-
波特率:数据传输速率(bps),常见9600、115200等,是基础参数;
-
数据位:每帧有效数据位数,最常用8位,对应芯片并行数据;
-
停止位:数据帧结束标识,常用1位,告知接收端传输完成;
-
校验位:校验传输错误,常用无校验(简化)或偶校验(提升可靠性)。
最常用帧格式(8位数据+1位停止+无校验):空闲位(高)→起始位(低)→数据位(低位在前)→停止位(高)→空闲位(高),起始位触发接收端同步。
1.3 硬件设计
核心是TX/RX交叉连接和电平匹配,分两种场景:
-
同电平设备(如STM32间):TX与RX交叉连接,两设备共地;严禁TX与TX直接相连,避免引脚损坏。
-
不同电平设备(如STM32与PC):需通过MAX232等电平转换芯片,否则会导致传输失败或芯片损坏。
注意事项:缩短信号线长度减少干扰;布线超1米加100Ω匹配电阻;严格共地避免干扰。
二、SPI总线的核心原理、工作模式与多设备挂载设计
2.1 核心原理
SPI是同步串行总线,特点是同步传输、全双工、高速(远高于UART),通过4根核心信号线通信。采用主从模式,主机产生时钟,控制通信时序,从机仅被动收发数据。
2.2 核心信号线与工作模式
2.2.1 核心信号线
标准4线制,功能缺一不可:
-
SCLK:主机产生的时钟信号,决定传输速率;
-
MOSI:主机向从机发送数据的通道;
-
MISO:从机向主机返回数据的通道;
-
SS/CS:主机控制的从机选择线,通常低电平有效(需参考 datasheet)。
注:简单外设可简化为3线制,省略MISO或MOSI,实现单向传输。
2.2.2 工作模式
由CPOL(时钟极性)和CPHA(时钟相位)决定,共4种模式,双方需一致否则通信失败:
-
CPOL:空闲时SCLK电平,0为低,1为高;
-
CPHA:数据采样时机,0为第一个跳变沿,1为第二个跳变沿。
常用Mode 0(CPOL=0,CPHA=0)和Mode 3(CPOL=1,CPHA=1),具体需结合外设配置。
2.3 多设备挂载设计
核心思路:共享SCLK、MOSI、MISO,为每个从机分配独立SS/CS,实现方式如下:
-
硬件:所有从机对应信号线与主机相连,SS/CS各接主机一个GPIO;
-
逻辑:通信时仅拉低目标从机SS/CS,其余保持高电平(高阻态),避免冲突。
注意事项:SCLK频率取所有从机支持最小值;SS/CS配置为推挽输出;信号线尽量等长减少串扰。
三、I2C总线的核心协议、通信机制与硬件设计
3.1 核心原理与特点
I2C是同步串行总线,特点是双线制、半双工、多主多从,适用于短距离、低速、多设备场景(如传感器、RTC)。无需独立从机选择线,通过从机地址区分,节省GPIO资源。
3.2 核心信号线与协议规范
3.2.1 核心信号线
仅2根信号线,均需4.7KΩ上拉电阻接VCC,确保空闲高电平:
-
SDA:传输数据和从机地址,核心数据通道;
-
SCL:主机产生(多主可协商),同步时序。
3.2.2 核心协议规范
通信时序固定:起始条件→地址传输→应答→数据传输→停止条件,核心细节:
-
起始(S):SCL高电平时,SDA高变低,启动通信;
-
地址传输:7位从机地址+1位读写位(0写1读);
-
应答(ACK/NACK):每传8位数据,接收端反馈低电平(成功)或高电平(失败);
-
数据传输:写操作主机发数据,读操作从机发数据,每次8位;
-
停止(P):SCL高电平时,SDA低变高,结束通信。
补充:常用7位地址(支持128个从机),10位地址适用于多从机场景;部分芯片可通过引脚配置地址,避免冲突。
3.3 硬件设计
核心是双线制+上拉电阻,注意事项:
-
上拉电阻:4.7KΩ~10KΩ,从机超8个选2.2KΩ增强驱动;
-
多设备:信号线共接,通过地址区分,地址不可重复;
-
布线:不超1米,尽量平行,超1米加匹配电阻。
通信失败排查:优先检查上拉电阻、从机地址、布线干扰。
四、嵌入式芯片中三类串行外设的配置流程与驱动开发基础
以STM32F103、HAL库为例,三类接口驱动开发核心流程一致:外设时钟使能→GPIO配置→接口参数配置→中断/DMA配置(可选)→数据收发,兼顾实用与可移植性。
4.1 通用配置流程(共性)
-
时钟使能:开启接口和对应GPIO时钟,时钟是外设工作基础;
-
GPIO配置:UART(TX复用推挽、RX浮空输入);SPI(SCLK/MOSI/SS复用推挽、MISO浮空输入);I2C(SDA/SCL复用开漏+上拉);
-
参数配置:匹配外设要求,配置各接口核心参数;
-
中断/DMA(可选):实现高速异步传输,降低CPU占用;
-
数据收发:调用HAL库对应函数,简化开发。
4.2 三类接口驱动开发差异(个性)
4.2.1 UART驱动开发
重点:波特率结合晶振配置,误差≤1%;优先用中断接收,减少CPU占用。
关键代码示例(简化,基于STM32 HAL库):
c
// 1. UART初始化
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;
if (HAL_UART_Init(&huart1) != HAL_OK) {
Error_Handler();
}
}
// 2. 阻塞式发送
HAL_UART_Transmit(&huart1, (uint8_t*)"Hello UART!", 11, 1000);
// 3. 中断接收及回调
uint8_t uart_rx_buf[10];
HAL_UART_Receive_IT(&huart1, uart_rx_buf, 10);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
// 业务处理
HAL_UART_Receive_IT(&huart1, uart_rx_buf, 10);
}
4.2.2 SPI驱动开发
重点:工作模式与从机一致;精准控制SS/CS电平;注重收发同步。
关键代码示例(简化,基于STM32 HAL库):
c
// 1. SPI初始化(主机Mode 0)
SPI_HandleTypeDef hspi1;
void MX_SPI1_Init(void) {
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
if (HAL_SPI_Init(&hspi1) != HAL_OK) {
Error_Handler();
}
}
// 2. SS/CS控制
#define SPI_CS_GPIO_Port GPIOA
#define SPI_CS_Pin GPIO_PIN_4
void SPI_Select_Slave(void) {HAL_GPIO_WritePin(SPI_CS_GPIO_Port, SPI_CS_Pin, GPIO_PIN_RESET);}
void SPI_Deselect_Slave(void) {HAL_GPIO_WritePin(SPI_CS_GPIO_Port, SPI_CS_Pin, GPIO_PIN_SET);}
// 3. 全双工收发
uint8_t spi_tx_buf[5] = {0x01, 0x02, 0x03, 0x04, 0x05};
uint8_t spi_rx_buf[5];
SPI_Select_Slave();
HAL_SPI_TransmitReceive(&hspi1, spi_tx_buf, spi_rx_buf, 5, 1000);
SPI_Deselect_Slave();
4.2.3 I2C驱动开发
重点:从机地址含读写位;重视应答处理;时钟频率匹配外设。
关键代码示例(简化,基于STM32 HAL库,写EEPROM):
c
// 1. I2C初始化(100KHz)
I2C_HandleTypeDef hi2c1;
void MX_I2C1_Init(void) {
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 100000;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
if (HAL_I2C_Init(&hi2c1) != HAL_OK) {
Error_Handler();
}
}
// 2. 写数据(EEPROM地址0x00写0x12)
#define EEPROM_ADDR 0xA0
uint8_t i2c_tx_buf[2] = {0x00, 0x12};
HAL_I2C_Master_Transmit(&hi2c1, EEPROM_ADDR, i2c_tx_buf, 2, 1000);
// 3. 读数据(读EEPROM地址0x00)
uint8_t i2c_rx_buf[1];
HAL_I2C_Master_Transmit(&hi2c1, EEPROM_ADDR, (uint8_t*)&0x00, 1, 1000);
HAL_I2C_Master_Receive(&hi2c1, 0xA1, i2c_rx_buf, 1, 1000);
4.3 驱动开发注意事项
-
参数匹配:双方协议参数一致,否则通信失败;
-
中断优先级:合理配置,避免嵌套导致异常;
-
延时控制:按外设 datasheet 配置延时,避免操作未完成;
-
错误处理:添加返回值判断,提升系统稳定性。
五、典型实战案例:传感器对接与外设扩展全流程实现
以STM32F103为控制器,对接三类典型外设,完整实现硬件设计、驱动开发与测试,助力理论落地。
案例1:UART对接串口屏(显示与交互)
5.1.1 需求说明
UART1对接TFT串口屏,实现文字显示与触摸交互,通信参数:115200bps、8N1。
5.1.2 硬件设计
-
STM32 USART1_TX(PA9)→串口屏RX;
-
STM32 USART1_RX(PA10)→串口屏TX;
-
STM32 GND→串口屏GND,串口屏VCC接对应电源。
注:5V串口屏需加SN74LS245电平转换,避免损坏STM32。
5.1.3 驱动开发与功能实现
-
初始化UART1,开启接收中断;
-
编写串口屏控制指令(清空、显示文字);
-
中断回调解析触摸指令,执行对应逻辑;
-
主函数循环显示,等待触摸交互。
关键代码(简化,基于STM32 HAL库):
c
// 发送串口屏指令
void UART_Send_LCD_Command(char* cmd) {
HAL_UART_Transmit(&huart1, (uint8_t*)cmd, strlen(cmd), 1000);
}
// 主函数
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
UART_Send_LCD_Command("CLR(0);\r\n");
HAL_Delay(500);
UART_Send_LCD_Command("DISP_STR(10,10,\"STM32 UART LCD Test\");\r\n");
while (1) {HAL_Delay(100);}
}
// 中断回调解析触摸指令
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
if (huart == &huart1) {
if (strstr((char*)uart_rx_buf, "TOUCH") != NULL) {
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
}
HAL_UART_Receive_IT(&huart1, uart_rx_buf, 30);
}
}
案例2:SPI对接SPI Flash(数据存储)
5.2.1 需求说明
SPI1对接W25Q64(8MB),实现数据读写与校验,参数:Mode 0、10MHz、8位数据。
5.2.2 硬件设计
-
STM32 SPI1_SCLK(PA5)→W25Q64 SCK;
-
STM32 SPI1_MOSI(PA7)→W25Q64 DI;
-
STM32 SPI1_MISO(PA6)→W25Q64 DO;
-
STM32 GPIOA4→W25Q64 CS;
-
STM32 3.3V→W25Q64 VCC/VCCQ,共地。
5.2.3 驱动开发与功能实现
-
初始化SPI1,匹配W25Q64参数;
-
编写读ID、扇区擦除、页写、读数据函数;
-
主函数实现:写数据→擦除(可选)→读数据→校验。
关键代码(简化,基于STM32 HAL库):
c
// 读W25Q64 ID(验证通信)
uint32_t W25Q64_Read_ID(void) {
uint32_t id = 0;
SPI_Select_Slave();
HAL_SPI_Transmit(&hspi1, (uint8_t*)&0x90, 1, 1000);
HAL_SPI_Receive(&hspi1, (uint8_t*)&id, 3, 1000);
SPI_Deselect_Slave();
return id & 0xFFFF; // 标准ID 0x4017
}
// 扇区擦除
void W25Q64_Erase_Sector(uint32_t addr) {
W25Q64_Write_Enable();
SPI_Select_Slave();
HAL_SPI_Transmit(&hspi1, (uint8_t*)&0x20, 1, 1000);
HAL_SPI_Transmit(&hspi1, (uint8_t*)&addr, 3, 1000);
SPI_Deselect_Slave();
W25Q64_Wait_Busy();
}
// 主函数测试
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_SPI1_Init();
if (W25Q64_Read_ID() != 0x4017) Error_Handler();
uint8_t write_buf[] = "SPI Flash Test: Hello World!";
uint8_t read_buf[30] = {0};
uint32_t addr = 0x000000;
W25Q64_Erase_Sector(addr);
W25Q64_Write_Page(addr, write_buf, strlen((char*)write_buf));
W25Q64_Read_Data(addr, read_buf, strlen((char*)write_buf));
if (memcmp(write_buf, read_buf, strlen((char*)write_buf)) != 0) Error_Handler();
while (1) {}
}
案例3:I2C对接温湿度传感器(数据采集)
5.3.1 需求说明
I2C1对接DHT11(I2C版),采集温湿度并通过UART1发送到PC,参数:100KHz、从机地址0x40。
5.3.2 硬件设计
-
STM32 I2C1_SDA(PB7)→DHT11 SDA;
-
STM32 I2C1_SCL(PB6)→DHT11 SCL;
-
STM32 3.3V→DHT11 VCC,共地;
-
SDA、SCL串4.7KΩ上拉电阻。
5.3.3 驱动开发与功能实现
-
初始化I2C1,匹配DHT11参数;
-
编写测量、读数据、校验函数;
-
主函数1秒采集一次,格式化后发送到PC。
关键代码(简化,基于STM32 HAL库):
c
// 读取DHT11温湿度,0成功1失败
uint8_t DHT11_Read_Data(uint8_t* temp, uint8_t* humi) {
uint8_t buf[5] = {0};
HAL_I2C_Master_Transmit(&hi2c1, 0x40, (uint8_t*)&0xE0, 1, 1000);
HAL_Delay(10);
if (HAL_I2C_Master_Receive(&hi2c1, 0x41, buf, 5, 1000) != HAL_OK) return 1;
if ((buf[0]+buf[1]+buf[2]+buf[3]) != buf[4]) return 1;
*humi = buf[0]; *temp = buf[2];
return 0;
}
// 主函数
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_I2C1_Init();
uint8_t temp, humi;
char uart_buf[50];
while (1) {
if (DHT11_Read_Data(&temp, &humi) == 0) {
sprintf(uart_buf, "Temperature: %d℃, Humidity: %d%%\r\n", temp, humi);
HAL_UART_Transmit(&huart1, (uint8_t*)uart_buf, strlen(uart_buf), 1000);
} else {
HAL_UART_Transmit(&huart1, (uint8_t*)"Read DHT11 Error!\r\n", 18, 1000);
}
HAL_Delay(1000);
}
}
六、多设备挂载的总线设计与可靠性优化技巧
多设备挂载是嵌入式外设扩展的常见场景,核心是在保证总线稳定的前提下,实现多设备高效协同,以下结合三类接口特性,给出针对性设计方案与可靠性优化技巧。
6.1 三类接口多设备挂载核心设计
-
UART多设备挂载:采用"主机-从机"星形拓扑,主机通过GPIO控制从机使能端,同一时刻仅一个从机响应,避免数据冲突;无需时钟同步,布线简单,适合低速多设备场景(如多个串口传感器)。
-
SPI多设备挂载:共享SCLK、MOSI、MISO三根总线,每个从机分配独立SS/CS引脚,主机通过拉低对应SS/CS实现设备选择;支持高速传输,适合多高速外设场景(如SPI Flash、OLED屏)。
-
I2C多设备挂载:共享SDA、SCL两根总线,通过从机地址区分设备,无需额外GPIO;支持多主模式,布线最简洁,适合低速、多小型外设场景(如多个温湿度传感器、RTC)。
6.2 可靠性优化技巧
-
总线负载优化:I2C从机数量不超过12个(7位地址),超量时采用I2C中继器;SPI总线负载不超过4个,避免信号衰减,必要时添加总线缓冲器。
-
布线优化:多设备布线时,总线尽量短且等长,远离功率器件;SPI、I2C总线添加100Ω匹配电阻,减少串扰;UART布线超1米时,采用屏蔽线。
-
时序优化:SPI多设备挂载时,SCLK频率取所有从机支持的最小值;I2C通信时,添加适当延时,确保从机有足够时间响应;UART多设备切换时,需重新同步通信参数。
-
容错设计:驱动中添加应答检测、超时处理,避免单个设备故障导致整个总线瘫痪;关键数据传输时,添加校验位(如CRC),提升数据可靠性。
七、三类接口的性能对比与适用场景划分
三类接口各有优劣,需结合传输速率、布线复杂度、设备数量、成本等因素合理选型,以下是完整的性能对比与场景划分,助力开发者快速匹配需求。
7.1 完整性能对比
| 对比维度 | UART | SPI | I2C |
|---|---|---|---|
| 通信方式 | 异步,无时钟 | 同步,有时钟 | 同步,有时钟 |
| 信号线数量 | 2根(全双工);1根(半双工) | 4根(标准);3根(简化),全双工 | 2根,半双工 |
| 传输速率 | 较低(通常≤115200bps) | 较高(可达几十MHz) | 中等(标准100KHz,快速400KHz) |
| 多设备支持 | 支持,需额外GPIO控制使能 | 支持,需独立SS/CS引脚 | 支持,通过地址区分,无需额外GPIO |
| 布线复杂度 | 简单 | 中等 | 最简单 |
| 成本 | 低,无需额外器件 | 中,需额外GPIO | 低,仅需上拉电阻 |
7.2 适用场景划分
-
UART:适用于低速、点对点/简单多设备通信,如串口调试、GPS模块、蓝牙模块、串口屏等,优势是布线简单、成本低,无需时钟同步。
-
SPI:适用于高速、短距离、多外设通信,如SPI Flash、OLED屏、ADC芯片等,优势是传输速率高、全双工,适合对速率要求较高的场景。
-
I2C:适用于低速、短距离、多小型外设通信,如温湿度传感器、RTC、EEPROM等,优势是布线简洁、节省GPIO,适合外设数量多、对速率要求不高的场景。
八、总结:嵌入式芯片中通用串行外设的应用设计规范
结合前文原理、硬件设计、驱动开发与实战案例,总结嵌入式通用串行外设(UART/SPI/I2C)的应用设计规范,助力开发者规避设计误区、提升系统稳定性与可移植性。
8.1 选型规范
-
优先根据传输速率选型:高速场景选SPI,中速场景选I2C,低速场景选UART;
-
结合外设数量选型:多外设、GPIO紧张场景选I2C,高速多外设场景选SPI,少外设、低成本场景选UART;
-
结合布线环境选型:长距离、抗干扰要求高场景选UART(屏蔽线),短距离、高速场景选SPI,板内短距离、多外设场景选I2C。
8.2 硬件设计规范
-
电平匹配:不同电平设备间必须添加电平转换芯片(如UART的MAX232、5V与3.3V设备的SN74LS245);
-
布线要求:总线尽量短、等长,远离功率器件与干扰源;SPI/I2C添加匹配电阻,UART长距离用屏蔽线;
-
多设备挂载:严格遵循各类接口的挂载规则,I2C避免地址冲突,SPI合理分配SS/CS引脚,UART控制从机使能端。
8.3 驱动开发规范
-
参数统一:通信双方的波特率、数据位、停止位(UART),工作模式(SPI),时钟频率、从机地址(I2C)必须严格一致;
-
容错设计:添加应答检测、超时处理、数据校验,提升驱动可靠性;中断驱动需合理配置优先级,避免嵌套异常;
-
可移植性:基于HAL库、LL库开发,封装通用接口函数,便于切换不同芯片平台;驱动代码添加详细注释,明确参数含义与操作逻辑。
8.4 调试规范
-
分步调试:先调试硬件(用万用表检测引脚电平、总线连接),再调试驱动(先初始化,再测试简单数据收发),最后调试多设备协同;
-
故障排查:通信失败优先检查参数匹配、硬件连接、电平匹配,再排查驱动逻辑与时序问题;
-
性能优化:根据实际需求调整传输速率、延时参数,平衡传输效率与稳定性。
综上,UART、SPI、I2C作为嵌入式外设扩展的核心接口,其应用设计需兼顾选型合理性、硬件可靠性、驱动规范性。掌握本文所述规范与技巧,可高效完成嵌入式系统外设扩展设计,提升开发效率与系统稳定性。