SPI详解

   SPI


1,概念:SPI是一种高速的,全双工,同步的通信总线。
高速:SPI1和SPI2在stm32f103最高波特率为18MHZ,SPI1和SPI4在stm32f407最高波特率为37.5MHZ,SPI2和SPI3在stm32f407最高波特率为21MHZ.
全双工:可以同时接收和发送数据。
同步:接收端和发送端必须在同一时刻接收和发送。

2,接口
它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,也可以三线进行单向传输。
SDI – 主设备数据输入,从设备数据输出;
SDO – 主设备数据输出,从设备数据输入;
SCLK – 时钟信号,由主设备产生;
CS – 从设备使能信号,由主设备控制。


3,SPI的相位和极性
CPOL极性:当CPOL为0时,SCLK在空闲时为低电平,当CPOL为1时,SCLK空闲时为高电平。
CPHA相位:当CPHA为0时,在SCLK的前一边沿采样,后一边沿输出。当CPHA为1时,在SCLK的前一边沿输出,后一边沿采样。


4,SPI初始化配置
①,spi的GPIO口配置
②,spi的输入输出模式:双线输入输出,双线输入,单线输出,单线输入
③,spi的主从模式:主模式,从模式
④,spi的极性和相位配置
⑤,spi的波特率配置

⑥,spi的发送顺序:高位先发送,低位先发送

举例如下:

void SPI2_Init(void)
{	 
  GPIO_InitTypeDef  GPIO_InitStructure;
  SPI_InitTypeDef  SPI_InitStructure;
	
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//开启GPIOA时钟
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//开启GPIOB时钟
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);//开启SPI2时钟
 
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//100MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;//下拉输入
  GPIO_Init(GPIOB, &GPIO_InitStructure);
	
  GPIO_PinAFConfig(GPIOB,GPIO_PinSource13,GPIO_AF_SPI2); //PB13引脚复用到SPI2
  GPIO_PinAFConfig(GPIOB,GPIO_PinSource14,GPIO_AF_SPI2); //PB14引脚复用到SPI2
  GPIO_PinAFConfig(GPIOB,GPIO_PinSource15,GPIO_AF_SPI2); //PB15引脚复用到SPI2	
 
  RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI2,ENABLE);//使能SPI2时钟
  RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI2,DISABLE);//不使能SPI2时钟

  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI为双向双线全双工模式
  SPI_InitStructure.SPI_Mode = SPI_Mode_Master;		//设置为spi主模式
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;		//设置SPI为8位数据格式
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;		//串行同步时钟的空闲状态为低电平
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;	//设置SPI在SCLK的前一边沿采样,后一边沿输出。
  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;		//配置CS引脚为软件控制
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;		//设置SPI的波特率预分频值为256
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;	//指定SPI传输从高位开始
  SPI_InitStructure.SPI_CRCPolynomial = 7;	//CRC值计算的多项式
  SPI_Init(SPI2, &SPI_InitStructure);  
  SPI_Cmd(SPI2, ENABLE); //使能SPI2	 
}


5,SPI读写数据流程
①,CS引脚拉低,使能spi从设备
②,等待spi-buffer为空
③,把需要发送的数据放入spi-buffer(若为读数据,则发送0xff)
④,等待接收完一个数据,把spi-buffer数据放入内存(若为写数据,则接收的数据无用,可丢弃)
⑤,CS引脚拉高,失能spi从设备

举例如下:

u8 SPI2_ReadWriteByte(u8 TxData)
{		
	u16 retry = 0;	
	u8 ret;
	SPI2_NSS(0);    //拉低CS电平
	while(0 == (SPI2->SR & 1 << 1)) //等待发送区空
	{
		retry++;
		if(retry > SPI_REG_TIMEOUT)
		{
			SPI_PRINT("debug SPI Send Data TIMEOUT\r\n");
			return 0;
		}
	}			   
	SPI2->DR = TxData; //发送一个byte 
	retry = 0;
	while(0 == (SPI2->SR & 1 << 0)) //等待接收完一个byte  
	{
		retry++;
		if(retry > SPI_REG_TIMEOUT)
		{
			SPI_PRINT("debug SPI Send Data TIMEOUT\r\n");
			return 0;
		}
	}	  						    
	ret = SPI2->DR; //保存接收到的数据
	SPI2_NSS(1);    //拉高CS电平			    
	return ret;
}



6,SPI配置心得
在主设备这边配置SPI接口时钟的时候一定要弄清楚从设备的时钟要求,因为主设备这边的时钟极性和相位都是以从设备为基准的。因此在时钟极性的配置上一定要搞清楚从设备是在时钟的上升沿还是下降沿接收数据,是在时钟的下降沿还是上升沿输出数据。但要注意的是,由于主设备的SDO连接从设备的SDI,从设备的SDO连接主设备的SDI,从设备SDI接收的数据是主设备的SDO发送过来的,主设备SDI接收的数据是从设备SDO发送过来的,所以主设备这边SPI时钟极性的配置(即SDO的配置)跟从设备的SDI接收数据的极性是相反的,跟从设备SDO发送数据的极性是相同的。

版权声明:本文为qzl1221原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。