各总线协议汇总(二)- SPI

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_33194301/article/details/87923014

二、SPI(Serial Peripheral Interface)

SPI和IIC一样也是有模拟和硬件两种方式(模拟方式都是根据相应的时序图来写)。

SCLK:时钟信号,由主机发出(master)
MISO:主机输入从机输出(master input slave output)
MOSI:主机输出从机输入(master output slave input)
CS:片选,从设备使能信号(chip selection)
在这里插入图片描述
SPI四种工作模式:
CPOL:时钟极性选择(clock polarity),决定SPI空闲(idle)时的极性
0–> 低电平           1–>高电平  
CPHA:时钟相位选择(clock phase),决定SPI在第几条边沿采样
0–> 在SCLK第一条边沿采样    1–> 在SCK第二个条边沿采样
SPI时序图

(一)、硬件SPI

#define SPI_SCLK_PORT		GPIOA
#define SPI_MISO_PORT		GPIOA
#define SPI_MOSI_PORT		GPIOA
#define SPI_CS_PORT		    GPIOA

#define SPI_SCLK_PIN		GPIO_Pin_5
#define SPI_MISO_PIN		GPIO_Pin_6
#define SPI_MOSI_PIN		GPIO_Pin_7
#define SPI_CS_PIN			GPIO_Pin_4

#define SPI_CS_ENABLE		GPIO_ResetBits(SPI_CS_PORT,SPI_CS_PIN)
#define SPI_CS_DISENABLE	GPIO_SetBits(SPI_CS_PORT,SPI_CS_PIN)

/*******************************************************
*	FunctionName:	SPI_GPIO_Config
*	Function:			Configure SPI_GPIO
*	Arguments:		None
*	Return:			None
*	Author			seanOY
********************************************************/
//SPI引脚设置
void SPI_GPIO_Config(void)
{
 GPIO_InitTypeDef GPIO_InitStructure;
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能引脚时钟
 //SCLK configuration
 
  GPIO_InitStructure.GPIO_Pin = SPI_SCLK_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(SPI_SCLK_PORT, &GPIO_InitStructure);

  //MISO configuration
  GPIO_InitStructure.GPIO_Pin = SPI_MISO_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(SPI_MISO_PORT, &GPIO_InitStructure);			
   
  //MOSI configuration
  GPIO_InitStructure.GPIO_Pin = SPI_MOSI_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(SPI_MOSI_PORT, &GPIO_InitStructure);    
  
  //CS configuration
  GPIO_InitStructure.GPIO_Pin = SPI_CS_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(SPI_CS_PORT, &GPIO_InitStructure);	
	
}

/*******************************************************
*	FunctionName:	SPI_Configuration
*	Function:			Configure SPI
*	Arguments:		None
*	Return:			None
*	Author			seanOY
********************************************************/
//SPI配置函数
void SPI_Configuration(void)
{
	SPI_InitTypeDef SPI_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);//使能SPI1时钟
	
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;	//预分频值
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;				//数据捕获于第二个边沿
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;					//时钟在idle为高电平
	SPI_InitStructure.SPI_CRCPolynomial = 7;    	//CRC校验计算的多项式的数据位长度
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;		//帧格式为8位
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;		//双线全双工
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;	
    //设置最先传输的字节为最高有效位	(the Most Significant Bit)
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;					//主机模式
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;							//软件控制NSS信号
	SPI_Init(SPI1,&SPI_InitStructure);										//初始化SPI1
	SPI_Cmd(SPI1, ENABLE);														//使能SPI1
}

//SPI是环形结构,有读就有写
/*******************************************************
*	FunctionName:	SPI_Writebyte
*	Function:			Write a byte
*	Arguments:		data
*	Return:			None
*	Author			seanOY
********************************************************/
void SPI_Writebyte(unsigned char data)
{
	while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
	SPI_I2S_SendData(SPI1, data);
	while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
	SPI_I2S_ReceiveData(SPI1);
}
 
/*******************************************************
*	FunctionName:	SPI_Readbyte
*	Function:			Read a byte
*	Arguments:		data
*	Return:			None
*	Author			seanOY
********************************************************/
unsigned char SPI_Readbyte(unsigned char data)
{
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPI1, data);
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
return SPI_I2S_ReceiveData(SPI1);
}


读的时候要注意一个问题,因为从模式是没法提供时钟的,所以主模式下必须要在接收的同时提供时钟。办法就是发送一个字节来实现,因为还是上面说的,发送一个字节就意味着收到一个字节,代码和写完全一样,只要把读出来的字节保存即可。

(二)、模拟SPI

#define SPI_Delay()  DelayUs ( 200 )
//这里的DelayUs是用systick定时的,我就不将这些程序打出来了

static void SPI_Config ( void )
{
  GPIO_InitTypeDef GPIO_InitStructure;
  
   //SCLK configuration
  RCC_APB2PeriphClockCmd ( RCC_APB2Periph_GPIOA, ENABLE );
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

  //MISO configuration
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(GPIOA, &GPIO_InitStructure);			
   
  //MOSI configuration
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOA, &GPIO_InitStructure);    
  
  //CS configuration
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
}

void SPI_SendByte ( uint8_t byte )
{
  uint8_t counter;
  for(counter=0;counter<8;counter++)
	{
    if ( byte & 0x80 )
      MOSI_1 ();
    else 
      MOSI_0 ();
    SPI_Delay();
    SCK_0 ();
    SPI_Delay();
    SCK_1();
    SPI_Delay();
    byte <<= 1; 
  } 	
}

uint8_t SPI_ReadByte ( void )
{
  uint8_t counter;
  uint8_t SPI_Data;
  for(counter=0;counter<8;counter++)
  {
    SPI_Data <<= 1;
    SCK_0 ();
    SPI_Delay();
    if ( GPIO_ReadInputDataBit ( GPIOA, GPIO_Pin_6 ) == 1)
     SPI_Data |= 0x01;
    SPI_Delay();
    SCK_1 ();
    SPI_Delay();
  }
  return SPI_Data;
}

猜你喜欢

转载自blog.csdn.net/qq_33194301/article/details/87923014