SPI运用

1.SPI介绍

SPI:串行外设设备接口,是一种高速的,全双工的,同步的通讯总线,并且只在芯片的引脚上占用四根线。主要用用在EEPROM,FLASH,实时时钟,AD转换器之间。

:(1)SPI会有主从机(或者对于MCU来说会有主模式,从模式)之分,它的区分依据是SCLK同步时钟和SS片选是由谁输出,输出方就会被定义为工作在主机(主模式)状态下。(2)如果在一个系统中只含有一个采用SPI通信的外设,那么我们可以将外设的SS引脚直接连接板子的GND引脚,这样使得外设始终被选中,从而节省了连接线。

通信总是由主设备发起。主设备通过MOSI脚把数据发送给从设备,从设备通过MISO引脚回传数据。这意味全双工通信的数据输出和数据输入是用同一个时钟信号同步的;时钟信号由主设备通过SCK脚提供。

在这里插入图片描述

2.SPI框图

在这里插入图片描述
在这里插入图片描述

3.SPI的工作模式

时钟极性(CPOL):没有数据传送时的空闲电平状态

​ 0:SCK在空闲状态是低电平

​ 1:SCK在空闲状态是高电平

时钟相位(CPHA):时钟线在第几个时钟边沿采样数据
0: SCK的第一(奇数)边沿进行数据位采样,数据在第一个时钟边沿锁存

​ 1: SCK的第二(偶数)边沿进行数据位采样,数据在第二个时钟边沿锁存

SPI模式CPOLCPHA空闲状态时钟极性采样跳变沿
000低电平上升沿从数据线上采样,下降沿输出数据到数据线(第一个跳变沿)
101低电平下降沿采样,上升沿输出(第二个跳变沿)
210高电平下降沿采样,上升沿输出
311高电平上升沿采样,下降沿输出

以模式0举例:

在这里插入图片描述

4.SPI相关寄存器介绍

SPI_CR1 : SPI控制寄存器1 用于配置SPI工作参数

SPI_SR:SPI状态寄存器用于查询当前SPI传输状态(TXE、RXN)

SPI_DR: SPI数据寄存器用于存放待发送数据或接收数据,有两个缓冲区

5.SPI实现方式

第一种方式是采用STM32的GPIO模拟SPI时序的方式进行读写Flash芯片;另一种方式采用STM32片内自带的SPI外设进行读写Flash芯片。

5.1 硬件实现spi

配置板上SPI的寄存器

void SPI2_Init(void)
{
 	GPIO_InitTypeDef GPIO_InitStructure;
    SPI_InitTypeDef  SPI_InitStructure;
	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB, ENABLE );//PORTB时钟使能 
	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_PP;  //PB13/14/15复用推挽输出 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB

 	GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);  //PB13/14/15上拉

	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;		//设置SPI工作模式:设置为主SPI
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;		//设置SPI的数据大小:SPI发送接收8位帧结构
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;		//串行同步时钟的空闲状态为高电平
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;	//串行同步时钟的第二个跳变沿(上升或下降)数据被采样
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;		//NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;		//定义波特率预分频的值:波特率预分频值为256
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;	//指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
	SPI_InitStructure.SPI_CRCPolynomial = 7;	//CRC值计算的多项式
	SPI_Init(SPI2, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
 
	SPI_Cmd(SPI2, ENABLE); //使能SPI外设
	
	SPI2_ReadWriteByte(0xff);//启动传输		 
 

}   
//SPI 速度设置函数
//SpeedSet:
//SPI_BaudRatePrescaler_2   2分频   
//SPI_BaudRatePrescaler_8   8分频   
//SPI_BaudRatePrescaler_16  16分频  
//SPI_BaudRatePrescaler_256 256分频 
  
void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler)
{
  assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));
	SPI2->CR1&=0XFFC7;
	SPI2->CR1|=SPI_BaudRatePrescaler;	//设置SPI2速度 
	SPI_Cmd(SPI2,ENABLE); 

} 

//SPIx 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI2_ReadWriteByte(u8 TxData)
{		
	u8 retry=0;				 	
	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位
		{
		retry++;
		if(retry>200)return 0;
		}			  
	SPI_I2S_SendData(SPI2, TxData); //通过外设SPIx发送一个数据
	retry=0;

	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) //检查指定的SPI标志位设置与否:接受缓存非空标志位
		{
		retry++;
		if(retry>200)return 0;
		}	  						    
	return SPI_I2S_ReceiveData(SPI2); //返回通过SPIx最近接收的数据					    
}

5.2 软件实现spi

确定模式、数据高低位、传送速率都由从设备确定。经常使用模式0、3

