SPI总线协议的基础知识

接口/总线/驱动

1139人已加入

描述

一,spi总线协议

SPI,是英语 Serial Peripheral Interface 的缩写顾名思义就是串行外围设备接口。SPI是一种高速的,全双工,同步的通信总线, 并且在芯片的管脚上只占用四根线。

SPI 是一个环形总线结构,由 ss(cs)、sck、sdi(mosi)、sdo(miso) 构成,其时序其实很简单,主要是在 sck 的控制下,两个双向移位寄存器进行 数据交换。 上升沿发送、下降沿接收、高位先发送。 上升沿到来的时候,sdo 上的电平将被发送到从设备的寄存器中。 下降沿到来的时候,sdi 上的电平将被接收到主设备的寄存器中。

寄存器中数据的收发格式可以参考一下图片,在第一个脉冲周期的上升沿阶段,进行采样,主机数据整体左移,主机最低位在脉冲周期的下降沿阶段接收从机发来的数据0。如此循环往复实现8为数据的收发。

时序图

二,spi的时序图

SPI 模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置。

时钟极性(CPOL)对传输协议没有重大的影响。如果 CPOL=0,串行同步时钟的空闲状态为低电平;如果 CPOL=1,串行同步时钟的空闲状态为高电平。

时钟相位(CPHA) 能够配置用于选择两种不同的传输协议之一进行数据传输。如果 CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如 果 CPHA=1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。

SPI 主模块和与之通信的外设音时钟相位和极性应该一致。 SPI 时序图详解---SPI 接口在模式 0 下输出第一位数据的时刻 SPI 接口有四种不同的数据传输时序,取决于 CPOL 和 CPHL 这两位的组合。图 1 中表现了这四种时序,时序与 CPOL、CPHL 的 关系也可以从图中看出

时序图

三,spi框图

SPI的方框图见下图

时序图

MISO:主设备输入/从设备输出引脚。该引脚在从模式下发送数据,在主模式下接收数据。

MOSI:主设备输出/从设备输入引脚。该引脚在主模式下发送数据,在从模式下接收数据。

SCK:串口时钟,作为主设备的输出,从设备的输入

需要关注的是他的NSS引脚,具体的框图如下

时序图

3.1如果STM32作为SPI总线的主设备

如果是硬件模式,并且SSOE等于1,那么NSS引脚电平可以送入内部NSS,内部NSS也可以通过输出控制单元送到NSS引脚上,就可以实现对从设备的控制了。NSS引脚在工作时会出现低电平,可以打开外部从设备。如果SSOE等于0,这时内部NSS信息就无法送到NSS引脚了,那不是无法控制从设备了么?对,此时NSS引脚就无法控制从设备了,不过如果有其他的主设备将NSS引脚拉低,那么此时SPI就自动进入了从模式,可以接收数据了,实现了多主模式下(比如两个STM32通过SPI进行通信,都可能是主设备)的功能切换。

如果是软件模式,内部NSS的信息来自于SSI标志位,但内部NSS信息无法送到NSS引脚,此时就需要用户使用其他的GPIO引脚来控制从设备。

3.2如果STM32作为SPI总线的从设备

如果是硬件模式,且SSOE=0,NSS引脚信号可以送入到内部NSS,STM32可以受控于其他主设备。如果此时SSOE=1,那么SPI也可以切换成主模式,实现从到主的切换。

如果是软件模式,如果SSI=0,那么内部NSS一直为低电平,总是被选中,可以随时和外部设备通信(此时STM32自己是从设备)。

因此:一般STM32做主设备,且管理一个从设备,可以设置成硬件模式。 如果管理多个从设备,可以设置软件模式。如果STM32做从设备,尽量也设置成软件模式,让SSI=0,就可以随时接收数据了。

四,spi相关寄存器

从spi的框图中可以看出,spi相关的寄存器主要有SPI_CR1,SPI_CR2,SPI_SR,SPI_DR这四个寄存器。

SPI_CR1是spi的控制寄存器,通过这个寄存器我们可以设置spi的一些功能参数,具体寄存器内容如下图:

时序图

寄存器低六位分别是控制时钟相位,时钟极性,主从设备选择和通讯波特率的。具体细节可以查看手册。除了寄存器操作,我们也可以直接使用库函数操作。下面列出了部分常用库函数:

SPI_Init 根据 SPI_InitStruct 中指定的参数初始化外设 SPIx 寄存器
SPI_Cmd使能或者失能 SPI 外设
SPI_SendData 通过外设 SPIx 发送一个数据
SPI_ReceiveData 返回通过 SPIx 最近接收的数据
SPI_NSSInternalSoftwareConfig 为选定的 SPI 软件配置内部 NSS 管脚

SPI 状态寄存器(SPI_SR),通过状态寄存器可以得知spi当前状态,是否在发送数据,以及接受和发送缓冲区是否为空,通过这些判断来实现数据的收发,当然也有写好的库函数可以直接操作。

时序图

具体的初始化操作:

void spi_Init(void)
{
	/*spi引脚配置
CS PB0
CSK PA5 
MISO PA6 
MOSI PA57
*/
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	GPIO_InitTypeDef  GPIO_Port;//定义结构体变量	
	GPIO_Port.GPIO_Mode = GPIO_Mode_AF_PP;//模式	
	GPIO_Port.GPIO_Pin = GPIO_Pin_5; //引脚
	GPIO_Port.GPIO_Speed = GPIO_Speed_50MHz;//速度
	GPIO_Init(GPIOA,&GPIO_Port);
	GPIO_Port.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Port.GPIO_Pin = GPIO_Pin_0; //引脚
	GPIO_Init(GPIOB,&GPIO_Port);
	GPIO_Port.GPIO_Mode = GPIO_Mode_IN_FLOATING;//模式
	GPIO_Port.GPIO_Pin = GPIO_Pin_6; //引脚
	GPIO_Init(GPIOA,&GPIO_Port);//MISO
	GPIO_Port.GPIO_Mode = GPIO_Mode_AF_PP;//模式
	GPIO_Port.GPIO_Pin = GPIO_Pin_7; //引脚
	GPIO_Init(GPIOA,&GPIO_Port);//MOSI

	GPIO_SetBits(GPIOA,GPIO_Pin_4);//把CS信号线拉高
	
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);//spi时钟使能
	SPI_InitTypeDef SPI_InitStruct;
	SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;//二分频,36mhz
	SPI_InitStruct.SPI_CPHA =SPI_CPHA_1Edge;//相位设置,第一个跳变沿采样
	SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;//极性设置,空状态低电平
	SPI_InitStruct.SPI_CRCPolynomial = 0x7;//CRC校验值
	SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;//数据大小,SPI 发送接收 8 位帧结构
	SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//通讯方式,SPI 设置为双线双向全双工
	SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;//数据传输从高位开始
	SPI_InitStruct.SPI_Mode = SPI_Mode_Master;//主从设置,设置为主模式
	SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;//nss控制选择,有内部寄存器控制
	SPI_Init(SPI1,&SPI_InitStruct);
	
	SPI_Cmd(SPI1,ENABLE);
}
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分