一、简介
SPI(Serial Peripheral Interface)串行外设接口总线(SPI)最早由Motorola首先提出,采用主从模式(Master—Slave)架构,支持一个或多个Slave设备;广泛应用于MCU和外设模块如E2PROM、ADC、显示驱动器等的连接。
1.1 特点
<1>通信接口特性:全双工、同步、串行
<2>SPI通信接口有4个引脚:MOSI,MISO,SCLK,NSS
MOSI(Master Out Slave In):主机输出从机输入(发送端口) - USART_TX(相当于)
MISO(Master In Slave Out):主机输入从机输出(接收端口) - USART_RX(相当于)
SCLK/SCK/SCL: 时钟线,时钟信号只能由主机发出---同步
NSS/CS(Chip Select): 片选引脚(低电平有效,等价于从机的通信开关)
<3>SPI接口支持SPI协议、IIS协议;默认为SPI协议
<4>通信校验:CRC校验(由于短距离传输,干扰信号少,一般不用校验)
<5>8或16位传输帧格式选择、数据低位优先发送或高位优先发送、可编程的时钟极性和相位
<6>硬件SPI的最大的时钟频率、通信速度为18Mhz;有八种预分频系数
1.2 资源
SPI资源
SM32F103ZET6,SPI接口数量为3个【SPI1,SPI2,SP3】
SPI1->APB2(72MHZ)、SPI2->APB1(36MHZ)、SPI3->APB1(36MHZ)
SPI挂在不同的72MHz/36MHz时钟线上,须经过分频且最后的传输速率不超过18MHz
SPI端口配置
SPI引脚
1.3 框图
主从机连接
主机连接了2个从机
如果主机想要和Slave1进行通信,则只需要在通信前将CS1引脚拉低选中从机1即可通信;
如果主机想要和Slave2进行通信,则只需要在通信前将CS2引脚拉低选中从机2即可通信;
注意:当有多个从机连接一个主机时,由于同一个从机输出连接在一个,如果同时开启会造成冲突,所以当从机未被选中前,从机的MISO引脚必须为高阻态
系统框图
SPI只有一个移位寄存器,所以数据从移位寄存器移出数据后,也必须移入数据到移位寄存器
当MCU作为主机时
①发送的数据从移位寄存器通过MO口发出;同时接受的数据通过MI口进入移位寄存器
当MCU作为从机时
② 发送的数据从移位寄存器通过SO口发出;同时接受的数据通过SI口进入移位寄存器

接发逻辑
当MCU作为主机时 要发送0x78(设置低位先发送),则往发送缓冲区中写入0x78
第一个时钟周期,将0x78的第0位通过MOSI口移出,通过MISO口移入1个bit到移位寄存器
第二个时钟周期,将0x78的第1位通过MOSI口移出,再通过MISO口移入1个bit到移位寄存器
到第八个时钟周期,将0x78的第7位通过MOSI口移出,再通过MISO口移入1个bit到移位寄存器
0x78发送完后,移位寄存器一共接收到8位后,将这八位加载到接收缓冲区
二、时序传输
根据CPOL位与CPHA位的不同组合会有四种时序逻辑
模式0: CPOL=0,CPHA=0 | 空闲时时钟为低电平, 在时钟的第一个边沿移入数据(或进行采样输入) 在时钟的第二个边沿移出数据 |
模式1: CPOL=0,CPHA=1 | 空闲时时钟为低电平, 在时钟的第一个边沿移出数据, 在时钟的第二个边沿移入数据(或进行采样输入) |
模式2: CPOL=1,CPHA=0 | 空闲时时钟为高电平, 在时钟的第一个边沿移入数据(或进行采样输入) 在时钟的第二个边沿移出数据 |
模式3: CPOL=1,CPHA=1 | 空闲时时钟为高电平, 在时钟的第一个边沿移出数据, 在时钟的第二个边沿移入数据(或进行采样输入) |
模式0:CPOL=0,CPHA=0时序图(最常用)
对于模式0,时钟空闲时为低电平,规定高位发送
对于CPHA=0,则第一个上升沿主机输入数据,但是想要输入数据就必须先要发送数据,所以说当SS为下降沿时主机通过MOSI发送高位数据 ,从机通过MOSI发送高位数据;当SCK第一个时钟沿时主机通过MOSI发送高位数据 ,从机通过MOSI发送高位数据
模式1:CPOL=0,CPHA=1时序图
对于模式1,时钟空闲时为低电平,规定高位发送
当上升沿时,主机通过MOSI发送高位数据 ,从机通过MOSI发送高位数据
当下升沿时,主机通过MISO进行采样接收数据 ,从机通过MISO进行采样接收数据
主模式、全双工、连续传输
在这里重点需要看两个关键的标志位发送寄存器为空标志位(TXE),接收标志位非空标志位(RXNE)
当TXE=1时,说明发送缓冲区为空,表示可以发数据了;这时0xF1进入发送缓冲区,发送缓冲区的0xF1进入移位寄存器,此时发送缓冲区又变为空,这时0xF2可以进入发送缓冲区等待移位寄存器变为空;当0xF1输出完时,相应的主机也接收完了,此时的接收缓冲区为非空,当RXNE=1,时表示可以读数据了
/****************************
*@brief SPI数据交换
*@param void
*@return void
*@note void
*@time 2025-6-29
******************************/
int8_t HardWare_SPI_Dataswap(uint8_t Send_Data)
{
while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE)==0) ;//TXE=1时,说明发送缓冲区为空,表示可以发数据了
SPI_I2S_SendData(SPI2, Send_Data) ;
while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE)==0);//RXNE=1时,说明接受缓冲区为非空,表示可以读数据了
return SPI_I2S_ReceiveData(SPI2) ;
}
三、 硬件SPI读写W25Q64
虽然W25Q64可支持SPI最大频率为80MHz,但开发板的硬件SPI支持的最大速率为18MHz;软件模式SPI的引脚与硬件SPI引脚配置不同
/****************************
*@brief SPI引脚初始化
*@param void
*@return void
*@note
PB13->CLK推挽复用输出
PB14->MISO->DO浮空或上拉
PB12->CS 软件控制,推挽输出
PB15->MOSI->DI推挽复用输出
36MHz
*@time 2025-6-29
******************************/
void HardWare_SPI_Init(void)
{
/*1.打开IO引脚、SPI2时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);
/*2.引脚模式配置*/
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP ;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_13|GPIO_Pin_15 ;//输出配置为推挽复用输出
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz ;
GPIO_Init(GPIOB,&GPIO_InitStruct) ;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU ;//输入配置为浮空输入
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_14 ;
GPIO_Init(GPIOB,&GPIO_InitStruct) ;
/*3.SPI初始化*/
SPI_InitTypeDef SPI_InitStruct;
SPI_InitStruct.SPI_BaudRatePrescaler =SPI_BaudRatePrescaler_2 ;//二分频,18MHz
SPI_InitStruct.SPI_CPHA =SPI_CPHA_1Edge ;
SPI_InitStruct.SPI_CPOL =SPI_CPOL_Low ;//模式0
SPI_InitStruct.SPI_CRCPolynomial =0x0007 ;
SPI_InitStruct.SPI_DataSize =SPI_DataSize_8b ;
SPI_InitStruct.SPI_Direction =SPI_Direction_2Lines_FullDuplex ;
SPI_InitStruct.SPI_FirstBit =SPI_FirstBit_MSB ;
SPI_InitStruct.SPI_Mode =SPI_Mode_Master ;
SPI_InitStruct.SPI_NSS =SPI_NSS_Soft ;
SPI_Init(SPI2,&SPI_InitStruct);
/*4.使能*/
SPI_Cmd(SPI2,ENABLE);
/*5.片选信号配置*/
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP ;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_12 ;//输出配置为推挽输出
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz ;
GPIO_Init(GPIOB,&GPIO_InitStruct) ;
/*6.CS默认为高电平*/
GPIO_WriteBit( GPIOB,GPIO_Pin_12,(BitAction)SET) ;
}
完整的代码在我的资源下载,代码部分有跨页写入
四、软件模拟SPI
软件模拟就放在下个文章了