STM32---SPI

SPI
1.SPI介绍
SPI主要应用在EEPROM、FLASH、实时时钟、AD转换器、数字信号处理器、数字信号解码器。

4条信号线

**MISO:**主设备输入/从设备输出引脚。主机从这条信号线读入数据,从机的数据由这条信号线输出到主机,即在这条线上数据的方向为从机到主机。

**MOSI:**主设备输出/从设备输入引脚。主机的数据从这条信号线输出,从机由这条信号线读入主机发送的数据。即这条线上数据的方向为主机到从机。

**SCLK:**时钟信号线,用于通信数据同步。由主机产生,决定了通信的速率。STM32的SPI时钟频率最大为fpclk/2,通讯速率受限于低速设备。

**CS:**从设备选择信号线,也称片选信号线。有多少从设备主机上就有多少CS线,主机通过拉低CS信号线,对从设备进行选择。

2.SPI原理分析
image-20220406111809152

2.1 SPI接口引脚
通常使用SPI总线就是SCK、MISO、MOSI,片选线可使用任意GPIO口。

SPI1 、 SPI4 、SPI5 、SPI6是挂接在APB2总线上。

SPI2、 SPI3挂接在APB1总线上。

2.2时钟控制逻辑
SCK线的时钟信号,由波特率发生器根据“控制寄存器CR1”中的BR[0:2]位控制,该位是对fpclk时钟的分频因子。

BR[2:0] 波特率 BR[2:0] 波特率
000 fpclk/2 100 fpclk/32
001 fpclk/4 101 fpclk/64
010 fpclk/8 110 fpclk/128
011 fpclk/16 111 fpclk/256
2.3数据控制逻辑
SPI的MOSI及MISO都连接到数据移位寄存器上,数据移位寄存器的内容来源于接收缓冲区以及MISO、MOSI。当向外发送数据的时候,数据移位寄存器以“发送缓冲区”为数据源,把数据一位一位地通过数据线发送出去。当从外部接收数据的时候,数据移位寄存器把数据线采样到的数据一位一位地存储到“接收缓冲区”中。通过写SPI的“数据寄存器DR”把数据填充到发送缓冲区中。其中数据帧长度可以通过“控制寄存器CR1”的“DFF位”配置成8位及16位模式。配置“LSBFIRST位”可选择MSB先行还是LSB先行。

2.4整体逻辑控制
整体控制逻辑负责协调整个SPI外设,控制逻辑的工作模式根据我们配置的“控制寄存器(CR1/CR2)“的参数改变。

基本控制参数包括:SPI模式、波特率、LSB先行、主从模式、单双向模式。

另外,控制逻辑还负责控制SPI中断信号、DMA请求及控制NSS信号线。

