目录
[3.1 CPOL(Clock Polarity,时钟极性):](#3.1 CPOL(Clock Polarity,时钟极性):)
[3.2 CPHA(Clock Phase,时钟相位):](#3.2 CPHA(Clock Phase,时钟相位):)
[3.3 四种工作模式](#3.3 四种工作模式)
1.SPI简介
STM32 的**SPI(Serial Peripheral Interface)**是一个串行外设接口,它允许STM32微控制器与其他设备(如传感器、存储器等)进行高速、全双工、同步的串行通信。通常包含SCK(串行时钟)、MOSI(主设备输出/从设备输入)、MISO(主设备输入/从设备输出)和NSS(片选信号)这4条线,支持多个从设备连接到一个主设备上。
2.SPI工作原理
MISO 主设备数据输入,从设备数据输出。
MOSI 主设备数据输出,从设备数据输入。
SCLK 时钟信号,由主设备产生。
CS 从设备片选信号,由主设备控制。
主机和从机都存在一个移位寄存器 ,主机将要传输的数据保存到移位寄存器上,然后通过MOSI发送到从机的移位寄存器上,从机同时也将自己移位寄存器保存的数据通过MISO发送到主机的移位寄存器中,这样就完成了一次主机和从机的数据交换**,如果是进行写操作,主机只要无视从机返回的数据就可以了,如果是进行读操作,那么主机就要发送对应字节的空字节来推动从机的数据传输**。
3.SPI时序
SPI通过CS片选设备以后开启数据传输,数据传输根据主机CPHA(Clock Phase,时钟相位)和CPOL(Clock Polarity,时钟极性)配置的不同衍生出了不同的工作模式。
3.1 CPOL(Clock Polarity,时钟极性):
CPOL决定了SPI通信中空闲状态的时钟电平。
当CPOL=0时 ,空闲状态下SCK(Serial Clock,串行时钟)为低电平。
当CPOL=1时,空闲状态下SCK为高电平。
3.2 CPHA(Clock Phase,时钟相位):
CPHA决定了数据是在SCK的第一个边沿还是第二个边沿被采样。
当CPHA=0时 ,数据在SCK的第一个边沿被采样(对于MOSI是上升沿,对于MISO可能是下降沿,取决于具体实现)。
当CPHA=1时,数据在SCK的第二个边沿被采样(对于MOSI是下降沿,对于MISO可能是上升沿)。
CPHA的选择影响数据传输的时序和稳定性。
3.3 四种工作模式
通过CPOL和CPHA的不同值组合,可以实现SPI的四种模式:
模式1 (CPOL=0, CPHA=0):空闲时SCK为低电平,数据在SCK的第一个上升沿被主设备发送,并在第一个下降沿被从设备接收。
模式2 (CPOL=0, CPHA=1):空闲时SCK为低电平,数据在SCK的第二个上升沿被采样。
模式3 (CPOL=1, CPHA=0):空闲时SCK为高电平,数据在SCK的第一个下降沿被主设备发送,并在第一个上升沿被从设备接收。
模式4(CPOL=1, CPHA=1):空闲时SCK为高电平,数据在SCK的第二个下降沿被采样。
4.相关代码
本文基于STM32F429的HAL库为例讲解一下相关代码。
4.1使能片选信号
cpp
#define W25QXX_CS PFout(6) //W25QXX的片选信号
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOF_CLK_ENABLE(); //使能GPIOF时钟
//PF6
GPIO_Initure.Pin=GPIO_PIN_6; //PF6
GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FAST; //快速
HAL_GPIO_Init(GPIOF,&GPIO_Initure); //初始化
W25QXX_CS=1; //SPI 不选中
使能GPIO_PF6为SPI的**片选信号,**默认拉高不选中。
4.2使能通讯线
cpp
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOF_CLK_ENABLE(); //使能GPIOF时钟
__HAL_RCC_SPI5_CLK_ENABLE(); //使能SPI5时钟
//PF7,8,9
GPIO_Initure.Pin=GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9;
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FAST; //快速
GPIO_Initure.Alternate=GPIO_AF5_SPI5; //复用为SPI5
HAL_GPIO_Init(GPIOF,&GPIO_Initure);
使能SPI5,GPIO_F7/F8/F9推挽输出,默认上拉,快速,复用为SPI5。
4.3初始化SPI
cpp
void SPI5_Init(void)
{
SPI5_Handler.Instance=SPI5; //SP5
SPI5_Handler.Init.Mode=SPI_MODE_MASTER; //设置SPI工作模式,设置为主模式
SPI5_Handler.Init.Direction=SPI_DIRECTION_2LINES; //设置SPI单向或者双向的数据模式:SPI设置为双线模式
SPI5_Handler.Init.DataSize=SPI_DATASIZE_8BIT; //设置SPI的数据大小:SPI发送接收8位帧结构
SPI5_Handler.Init.CLKPolarity=SPI_POLARITY_HIGH; //串行同步时钟的空闲状态为高电平
SPI5_Handler.Init.CLKPhase=SPI_PHASE_2EDGE; //串行同步时钟的第二个跳变沿(上升或下降)数据被采样
SPI5_Handler.Init.NSS=SPI_NSS_SOFT; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
SPI5_Handler.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_256;//定义波特率预分频的值:波特率预分频值为256
SPI5_Handler.Init.FirstBit=SPI_FIRSTBIT_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
SPI5_Handler.Init.TIMode=SPI_TIMODE_DISABLE; //关闭TI模式
SPI5_Handler.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE;//关闭硬件CRC校验
SPI5_Handler.Init.CRCPolynomial=7; //CRC值计算的多项式
HAL_SPI_Init(&SPI5_Handler);//初始化
__HAL_SPI_ENABLE(&SPI5_Handler); //使能SPI5
SPI5_ReadWriteByte(0Xff); //启动传输
}
设置SPI为主机工作模式,双线模式,空闲状态为高电平,数据为8位帧结构,二个跳变沿(上升或下降)数据被采样,片选信号由软件控制,数据传输从MSB位开始,关闭硬件CRC校验,最后启动传输。
我们把 SPI5 的频率设置成了最低的256 分频,后面我们可以随时通过函数SPI5_SetSpeed 来设置 SPI5 的速度。
4.4设置SPI速度
cpp
void SPI5_SetSpeed(u8 SPI_BaudRatePrescaler)
{
assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性
__HAL_SPI_DISABLE(&SPI5_Handler); //关闭SPI
SPI5_Handler.Instance->CR1&=0XFFC7; //位3-5清零,用来设置波特率
SPI5_Handler.Instance->CR1|=SPI_BaudRatePrescaler;//设置SPI速度
__HAL_SPI_ENABLE(&SPI5_Handler); //使能SPI
}
SPI_BaudRatePrescaler可以设置 2/4/8/16/32/64/128/256分频,例如STM32F429的APB1为90MHz,设置2分频就是45MHz。
4.5读写数据
cpp
void read(){
W25QXX_CS=0;//拉低片选信号
SPI5_ReadWriteByte(0XFF);
W25QXX_CS=1; //拉高片选信号
}
u8 SPI5_ReadWriteByte(u8 TxData)
{
u8 Rxdata;
HAL_SPI_TransmitReceive(&SPI5_Handler,&TxData,&Rxdata,1, 1000);
return Rxdata; //返回收到的数据
}
读写数据都是用SPI5_ReadWriteByte这个函数,不同的是,如果是写入数据,不用管接收到的从机数据是什么,如果是读取数据,则可以发送0XFF,读取从机返回的对应数据。