1.SPI介绍

SPI 协议是由摩托罗拉公司提出的通讯协议(Serial Peripheral Interface),即串行外围设
备接口,是一种高速全双工的通信总线。它被广泛地使用在 ADC、LCD 等设备与 MCU 间,
要求通讯速率较高的场合。

1.1特点

  • 全双工通信
    主设备和从设备能够同时发送和接收数据,提高数据传输效率。
  • 同步串行通信
    通过时钟信号同步数据传输,确保数据的准确接收。
  • 主从架构
    一个主设备控制时钟和片选信号,多个从设备通过片选线选择和通信。SPI总线设计为单主机,多主机情况下需要额外硬件管理。通过多条片选线或外部解码器管理多个从设备。每个从设备通常需要独立片选线,增加I/O资源消耗。
  • 高速传输能力
    通常支持几MHz甚至几十MHz的时钟频率,适合高速数据交换。主机最大传输速率可达fPCLK/2。
  • 简单的硬件接口
    仅需4条主要信号线(SCLK、MOSI、MISO、SS),硬件设计简单。
  • 缺少标准错误检测机制
    SPI本身不提供CRC或校验功能,错误检测需上层协议实现。
  • 无仲裁机制
    主设备完全控制总线,不能自动处理总线冲突。
  • 不适合长距离通信
    由于没有差分信号和驱动能力,适合板内或短距离通信。

应用场景

  • 传感器数据采集
  • 闪存、EEPROM读写
  • LCD显示屏驱动
  • 音频DAC/ADC通信
  • 其他高速外设接口

1.2 物理连接

在这里插入图片描述

  • SS( Slave Select):从设备选择信号线,常称为片选信号线,也称为 NSS、CS。当主机要选择从设备时,把该从设备的 NSS 信号线设置为低电平,该从设备即被选中,即片选有效,接着主机开始与被选中的从设备进行 SPI 通讯。所以SPI 通讯以 NSS 线置低电平为开始信号,以 NSS 线被拉高作为结束信号。
  • SCK (Serial Clock):时钟信号线,用于通讯数据同步。它由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不一样,如 STM32 的 SPI 时钟频率最大为fpclk/2,两个设备之间通讯时,通讯速率受限于低速设备。主机SCK频率必须 ≤ 从设备最大支持频率。
  • MOSI (Master Output, Slave Input):主设备输出/从设备输入引脚。主机的数据从这条信号线输出,从机由这条信号线读入主机发送的数据,即这条线上数据的方向为主机到从机。传输数据时,MSB和LSB先行都是可以设置的。
  • MISO(Master Input,,Slave Output):主设备输入/从设备输出引脚。主机从这条信号线读入数据,从机的数据由这条信号线输出到主机,即在这条线上数据的方向为从机到主机。传输数据时,MSB和LSB先行都是可以设置的。

1.3 协议时序

四种模式:
在这里插入图片描述

下图中是模式1,空闲时SCK低电平,采样是偶边沿采样(上升和下降各算一个边沿)。
在这里插入图片描述
NSS、SCK、MOSI 信号都由主机控制产生,而 MISO 的信号由从机产生,主机通过该信号线读取从机的数据。MOSI 与 MISO 的信号只在 NSS 为低电平的时候才有效,在 SCK 的每个时钟周期 MOSI 和 MISO 传输一位数据。

  • 通讯的起始和停止信号
    标号①处,NSS 信号线由高变低,是 SPI 通讯的起始信号。NSS 是每个从机各自独占的信号线,当从机在自己的 NSS 线检测到起始信号后,就知道自己被主机选中了,开始准备与主机通讯。在图中的标号⑥处,NSS 信号由低变高,是 SPI 通讯的停止信号,表示本次通讯结束,从机的选中状态被取消。

  • 数据有效性、
    如图②-⑤,MOSI 及 MISO 的数据在 SCK 的上升沿期间变化输出,在 SCK 的下降沿被采样。即在 SCK 的下降沿时刻,MOSI 及 MISO 的数据有效,高电平时表示数据“1”,为低电平时表示数据“0”。SPI 每次数据传输可以 8 位或 16 位为单位,每次传输的单位数不受限制。

