spi简介——精选推荐
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
◆ SPI 工作原理总结
① 硬件上为4根线。
② 主机和从机都有一个串行移位寄存器,主机通过向它的SPI 串行寄存器写入一个字节来发起一次传输。
③ 串行移位寄存器通过MOSI 信号线将字节传送给从机,从机也将自己的串行移位寄存器中的内容通过MISO 信号线返回给主机。
这样,两个移位寄存器中的内容就被交换。
④ 外设的写操作和读操作是同步完成的。
如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。
◆ SPI 接口简介
SPI:Serial Peripheral interface 的缩写,顾名思义就是串行外围设备接口。
SPI ,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,主要应用在 EEPROM ,FLASH ,实时时钟,AD 转换器,还有数字信号处理器和数字信号解码器之间。
STM32F4 的 SPI 功能很强大, SPI 时钟最高可以到 37.5Mhz ,支持 DMA 。
◆ SPI 内部结构简明图
移位寄存器
移位寄存器SPI 时钟发生器MISO MOSI SCLK
VDD
CS CS
主机(Master)
从机(Slave)
SPI 接口一般使用4条线通信:
MISO 主设备数据输入,从设备数据输出。
MOSI 主设备数据输出,从设备数据输入。
SCLK 时钟信号,由主设备产生。
CS从设备片选信号,由主设备控制。
◆SPI接口框图
⏹STM32 SPI接口可配置为支持SPI协议或者支持I2S音频协议,默认是SPI模式。
可
以通过软件切换到I2S方式。
27.2.1 SPI 特性
● 基于三条线的全双工同步传输
● 基于双线的单工同步传输,其中一条可作为双向数据线
● 8 位或16 位传输帧格式选择
● 主模式或从模式操作
● 多主模式功能
● 8 个主模式波特率预分频器(最大值为f PCLK/2)
● 从模式频率(最大值为f PCLK/2)
● 对于主模式和从模式都可实现更快的通信
● 对于主模式和从模式都可通过硬件或软件进行NSS 管理:动态切换主/从操作
● 可编程的时钟极性和相位
● 可编程的数据顺序,最先移位MSB或LSB
● 可触发中断的专用发送和接收标志
● SPI 总线忙状态标志
● SPI TI 模式
● 用于确保可靠通信的硬件CRC 功能:
—在发送模式下可将CRC 值作为最后一个字节发送
—根据收到的最后一个字节自动进行CRC 错误校验
● 可触发中断的主模式故障、上溢和CRC 错误标志
● 具有DMA 功能的1字节发送和接收缓冲器:发送和接收请求
时钟相位和时钟极性
通过SPI_CR1 寄存器中的CPOL 和CPHA 位,可以用软件选择四种可能的时序关系。
CPOL (时钟极性)位控制不传任何数据时的时钟电平状态。
此位对主器件和从器件都有作用。
如果复位CPOL,SCK 引脚在空闲状态处于低电平。
如果将CPOL 置1,SCK 引脚在空闲状态处于高电平。
如果将CPHA(时钟相位)位置1,则SCK 引脚上的第二个边沿(如果复位CPOL 位,则
为下降沿;如果将CPOL 位置1,则为上升沿)对MSBit 采样。
即,在第二个时钟边沿锁存数据。
如果复位CPHA 位,则SCK 引脚上的第一个边沿(如果将CPOL 位置1,则为下降沿;如果复位CPOL 位,则为上升沿)对MSBit 采样。
即,在第一个时钟边沿锁存数据。
CPOL (时钟极性)和CPHA(时钟相位)位的组合用于选择数据捕获时钟边沿。
数据帧格式
移出数据时MSB在前还是LSB在前取决于SPI_CR1寄存器中LSBFIRST位的值。
每个数据帧的长度均为8位或16位,具体取决于使用SPI_CR1寄存器中的DFF位。
所选的数据帧格式适用于发送和/或接收。
从器件选择(NSS) 引脚管理
可以使用SPI_CR1 寄存器中的SSM 位设置硬件或软件管理从器件选择。
● 软件管理NSS (SSM = 1)
从器件选择信息在内部由SPI_CR1 寄存器中的SSI 位的值驱动。
外部NSS 引脚空闲,可供其它应用使用。
● 硬件管理NSS (SSM = 0)
根据NSS 输出配置(SPI_CR1 寄存器中的SSOE 位),硬件管理NSS 有两种模式。
— NSS 输出使能(SSM = 0,SSOE = 1)
仅当器件在主模式下工作时才使用此配置。
当主器件开始通信时,NSS 信号驱动为低电平,并保持到SPI 被关闭为止。
— NSS 输出禁止(SSM = 0,SSOE = 0)
对于在主模式下工作的器件,此配置允许多主模式功能。
对于设置为从模式的器件,NSS 引脚用作传统NSS 输入:在NSS 为低电平时片选该从器件,在NSS 为高电平时取消对它的片选。
状态标志
应用可通过三种状态标志监视SPI 总线的状态。
发送缓冲区为空(TXE)
此标志置1 时,表示发送缓冲区为空,可以将待发送的下一个数据加载到缓冲区中。
对SPI_DR 寄存器执行写操作时,将清零TXE 标志。
接收缓冲区非空(RXNE)
此标志置1 时,表示接收缓冲区中存在有效的已接收数据。
读取SPI_DR 时,将清零该标志。
BUSY
BSY标志由硬件置1和清零(对此标志执行写操作没有任何作用)。
BSY标志用于指示SPI通信的状态。
BSY 置1 时,表示SPI 正忙于通信。
在主模式下的双向通信接收模式(MSTR=1 且BDM=1 且BDOE=0)有一个例外情况,BSY 标志在接收过程中保持低电平。
如果软件要关闭SPI 并进入停止模式(或关闭外设时钟),可使用BSY 标志检测传输是否
结束以避免破坏最后一个数据的传输。
为此,必须严格遵循下述步骤。
BSY 标志还可用于避免在多主模式系统中发生写冲突。
传输开始时,BSY 标志将置1,但在主模式下的双向通信接收模式(MSTR=1 且BDM=1 且BDOE=0)下例外。
在以下情况硬件将清零该标志:
● 传输完成时(主模式下的连续通信除外)
● 关闭SPI 时
● 发生主模式故障时(MODF=1)
当通信不连续时,BSY 标志在各通信之间处于低电平。
当通信连续时:
● 在主模式下,BSY 标志在所有传输期间均保持高电平
● 在从模式下,BSY 标志在各传输之间的一个SPI 时钟周期内变为低电平
注意:请勿使用BSY标志处理每次数据发送或接收,最好改用TXE 标志和RXNE 标志。
SPI 中断
⏹常用寄存器
●SPI控制寄存器1(SPI_CR1)
SPI控制寄存器1 (SPI_CR1)(不用于I2S模式)SPI control register 1
偏移地址:0x00 复位值:0x0000
●SPI控制寄存器2(SPI_CR2)
●SPI状态寄存器(SPI_SR)
●SPI数据寄存器(SPI_DR)
●SPI_I2S配置寄存器(SPI_I2S_CFGR)
●SPI_I2S预分频寄存器(SPI_I2SPR)
◆SPI相关库函数:
stm32f4xx_spi.c/stm32f4xx_spi.h
void SPI_I2S_DeInit(SPI_TypeDef* SPIx);
void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);
void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);
void SPI_I2S_ITConfig(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT, FunctionalState NewState);
void SPI_I2S_DMACmd(SPI_TypeDef* SPIx, uint16_t SPI_I2S_DMAReq, FunctionalState NewState);
void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);
void SPI_DataSizeConfig(SPI_TypeDef* SPIx, uint16_t SPI_DataSize);
FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);
void SPI_I2S_ClearFlag(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);
ITStatus SPI_I2S_GetITStatus(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT);
void SPI_I2S_ClearITPendingBit(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT);
◆程序配置过程:
①使能SPIx和IO口时钟
RCC_AHBxPeriphClockCmd() / RCC_APBxPeriphClockCmd();
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); //使能SPI1时钟
②初始化IO口为复用功能
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
③设置引脚复用映射:
GPIO_PinAFConfig();
②初始化SPIx,设置SPIx工作模式
void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);
这一步全部是通过SPI1_CR1来设置,我们设置SPI1为主机模式,设置数据格式为8位,然后通过CPOL和CPHA位来设置SCK时钟极性及采样方式。
并设置SPI1的时钟频率(最大37.5Mhz),以及数据的格式(MSB在前还是LSB在前)。
SPI_InitTypeDef 的定义:
typedef struct
{
uint16_t SPI_Direction;
uint16_t SPI_Mode;
uint16_t SPI_DataSize;
uint16_t SPI_CPOL;
uint16_t SPI_CPHA;
uint16_t SPI_NSS;
uint16_t SPI_BaudRatePrescaler;
uint16_t SPI_FirstBit;
uint16_t SPI_CRCPolynomial;
}SPI_InitTypeDef;
第一个参数SPI_Direction:是用来设置SPI的通信方式,可以选择为半双工,全双工,以及串行发和串行收方式,这里我们选择全双工模式SPI_Direction_2Lines_FullDuplex。
第二个参数SPI_Mode:用来设置SPI的主从模式,这里我们设置为主机模式SPI_Mode_Master,当然有需要你也可以选择为从机模式SPI_Mode_Slave。
第三个参数SPI_DataSiz:为8位还是16位帧格式选择项,这里我们是8位传输,选择
SPI_DataSize_8b。
第四个参数SPI_CPOL:用来设置时钟极性,我们设置串行同步时钟的空闲状态为高电平所以我们选择SPI_CPOL_High。
第五个参数SPI_CPHA:用来设置时钟相位,也就是选择在串行同步时钟的第几个跳变沿(上升或下降)数据被采样,可以为第一个或者第二个条边沿采集,这里我们选择第二个跳变沿,所以选择SPI_CPHA_2Edge
第六个参数SPI_NSS设置NSS信号由硬件(NSS管脚)还是软件控制,这里我们通过软件控制NSS,而不是硬件自动控制,所以选择SPI_NSS_Soft。
第七个参数SPI_BaudRatePrescaler:很关键,就是设置SPI波特率预分频值也就是决定SPI的时钟的参数,从2分频到256分频8个可选值,初始化的时候我们选择256分频值
SPI_BaudRatePrescaler_256, 传输速度为84M/256=328.125KHz。
第八个参数SPI_FirstBit:设置数据传输顺序是MSB位在前还LSB位在前,这里我们选择
SPI_FirstBit_MSB高位在前。
第九个参数SPI_CRCPolynomial:是用来设置CRC校验多项式,提高通信可靠性,大于1即可。
SPI_InitTypeDef SPI_InitStructure;
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //双线双向全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主SPI
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // SPI 发送接收8 位帧结构
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//串行同步时钟的空闲状态为高电平
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//第二个跳变沿数据被采样
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS 信号由软件控制
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //预分频256
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从MSB 位开始
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC 值计算的多项式
SPI_Init(SPI2, &SPI_InitStructure); //根据指定的参数初始化外设SPIx 寄存器
③使能SPIx
void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);
④SPI传输数据
void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx)
查看SPI传输状态
SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE);
SPI 初始化函数的最后有一个启动传输,这句话最大的作用就是维持MOSI为高电平,而且这句话也不是必须的,可以去掉。