[STM32F4] STM32F4的SPI

[复制链接]
 楼主| SHOPQQ 发表于 2023-8-23 11:13 | 显示全部楼层 |阅读模式
SPI协议简介

SPI协议是由摩托罗拉公司提出的通讯协议,即串行外围设备接口。是一种高速全双工的通信总线。

SPI物理层

SPI协议使用3条总线以及片选线。3条总线分别是SCK、MOSI、MISO,片选线为SS(NSS、CS)

1203064e5792fb6ac8.png


SS:从设备选择信号线,常称为片选信号线。还有NSS、CS标记。当多个SPI从设备与SPI主机相连时,其他信号SCK、MOSI、MISO都是并联到相同的SPI总线上的,所有的从设备都在使用这三条总线。但是每一条设备都有一个独立的SS信号线,该信号线独占主机的一个引脚,由多少个从设备,就有多少条片选信号线。当主机要选择从设备时,就将该设备的SS信号线拉低,表示选中该设备。SPI通讯以SS置低为开始信号,拉高作为结束信号

SCK:时钟信号线、

MOSI:主设备输出/从设备输入引脚

MISO:主设备输入/从设备输出引脚



评论

———————————————— 版权声明:本文为CSDN博主「晨阳雾思」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/wsqgoforit/article/details/128085806  发表于 2023-8-23 11:16
 楼主| SHOPQQ 发表于 2023-8-23 11:13 | 显示全部楼层
SPI协议层
SPI基本通讯过程
9105364e57950f30c8.png
NSS、SCK、MOSI信号都由主机控制产生,MISO的信号由从机产生,主机通过该信号线读取从机的数据。MOSI和MISO的信号只在NSS为低电平的时候才有效,在SCK的每个时钟周期MOSI和MISO传输一位数据。
 楼主| SHOPQQ 发表于 2023-8-23 11:15 | 显示全部楼层
通讯的起始和停止信号
NSS信号线由高变低,是SPI通讯的起始信号。当从机检测到起始信号后,就知道自己被主机选中,开始准备与主机通信。
NSS信号线由低变高,是SPI通讯的停止信号。表示本次通讯结束,从机的选中状态被取消。
 楼主| SHOPQQ 发表于 2023-8-23 11:16 | 显示全部楼层
数据有效性
SPI使用MOSI和MISO信号线来传输数据,使用SCK信号线进行数据传输。
MOSI和MISO数据线在SCK的每个时钟周期传输一位数据,且数据输入输出是同步进行。
MOSI和MISO的数据在SCK的上升沿期间变化输出,在SCK的下降沿被采样。
SPI每次可以传输8位或16位为单位,不受限制。
MSB先行或LSB先行没有做硬性规定,但要保证两个SPI设备之间使用同样协定。一般采用MSB先行模式。
 楼主| SHOPQQ 发表于 2023-8-23 11:16 | 显示全部楼层
CPOL/CPHA以及通讯模式
时钟极性CPOL是指SPI通讯设备处于空闲状态时,SCK信号线的电平信号(SPI通讯开始前,NSS线为高电平时SCK的状态)。CPOL=0时,SCK在空闲状态为低电平,CPOL=1则相反。
 楼主| SHOPQQ 发表于 2023-8-23 11:16 | 显示全部楼层
时钟相位CPHA是指数据的采样时刻,当CPHA=0,MOSI或MISO数据线上的信号将会在SCK时钟线的“奇数边沿”被采样,当CPHA=1时,数据线信号将会在SCK时钟线的“偶数边沿”被采样
 楼主| SHOPQQ 发表于 2023-8-23 11:16 | 显示全部楼层
 楼主| SHOPQQ 发表于 2023-8-23 11:17 | 显示全部楼层
STM32的SPI特性以及架构
SPI外设简介
STM32的SPI外设可用作主机和从机,支持最高的SCK时钟频率f_pclk/2,完全支持SPI协议的四种模式,数据帧长度可设置为8位或16位,可设置MSB或LSB先行。
可以支持双线全双工、双向单线以及单线模式。其中双线单向模式可以同时使用MOSI和MISO数据线向一个方向传输数据。
 楼主| SHOPQQ 发表于 2023-8-23 11:25 | 显示全部楼层
SPI架构剖析
6039164e57c26bf214.png
 楼主| SHOPQQ 发表于 2023-8-23 11:25 | 显示全部楼层
通讯引脚
STM32芯片有多个SPI外设,它们SPI通讯信号引到不同的GPIO引脚,使用时必须配置到这些指定的引脚。
 楼主| SHOPQQ 发表于 2023-8-23 11:26 | 显示全部楼层
时钟控制逻辑
通过控制寄存器CR的BR[0:2]位控制,可以实现2、4、8、16、32、64、128、256分频。
 楼主| SHOPQQ 发表于 2023-8-23 11:26 | 显示全部楼层
数据控制逻辑
SPI的MOSI以及MISO都连接到数据移位寄存器上,数据移位寄存器的内容来源于接收缓冲区、发送缓冲区以及MOSI、MISO线。
当向外发送数据的时候,数据移位寄存器以“发送缓冲区”为数据源,把数据一位一位通过数据线发送出去。
当从外部接收数据时,数据移位寄存器把数据线采样到的数据一位一位存储到“接收缓冲区”中。
通过写SPI的数据寄存器DR把数据填充到发送缓冲区中。通过读数据寄存器DR可以获取接收缓冲区中的内容。
数据帧长度->CR1的DFF位配置为8位或16位模式
MSB或LSB先行->LSBFIRST位
 楼主| SHOPQQ 发表于 2023-8-23 11:27 | 显示全部楼层
