在嵌入式系统中,串行外设接口(Serial Peripheral Interface,SPI)及其扩展(如 DSPI 和 QSPI)被广泛应用于与外部设备(如传感器、存储器、LCD 控制器等)高速通信。
主要区别对比如下:
1
SPI
SPI(Serial Peripheral Interface)是一种全双工同步串行通信协议,通常由主设备(Master)和从设备(Slave)构成。SPI 总线主要有以下四根信号线:
SPI 的优点在于结构简单、传输速率快和实现灵活,缺点是单条数据线在高速通信中的带宽有限,且当从设备增多时,硬件连接和线路管理较为复杂。
下面的 C 语言伪代码展示了基于常见 MCU 的 SPI 初始化配置与数据收发流程:
// SPI初始化函数
void SPI_Init(void) {
// 配置引脚模式:SCLK, MOSI, MISO, CS
ConfigGPIO(SCLK_PIN, GPIO_MODE_AF_PP);
ConfigGPIO(MOSI_PIN, GPIO_MODE_AF_PP);
ConfigGPIO(MISO_PIN, GPIO_MODE_AF_PP);
ConfigGPIO(CS_PIN, GPIO_MODE_OUTPUT_PP);
// 配置 SPI 外设参数:主模式、数据位、极性、相位、波特率等
SPI_HandleTypeDef hspi;
hspi.Instance = SPI1;
hspi.Init.Mode = SPI_MODE_MASTER;
hspi.Init.Direction = SPI_DIRECTION_2LINES;
hspi.Init.DataSize = SPI_DATASIZE_8BIT;
hspi.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi.Init.NSS = SPI_NSS_SOFT;
hspi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
hspi.Init.FirstBit = SPI_FIRSTBIT_MSB;
SPI_Init(&hspi);
SPI_Enable(&hspi);
}
// SPI数据发送与接收
uint8_t SPI_TransmitReceive(uint8_t txData) {
uint8_t rxData = 0;
SPI_Transmit(&hspi, &txData, 1);
SPI_Receive(&hspi, &rxData, 1);
return rxData;
}
2
DSPI
DSPI(在某些平台上也称为 Dual Serial Peripheral Interface)是在传统 SPI 的基础上加入了更多硬件特性而演进出来的接口。
以 Freescale/NXP 的 DSPI 模块为例,其主要特点包括:
这使得 DSPI 在很多现代 MCU 中已经取代了传统 SPI 成为主流接口之一。
从用户接口角度看,DSPI 与标准 SPI 保持了相似的信号结构(SCLK、MOSI、MISO、CS),但在硬件内部及数据传输效率上做了以下扩展:
这些改进使得 DSPI 在多任务、高速、大数据量传输的场景中表现得更加优越。
以下伪代码展示 DSPI 初始化过程,与 SPI 类似,但增加了 FIFO 等增强功能的配置:
// DSPI初始化函数(基于具体MCU库,示例仅供参考)
void DSPI_Init(void) {
// 配置IO引脚(与SPI类似)
ConfigGPIO(SCLK_PIN, GPIO_MODE_AF_PP);
ConfigGPIO(MOSI_PIN, GPIO_MODE_AF_PP);
ConfigGPIO(MISO_PIN, GPIO_MODE_AF_PP);
ConfigGPIO(CS_PIN, GPIO_MODE_OUTPUT_PP);
// 配置 DSPI 控制器参数
DSPI_HandleTypeDef hdspi;
hdspi.Instance = DSPI1;
hdspi.Init.Master = ENABLE;
hdspi.Init.Direction = DSPI_DIRECTION_2LINES;
hdspi.Init.DataSize = DSPI_DATASIZE_8BIT;
hdspi.Init.CLKPolarity = DSPI_POLARITY_LOW;
hdspi.Init.CLKPhase = DSPI_PHASE_1EDGE;
hdspi.Init.NSS = DSPI_NSS_SOFT;
hdspi.Init.BaudRatePrescaler = DSPI_BAUDRATEPRESCALER_8;
// 配置内置FIFO缓存深度
hdspi.Init.FIFOThreshold = DSPI_FIFO_THRESHOLD_4;
DSPI_Init(&hdspi);
DSPI_Enable(&hdspi);
}
3
QSPI
QSPI(Quad SPI)设计初衷是为了满足高速数据传输的需求,尤其适用于外部 Flash 存储器。与传统 SPI 相比,QSPI 除了有标准的 SCLK 与 CS 外,还增加了四个双向数据引脚(IO0、IO1、IO2、IO3),支持多种数据传输模式(单/双/四线模式),具体特点如下:
QSPI 典型的传输操作可分为以下阶段:
这种协议支持存储器映射模式,可直接将外部设备当作内存使用,非常适用于代码执行(XIP,eXecute In Place)以及数据高速缓存场景。
以下代码展示了 QSPI 模块初始化及基本数据读取操作的伪代码示例(具体 API 可能因厂商 SDK 不同而差异较大):
// QSPI初始化函数
void QSPI_Init(void) {
// 配置 QSPI 引脚:SCLK、CS、IO0-IO3
ConfigGPIO(QSPI_SCLK_PIN, GPIO_MODE_AF_PP);
ConfigGPIO(QSPI_CS_PIN, GPIO_MODE_AF_PP);
ConfigGPIO(QSPI_IO0_PIN, GPIO_MODE_AF_PP);
ConfigGPIO(QSPI_IO1_PIN, GPIO_MODE_AF_PP);
ConfigGPIO(QSPI_IO2_PIN, GPIO_MODE_AF_PP);
ConfigGPIO(QSPI_IO3_PIN, GPIO_MODE_AF_PP);
// 配置 QSPI 控制器参数
QSPI_HandleTypeDef hqspi;
hqspi.Instance = QSPI1;
hqspi.Init.ClockPrescaler = 2;
hqspi.Init.FlashSize = 23; // 假设容量为8 Mbit(2^23)
hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE;
hqspi.Init.FlashID = QSPI_FLASH_ID_1;
hqspi.Init.DualFlash = QSPI_DUALFLASH_DISABLE;
QSPI_Init(&hqspi);
QSPI_Enable(&hqspi);
}
// QSPI数据读取示例(简化流程)
void QSPI_Read(uint32_t address, uint8_t* buffer, uint32_t size) {
QSPI_CommandTypeDef cmd;
cmd.Instruction = 0x03; // 普通读命令
cmd.Address = address;
cmd.AddressSize = QSPI_ADDRESS_24_BITS;
cmd.DummyCycles = 8;
cmd.DataMode = QSPI_DATA_4_LINES; // 使用四线模式读取数据
QSPI_Receive(&hqspi, &cmd, buffer, size);
}
4
选择建议
低速/简单应用: 当应用对传输速率要求不高时,使用 SPI 能够满足大多数外设的通信需求,且接口简单、兼容性良好。
高速/高数据量应用: 当系统对数据吞吐量及实时性要求较高时,采用 DSPI 能充分利用其 FIFO 缓冲和高速传输优势。
大容量存储及XIP: 对于存储器映射和高数据带宽需求的应用,如外部 NOR Flash 存储器读取、执行外部代码等场景,QSPI 是最佳选择。
在实际项目中,系统资源、外设接口需求以及性能瓶颈都会影响最终选择。建议在设计初期进行充分的评估和测试,确保选择最适合方案。