SPI(Serial Peripheral Interface)是由摩托罗拉公司于1980年代提出的同步串行通信协议,主要用于短距离高速芯片间通信。作为四线制全双工通信协议,它以简单的硬件实现和高效的传输速率著称,广泛应用于存储器、传感器、显示模块等嵌入式设备中。
典型应用场景:
特性 | SPI | I2C | UART |
---|---|---|---|
通信方式 | 全双工 | 半双工 | 全双工 |
拓扑结构 | 点对点/主从 | 多主多从 | 点对点 |
最大速率 | 100Mbps+ | 3.4Mbps | 115200bps |
信号线数量 | 4 | 2 | 2 |
寻址方式 | 硬件片选 | 软件地址 | 无地址 |
https://img-blog.csdnimg.cn/20210720172318888.png
c
Copy
// 典型SPI数据传输伪代码
void SPI_Transfer(uint8_t *txData, uint8_t *rxData, int length)
{
CS_LOW(); // 起始信号
for(int i=0; i<length; i++){
// 时钟边沿触发数据交换
for(int bit=7; bit>=0; bit--){
MOSI = (txData[i] >> bit) & 0x01;
SCLK_TOGGLE();
rxData[i] |= (MISO << bit);
SCLK_TOGGLE();
}
}
CS_HIGH(); // 停止信号
}
参数 | 描述 | 典型值 |
---|---|---|
t_SCLK | 时钟周期 | 20ns@50MHz |
t_SU | 数据建立时间 | 5ns |
t_HOLD | 数据保持时间 | 3ns |
t_CS2CLK | CS有效到第一个时钟边沿的延迟 | 10ns |
https://img-blog.csdnimg.cn/20210720172536907.png
c
Copy
typedef enum {
SPI_MODE0 = 0, // CPOL=0, CPHA=0
SPI_MODE1, // CPOL=0, CPHA=1
SPI_MODE2, // CPOL=1, CPHA=0
SPI_MODE3 // CPOL=1, CPHA=1
} SPI_Mode;
c
Copy
void SPI1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
SPI_HandleTypeDef hspi1 = {0};
// 时钟使能
__HAL_RCC_SPI1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
// MOSI(PA7), MISO(PA6), SCLK(PA5)
GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// SPI参数配置
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_4;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
HAL_SPI_Init(&hspi1);
}
对比维度 | SPI | I2C |
---|---|---|
传输速率 | 高速(50MHz+) | 中速(400kHz-1MHz) |
引脚资源 | 4线+N*CS | 2线 |
寻址方式 | 硬件片选 | 7/10位地址 |
拓扑复杂度 | 多CS线导致布线复杂 | 总线式易于扩展 |
功耗 | 较高(持续时钟) | 较低(时钟拉伸) |
选择SPI当:
选择I2C当:
c
Copy
// SSD1306写命令函数
void OLED_WriteCmd(uint8_t cmd)
{
OLED_CS_LOW();
OLED_DC_CMD(); // 命令模式
HAL_SPI_Transmit(&hspi1, &cmd, 1, 100);
OLED_CS_HIGH();
}
// 初始化序列示例
const uint8_t init_seq[] = {
0xAE, // 关闭显示
0xD5, 0x80, // 设置时钟分频
0xA8, 0x3F, // 设置多路复用率
// ...其他初始化命令
};
void OLED_Init(void)
{
for(int i=0; i<sizeof(init_seq); i++){
OLED_WriteCmd(init_seq[i]);
}
}
c
Copy
#define W25Q_CMD_READ 0x03
#define W25Q_CMD_WRITE 0x02
void W25Q_ReadData(uint32_t addr, uint8_t *buffer, uint16_t len)
{
uint8_t cmd[4] = {
W25Q_CMD_READ,
(addr >> 16) & 0xFF,
(addr >> 8) & 0xFF,
addr & 0xFF
};
W25Q_CS_LOW();
HAL_SPI_Transmit(&hspi1, cmd, 4, 100);
HAL_SPI_Receive(&hspi1, buffer, len, 1000);
W25Q_CS_HIGH();
}
通过本文的系统讲解,我们深入剖析了SPI协议的各个技术细节。在实际项目开发中,建议:
随着物联网设备的爆发式增长,SPI作为经典的高速通信协议,仍将在嵌入式领域发挥重要作用。掌握其核心原理并积累实战经验,是嵌入式工程师的必备技能。