整体控制逻辑
控制逻辑负责协调整个SPI外设,控制逻辑的工作模式根据我们配置的”控制寄存器CR1/CR2“的参数而改变,基本的控制参数包括SPI模式、波特率、LSB先行、主从模式等
在SPI外设工作时,控制逻辑会根据外设的工作状态修改”状态寄存器SR“。我们可以通过读取状态寄存器的相关位来了解SPI的工作状态。
控制逻辑还根据要求,负责产生SPI中断信号、DMA请求以及控制NSS信号线
一般实际应用过程中,我们一般不使用SPI外设标准的NSS信号线,而是简单的使用普通的GPIO
 楼主| SHOPQQ 发表于 2023-8-23 11:27 | 显示全部楼层
通讯过程
主模式收发流程如下
1、控制NSS信号线,产生起始信号
2、把要发送的数据写入”数据寄存器DR“中,该数据会被存储到发送缓冲区
3、通讯开始,SCK时钟运转。MOSI把发送缓冲区里面的数据一位一位的传输出去,MISO把数据一位一位存储进接收缓冲区
4、发送完一帧数据时,“状态寄存去SR”的“TXE标志位”会被置1,表明传输完一帧,发送缓冲区已空。
接收完一帧数据时,“状态寄存去SR”的“RXNE标志位”会被置1,表明传输完一帧,接收缓冲区非空。
5、等待到“TXE标志位”为1时,若还要继续发送数据,则再次往“数据寄存器DR”写入数据即可
等待到“RXNE标志位”为1时,通过读取“数据寄存器”可以获取接收缓冲区中的内容
假如我们使能了TXE或RXNE中断,TXE或RXNE置1时会产生SPI中断信号,进入到同一个中断服务函数中,我们可以在该中断程序中检查寄存器位来了解是哪一个事件,在分别处理。
可以使用DMA方式来收发”数据寄存器DR“中的数据。
 楼主| SHOPQQ 发表于 2023-8-23 11:28 | 显示全部楼层
SPI结构体以及具体实现代码详解
SPI初始化结构体
  1. typedef struct
  2. {
  3.   uint16_t SPI_Direction;  //设置SPI的单双向模式
  4.   uint16_t SPI_Mode;     //设置SPI的主从模式
  5.   uint16_t SPI_DataSize;   //设置数据帧长度 8/16位
  6.   uint16_t SPI_CPOL;     //设置时钟极性CPOL
  7.   uint16_t SPI_CPHA;     //设置时钟相位CPHA       
  8.   uint16_t SPI_NSS;      //设置NSS引脚是由硬件/软件控制
  9.   uint16_t SPI_BaudRatePrescaler;   //设置时钟分频因子
  10.   uint16_t SPI_FirstBit;           //设置MSB/LSB先行
  11.   uint16_t SPI_CRCPolynomial;    //设置CRC校验的表达式
  12. }SPI_InitTypeDef;
 楼主| SHOPQQ 发表于 2023-8-23 11:28 | 显示全部楼层
SPI配置大体逻辑
  1. void SPI_Init(void){
  2.     配置SPI的GPIO初始化;
  3.         SPI_InitStructure SPI_InitStructure;
  4.     配置SPI结构体里面的各个参数;
  5.     SPI_Init(xx,&SPI_InitsStructure);
  6.     SPI_Cmd(xx,ENABLE);
  7. }
 楼主| SHOPQQ 发表于 2023-8-23 11:28 | 显示全部楼层
SPI接收/发送一个字节的数据
  1. #define Bummy_Byte OXFF
  2. //使用SPI发送一个字节的数据
  3. u8 SPI_XX_SendByte(u8 byte){
  4.     SPITimeout = SPIT_FLAG_TIMEOUT;

  5.     while(SPI_I2S_GetFlagStatus(XX_SPI,SPI_I2S_FLAG_TXE) == RESET){
  6.         if((SPITimeout--) == 0)
  7.             return SPI_TIME_UserCallback(0);
  8.     }
  9.     SPI_I2S_SendData(XX_SPI,byte);
  10.     SPITimeout = SPIT_FLAG_TIMEOUT;
  11.     while(SPI_I2S_GetFlagStatus(XX_SPI,SPI_I2S_FLAG_RXNE) == RESET){
  12.         if((SPITimeout--) == 0)
  13.             return SPI_TIME_UserCallback(1);
  14.     }
  15.     return SPI_I2S_ReceiveData(FLASH_SPI);
  16. }

  17. //使用SPI读取一个字节的数据
  18. //SPI的接收过程和发送过程实质是一样的
  19. //Dummy_Byte 是一个任意值
  20. u8 SPI_XX_ReadByte(void){
  21.     return (SPI_XX_SendByte(Dummy_Byte));
  22. }
您需要登录后才可以回帖 登录 | 注册

本版积分规则

9

主题

183

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部

9

主题

183

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部