前言
1、SPI 协议是由摩托罗拉公司提出的通讯协议 (Serial Peripheral Interface),即串行外围设备接口,是一种高速全双工的通信总线。它被广泛地使用在 ADC、LCD 等设备与 MCU 间,要求通讯速率较高的场合。
2、主机发送一个字节数据到从机,同时从机也会发送一个字节数据到主机(写操作和读操作是同步完成的,如果只想进行写操作,只需忽略接收到的字节即可。如果要读一个字节,就需要发送一个空字节来引发从机的传输)
3、W25Q128将16M的容量分为256个块(block),每个块大小为64K字节,每个块又分为16个扇区(sector),每个扇区4K字节。W25Q128的最小擦除单位为一个扇区,也就是每次必须擦除4k字节。这样我们需要给W25Q128开辟一个至少4K的缓存区,这样对SEAM要求比较高,要求芯片必须有4k以上SRAM才能很好的操作。
4、W25Q128支持标准SPI,还支持双输出/四输出的SPI,最大SPI时钟可以达到80Mhz(双输出时相当于160Mhz,四输出时相当于320Mhz)
5、我觉得HAL库的回调函数,就是不管使能了什么中断,都是进入这个回调函数中,然后通过判断中断标志位选择相应的中断回调函数。而中断和DMA应该都是直接调用一个函数就行了,看下回调函数中相应的位置有没有把相应的中断标志位给取消掉,一般都是取消了的,然后用的时候再调用。
6、接线方面,主机的MISO接从机的MISO。因为从机知道自己是从机,他的命名就是站在自己是从机的角度上命名的。
SPI讲解
使用简介
1、SPI 通讯使用 3 条总线及片选线,3 条总线分别为 SCK、MOSI、MISO,片选线为 SS
- SS*(* Slave Select):片选信号线,也称为NSS/CS。当有多个 SPI 从设备与 SPI 主机相连时,共用SCK、MOSI 及 MISO这 3 条总线; SPI 协议中没有设备地址,它使用 NSS 信号线来寻址,当主机要选择从设备时,把该从设备的 NSS 信号线设置为低电平,该从设备即被选中,即片选有效,接着主机开始与被选中的从设备进行 SPI 通讯。所以 SPI 通讯以CS 线置低电平为开始信号,以NSS 线被拉高作为结束信号。
- SCK (Serial Clock):时钟信号线,用于通讯数据同步。它由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不一样,如 STM32 的 SPI 时钟频率最大为 fpclk/2,两个设备之间通讯时,通讯速率受限于低速设备。
- MOSI (Master Output,Slave Input):主设备输出/从设备输入引脚。主机的数据从这条信号线输出,从机由这条信号线读入主机发送的数据,即这条线上数据的方向为主机到从机。
- MISO(Master Input,,Slave Output):主设备输入/从设备输出引脚。主机从这条信线读入数据,从机的数据由这条信号线输出到主机,即在这条线上数据的方向为从机到主机。
2、STM32 的 SPI 外设:
- 可用作通讯的主机及从机
- 支持最高的 SCK 时钟频率为 fpclk/2 (STM32F103 型号的芯片默认 f:sub:pclk1 为 72MHz,fpclk2 为 36MHz)
- 完全支持 SPI 协议的 4 种模式
- 数据帧长度可设置为 8 位或 16 位
- 可设置数据 MSB 先行或 LSB 先行。
- 支持双线全双工 (前面小节说明的都是这种模式)、双线单向以及单线模式。
- 其中双线单向模式可以同时使用 MOSI 及 MISO 数据线向一个方向传输数据,可以加快一倍的传输速度。
- 而单线模式则可以减少硬件接线,当然这样速率会受到影响。我们只讲解双线全双工模式。
3、收发流程
- 在发送的不同阶段会对状态寄存器SR的不同数据位写入参数,我们通过读取这些寄存器标志来了解通讯状态
- (1)控制NSS,产生起始信号(NSS置低电平)
- (2)将数据写入到“数据寄存器 DR”中,该数据会被存储到发送缓冲区;
- (3)通讯开始,SCK 时钟开始运行。MOSI 把发送缓冲区中的数据一位一位地传输出去;MISO则把数据一位一位地存储进接收缓冲区中
- (4) 当发送完一帧数据的时候,“状态寄存器 SR”中的“TXE 标志位”会被置 1,表示传输完一帧,发送缓冲区已空;类似地,当接收完一帧数据的时候,“RXNE 标志位”会被置 1,表示传输完一帧,接收缓冲区非空
- (5)等待到“TXE 标志位”为 1 时,若还要继续发送数据,则再次往“数据寄存器 DR”写入数据即可;等待到“RXNE 标志位”为 1 时,通过读取“数据寄存器 DR”可以获取接收缓冲区中的内容。
- 注:假如我们使能了 TXE 或 RXNE 中断,TXE 或 RXNE 置 1 时会产生 SPI 中断信号,进入同一个中断服务函数,到 SPI 中断服务程序后,可通过检查寄存器位来了解是哪一个事件,再分别进行处理。也可以使用 DMA 方式来收发“数据寄存器 DR”中的数据。
通信模式
1、SPI 一共有四种通讯模式,它们的主要区别是总线空闲时 SCK 的时钟状态以及数据采样时刻,所以共有四种模式。由时钟极性和时钟相位决定。在SPI_CR1寄存器中设置
- 时钟极性 CPOL:是指 SPI 通讯设备处于空闲状态时,SCK 信号线的电平信号 (即 SPI 通讯开始前、NSS 线为高电平时 SCK 的状态)。CPOL=0 时,SCK 在空闲状态时为低电平,CPOL=1 时,则相反。
- 时钟相位 CPHA :是指数据的采样的时刻,当 CPHA=0 时,MOSI 或 MISO 数据线上的信号将会在SCK 时钟线的“奇数边沿”被采样。当 CPHA=1 时,数据线在 SCK 的“偶数边沿”采样。
2、在 SPI_CR1 寄存器中进行设置
位 | 配置 |
---|---|
位1 CPOL:时钟极性 (Clock Polarity) | 0:空闲状态时,SCK保持低电平。 1:空闲状态时,SCK保持高电平 |
位 0 CPHA:时钟相位 (Clock Phase) | 0:从第一个时钟边沿(奇数边沿)开始采样数据 。1:从第二个时钟边沿(偶数边沿)开始采样数据 |
在黑色箭头(采样时间点)被采集
3、由 CPOL 及 CPHA 的不同状态,SPI 分成了四种模式,主机与从机需要工作在相同的模式下才可以正常通讯,实际中采用较多的是“模式 0”与“模式 3”
数据控制逻辑
1、SPI 的 MOSI 及 MISO 都连接到数据移位寄存器上,数据移位寄存器的数据来源及目标接收、发送缓冲区以及MISO、MOSI 线。
2、当向外发送数据的时候,数据移位寄存器以“发送缓冲区”为数据源,把数据一位一位地通过数据线发送出去;当从外部接收数据的时候,数据移位寄存器把数据线采样到的数据一位一位地存储到“接收缓冲区”中。
3、通过写 SPI 的“数据寄存器 DR”把数据填充到发送 F 缓冲区中,通讯读“数据寄存器 DR”,可以获取接收缓冲区中的内容。
4、其中数据帧长度可以通过“控制寄存器 CR1”的“DFF 位”配置成 8 位及 16 位模式;
5、配置“LSBFIRST位”可选择 MSB 先行还是 LSB 先行(先发送高位还是先发送低位)
CR1
位 | 配置 |
---|---|
位 7 LSBFIRST:帧格式 (Frame format) | 0:先发送 MSB。1:先发送 LSB |
位 11 DFF:数据帧格式 (Data frame format) | 0:为发送/接收选择 8 位数据帧格式。1:为发送/接收选择 16 位数据帧格式 |
状态标志
在外设工作时,控制逻辑会根据外设的工作状态修改“状态寄存器(SR)”,我们只要读取状态寄存器相关的寄存器位,就可以了解 SPI 的工作状态了。应用程序通过三个状态标志,可以完全监控SPI总线的状态。
SPI_SR
- 发送缓冲区空闲标志(TXE)
位 1 TXE:发送缓冲区为空 (Transmit buffer empty)
0:发送缓冲区非空
1:发送缓冲区为空,可以写下一个待发送的数据进入缓冲器。
当写入SPI_DR时,TXE标志被清除 - 接收缓冲器非空(RXNE)
位 0 RXNE:接收缓冲区非空 (Receive buffer not empty)
0:接收缓冲区为空
1:接收缓冲区非空,表明在接收缓冲器中包含有效的接收数据。读SPI数据寄存器可以清除此标志 - 忙(Busy)标志
0:SPI(或 I2S)不繁忙
1:SPI(或 I2S)忙于通信或者发送缓冲区不为空
此标志由硬件置 1 和清零(写入此位无效果),此标志表明SPI通信层的状态
SPI中断
控制逻辑还根据要求,负责控制产生 SPI 中断信号、DMA 请求及控制 NSS 信号线。
SPI_CR2
时钟
SPI1用的是APB2的时钟(84Mhz),HAL库中直接有了
需要对时钟进行分频(至少二分频),才能用
Flash
硬件
正点原子使用的是W25Q128,野火用的是W25Q64
W25QXX指令
根据下面命令,编写函数。一般情况下,主机传输的第一个数据,会被Flash视为操作指令
状态寄存器
通过状态寄存器可以检测和配置器件的状态
注意事项
1、需要先擦除才能再写入,按扇区操作,最小需要擦除一个扇区
2、CPOL和CPHA一定要配对,不然读不了数据,我读出来是ffff,不同设备不一样,野火就是第一个边沿,所以用的正点的就出错了
读写
根据不同的指令,按照给定的时序进行功能的操作,这个DI就是输入给器件的,DO就是器件输出给32的,需要我们读取的数据
SPI配置
SPI结构体讲解
配置也就是配置结构体中的东西
/* SPI1 init function */
void MX_SPI1_Init(void)
{
/* USER CODE BEGIN SPI1_Init 0 */
/* USER CODE END SPI1_Init 0 */
/* USER CODE BEGIN SPI1_Init 1 */
/* USER CODE END SPI1_Init 1 */
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER; /* 设置 SPI 的主/从机端模式 */
hspi1.Init.Direction = SPI_DIRECTION_2LINES; /* 设置 SPI 的单双向模式 */
hspi1.Init.DataSize = SPI_DATASIZE_8BIT; /* 设置 SPI 的数据帧长度,可选 8/16 位 */
hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;/* 设置时钟极性 CPOL,可选高/低电平 */
hspi1.Init.CLKPhase = SPI_PHASE_2EDGE; /* 设置时钟相位,可选奇/偶数边沿采样 */
hspi1.Init.NSS = SPI_NSS_SOFT;/* 设置 NSS 引脚由 SPI 硬件控制还是软件控制 */
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;/* 设置时钟分频因子,fpclk/分频数 =fSCK */
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;/* 设置 MSB/LSB 先行 */
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;/* 指定是否启用 TI 模式 */
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;/* 指定是否启用 CRC 计算 */
hspi1.Init.CRCPolynomial = 10;/* 设置 CRC 校验的表达式 */
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN SPI1_Init 2 */
__HAL_SPI_ENABLE(&hspi1); //使能SPI1,CubeMX只是配置好,需要自己使能(添加这一句就可以了,到时候再试试看直接操作寄存器怎么样)。
// (hspi1.Instance->CR1) |= (0x00000040);//0100 0000 第六位,也可以从上面的函数中一步一步查看下去
/* USER CODE END SPI1_Init 2 */
}
结构体成员说明:
- Mode:本成员设置 SPI 工作在主机模式 (SPI_MODE_MASTER) 或从机模式 (SPI_MODE_SLAVE ),这两个模式的最大区别为 SPI 的 SCK 信号线的时序,SCK 的时序是由通讯中的主机产生的。若被配置为从机模式,STM32 的 SPI 外设将接受外来的 SCK 信号。
- Direction:本成员设置 SPI 的通讯方向,可设置为双线全双工 (SPI_DIRECTION_2LINES),双线只接收 (SPI_DIRECTION_2LINES_RXONLY),单线 SPI_DIRECTION_1LINE。
- DataSize:本成员可以选择 SPI 通讯的数据帧大小是为 8 位 (SPI_DATASIZE_8BIT) 还是 16位 (SPI_DATASIZE_16BIT)。
- CLKPolarity 和 CLKPhase:这两个成员配置 SPI 的时钟极性 CLKPolarity 和时钟相位CLKPhase,这两个配置影响到 SPI 的通讯模式。时钟极性 CLKPolarity 成员,可设置为高电平 (SPI_POLARITY_HIGH)或低电平 (SPI_POLARITY_LOW)。时钟相位 CPHA 则可以设置为 SPI_PHASE_1EDGE(在SCK 的奇数边沿采集数据) 或 SPI_PHASE_2EDGE ( 在 SCK 的偶数边沿采集数据) 。
- NSS:本成员配置 NSS 引脚的使用模式,可以选择为硬件模式 (SPI_NSS_HARD ) 与软件模式 ( SPI_NSS_SOFT ),在硬件模式中的 SPI 片选信号由 SPI 硬件自动产生,而软件模式则需要我们亲自把相应的 GPIO 端口拉高或置低产生非片选和片选信号。实际中软件模式应用比较多。
- BaudRatePrescaler:本成员设置波特率分频因子,分频后的时钟即为 SPI 的 SCK 信号线的时钟频率。这个成员参数可设置为 fpclk 的 2、4、6、8、16、32、64、128、256 分频。
- FirstBit:所有串行的通讯协议都会有 MSB 先行 (高位数据在前) 还是 LSB 先行 (低位数据在前) 的问题,而 STM32 的 SPI 模块可以通过这个结构体成员,对这个特性编程控制。
- TIMode :指定是否启用 TI 模式。可选择为使能 ( SPI_TIMO DE_ENABLE ) 与不使能( SPI_TIMODE_DISABLE )。
- CRCCalculation :指定是否启用 CRC 计算。
- SPI_CRCPolynomial:这是 SPI 的 CRC 校验中的多项式,若我们使用 CRC 校验时,就使用这个成员的参数 (多项式),来计算 CRC 的值。
配置完这些结构体成员后,我们要调用 HAL_SPI_Init 函数把这些参数写入到寄存器中,实现 SPI的初始化,然后调用 __HAL_SPI_ENABLE 来使能 SPI 外设。
HAL库配置讲解
1、模式:
- 全双工主机,全双工从机
- 半双工主,半双工从。只使用一条数据线的全双工,同一时间只能收或者发
- 后面四个单工,就是一条线上只能收或者发
2、选择NSS引脚控制是硬件模式 (SPI_NSS_HARD ) 还是软件模式 ( SPI_NSS_SOFT ),在硬件模式中的 SPI 片选信号由 SPI 硬件自动产生,而软件模式则需要我们亲自把相应的 GPIO 端口拉高或置低产生非片选和片选信号。实际中软件模式应用比较多。例程中使用的就是软件控制
当需要使用硬件模式时:
(1)主模式下是否支持多主的模式,如果支持就要选择input,不支持则选择output这个选项
(2)当作为从设备,就选择input
3、对于SPI有两种帧格式,一种是摩托罗拉,一种TI约定的SPI协议。用摩托罗拉就行。当上面选择硬件管理NSS引脚的时候,这里才能选择TI模式
4、每一帧的数据传输是传输8位的还是16位的。第一个传输的位是高位还是低位(高位先传输还是低位先传输)
5、空闲时保持高电平,第二个边沿采集,这个一定要配置 对,不然无法正常通信