SPI是是一种高速的,全双工,同步的总线通信方式。STM32F1低中容量设备的SPI模块支持主从两种模式。
一、SPI协议介绍
1.硬件连接
SPI使用三条数据总线和一条片选线: MOSI、MISO、SCK、NSS(CS)
MOSI(SDO):主设备输出/从设备输入。用于将数据从主机输出到从机。
MISO(SDI):主设备输入/从设备输出。数据经此由从机至主机,主机接收数据。
NSS:片选信号线。由主机通过此线使能从机。在一主多从的通讯模式下,只能同时有一个从机被使能。
SPI器件间的连接很简单,如图,只要名字相同线相连即可,主也可以比较方便地反转。
2.通信时序
SPI传输模式的精髓在时钟极性(CPOL)和时钟相位(CPHA)
CPOL控制空闲状态下时钟总线SCK的电平:
CPOL=0(LOW);时钟线空闲为低电平
CPOL=1(HIGH);时钟线空闲为高
CPHA控制采样位置和信号跳变位置:
※SPI传输从高位(MSB)开始还是低位(LSB)开始可以由用户设置
二、STM32F1的SPI模块
1.在CubeMX中进行配置
数据位可选择8或16
CPHA可选择从第一个数据沿开始(CPHA=0)或从第二个数据沿开始(CPHA=1)
可选择高位先发送或是低位先发送
可选择是否使用NSS以及NSS功能(输入、输出用)。用户也可以用普通IO的中断输入/输出功能模拟NSS,这种方法相对更加灵活。
※ 按照个人开发经验,SPI一般配置为双线双向全双工情况比较多。
※STM32的 NSS引脚说明和工作极其复杂,建议开发者禁用硬件NSS,自行定义普通GPIO实现片选功能。(NSS脚禁用后可以进行GPIO配置当作普通IO控制实现CS功能)
2.相关寄存器
API:
1.初始化结构体LL_SPI_InitTypeDef
typedef struct
{
uint32_t TransferDirection;/*
数据线配置;通过调用LL_SPI_SetTransferDirection()实现;
@ref: LL_SPI_FULL_DUPLEX //全双工,双线双向
LL_SPI_SIMPLEX_RX //双线双向模式下禁止输出,仅能输入
LL_SPI_HALF_DUPLEX_RX //单线,仅能接收
LL_SPI_HALF_DUPLEX_TX //单线,仅能发送
※单线模式下,工作于Master时使用MOSI脚;Slave时为MISO脚
*/
uint32_t Mode;/*
设置主从模式,通过LL_SPI_SetMode()实现;
@ref: LL_SPI_MODE_MASTER //主模式,配置时若NSS由软件管理会将电平置高
LL_SPI_MODE_SLAVE
*/
uint32_t DataWidth;/*
设置数据长度;通过LL_SPI_SetDataWidth()实现;
@ref: LL_SPI_DATAWIDTH_8BIT //8位
LL_SPI_DATAWIDTH_16BIT //16位
*/
uint32_t ClockPolarity;/*
设置时钟极性(CPOL),通过LL_SPI_SetClockPolarity()实现
@ref: LL_SPI_POLARITY_LOW //低电平(CPOL=0)
LL_SPI_POLARITY_HIGH //高电平(CPOL=1)
*/
uint32_t ClockPhase;/*
设置时钟相位,通过LL_SPI_SetClockPhase()实现
@ref: LL_SPI_PHASE_1EDGE //CPHA =0
LL_SPI_PHASE_2EDGE //CPHA=1
*/
uint32_t NSS;/*
配置NSS(CS),通过LL_SPI_SetNSSMode()实现;
@ref: LL_SPI_NSS_SOFT //通过软件管理NSS;※此时NSS引脚无法进行I/O操作控制
//CubeMx配置为Disable时配置为此模式(相当于禁用了NSS)
//此时可以通过操作SPI_CR1- >SSI位控制该位电平;LL库未提供函数;
LL_SPI_NSS_HARD_INPUT //说不清除,手册和库函数说明冲突,建议不用
LL_SPI_NSS_HARD_OUTPUT//同样,不建议配置
//鉴于片选复杂性,推荐开发者直接通过GPIO直接模拟NSS(CS)功能,可用原NSS
*/
uint32_t BaudRate;/*
配置波特率分频,通过LL_SPI_SetBaudRatePrescaler()实现;
@ref: LL_SPI_BAUDRATEPRESCALER_DIVx //x为2^n,max=128
*/
uint32_t BitOrder;/*
配置发送位顺序,通过LL_SPI_SetTransferBitOrder()实现;
@ref: LL_SPI_LSB_FIRST //低位先
LL_SPI_MSB_FIRST //高位先
*/
uint32_t CRCCalculation;/*!< Specifies if the CRC calculation is enabled or not.
This parameter can be a value of @ref SPI_LL_EC_CRC_CALCULATION.
This feature can be modified afterwards using unitary functions @ref LL_SPI_EnableCRC() and @ref LL_SPI_DisableCRC().*/
uint32_t CRCPoly;/*!< Specifies the polynomial used for the CRC calculation.
This parameter must be a number between Min_Data = 0x00 and Max_Data = 0xFFFF.
This feature can be modified afterwards using unitary function @ref LL_SPI_SetCRCPolynomial().*/
} LL_SPI_InitTypeDef;
2.初始化函数
ErrorStatus LL_SPI_Init(SPI_TypeDef *SPIx, LL_SPI_InitTypeDef *SPI_InitStruct);/*
初始化SPI;
*/
void LL_SPI_StructInit(LL_SPI_InitTypeDef *SPI_InitStruct)/*
初始化SPI配置结构体
*/
ErrorStatus LL_SPI_DeInit(SPI_TypeDef *SPIx)/*
初始化SPI模块
*/
3.开启/关闭模块
__STATIC_INLINE void LL_SPI_Enable(SPI_TypeDef *SPIx);/*
开启SPI模块
*/
__STATIC_INLINE void LL_SPI_Disable(SPI_TypeDef *SPIx);/*
关闭SPI模块
*/
__STATIC_INLINE uint32_t LL_SPI_IsEnabled(SPI_TypeDef *SPIx);/*
检测开启状态
*/
※与UART不同,目前版本CubeMX自动生成代码不会开启SPI,需用户手动开启
关闭SPI需要在传输完成后
4.标志位/状态位
MODF:主模式失效错误标志。在NSS引脚硬件模式管理下,主设备的NSS脚被拉低时;或者在NSS引脚软件模式管理下,SSI位被置0时被置位。同时SPI模块被关闭。 在使用LL库时若不使用NSS功能,则不会出现置位情况
※RXNE:接收缓冲非空。与USART类似,当※接收数据寄存器完全完成一次数据接收时,该位被置位。※对读取数据寄存器RDR的读取操作可以硬件清零该位。
※TXE:发送缓冲空。当发送数据寄存器数据被送出时,该位被置位。对发送数据寄存器TDR的写入操作可以硬件清零该位。
BSY:忙标志。SPI在通讯时该位为1。该位完全由硬件控制。在主模式的双向接收模式下 (MSTR=1、BDM=1并且BDOE=0),在接收期间BSY标志保持为低。不要使用BSY标志处理每一个数据项的发送和接收,最好使用TXE和RXNE标志。
OVR:溢出错误。接收数据时,当发送端设备已经发送了数据字节,而STM32还没有清除前一个数据字节产生的RXNE时,即为溢出错误。
当溢出时,读SPI_DR寄存器返回的是之前未读的数据,所有随后传送的数据都被丢弃。
※与USART不同,SPI模块TXE与RXNE位是只读的,其值由硬件管理。
__STATIC_INLINE uint32_t LL_SPI_IsActiveFlag_BSY(SPI_TypeDef *SPIx);/*
检测BSY是否置位,该位无法软件控制
*/
__STATIC_INLINE uint32_t LL_SPI_IsActiveFlag_OVR(SPI_TypeDef *SPIx);/*
检测OVR是否置位(发生过载错误)
*/
__STATIC_INLINE void LL_SPI_ClearFlag_OVR(SPI_TypeDef *SPIx);/*
置位OVR
*/
__STATIC_INLINE uint32_t LL_SPI_IsActiveFlag_TXE(SPI_TypeDef *SPIx);/*
检测TXE是否置位
*/
__STATIC_INLINE uint32_t LL_SPI_IsActiveFlag_RXNE(SPI_TypeDef *SPIx);/*
检测RXNE是否置位
*/
__STATIC_INLINE uint32_t LL_SPI_IsActiveFlag_MODF(SPI_TypeDef *SPIx);
__STATIC_INLINE void LL_SPI_ClearFlag_MODF(SPI_TypeDef *SPIx);
5.中断控制
__STATIC_INLINE void LL_SPI_EnableIT_ERR(SPI_TypeDef *SPIx);/*
使能ERR错误中断*/
__STATIC_INLINE void LL_SPI_DisableIT_ERR(SPI_TypeDef *SPIx);/*
禁用ERR错误中断*/
__STATIC_INLINE uint32_t LL_SPI_IsEnabledIT_ERR(SPI_TypeDef *SPIx);/*
检测是否开启ERR中断*/
__STATIC_INLINE void LL_SPI_EnableIT_RXNE(SPI_TypeDef *SPIx);/*
使能RXNE接收缓冲非空中断*/
__STATIC_INLINE void LL_SPI_DisableIT_RXNE(SPI_TypeDef *SPIx);/*
禁用RXNE接收缓冲非空中断*/
__STATIC_INLINE uint32_t LL_SPI_IsEnabledIT_RXNE(SPI_TypeDef *SPIx)/*
检测是否开启RXNE接收缓冲非空中断*/
__STATIC_INLINE void LL_SPI_EnableIT_TXE(SPI_TypeDef *SPIx);/*
使能TXE发送缓冲空中断*/
__STATIC_INLINE void LL_SPI_DisableIT_TXE(SPI_TypeDef *SPIx);/*
禁用TXE发送缓冲空中断*/
__STATIC_INLINE uint32_t LL_SPI_IsEnabledIT_TXE(SPI_TypeDef *SPIx)/*
检测是否开启TXE发送缓冲空中断*/
6.SPI 收/发函数
__STATIC_INLINE uint8_t LL_SPI_ReceiveData8(SPI_TypeDef *SPIx);/*
从接收寄存器(缓冲区)DR中读取8位数据;
*/
__STATIC_INLINE uint16_t LL_SPI_ReceiveData16(SPI_TypeDef *SPIx);/*
从接收寄存器(缓冲区)DR中读取16位数据;
*/
__STATIC_INLINE void LL_SPI_TransmitData8(SPI_TypeDef *SPIx, uint8_t TxData);/*
向发送寄存器(缓冲区)DR中写入8位数据
*/
__STATIC_INLINE void LL_SPI_TransmitData16(SPI_TypeDef *SPIx, uint16_t TxData);/*
向发送寄存器(缓冲区)DR中写入16位数据
*/
SPI模块DMA的使用
相关函数:
待实验
__STATIC_INLINE void LL_USART_EnableDMAReq_RX(USART_TypeDef *SPIx);/*
使能接收DMA,启用后DR有数据时将允许发送DMA请求;具体见示例用法*/
__STATIC_INLINE void LL_USART_DisableDMAReq_RX(USART_TypeDef *SPIx);/*
禁用接收DMA*/
__STATIC_INLINE uint32_t LL_USART_IsEnabledDMAReq_RX(USART_TypeDef *SPIx);/*
检测是否使能接收DMA*/
__STATIC_INLINE void LL_USART_EnableDMAReq_TX(USART_TypeDef *SPIx);/*
使能发送DMA*/
__STATIC_INLINE void LL_USART_DisableDMAReq_TX(USART_TypeDef *SPIx);/*
禁用发送DMA*/
__STATIC_INLINE uint32_t LL_USART_IsEnabledDMAReq_TX(USART_TypeDef *SPIx);/*
检测是否使能发送DMA*/
/**************************************************/
__STATIC_INLINE uint32_t LL_USART_DMA_GetRegAddr(USART_TypeDef *SPIx);/*
返回SPI模块数据寄存器DR地址;无论是否启用DMA均可用
*/
发送时,在每次TXE被设置为’1’时发出DMA请求,此时软件控制DMA写数据至SPI_DR寄存器,TXE标志因此而被清除。
接收时,在每次RXNE被设置为’1’时发出DMA请求,在开启情况下DMA控制器从SPI_DR寄存器读出数据,RXNE标志因此而被清除。
LL的DMA使用与UART相似,可以参考之前的文章。
SPI在双向全双工传输数据的时候,每发出一字节数据的同时也会接收一字节数据,因此在作为主机接收的时候,应当考虑 如何处理接收到的无用数据。否则会出现OVR。
另外,由于在双向模式下配置为主机时,只有当SPI在写数据时时钟信号才能产生。处于master工作模式下,SPI的时钟只有在往DR寄存器里面写数据的时候才会产生,读是不会产生的。所以要读取slave shift out的数据,master必须先发一个“DUMMY”数据以产生时钟。
建议配置STM32为双向主机、从机; 配置为主机接收前读取一次DR,再发送DUMMY(建议发0x00或0xFF,不要增加没必要的干扰)
-
数据寄存器
+关注
关注
0文章
33浏览量
7734 -
SPI接口
+关注
关注
0文章
258浏览量
34309 -
CPHA
+关注
关注
0文章
8浏览量
9365 -
USART串口
+关注
关注
0文章
32浏览量
6791 -
stm32f1
+关注
关注
1文章
56浏览量
12157
发布评论请先 登录
相关推荐
评论