【嵌入式STM32】SPI总结
在大容量产品和互联型产品上,SPI接口可以配置为支持SPI协议或者支持2S音频协议。SPI接口默认工作在SPI方式,可以通过软件把功能从SPI模式切换到I2S模式。注意在小容量和中容量产品上,不支持I2S音频协议。通讯引脚SCK引脚:SPI1/SPI2/SPI3芯片手册中告知,最大速率18Mbps。NSS引脚:有两种NSS模式,软件和硬件。软件模式即可以用指定的GPIO来进行从设备片选。一般采用这
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采集电压
待补充
更多推荐



所有评论(0)