不同的CPOL

在这里插入图片描述
在这里插入图片描述

2.工程实践

2.1 外设介绍

在大容量产品和互联型产品上,SPI接口可以配置为支持SPI协议或者支持2S音频协议。SPI接口默认工作在SPI方式,可以通过软件把功能从SPI模式切换到I2S模式。注意在小容量和中容量产品上,不支持I2S音频协议。
在这里插入图片描述

  • 通讯引脚
    SCK引脚:SPI1/SPI2/SPI3芯片手册中告知,最大速率18Mbps。
    在这里插入图片描述

NSS引脚:有两种NSS模式,软件和硬件。软件模式即可以用指定的GPIO来进行从设备片选。一般采用这种模式。
MISO/MOSI引脚:数据收发引脚,通过一个数据寄存器(DR)进行收发控制。

主模式收发流程如下: (1) 控制 NSS 信号线,产生起始信号; (2) 把要发送的数据写入到“数据寄存器 DR”中,该数据会被存储到发送缓冲区; (3) 通讯开始,SCK 时钟开始运行。MOSI
把发送缓冲区中的数据一位一位地传输出去;MISO 则把数据一位一位地存储进接收缓冲区中; (4) 当发送完一帧数据的时候,“状态寄存器
SR”中的“TXE 标志位”会被置 1,表示传输完一帧,发送缓冲区已空;类似地,当接收完一帧数据的时候,“RXNE标志位”会被置
1,表示传输完一帧,接收缓冲区非空; (5) 等待到“TXE 标志位”为 1
时,若还要继续发送数据,则再次往“数据寄存器DR”写入数据即可;等待到“RXNE 标志位”为 1
时,通过读取“数据寄存器DR”可以获取接收缓冲区中的内容。

2.2 MX配置说明

在这里插入图片描述

  • Mode: Full-Duplex Master
    Full-Duplex 表示SPI支持同时发送和接收数据。Master 表示该设备作为主机控制时钟和片选信号。

  • Hardware NSS Signal: Disable
    是否启用硬件自动管理NSS(片选)信号,Disable 表示由软件手动控制NSS引脚,通常用户自行控制片选信号。

  • Frame Format: Motorola
    SPI数据帧格式,STM32支持两种帧格式:
    Motorola 标准SPI协议格式,最常用。
    TI 表示Texas Instruments的特定帧格式,较少用。、

  • Data Size: 8 Bits
    每次传输的数据位宽,这里表示每个数据单元是8位(1字节)。
    SPI也支持16位或其他位宽。

  • First Bit: MSB First
    传输数据时,先发送最高有效位(MSB)还是最低有效位(LSB)。

  • Prescaler (for Baud Rate): 4
    SPI时钟分频系数,用于控制SPI时钟频率。因为SPI1挂接在APB2总线上,需要除以4来保证不大于18Mbits/s

  • CPOL 时钟极性:
    SPI时钟空闲状态的电平。
    Low(0):空闲时SCK为低电平。
    High(1):空闲时SCK为高电平。

  • CPHA:
    SPI时钟采样数据的时刻。
    1 Edge (First Edge):数据在第一个时钟边沿采样。
    2 Edge (Second Edge):数据在第二个时钟边沿采样。
    CPOL和CPHA组合决定SPI模式(Mode0~Mode3)。

生成代码后,spi.c中多出的关键代码:

  hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_MASTER;
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi1.Init.NSS = SPI_NSS_SOFT;
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi1.Init.CRCPolynomial = 10;
  if (HAL_SPI_Init(&hspi1) != HAL_OK)
  {
    Error_Handler();
  }

