硬件通信协议
I²C、SPI、UART、USB 是四种常用的硬件通信协议,它们在不同场景下负责计算机(上位机)与芯片(下位机)之间的数据传输与控制。
UART
UART通信基于异步传输,无需共享时钟信号,双方通过预定义的波特率(每秒比特数(bps))同步数据时序。
引脚
发送端(TX):将并行数据转换为串行比特流,按协议格式发送。
接收端(RX):检测起始位,按波特率采样数据位,恢复原始数据。
关键特点
全双工:TX 和 RX 可同时工作(需两线)。
异步:无时钟线,依赖波特率同步。
灵活性:支持多种数据格式和波特率。
代码
import serial
# 配置串口
ser = serial.Serial(
port='COM3', # 端口号(Windows)或 '/dev/ttyUSB0'(Linux)
baudrate=115200, # 波特率
bytesize=serial.EIGHTBITS, # 数据位
parity=serial.PARITY_NONE, # 校验位
stopbits=serial.STOPBITS_ONE, # 停止位
timeout=1 # 读取超时(秒)
)
# 发送数据
ser.write(b'Hello from PC!\n')
# 接收数据
while True:
data = ser.read(10) # 读取 10 字节
if data:
print("Received:", data)
疑问
- 停止位的作用?
- 同步时序、标识帧结束、抗干扰。
- 异步通信容错:UART双方使用独立时钟源,可能存在微小频率偏差。停止位提供额外时间窗口,允许接收端调整采样点位置,确保正确采样数据位。
- 恢复空闲电平:停止位强制线路回到高电平(逻辑1),为下一次传输的起始位下降沿提供明确的触发信号。
- UART数据位长度为什么是8位?
UART的数据位长度是可靠性、效率、兼容性、成本 多方博弈的结果。对于大数据量需求,更优方案是分帧传输或换用同步通信协议(如SPI、USB)。
| 因素 | 过短数据位(如5位) | 适中数据位(如8位) | 过长数据位(如16位) |
|---------------------|--------------------------|-----------------------------|-----------------------------|
| 传输效率 | 固定开销占比高 | 平衡开销与容错性 | 单帧效率高,但重传成本增加 |
| 可靠性 | 时钟偏差影响小 | 误差容忍度适中 | 时钟累积偏差风险显著上升 |
| 硬件成本 | 简单易实现 | 兼容主流硬件 | 需更大缓冲区、复杂逻辑 |
| 协议兼容性 | 需适配旧协议 | 行业标准支持 | 破坏现有生态 |
| 应用场景 | 适合短指令(如电传打字机)| 覆盖绝大多数需求 | 仅少数场景需要,可用分帧替代 |
SPI
SPI(Serial Peripheral Interface,串行外设接口) 是一种高速、全双工、同步的串行通信协议,广泛用于嵌入式系统中微控制器(MCU)与外围设备(如传感器、存储器、显示屏等)之间的短距离通信。
引脚
SPI协议通过4根信号线实现通信:
信号线 方向 作用
SCLK 主 → 从 主设备生成的同步时钟信号。
MOSI 主 → 从 主设备输出数据,从设备输入(Master Out Slave In)。
MISO 从 → 主 从设备输出数据,主设备输入(Master In Slave Out)。
CS/SS 主 → 从 主设备控制从设备的片选信号(低电平有效)。
关键特点
全双工通信。
同步传输:依赖主设备提供的时钟信号(SCLK)同步数据。
主从架构:一个主设备(Master)控制通信,可连接多个从设备(Slave)。
硬件片选:每个从设备需独立的片选信号(CS/SS)进行寻址。
无固定协议格式:数据帧格式灵活(数据位宽、传输顺序可配置)。
代码
// SPI初始化配置(模式0,8位数据,MSB First)
SPI_HandleTypeDef hspi1;
void SPI_Init() {
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; // CPOL=0
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; // CPHA=0
hspi1.Init.NSS = SPI_NSS_SOFT; // 软件控制CS
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4; // 时钟分频
HAL_SPI_Init(&hspi1);
}
// 发送并接收数据
uint8_t tx_data[] = {0xAA, 0x55};
uint8_t rx_data[2];
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); // 拉低CS
HAL_SPI_TransmitReceive(&hspi1, tx_data, rx_data, 2, 100);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); // 拉高CS
疑问
-
时钟信号如何使用的?
时钟信号定义什么时候采集数据
时钟极性(CPOL):定义时钟空闲状态的电平。
CPOL=0:SCLK空闲时为低电平。
CPOL=1:SCLK空闲时为高电平。
时钟相位(CPHA):定义数据采样的边沿。
CPHA=0:在时钟的第一个边沿(上升沿或下降沿)采样数据。
CPHA=1:在时钟的第二个边沿采样数据。
模式0(CPOL=0, CPHA=0)
SCLK: __/¯¯_/¯¯_/¯¯__...
MOSI: D0 D1 D2 ...
MISO: D0 D1 D2 ...
↑ ↑ ↑ (采样时刻) -
片选信号如何使用的?
-
没有固定帧格式吗?为什么不需要起始停止位?

浙公网安备 33010602011771号