【STM32-HAL】 SPI通信与Flash数据写入实战
文章目录
- 1.参考教程
- 2. 4种时间模式
- 3. 3个编程接口
- 3.1 `HAL_StatusTypeDef HAL_SPI_Transmit(...)` :
- 3.1.1 参数说明
- 3.1.2 例子
- 3.2 `HAL_StatusTypeDef HAL_SPI_Receive(...)` :
- 3.2.1参数说明
- 3.2.2 例子
- 3.3 `HAL_StatusTypeDef HAL_SPI_TransmitReceive(...)` :
- 3.3.1 参数说明
- 3.3.2 例子
- 4. spi应用-flash数据写入
- 4.1 flash数据写入过程
- 4.2 流程
- 5. 流程实现
- 5.1 写使能
- 5.2 扇区擦除
- 5.3 页编程
- 6. 代码实现
- 5. spi应用-flash数据加载
- 5.1 流程
- 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
类型状态值 ,作用为“发送”。
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
类型状态值 ,作用为“接收”。
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
类型状态值 ,作用为“发送同时接收”。
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. 代码实现
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; //页编程指令码是0x02pageProgCmd[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 代码实现
// 函数:读取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状态
}