STM32/软件SPI和硬件SPI
本文详细介绍了STM32微控制器中软件SPI和硬件SPI的实现方法及其优缺点。SPI是一种高速全双工同步串行通信协议,通过NSS、SCK、MOSI和MISO四条线进行数据传输,传输速率远高于I2C。软件SPI利用普通GPIO引脚模拟SPI信号,灵活性高但速度慢,CPU占用高。硬件SPI则通过专用硬件模块实现,速度快,CPU占用低,但引脚固定,资源有限。文章还提供了SPI初始化和一个字节数据交换的示
目录
1 SPI协议
1.1 SPI协议
一种高速、全双工的同步串行通信协议,主要用于短距离通信。

1.2 四线传输
NSS:从机选择信号线,当多个设备与主机相连并选择指定从机通信时,就把指定从机所连接的NSS信号线置为低电平便可进行通信,置高电平似为结束,主机产生信号。
SCK:时钟信号线,用于同步数据传输,主机产生信号。
MOSI:主设备输出/从设备输入引脚,主机输出信号,从机输入信号,主机产生信号。
MISO:主设备输入/从设备输出引脚,从机输出信号,主机输入信号,从机产生信号。
1.3 传输速率
远大于I2C通信协议传输速率(可高达10MHz)。
2 SPI协议的基本操作
2.1 数据传输
数据以同步数据移位寄存器的方式进行传输并且每个时钟周期交换1位(1个字节=8位)。在前面我们说到SPI是四线传输,其中MOSI和MISO连接在数据移位寄存器上。发送时,数据从“发送缓冲区”加载到发送移位寄存器,再逐位移出到MOSI线;接收时,MISO线上的数据被逐位移入接收移位寄存器,最终存入“接收缓冲区”(注意:数据的逐位移出或移入均为同步执行,而“接收缓冲区”中的数据去向取决于主机的软件配置和系统设计)。SCK时钟信号线用于同步数据位传输。
2.2 起始/停止信号
起始信号:NSS信号线由高电平变为低电平;
停止信号:NSS信号线由低电平变为高电平;
2.3 数据的有效性(以CPOL=0,CPHA=1为例)
MOSI和MISO的数据在SCK时钟信号的上升沿触发,在下降沿采样(数据在时钟下降沿时刻数据有效,其他时刻数据无效)高电平表示数据“1”,低电平表示数据“0”.

2.4 时钟极性和时钟相位
时钟极性CPOL:指SCK在空闲状态(无数据传输状态)下的电平高低;当CPOL=0时表示低电平开始,通信时由低电平跳变为高电平;CPOL=1时表示高电平开始,通信时由高电平跳变为低电平。
时钟相位CPHA:指MOSI和MISO数据的采样时刻,当CPHA=0时MOSI和MISO数据线上的数据将在SCK时钟线的“第一个边沿”被采样,“第二个边沿”进行数据切换;当CPHA=1时MOSI和MISO数据线上的数据将在SCK时钟线的“第二个边沿”被采样,“第一个边沿”进行数据切换。
3 SPI的四种模式

3.1 SPI模式“0”

3.2 SPI模式“1”

3.3 SPI模式“2”

3.4 SPI模式“3”

4 SPI主模式下全双工发送和接收过程模式