3.SPI相关寄存器
3.1 GPIO结构体
typedef struct
{
uint32_t GPIO_Pin;//GPIO引脚
GPIOMode_TypeDef GPIO_Mode; //GPIO模式,输入输出
GPIOSpeed_TypeDef GPIO_Speed; //GPIO速率
GPIOOType_TypeDef GPIO_OType; //设置输出模式:开漏、推挽
GPIOPuPd_TypeDef GPIO_PuPd;//设置上下拉模式
}GPIO_InitTypeDef;
3.2 SPI结构体
typedef struct
{
uint16_t SPI_Direction;//用于设置SPI的通信方向:双线全双工、双线只接收、单线只接收、单线只发送
uint16_t SPI_Mode;//设置SPI的主从机模式:设置为主机模式时SCK信号由主机发出,从机只接收SCK模式
uint16_t SPI_DataSize;//SPI通信的数据帧长度:可选8/16位
uint16_t SPI_CPOL;//设置空闲状态时时钟极性:可设置为高电平或低电平
uint16_t SPI_CPHA;//设置时钟相位:选择在串行同步时钟的第几个跳变沿(上升或下降)数据被采样,可选奇数边沿采集数据或偶数边沿采集数据
uint16_t SPI_NSS;//用于设置NSS引脚的使用模式:可选硬件模式或软件模式,硬件模式有SPI硬件自动产生,软件模式则使用相应的GPIO口
uint16_t SPI_BaudRatePrescaler;//设置波特率分频因子,分频后的时钟即为SPI的SCK信号线的时钟频率。可设置fpclk的2/4/6/8/16/32/64/128/256分频。
uint16_t SPI_FirstBit;//用于设置数据传输顺序时MSB位(最高有效位、大端模式)在前还是LSB位(最低有效位、小端模式)在前
uint16_t SPI_CRCPolynomial;//用于设置CRC校验多项式
}SPI_InitTypeDef;
uint16_t SPI_Direction;//用于设置SPI的通信方向:双线全双工、双线只接收、单线只接收、单线只发送
#define SPI_Direction_2Lines_FullDuplex ((uint16_t)0x0000)//双线全双工
#define SPI_Direction_2Lines_RxOnly ((uint16_t)0x0400)//双线只接收
#define SPI_Direction_1Line_Rx ((uint16_t)0x8000)//单线只接收
#define SPI_Direction_1Line_Tx ((uint16_t)0xC000)//单线只发送
uint16_t SPI_Mode;//设置SPI的主从机模式:设置为主机模式时SCK信号由主机发出,从机只接收SCK模式
#define SPI_Mode_Master ((uint16_t)0x0104)//主机模式
#define SPI_Mode_Slave ((uint16_t)0x0000)//从机模式
uint16_t SPI_DataSize;//SPI通信的数据帧长度:可选8/16位
#define SPI_DataSize_16b ((uint16_t)0x0800)//16位
#define SPI_DataSize_8b ((uint16_t)0x0000)//8位
4.SPI配置方法
4.1 SPI初始化分步配置
4.1.1 使能SPI时钟配置引脚复用功能
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);//使能SPI1时钟

GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1); //PB3复用为 SPI1
GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1); //PB4复用为 SPI1
GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1); //PB5复用为 SPI1
GPIO_InitTypeDef  GPIO_InitStructure;

//GPIOFB3,4,5初始化设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;//PB3~5复用功能输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
4.1.2初始化SPI结构体
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寄存器
4.1.3使能SPI
SPI_Cmd(SPI1, ENABLE); //使能SPI外设
4.2 SPI初始化整体
//以下是SPI模块的初始化代码,配置成主机模式
//SPI口初始化
//这里针是对SPI1的初始化
void SPI1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);//使能SPI1时钟

GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1); //PB3复用为 SPI1
GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1); //PB4复用为 SPI1
GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1); //PB5复用为 SPI1

//GPIOFB3,4,5初始化设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;//PB3~5复用功能输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化

//这里只针对SPI口初始化
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,ENABLE);//复位SPI1
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,DISABLE);//停止复位SPI1

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寄存器

SPI_Cmd(SPI1, ENABLE); //使能SPI外设

SPI1_ReadWriteByte(0xff);//启动传输 

}
4.3 SPI速度设置函数
//SPI1速度设置函数
//SPI速度=fAPB2/分频系数
//@ref SPI_BaudRate_Prescaler:SPI_BaudRatePrescaler_2~SPI_BaudRatePrescaler_256
//fAPB2时钟一般为84Mhz:
void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler)
{
SPI1->CR1&=0XFFC7;//位3-5清零,用来设置波特率
SPI1->CR1|=SPI_BaudRatePrescaler; //设置SPI1速度
SPI_Cmd(SPI1,ENABLE); //使能SPI1
}
4.4 SPI读写字节
//SPI1 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI1_ReadWriteByte(u8 TxData)
{

while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);//等待发送区空
SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个byte 数据
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); //等待接收完一个byte
return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据

}

讲解不到位的希望大家指出,有需要我讲解的部分,希望大家提出,我会出文档讲解。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值