发布时间:2023-12-02 09:30
SPI是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。
SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省了空间,提供方便,主要应用在EEPROM,FLASH,实时时钟,AD转换器还有数字信号处理直接。
SPI总线包括4条逻辑线,定义如下:
MISO:Master input slave output 主机输入,从机输出(数据来自从机);
MOSI:Master output slave input 主机输出,从机输入(数据来自主机);
SCLK:Serial Clock 串行时钟信号,由主机产生发送给从机;SCLK也可以是SCK
SS:Slave Select 片选信号,由主机发送,以控制与哪个从机通信,通常是低电平有效信号。
①硬件为四根线。
②主机和从机都有一个串行移位寄存器,主机通过SPI的串行移位寄存器写入一个字节发送一次传输。
③串行移位寄存器通过MOSI信号线将字节发送给从机,从机将自己的串行移位寄存器的内容通过MISO返回给主机,这样两个移位寄存器的内容被交换。
④外设的读写操作同步完成,只进行写操作,则忽略读操作;主机只进行从机的读操作,则主机须发送一个空字节给从机引发传输。
SPI_CR寄存器的CPOL和CPHA位,能够组合成四种可能的时序关系,CPOL(时钟极性)位控制在没有数据传输时时钟的空闲状态电平,此位对主模式和从模式下的设备都有效。如果CPOL被清’0’,SCK引脚在空闲状态下保持低电平;如果CPOL被置‘1’,SCK引脚在空闲状态保持高电平。
如果CPHA位被清0,SCK时钟的第一个边沿(0为下降沿,1为上升沿)进行数据位采集,数据在第一个时钟边沿被锁存。
如果CPHA(时钟相位)位被置‘1’,SCK时钟的第二个边沿(0为下降沿,1为上升沿)进行数据位的采样,数据在第二个时钟边沿被锁存.
通过三个标志可以完全监控SPI总线的状态
此标志为1的时候,表示发送缓冲寄存器为空,可以写入下一个待发送数据进入缓冲器中,当写入SPI_DR(数据寄存器)时,TXE标志被清除。
此标志为1表明接收缓冲器中包含有效数据,读SPI数据寄存器可以清楚此标志。
BSY标志由硬件设置与清楚,此标志表明SPI通信层的状态。
可以设置SPI_CR1寄存器的SSM位来使能这种模式,在这种模式下NSS引脚可以用作它用,而内部NSS信号电平可以通过写SPI_CR1的SSI位来驱动。
第一种情况:NSS输出使能,当STM32工作为SPI模式的时,NSS输出已经通过SPI_CR2寄存器的SSOE位使能,这时NSS引脚被拉低,所有NSS引脚与这个主SPI的NSS引脚相连并配置为硬件NSS的SPI设备,将自动变成从的SPI设备。
第二种情况:NSS输出被关闭:允许操作于多主环境。
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; //NSS位
uint16_t SPI_BaudRatePrescaler; //波特率
uint16_t SPI_FirstBit; //选择数据传送方向高位开始还是低位开始
uint16_t SPI_CRCPolynomial; //CRC校验位
}SPI_InitTypeDef;
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);
void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);
void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);
FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);
#include "SPI.h"
#include "stm32f10x.h"
void SPI2_Init(void)//SPI2初始化 主模式
{
SPI_InitTypeDef SPI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable SPI1 and GPIOA clocks */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);
/* Configure SPI1 pins: NSS, SCK, MISO and MOSI */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;//SCK MOSI
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//PB13/14/15复用推挽输出
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15);//PB13/14/15上拉
/* SPI2 configuration */
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //SPI2设置为两线全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置SPI2为主模式
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信号由软件(使用SSI位)管理
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; //定义波特率预分频的值:波特率预分频值为8,分频后为9MHZ
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从MSB位开始 高位
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式
SPI_Init(SPI2, &SPI_InitStructure);
/* Enable SPI2 */
SPI_Cmd(SPI2, ENABLE); //使能SPI1外设
}
u8 SPI2_ReadWriteByte(u8 Data)
{
unsigned char t = 0;
while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET)
{
t++;
if(t>=200)
{
return 0;
}
}
SPI_I2S_SendData(SPI2, Data);
while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET)
{
t++;
if(t>=200)
{
return 0;
}
}
return SPI_I2S_ReceiveData(SPI2);
}
void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler)
{
assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));
SPI2->CR1 &= 0XFFC7; //&1111 1111 1100 0111 把D3~D4清零
SPI2->CR1 |= SPI_BaudRatePrescaler; //这里是把SPI_BaudRatePrescaler的值与SPI2->CR1中的值按2进制的方式相加
//如果SPI_BaudRatePrescaler的值为0x038,二进制数为00111000与1111 1111 1100 0111相或后结果是1111 1111 1111 1111->0XFFFF;
SPI_Cmd(SPI2, ENABLE);
}
#ifndef __spi_h__
#define __spi_h__
#include "stm32f10x.h"
void SPI2_Init(void) ;//SPI2初始化 主模式
u8 SPI2_ReadWriteByte(u8 dat);
void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler);
#endif
#include "spi.h"
#include "stm32f10x.h"
void SPI2_Init(void)//SPI2初始化 主模式
{
SPI_InitTypeDef SPI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable SPI1 and GPIOA clocks */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);
/* Configure SPI1 pins: NSS, SCK, MISO and MOSI */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;//SCK MOSI
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15);
/* SPI2 configuration */
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //SPI2设置为两线全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置SPI2为主模式
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信号由软件(使用SSI位)管理
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; //定义波特率预分频的值:波特率预分频值为8,分频后为9MHZ
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从MSB位开始 高位
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式
SPI_Init(SPI2, &SPI_InitStructure);
/* Enable SPI2 */
SPI_Cmd(SPI2, ENABLE); //使能SPI1外设
}
//SPIx 读写一个字节
//返回值:读取到的字节
//#define SPI_RW SPI1_ReadWriteByte //代替函数名,以便适用于后面函数
u8 SPI2_ReadWriteByte(u8 Data)
{
unsigned char t = 0;
while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET)
{
t++;
if(t>=200)
{
return 0;
}
}
SPI_I2S_SendData(SPI2, Data);
while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET)
{
t++;
if(t>=200)
{
return 0;
}
}
return SPI_I2S_ReceiveData(SPI2);
}
void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler)
{
assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));
SPI2->CR1 &= 0XFFC7; //&1111 1111 1100 0111 把D3~D4清零
SPI2->CR1 |= SPI_BaudRatePrescaler; //这里是把SPI_BaudRatePrescaler的值与SPI2->CR1中的值按2进制的方式相加
//如果SPI_BaudRatePrescaler的值为0x038,二进制数为00111000与1111 1111 1100 0111相或后结果是1111 1111 1111 1111->0XFFFF;
SPI_Cmd(SPI2, ENABLE);
}