4.1 BSY标志
- 在SPI通信中,BSY标志是由硬件自动设置和清除的状态标志位,用于指示SPI总线当前是否处于忙碌状态
- 当BSY标志为1时,表示SPI总线正在传输数据,禁止操作SPI控制寄存器(如写入数据或修改配置)。
- 当BSY标志为0时,表示SPI总线空闲,允许配置或启动新的传输。
- 主机开始发送数据时,BSY自动置位。
- 移位寄存器为空且无新数据传输时,BSY自动清零。
4.2 TXE标志
- TXE=1表示可以安全写入下一个数据,但不会阻塞传输
- 当发送缓冲区为空(可写入新数据)时,硬件自动置位TXE=1。
- 数据从发送缓冲区转移到移位寄存器后,TXE自动置1。
- 写入数据到SPI_DR寄存器会清除TXE标志(硬件自动清除)。
4.3 RXNE标志
- RXNE=1表示接收缓冲器有数据,可以读出;未及时读取数据,新接收的数据会覆盖旧数据
- 当接收缓冲区有数据可读时,硬件自动置位RXNE=1。
- 数据从移位寄存器完全移入接收缓冲区后,RXNE置1。
- 读取SPI_DR寄存器会清除RXNE标志(硬件自动清除)。
5 软/硬件SPI
5.1 软件SPI
通过软件模拟时序实现的SPI通信协议(即普通GPIO引脚模拟SPI信号,如NSS、SCK、MOSI、MISO)。软件SPI完全依赖CPU通过代码控制每个时钟周期和数据位的电平翻转。
优点:灵活性高,可用任意GPIO引脚;兼容性强,可模拟非标准SPI时序;不需要硬件资源,不依赖芯片SPI外设。
缺点:速度慢,受CPU处理速度限制无法满足高速设备需求;CPU占用高,影响实时性;时序不稳定,受中断、任务调度影响;多从机管理复杂,需手动控制多个NSS引脚。
5.2 硬件SPI
通过微控制器中的专用硬件模块实现SPI通信,通过配置寄存器来自动管理时序。硬件模块负责自动生成时钟信号(SCK)并处理数据的移位操作。
优点:高速通信,支持高频时钟适合高速设备;CPU占用低,数据传输由硬件自动处理;时序精准,时钟边沿和数据采样由硬件控制,稳定性高。
缺点:引脚固定,必须使用芯片指定的SPI引脚;资源有限,芯片的SPI外设数量有限;配置复杂,调试门槛较高。
5.3 SPI初始化(以SPI模式0为例)
5.3.1 软件SPI初始化
void Model_SPI_Init(void)
{
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/*设置默认电平*/
GPIO_SetBits(GPIOA, GPIO_Pin_4);
GPIO_ResetBits(GPIOA, GPIO_Pin_5);
}
5.3.2 硬件SPI初始化
void Model_SPI_Init(void)
{
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/*SPI初始化*/
SPI_InitTypeDef SPI_InitStructure;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStructure);
SPI_Cmd(SPI1, ENABLE);
/*设置默认电平*/
GPIO_SetBits(GPIOA, GPIO_Pin_4);
}
SPI_Mode(模式选择)
- SPI_Mode_Master(主模式)
- SPI_Mode_Slave(从模式)
SPI_Direction(通信方向选择)
- SPI_Direction_2Lines_FullDuplex:SPI设置为双线双向全双工
- SPI_Direction_2Lines_RxOnly:SPI设置为双线单向接收
- SPI_Direction_1Line_Rx:SPI设置为单线双向接收
- SPI_Direction_1Line_Tx:SPI设置为单线双向发送
SPI_DataSize(数据帧格式选择)
- SPI_DataSize_16b:SPI 发送接收 16 位帧结构
- SPI_DataSize_8b:SPI 发送接收 8 位帧结构
SPI_FirstBit(先行位选择)
- SPI_FisrtBit_MSB:数据传输从 MSB(高)位开始
- SPI_FisrtBit_LSB:数据传输从 LSB(低)位开始
SPI_BaudRatePrescaler(选择波特率预分频值)
- SPI_BaudRatePrescaler2:选择波特率预分频值为2
- SPI_BaudRatePrescaler4:选择波特率预分频值为4
- SPI_BaudRatePrescaler8:选择波特率预分频值为8
- SPI_BaudRatePrescaler16:选择波特率预分频值为16
- SPI_BaudRatePrescaler32:选择波特率预分频值为32
- SPI_BaudRatePrescaler64:选择波特率预分频值为64
- SPI_BaudRatePrescaler128:选择波特率预分频值为128
- SPI_BaudRatePrescaler256:选择波特率预分频值为256
SPI_CPOL(时钟极性选择)
- SPI_CPOL_High:时钟悬空高(CPOL=1)
- SPI_CPOL_Low:时钟悬空低(CPOL=0)
SPI_CPHA(时钟相位选择)
- SPI_CPHA_1Edge:数据捕获于第一个时钟沿(CPHA=0)
- SPI_CPHA_2Edge:数据捕获于第二个时钟沿(CPHA=1)
SPI_NSS(NSS控制模式选择)
- SPI_NSS_Hard:硬件控制NSS
- SPI_NSS_Soft:软件控制NSS
SPI_CRCPolynomial(CRC多项式设置)默认值为7
5.4 交换一个字节(以SPI模式0为例)
5.4.1 软件SPI
uint8_t Model_SPI_SwapByte(uint8_t ByteSend)
{
uint8_t i, ByteReceive = 0x00;
for (i = 0; i < 8; i ++)
{
Model_SPI_W_MOSI(!!(ByteSend & (0x80 >> i))); //写
GPIO_SetBits(GPIOA, GPIO_Pin_5);
if (Model_SPI_R_MISO()){ByteReceive |= (0x80 >> i);} //读
GPIO_ResetBits(GPIOA, GPIO_Pin_5);
}
return ByteReceive;
}
5.4.2 硬件SPI
uint8_t Model_SPI_SwapByte(uint8_t ByteSend)
{
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) != SET);
SPI_I2S_SendData(SPI1, ByteSend);
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) != SET);
return SPI_I2S_ReceiveData(SPI1);
}
注:本篇文章涉及位操作和结构体内容如不明白可阅读往期内容。
注:本篇文章主要讲解了什么是SPI和软/硬件SPI等理论及少部分实操,后续会继续发表关于SPI的具体应用。
注:以上内容仅个人理解,不具备唯一性和绝对正确性,仅供参考。
更多推荐



所有评论(0)