SPI通信简介
·SPI(Serial Peripheral Interface)是由Motorola公司开发的一种通用数据总线
·四根通信线:SCK(Serial Clock)、MOSl(Master Output Slave Input)、MISO(Master Output Slave Input)、SS(Slave Select)
·同步,全双工
·支持总线挂载多设备(一主多从)
(本节使用STM32的硬件SPI外设)
SPI时序
本节使用SPI模式0
SCK默认低电平,当SS第一个下降沿的时候,MOSI和MISO移出数据,在SCK的上升沿移入数据,如此循环八次,便交换一个字节,所以SPI通信其实就是交换数据。
见上图,主机想要给从机发送0xAA,交换一个字节后,主机接收到的是0x55,从机则接收到0xAA,我们如果只想接收数据,就随便发送一个数据给从机;只想发送数据实现某种功能,那么从机交换过来的数据我们可以不读。
本节演示SPI读写W25Q64

SPI硬件外设
配置SPI基本结构
初始化配置模板
void MySPI_Init(void)
{
//配置成SPI模式0
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);//SPI1是APB2的外设
//PA4配置为推挽输出
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);
//配置SCK和MOSI为复用推挽输出
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);
//配置MISO为上拉输入模式
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模式
SPI_InitStructure.SPI_Direction=SPI_Direction_2Lines_FullDuplex;//选择双线全双工
SPI_InitStructure.SPI_DataSize=SPI_DataSize_8b;//8位数据帧
SPI_InitStructure.SPI_FirstBit=SPI_FirstBit_MSB;//高位先行
SPI_InitStructure.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_128;//波特率分频器
SPI_InitStructure.SPI_CPOL=SPI_CPOL_Low;//SCK默认低电平
SPI_InitStructure.SPI_CPHA=SPI_CPHA_1Edge;//相当于选择CPHA为0,第一个边缘采样(移入)
SPI_InitStructure.SPI_NSS=SPI_NSS_Soft;//选择软件模拟NSS引脚
SPI_InitStructure.SPI_CRCPolynomial=7;//CRC校验,默认为7
SPI_Init(SPI1,&SPI_InitStructure);
SPI_Cmd(SPI1,ENABLE);
}
SPI1的SS对应PA4,SCK对应PA5,MISO对应PA6,MOSI对应PA7
片选引脚SS用软件模拟,所以配置为通用推挽输出;
SCK和MOSI要交由片上外设控制,所以配置为复用推挽输出;
MISO是输入端,所以配置为普通的上拉输入;
配置SPI结构体一般都是按照上面的参数来配置,SPI不同的模式差别也就在于时钟的相位,是第几个边沿采样的区别,本质上没有区别,这里配置成模式0;
最后要严谨一点的话,在初始化的最后还要将SS默认设置为高电平。
最后封装常用的起始位,停止位,交换数据函数即可:
void MySPI_W_SS(uint8_t BitValue)//置SS电平高低
{
GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)BitValue);
}
void MySPI_Start(void)//起始函数
{
MySPI_W_SS(0);
}
void MySPI_Stop(void)//终止函数
{
MySPI_W_SS(1);
}
uint8_t MySPI_SwapByte(uint8_t ByteSend)//交换数据
{
//TXE为1,TDR寄存器为空,表示可以发送数据
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)!=SET);
SPI_I2S_SendData(SPI1,ByteSend);//写DR会清除标志位
//等待RENE为1,RDR寄存器非空,表示可以读取接收到的数据
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)!=SET);
return SPI_I2S_ReceiveData(SPI1);//读DR同理
}
封装交换数据的函数时,可以参考下图
具体要怎么使用这些函数还得看SPI硬件外设的读写操作是怎么样的,不同的芯片用法也不同。