文章目录
- 1.参考教程
- [2. 4种时间模式](#2. 4种时间模式)
- [3. 3个编程接口](#3. 3个编程接口)
-
- [3.1 `HAL_StatusTypeDef HAL_SPI_Transmit(...)` :](#3.1
HAL_StatusTypeDef HAL_SPI_Transmit(...)
:) -
- [3.1.1 参数说明](#3.1.1 参数说明)
- [3.1.2 例子](#3.1.2 例子)
- [3.2 `HAL_StatusTypeDef HAL_SPI_Receive(...)` :](#3.2
HAL_StatusTypeDef HAL_SPI_Receive(...)
:) -
- 3.2.1参数说明
- [3.2.2 例子](#3.2.2 例子)
- [3.3 `HAL_StatusTypeDef HAL_SPI_TransmitReceive(...)` :](#3.3
HAL_StatusTypeDef HAL_SPI_TransmitReceive(...)
:) -
- [3.3.1 参数说明](#3.3.1 参数说明)
- [3.3.2 例子](#3.3.2 例子)
- [3.1 `HAL_StatusTypeDef HAL_SPI_Transmit(...)` :](#3.1
- [4. spi应用-flash数据写入](#4. spi应用-flash数据写入)
-
- [4.1 flash数据写入过程](#4.1 flash数据写入过程)
- [4.2 流程](#4.2 流程)
- [5. 流程实现](#5. 流程实现)
-
- [5.1 写使能](#5.1 写使能)
- [5.2 扇区擦除](#5.2 扇区擦除)
- [5.3 页编程](#5.3 页编程)
- [6. 代码实现](#6. 代码实现)
- [5. spi应用-flash数据加载](#5. spi应用-flash数据加载)
-
- [5.1 流程](#5.1 流程)
- [5.2 代码实现](#5.2 代码实现)
1.参考教程
[STM32 HAL库][SPI]外部flash实验
[STM32 HAL库][SPI]外部flash数据存取
2. 4种时间模式

3. 3个编程接口
3.1 HAL_StatusTypeDef HAL_SPI_Transmit(...)
:
用于通过 SPI 总线发送数据,返回 HAL_StatusTypeDef
类型状态值 ,作用为"发送"。
c
HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi,
uint8_t *pData,
uint16_t Size,
uint32_t Timeout)
3.1.1 参数说明
参数名 | 说明 |
---|---|
hspi |
填写 SPI 句柄的指针 |
pData |
填写要发送的数据 |
Size |
填写要发送的数据的数量,以字节为单位 |
Timeout |
超时时间,单位是 ms;HAL_MAX_DELAY 表示无限长的超时时间 |
3.1.2 例子

- 通信场景 :主机(单片机)向从机1发送数据
0x5a, 0x33
- 硬件连接 :涉及主机与从机的
MOSI
(主机输出从机输入)、MISO
(主机输入从机输出 ,从机1此处未动作 )、SCK
(时钟 )、NSS
(从机选择 ,NSS1 选中从机1 )引脚 - 代码逻辑 :
- 定义发送数据数组
uint8_t dataToSend[] = {0x5a, 0x33};
- 通过
HAL_GPIO_WritePin
函数拉低引脚选中从机1 - 调用
HAL_SPI_Transmit
函数发送数据 - 数据发送后,通过
HAL_GPIO_WritePin
函数拉高引脚取消选中从机1
- 定义发送数据数组
3.2 HAL_StatusTypeDef HAL_SPI_Receive(...)
:
用于通过 SPI 总线接收数据,返回 HAL_StatusTypeDef
类型状态值 ,作用为"接收"。
c
HAL_StatusTypeDef HAL_SPI_Receive(&hspi1, uint8_t *pData, uint16_t Size, uint32_t Timeout)
3.2.1参数说明
参数名 | 说明 |
---|---|
参数hspi | 填写SPI句柄的指针 |
参数pData | 填写接收缓冲区 |
参数Size | 填写要发送的数据的数量,以字节为单位 |
参数Timeout | 超时时间,单位是ms;HAL_MAX_DELAY表示无限长的超时时间 |
3.2.2 例子

- 功能描述:从从机1接收2个字节的数据
- 硬件连接:主机(单片机)与从机1通过MOSI、MISO、SCK、NSS1引脚连接,从机还有从机2、从机3,引脚连接逻辑同从机1
- 代码逻辑 :
- 定义接收缓冲区
uint8_t dataRcvd[] = {0xff, 0xff};
- 通过
HAL_GPIO_WritePin(..., GPIO_PIN_RESET);
函数拉低引脚选中从机1 - 调用
HAL_SPI_Receive(&hspi1, dataRcvd, 2, HAL_MAX_DELAY);
函数,参数含SPI句柄&hspi1
、接收缓冲区dataRcvd
、数据长度2
(字节)、超时时间HAL_MAX_DELAY
(无限超时 ) - 数据接收后,通过
HAL_GPIO_WritePin(..., GPIO_PIN_SET);
函数拉高引脚取消选中从机1
- 定义接收缓冲区
- 时序图 :展示NSS1(低电平选中 )、SCK(时钟信号 )、MOSI(发送
0xff
、0xff
波形 )、MISO(接收0x1f
、0x27
波形 )的时序关系 ,呈现SPI接收数据时各信号的变化 。
3.3 HAL_StatusTypeDef HAL_SPI_TransmitReceive(...)
:
用于通过 SPI 总线同时进行发送和接收数据操作,返回 HAL_StatusTypeDef
类型状态值 ,作用为"发送同时接收"。
c
HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi,
uint8_t *pTxData,
uint8_t *pRxData,
uint16_t Size,
uint32_t Timeout)
发送数据的同时接收数据
3.3.1 参数说明
参数名 | 说明 |
---|---|
参数hspi | 填写SPI句柄的指针 |
参数pTxData | 填写要发送的数据 |
参数pRxData | 填写接收数据缓冲区 |
参数Size | 发送数据的数量=接收数据的数量,以字节为单位 |
参数Timeout | 超时时间,单位是ms |
3.3.2 例子

- 功能描述 :发送
{0x5a, 0x33}
,同时接收 2 个字节数据 - 硬件连接 :主机(单片机)与从机 1 通过
MOSI
(主机输出从机输入 )、MISO
(主机输入从机输出 )、SCK
(时钟 )、NSS1
(从机选择 )引脚连接,从机还有从机 2、从机 3 ,引脚连接逻辑同从机 1 - 代码逻辑 :
- 定义发送数据数组
uint8_t txData[] = {0x5a, 0x33};
- 定义接收缓冲区
uint8_t rxData[2];
- 通过
HAL_GPIO_WritePin(..., GPIO_PIN_RESET);
函数拉低引脚选中从机 1 - 调用
HAL_SPI_TransmitReceive(&hspi1, txData, rxData, 2, HAL_MAX_DELAY);
函数,参数含 SPI 句柄&hspi1
、发送数据数组txData
、接收缓冲区rxData
、数据长度2
(字节)、超时时间HAL_MAX_DELAY
(无限超时 ) - 数据收发后,通过
HAL_GPIO_WritePin(..., GPIO_PIN_SET);
函数拉高引脚取消选中从机 1
- 定义发送数据数组
- 时序图 :展示
NSS1
(低电平选中 )、SCK
(时钟信号 )、MOSI
(发送0x5a
、0x33
波形 )、MISO
(接收0x1f
、0x27
波形 )的时序关系 ,呈现 SPI 同时收发数据时各信号的变化 。
4. spi应用-flash数据写入
4.1 flash数据写入过程

4.2 流程

5. 流程实现
5.1 写使能
抽水机的写使能是发送0x06
5.2 扇区擦除
扇区擦除的指令码是0x20,所以首先发送0x20,后面接着24位的扇区首地址

5.3 页编程
页编程指令码是0x02,后面跟着24位地址(往哪里写地址就发谁的地址),再后面是发要写入的数据,一次性可以写多个数据
6. 代码实现
c
static void SaveLEDState(uint8_t ledState)
{
// #1. 写使能
uint8_t writeEnableCmd[] = {0x06};
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi1, writeEnableCmd, 1, HAL_MAX_DELAY);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
// #2. 扇区擦除
uint8_t sectorErase[] = {0x20, 0x00, 0x00, 0x00};
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi1, sectorErase, 4, HAL_MAX_DELAY);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
HAL_Delay(100);
// #3. 写使能
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi1, writeEnableCmd, 1, HAL_MAX_DELAY);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
// #4. 页编程
uint8_t pageProgCmd[5];//要发送5个字节
pageProgCmd[0] = 0x02; //页编程指令码是0x02
pageProgCmd[1] = 0x00; //地址
pageProgCmd[2] = 0x00; //地址
pageProgCmd[3] = 0x00; //地址
pageProgCmd[4] = ledState; //要发送的数据
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi1, pageProgCmd, 5, HAL_MAX_DELAY);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
HAL_Delay(10);
}
在主函数写上 SaveLEDState( ledState);
就可以保存灯的状态数据。
5. spi应用-flash数据加载
5.1 流程
读取数据的指令码是0x03,先发0x03,后面跟24位地址,再往后从总线上读取数据。
首先声明数组,用于存放要发送的数据。先发送读取命令,然后接收数据。最后返回读取的数据。
5.2 代码实现
c
// 函数:读取LED状态(从Flash等存储设备)
// 功能:通过SPI总线发送读命令,接收并返回存储的LED状态数据
static uint8_t LoadLEDState(void)
{
// 读命令及地址:0x03为读数据指令,后三个0x00为起始地址
uint8_t readDataCmd[] = {0x03, 0x00, 0x00, 0x00};
uint8_t ledState; // 用于存储读取到的LED状态数据
// 选通从设备:拉低GPIOA_PIN_4(SPI从设备片选信号)
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
// 通过SPI发送读命令:向从设备发送读数据指令及地址,长度4字节,无限超时等待
HAL_SPI_Transmit(&hspi1, readDataCmd, 4, HAL_MAX_DELAY);
// 通过SPI接收数据:从从设备接收1字节数据(LED状态),存入ledState,无限超时等待
HAL_SPI_Receive(&hspi1, &ledState, 1, HAL_MAX_DELAY);
// 取消选通:拉高GPIOA_PIN_4,释放从设备片选
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
return ledState; // 返回读取到的LED状态
}