#define  SPI_WP_PIN          GPIO_Pin_6
#define  SPI_WP_PORT         GPIOC
 
#define  SPI_CS_PIN          GPIO_Pin_15
#define  SPI_CS_PORT         GPIOA
 
#define  SPI_CLK_PIN         GPIO_Pin_3
#define  SPI_CLK_PORT        GPIOB
 
#define  SPI_MOSI_PIN        GPIO_Pin_12
#define  SPI_MOSI_PORT       GPIOC
 
#define  SPI_MISO_PIN        GPIO_Pin_4
#define  SPI_MISO_PORT       GPIOB
 
#define  SPI_BASE			 SPI3
 
#define  SPI_FLASH_CS_LOW()    GPIO_ResetBits(SPI_CS_PORT, SPI_CS_PIN)  /* Select SPI MEM1: ChipSelect pin low  */
#define  SPI_FLASH_CS_HIGH()   GPIO_SetBits(SPI_CS_PORT, SPI_CS_PIN)    /* Deselect SPI MEM1: ChipSelect pin high */
 
#define  SPI_FLASH_WP_LOW()    GPIO_ResetBits(SPI_WP_PORT, SPI_WP_PIN)  //PC4
#define  SPI_FLASH_WP_HIGH()   GPIO_SetBits(SPI_WP_PORT, SPI_WP_PIN)    //PC4
//模拟MOSI
void SPI_MOSI(unsigned char Status)
{
	if(Status)
		GPIO_WriteBit(SPI_MOSI_PORT,SPI_MOSI_PIN,Bit_SET);
	else
		GPIO_WriteBit(SPI_MOSI_PORT,SPI_MOSI_PIN,Bit_RESET);
}
//模拟CLK
void SPI_CLK(unsigned char Status)
{
	if(Status)
		GPIO_WriteBit(SPI_CLK_PORT,SPI_CLK_PIN,Bit_SET);
	else
		GPIO_WriteBit(SPI_CLK_PORT,SPI_CLK_PIN,Bit_RESET);
}
//模拟MISO
unsigned char SPI_MISO(void)
{
	if(GPIO_ReadInputDataBit(SPI_MISO_PORT,SPI_MISO_PIN))
		return 1;
	else 
		return 0;
}
void gd25qxxInit(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
 
	GPIO_InitStructure.GPIO_Pin = SPI_MOSI_PIN; //PA5:SCK,PA7:MOSI
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;	//推挽输出
	GPIO_Init(SPI_MOSI_PORT, &GPIO_InitStructure);
	SPI_MOSI(1);
	
	GPIO_InitStructure.GPIO_Pin = SPI_CLK_PIN; //PA5:SCK,PA7:MOSI
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;	//推挽输出
	GPIO_Init(SPI_CLK_PORT, &GPIO_InitStructure);
	SPI_CLK(1);
 
	GPIO_InitStructure.GPIO_Pin = SPI_MISO_PIN ;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(SPI_MISO_PORT, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = SPI_CS_PIN;                             
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Init(SPI_CS_PORT, &GPIO_InitStructure);
	SPI_FLASH_CS_HIGH();
 
	GPIO_InitStructure.GPIO_Pin = SPI_WP_PIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Init(SPI_WP_PORT, &GPIO_InitStructure);
	SPI_FLASH_WP_HIGH();
}

uint8_t gd25qxxSendByte(uint8_t byte)// 模式0 上升沿从数据线采样,下降沿输出数据到数据线
{
	unsigned char i,Result = 0;;
	for(i=0;i<8;i++)
	{
		if(byte & 0x80)	SPI_MOSI(1);
		else SPI_MOSI(0);
		byte <<= 1;
        SPI_CLK(0);
		SPI_CLK(1);
		
		Result <<= 1;
		if(SPI_MISO()) Result |= 0x01;
	}
	SPI_CLK(0);
	return(Result);
}
uint32_t gd25qxxReadID(void)
{
  uint32_t Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;
  /* Select the FLASH: Chip Select low */
  SPI_FLASH_CS_LOW();
 
  /* Send "RDID " instruction */
  gd25qxxSendByte(0x9F);
 
  /* Read a byte from the FLASH */
  Temp0 = gd25qxxSendByte(DUMMY_BYTE);
 
  /* Read a byte from the FLASH */
  Temp1 = gd25qxxSendByte(DUMMY_BYTE);
 
  /* Read a byte from the FLASH */
  Temp2 = gd25qxxSendByte(DUMMY_BYTE);
 
  /* Deselect the FLASH: Chip Select high */
  SPI_FLASH_CS_HIGH();
 
  Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;
 
  return Temp;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值