SPI(SerialPeripheralinterface)------串行外设接口

1. 基本概念

SPI(SerialPeripheralinterface)(串行外设接口):是由 Motorola 公司开发的串行外围设备接口,是一种高速的全双工同步的通信总线。主要应用在 EEPROM,FLASH,实时时钟,AD 转换器,还有数字信号处理器和数字信号解码器等器件。

SPI 接口提供两个主要功能,支持 SPI 协议或 I2S 音频协议。默认情况下,选择的是 SPI 功能。可通过软件将接口从 SPI 切换到 I2S。
串行外设接口 (SPI) 可与外部器件进行半双工/全双工的同步串行通信。该接口可配置为主模式,在这种情况下,它可为外部从器件提供通信时钟 (SCK)。该接口还能够在多主模式配置下工作。
它可用于多种用途,包括基于双线的单工同步传输,其中一条可作为双向数据线,或使用CRC 校验实现可靠通信

总结一下, SPI的通信特点为同步串行全双工通信

SPI具备同步位交换特性:每当SPI发送一个单位数据,同时也会收到同样大小的一个单位数据

通信速度范围为 10MHZ – 100MHZ,在通信协议中属于非常快的,并且没有固定的数据帧格式,数据帧大小:8 / 16位

其主要应用在 EEPROM,FLASH,实时时钟,AD 转换器,还有数字信号处理器和数字信号解码器等器件。

1.1 总线构成

(1)五线制接口(四线制SPI)
MOSI:主出从入线(主机输出从机输入)
MISO:主入从出线(主机输入从机输出)
SCK:时钟线(主机向从机发送通信的时钟边沿信号)
CS/NSS:片选线(选择与主机通信的从设备)
GND:接地线

通信特点:同步串行全双工通信
在这里插入图片描述(2)四线制接口(三线制SPI)
IO:双向数据线(同一时间数据只能单向传输)
SCK:时钟线(主机向从机发送通信的时钟边沿信号)
CS/NSS:片选线(选择与主机通信的从设备)
GND:接地线

通信特点:同步串行半双工通信在这里插入图片描述

1.2 SPI同步通信时序

CPOL(极性)CPHA(相位) 共同决定时钟线采集数据的边沿信号。

CPOL(极性)决定时钟线初始化电平状态

  • CPOL = 0:初始电平为低电平

  • CPOL = 1:初始电平为高电平

CPHA(相位)决定采集数据的时钟边沿

  • CPHA = 0:时钟线的第一个边沿采集数据

  • CPHA = 1:时钟线的第二个边沿采集数据

在这里插入图片描述
由上图我们可以总结一下

极性/相位CPHA = 0CPHA = 1
CPOL = 0模式0(上升沿采集数据)模式1(下降沿采集数据)
CPOL = 1模式2(下降沿采集数据)模式3(上升沿采集数据)

一般来说,模式0和模式3是相互兼容的,模式1和模式2是相互兼容的。相同边沿采集的模式往往相互兼容。具体使用哪个模式要根据相对应模块手册的描述来决定。

1.3 STM32F407中的SPI资源情况

在这里插入图片描述
从图中可以看出:
SPI1挂载在APB2时钟总线上(时钟频率为84MHZ);
SPI2和SPI3挂载在APB1时钟总线上(时钟频率为42MHZ).

2. STM32F407中的SPI

2.1 SPI特性

● 基于三条线(MOSI MISO SCK)的全双工同步传输
● 基于双线(IO SCK)的单工同步传输,其中一条可作为双向数据线(半双工)
8 位或 16 位传输帧格式选择
● 主模式或从模式操作
● 多主模式功能
8 个主模式波特率预分频器(最大值为 f PCLK /2)—— 具体分频系数的选择应参考模块手册(支持的通信速度)
● 从模式频率(最大值为 f PCLK /2)
● 对于主模式和从模式都可实现更快的通信 —— 两根数据线同时用作发送或接收数据线
● 对于主模式和从模式都可通过硬件或软件进行 NSS 管理:动态切换主/从操作
● 可编程的**时钟极性(CPOL)和相位(CPHA)**
● 可编程的**数据顺序,最先移位 MSB (高位在前)或 LSB(低位在前)**
● 可触发中断的专用发送和接收标志
SPI 总线忙状态标志
SPI TI 模式(SPI 全双工模式)
● 用于确保可靠通信的硬件 CRC 功能
— 在发送模式下可将 CRC 值作为最后一个字节发送
— 根据收到的最后一个字节自动进行 CRC 错误校验
● 可触发中断的主模式故障、上溢和 CRC 错误标志
● 具有 DMA 功能的 1 字节发送和接收缓冲器:发送和接收请求

2.2 SPI框图及其配置流程

在这里插入图片描述
1.初始化配置函数

(1)使能要使用的GPIO和SPI的时钟总线

(2)初始化相关GPIO引脚,将SCLK,MOSI,MISO的GPIO模式配置为复用,CS为输出

(3)改变引脚映射

(4)配置SPI

  • 波特率预分频
  • 通信方式(全双工/半双工/单工)
  • 主从模式
  • 极性
  • 相位
  • 软硬件管理
  • 数据顺序
  • 数据大小

5)使能SPI

2.编写数据接收和发送函数

3. SPI初始化以及数据接收发送代码

//W25Q64_CS_H为拉高片选线电平
#define W25Q64_CS_H GPIO_SetBits(GPIOB,GPIO_Pin_14)
//W25Q64_CS_H为拉低片选线电平
#define W25Q64_CS_L GPIO_ResetBits(GPIOB,GPIO_Pin_14)


//SPI1初始化函数
void SPI1_Init(void)
{
	//1.开启时钟线
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
	
	
	//2.初始化相关GPIO引脚
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	W25Q64_CS_H;//初始化完片选线引脚后就将其拉高
	
	//3.引脚重映射
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);
	
	//4.配置SPI
	SPI_InitTypeDef SPI_InitStruct;
	SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;//2分频
	SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge;//从第二个边沿开始捕获
	SPI_InitStruct.SPI_CPOL = SPI_CPOL_High;//时钟线初始电平状态为高电平
	SPI_InitStruct.SPI_CRCPolynomial = 0X0007;//这里我们不需要CRC,故设置为此值
	SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;//数据大小为8位
	SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//全双工通信
	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);

	//5.使能SPI
	SPI_Cmd(SPI1, ENABLE);
	
}


//SPI1数据收发函数
u8 SPI1_TransferData(u8 data)
{
	//当发送缓冲区为空时再发送数据
	while(RESET == SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE));
	SPI_I2S_SendData(SPI1,data);
	
	//当接收缓冲区不为空时再接收数据
	while(RESET == SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE));
	return SPI_I2S_ReceiveData(SPI1)&0xFF;
	
}


  • 14
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值