1.SPI介绍
1.1 SPI****简介
SPI的全称是"Serial Peripheral Interface",意为串行外围接口,是Motorola首先在其MC68HCXX系列处理器上定义的。SPI接口主要应用在EEPROM、FLASH、实时时钟、AD转换器,还有数字信号处理器和数字信号解码器之间。SPI是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线。
SPI内部结构简易图:
MISO:master input slave output
MOSI:master output slave input
由主机产生sclck时钟

SPI 主要特点有: 可以同时发出和接收串行数据; 可以当作主机或从机工作; 提供频率可编程时钟; 发送结束中断标志; 写冲突保护; 总线竞争保护等。
SPI 总线有四种工作方式,通过时钟极性( CPOL)和时钟相位( CPHA)区分,这两者的组合,主要决定了在在时间的哪个位置采样数据。

STM32采用采用SPI的哪种工作方式,主要取决于外部的SPI设备,与外部设备保持一致就可以了。本试验中使用的flash, 使用CPHA =1且CPOL=1。
STM32F1的SPI 接口提供两个主要功能,支持 SPI 协议或 I2S 音频协议。(就是说,SPI的四个IO口,也可以配置成I2S的四个IO口!)
STM32F1的SPI时钟最高可以到 36MHz,支持 DMA功能。STM32F1的SPI内部结构图:

(1)标号1:SPI接口引脚
STM32F1芯片有多个 SPI 外设,它们的 SPI 通讯信号引出到不同的 GPIO 引脚上,使用时必须配置到这些指定的引脚。SPI1 是挂接在APB2总线上,而SPI2和SPI3挂接在APB1总线上。NSS是作片选功能,也可以用其余IO口代替。
(2)标号2:时钟控制逻辑
SCK 线的时钟信号,由波特率发生器根据" 控制寄存器 CR1"中的 BR[0:2]位控制,该位是对 fpclk 时钟的分频因子,对 fpclk 的分频结果就是 SCK 引脚的输出时钟频率,计算方法如下:

(3)标号3:数据控制逻辑
(4)标号4:整体逻辑控制
2.SPI****配置步骤
具体步骤如下:(SPI相关库函数在stm32f10x_spi.c和
stm32f10x_spi.h文件中)
(1)使能SPI及对应GPIO端口时钟并配置引脚的复用功能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE );
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
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);
(2)初始化SPI,包括数据帧长度、传输模式、MSB和LSB顺序等
void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);
typedef struct
{
uint16_t SPI_Direction; //设置SPI的单双向模式
uint16_t SPI_Mode; //设置 SPI 的主/从机端模式
uint16_t SPI_DataSize; //设置 SPI 的数据帧长度,可选 8/16 位
uint16_t SPI_CPOL; //设置时钟极性 CPOL,可选高/低电平
uint16_t SPI_CPHA; //设置时钟相位,可选奇/偶数边沿采样
uint16_t SPI_NSS; //设置 NSS 引脚由 SPI 硬件控制还是软件控制
uint16_t SPI_BaudRatePrescaler;//设置时钟分频因子
uint16_t SPI_FirstBit; //设置 MSB/LSB 顺序
uint16_t SPI_CRCPolynomial; //设置 CRC 校验的表达式
}SPI_InitTypeDef;
SPI_Direction:用于设置 SPI 的通信方向,可设置为双线全双工(SPI_Direction_2Lines_FullDuplex),双线只接收(SPI_Direction_2Lines_RxOnly),单线只接收(SPI_Direction_1Line_Rx)、单线只发送模式(SPI_Direction_1Line_Tx)。
SPI_Mode:用于设置 SPI 工作在主机模式(SPI_Mode_Master)或从机模式(SPI_Mode_Slave ),这两个模式的最大区别为 SPI 的 SCK 信号线的时序, SCK 的时序是由通讯中的主机产生的。若被配置为从机模式, STM32 的 SPI 外设将接受外来的 SCK 信号。
SPI_DataSize:用于设置SPI通信的数据帧长度,可以选择8位(SPI_DataSize_8b)或者 16 位(SPI_DataSize_16b)。
SPI_CPOL:用于设置时钟极性,可设置为高电平(SPI_CPOL_High)或低电平(SPI_CPOL_Low )。
SPI_CPHA:用于设置时钟相位,也就是选择在串行同步时钟的第几个跳变沿(上升或下降)数据被采样,可以为 SPI_CPHA_1Edge(在 SCK 的奇数边沿采集数据) 或SPI_CPHA_2Edge (在 SCK 的偶数边沿采集数据) 。
SPI_NSS:用于设置NSS引脚的使用模式,可以选择为硬件模式(SPI_NSS_Hard )与软件模式(SPI_NSS_Soft ),在硬件模式中的 NSS信号由 SPI 硬件自动产生,而软件模式则需要我们使用相应的 GPIO 端口来控制。
SPI_BaudRatePrescaler:用于设置波特率分频因子,分频后的时钟即为 SPI 的 SCK 信号线的时钟频率。可设置为 fpclk 的 2、 4、 6、 8、 16、 32、 64、 128、 256 分频。
SPI_FirstBit:用于设置数据传输顺序是 MSB 位在前还是 LSB 位在前。
SPI_CRCPolynomial:用于设置 CRC 校验多项式,提高通信可靠性。
本实验配置代码如下:
SPI_InitTypeDef SPI_InitStructure;
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(SPI1, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
(3)使能(开启)SPI
void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);
(4)SPI数据传输
void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);
(5)查看SPI传输状态
FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);
使用较多的是发送完成标志(SPI_I2S_FLAG_TXE)和接收完成标志(SPI_I2S_FLAG_RXNE)
SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE);