STM32F103C8T6芯片集成了两个SPI(Serial Peripheral Interface)外设模块(SPI1和SPI2),支持高速全双工或半双工的同步串行通信。其SPI功能专为连接外部设备(如存储器、传感器、显示屏等)设计,具备灵活的配置选项和高效的数据传输能力。
1.核心特性
-
工作模式
- 主模式(Master):控制通信时钟(SCK)并发起数据传输。
- 从模式(Slave):响应主设备的时钟,被动接收或发送数据。
- 多主支持:支持多主总线仲裁,避免数据冲突。
-
通信配置
- 时钟极性(CPOL)与相位(CPHA):
- CPOL=0:SCK空闲时为低电平;CPOL=1:SCK空闲时为高电平。
- CPHA=0:数据在时钟第一个边沿采样;CPHA=1:数据在时钟第二个边沿采样。
组合形成四种SPI模式(0-3),兼容不同外设的时序要求。
- 数据帧格式:
- 数据长度可配置为8位或16位。
- 支持MSB(高位先行)或LSB(低位先行)传输顺序。
- 通信速率:
- 主模式下SCK频率可配置(最高18 MHz @ 72 MHz系统时钟)。
- 分频系数通过寄存器设置,支持灵活调整。
- 时钟极性(CPOL)与相位(CPHA):
-
数据传输方式
- 轮询模式:通过状态寄存器查询传输状态,适用于低频率或简单应用。
- 中断模式:数据发送/完成时触发中断,提升CPU利用率。
- DMA模式:通过DMA控制器自动搬运数据,减少CPU负载,适合高速大数据量传输。
-
片选管理(NSS)
- 硬件模式:使用专用NSS引脚(如SPI1的PA4),自动控制片选信号。
- 软件模式:通过GPIO手动控制片选引脚电平,支持多从设备切换。
-
高级功能
- CRC校验:内置硬件CRC计算单元,支持数据传输完整性校验。
- TI模式兼容:支持TI串行协议格式,适配特定外设需求。
- 单线半双工:通过配置,复用MOSI和MISO为单一数据线。
2.硬件资源与引脚分配
- SPI1:
- 全功能主/从接口,支持高速通信。
- 引脚:SCK(PA5)、MISO(PA6)、MOSI(PA7)、NSS(PA4)。
- SPI2:
- 功能与SPI1相同,但时钟速率较低。
- 引脚:SCK(PB13)、MISO(PB14)、MOSI(PB15)、NSS(PB12)。
3.错误检测与处理
- 溢出错误(Overrun):从设备未及时读取接收数据寄存器,导致新数据覆盖旧数据。
- 模式错误(Mode Fault):多主竞争时检测到总线冲突。
- CRC错误:接收数据的CRC校验值与预期不符。
- 状态寄存器标志:通过查询或中断处理这些错误,确保通信可靠性。
4.上完整程序模版,复制可用,已详细注释
/* 包含STM32标准外设库头文件 */
#include "stm32f10x.h"
/* 硬件配置宏定义(示例:SPI1,全双工主模式,SCK=PA5, MISO=PA6, MOSI=PA7) */
#define SPIx SPI1
#define SPI_GPIO_PORT GPIOA
#define SPI_SCK_PIN GPIO_Pin_5
#define SPI_MISO_PIN GPIO_Pin_6
#define SPI_MOSI_PIN GPIO_Pin_7
#define SPI_BAUDRATE_PRESCALER SPI_BaudRatePrescaler_4 // 18 MHz @72MHz系统时钟
#define SPI_MODE SPI_Mode_Master // 主模式
#define SPI_CPOL SPI_CPOL_Low // 时钟极性(空闲低电平)
#define SPI_CPHA SPI_CPHA_1Edge // 数据采样在第1个边沿
#define SPI_DATASIZE SPI_DataSize_8b // 8位数据帧
#define SPI_FIRSTBIT SPI_FirstBit_MSB // 高位先行
#define SPI_NSS SPI_NSS_Soft // 软件控制NSS
/**
* @brief SPI初始化函数(配置GPIO和SPI参数)
* @param 无
* @retval 无
*/
void SPI_Config(void) {
GPIO_InitTypeDef GPIO_InitStruct;
SPI_InitTypeDef SPI_InitStruct;
/* 1. 使能时钟(SPI1在APB2总线,GPIOA在APB2) */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1 | RCC_APB2Periph_GPIOA, ENABLE);
/* 2. 配置SPI引脚(复用推挽输出用于SCK/MOSI,浮空输入用于MISO) */
GPIO_InitStruct.GPIO_Pin = SPI_SCK_PIN | SPI_MOSI_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(SPI_GPIO_PORT, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = SPI_MISO_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
GPIO_Init(SPI_GPIO_PORT, &GPIO_InitStruct);
/* 3. 配置SPI工作参数 */
SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // 全双工
SPI_InitStruct.SPI_Mode = SPI_MODE; // 主/从模式
SPI_InitStruct.SPI_DataSize = SPI_DATASIZE; // 数据位宽
SPI_InitStruct.SPI_CPOL = SPI_CPOL; // 时钟极性
SPI_InitStruct.SPI_CPHA = SPI_CPHA; // 时钟相位
SPI_InitStruct.SPI_NSS = SPI_NSS; // 软件/硬件NSS控制
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BAUDRATE_PRESCALER; // 波特率分频
SPI_InitStruct.SPI_FirstBit = SPI_FIRSTBIT; // 数据传输顺序
SPI_InitStruct.SPI_CRCPolynomial = 7; // CRC多项式(默认7)
SPI_Init(SPIx, &SPI_InitStruct);
/* 4. 使能SPI模块 */
SPI_Cmd(SPIx, ENABLE);
}
/**
* @brief SPI发送单字节数据(轮询模式)
* @param data: 待发送的字节
* @retval 接收到的字节
*/
uint8_t SPI_SendByte(uint8_t data) {
/* 等待发送缓冲区空 */
while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET);
/* 发送数据 */
SPI_I2S_SendData(SPIx, data);
/* 等待接收完成 */
while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) == RESET);
/* 返回接收到的数据 */
return SPI_I2S_ReceiveData(SPIx);
}
/**
* @brief SPI发送多字节数据(轮询模式)
* @param pTxData: 发送数据缓冲区指针
* @param pRxData: 接收数据缓冲区指针
* @param len: 数据长度
* @retval 无
*/
void SPI_TransmitReceive(uint8_t *pTxData, uint8_t *pRxData, uint16_t len) {
for (uint16_t i = 0; i < len; i++) {
pRxData[i] = SPI_SendByte(pTxData[i]); // 全双工传输
}
}
/********************* 模板使用示例 *********************
int main(void) {
uint8_t txData[4] = {0x01, 0x02, 0x03, 0x04};
uint8_t rxData[4];
SystemInit(); // 系统时钟初始化(72MHz)
SPI_Config(); // 配置SPI
// 手动控制NSS引脚(示例使用PA4作为软件NSS)
GPIO_ResetBits(GPIOA, GPIO_Pin_4); // 拉低NSS(选中从机)
SPI_TransmitReceive(txData, rxData, 4); // 传输数据
GPIO_SetBits(GPIOA, GPIO_Pin_4); // 拉高NSS(释放从机)
while(1);
}
********************************************************/
5.模板程序功能说明
-
模块化初始化
SPI_Config()
:完成GPIO复用配置、SPI参数设置及模块使能,支持主/从模式切换。- 通过宏定义硬件参数,适配不同SPI外设(SPI1/SPI2)及通信需求。
-
数据传输函数
SPI_SendByte()
:单字节全双工传输,返回接收到的数据(轮询模式)。SPI_TransmitReceive()
:多字节数据传输封装,支持连续读写操作。
-
软件NSS控制
示例中使用PA4作为手动控制的片选引脚,用户可根据需要修改为硬件NSS或扩展多从机管理。
6.关键参数详解
-
SPI模式(SPI_Mode)
SPI_Mode_Master // 主模式(控制SCK) SPI_Mode_Slave // 从模式(响应SCK)
-
时钟相位与极性组合
CPOL CPHA 模式 数据采样边沿 0 0(1Edge) 0 上升沿采样(奇数边沿) 0 1(2Edge) 1 下降沿采样 1 0(1Edge) 2 下降沿采样 1 1(2Edge) 3 上升沿采样(偶数边沿) -
波特率分频系数(SPI_BAUDRATE_PRESCALER)
SPI_BaudRatePrescaler_2 // 36 MHz (72MHz/2) SPI_BaudRatePrescaler_4 // 18 MHz (72MHz/4) SPI_BaudRatePrescaler_256 // 281.25 kHz (72MHz/256)
-
数据帧格式
SPI_DataSize_8b // 8位数据(常用) SPI_DataSize_16b // 16位数据(某些ADC/DAC使用)
7.使用注意事项
-
NSS信号管理
- 硬件NSS模式下,自动控制片选信号但灵活性较低。
- 软件NSS需手动控制GPIO电平,多从机系统需独立片选引脚。
-
电气匹配
- 主从设备需共享GND,长距离通信建议添加终端电阻(100Ω)。
- MISO引脚上拉电阻(4.7kΩ)可增强信号稳定性。
-
中断/DMA扩展
- 修改传输函数为中断或DMA模式,需配置
SPI_ITConfig()
和NVIC/DMA通道。 - 示例代码为轮询模式,适用于低速传输;高速场景建议使用DMA。
- 修改传输函数为中断或DMA模式,需配置
-
全双工与半双工
- 若仅需单向传输(如只发送),可配置为
SPI_Direction_1Line_Tx
半双工模式。 - 单线模式需复用MOSI/MISO引脚。
- 若仅需单向传输(如只发送),可配置为
8.典型应用场景
- 存储器扩展:连接SPI Flash(如W25Q128)或EEPROM存储数据。
- 传感器读取:采集温度(如MAX6675)、加速度(MPU6050)等传感器数据。
- 显示驱动:控制OLED或TFT屏幕(如ILI9341)。
- 通信桥接:作为主设备管理多个从设备(如多个ADC或DAC芯片)。