GPIO对应的代码(略)。

2.3 和FLASH通信

2.3.1 W25Q64 FLASH芯片

在这里插入图片描述

芯片为NOR FLASH,容量为64Mbit(8MB),其中WP 引脚可控制写保护功能,当该引脚为低电平时,禁止写入数据。我们直接接电源,不使用写保护功能。HOLD 引脚可用于暂停通讯,该引脚为低电平时,通讯暂停,数据输出引脚输出高阻抗状态,时钟和数据输入引脚无效。我们直接接电源,不使用通讯暂停功能。
同时它支持 SPI 模式 0 及模式 3,支持双线全双工,使用MSB 先行模式,支持最高通讯时钟为 104MHz,数据帧长度为 8 位。

2.3.2 片选GPIO配置

在这里插入图片描述
这里配置默认输出高电平,推挽输出,同时打上标签。

2.3.3 FLASH指令

在这里插入图片描述
在这里插入图片描述

2.3.4 阻塞式读写

//SPI片选
void SPI_FLASH_NSS(uint8_t nss)
{
    if (nss == 0)
    {
        HAL_GPIO_WritePin(FLASH_NSS_GPIO_Port, FLASH_NSS_Pin, GPIO_PIN_RESET);
    }
    else
    {
        HAL_GPIO_WritePin(FLASH_NSS_GPIO_Port, FLASH_NSS_Pin, GPIO_PIN_SET);
    }
}
//SPI收发1个字节 - 阻塞方式
uint8_t SPI_FLASH_SendReadByte(uint8_t Txbyte)
{
    uint8_t RxByte = 0;

    //数据收发
    HAL_SPI_TransmitReceive(&hspi1, &Txbyte, &RxByte, 1, 1000);
    
    return RxByte;
}

因为是全双工通信,要收也得发。

2.3.5 读取FLASH 的ID信息

来看看手册中给的读写时序图:
在这里插入图片描述
首先发送0x9F,然后再读写三次,获得ID信息。

代码如下:

#define Dummy_Byte 0xFF 
//SPI-FLASH 读取FLASHID
void SPI_FLASH_READ_FLASHID()
{
    //进行片选
    SPI_FLASH_NSS(0);
    
    //发送 JEDEC 指令
    SPI_FLASH_SendReadByte(0x9F);
    
    //读生产厂商
    uint8_t M = SPI_FLASH_SendReadByte(Dummy_Byte);
    
    //读存储器类型
    uint8_t T = SPI_FLASH_SendReadByte(Dummy_Byte);
    
    //读容量
    uint8_t C = SPI_FLASH_SendReadByte(Dummy_Byte);
    
    //片选结束
    SPI_FLASH_NSS(1);
}

最终读取的和下面W25Q64能匹配上。
在这里插入图片描述
因为只是测试SPI,读写FLASH这里不再罗列,现学现卖即可。

2.3 收发Dummy数据的作用

我们看到,FLASH W25Q64的读写其实在协议层是半双工的,先发,再收,然后收的时候发送的是dummy数据,发dummy的作用:

维持时钟同步!
主设备需继续发送 dummy 数据(通常为 0x00 或固定值),目的是持续生成 SCK 时钟,让从设备有机会在 MISO 线上输出有效数据;
dummy 数据对主设备无意义,仅用于 “驱动时钟”,从设备也不会处理这些数据(协议层已约定 “命令后的数据为有效输出”)。

2.4 SPI DMA读写

SPI阻塞式读写,需反复读取 SPI 状态寄存器(SR),注意是每bit都读,经过实际测试,11M频率下,HAL库读写16bit耗时5us左右,光轮询和检查参数的耗时就有3个us多。想要提速,要么用DMA,要么用LL库。
DMA需要区分单次写还是轮询,具体参考串口的DMA控制。
DMA启动的性能大约在1-2us,还是比阻塞快很多的。

2.5 ADC采集电压

待补充

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