环境
芯片:STM32F103ZET6
库:来自HAL的STM32F1XX.H
原理图
cubeMX配置
W25Q32.h
//
// Created by seven on 2024/9/2.
//
#ifndef SPI_W25Q32_H
#define SPI_W25Q32_H
#include "spi.h"
/**
* 初始化W25Q32闪存芯片
*/
void W25Q32_Init(void);
/**
* 停止W25Q32闪存芯片的操作
*/
void w25q32_Stop(void);
/**
* 读取W25Q32闪存芯片的ID
* @param sid 存储制造商ID的指针
* @param mid 存储设备ID的指针
*/
void W25Q32_ReadId(uint8_t *sid,uint16_t *mid);
/**
* 等待W25Q32闪存芯片不忙
* 原理是发送05h指令,循环读取状态寄存器(8位),直到第0位变为0,表示芯片不忙
*/
void Inf_W25Q32_WaitNotBusy(void);
/**
* 使能W25Q32闪存芯片的写操作
*/
void Inf_W25Q32_WriteEnable(void);
/**
* 禁止W25Q32闪存芯片的写操作
*/
void Inf_W25Q32_WriteDisable(void);
/**
* 擦除W25Q32闪存芯片的指定扇区
* @param block 块地址
* @param sector 扇区地址
*/
void Inf_W25Q32_SectorErase(uint8_t block,uint8_t sector);
/**
* 向W25Q32闪存芯片的指定页面写入数据
* @param block 块地址
* @param sector 扇区地址
* @param page 页面地址
* @param data 写入的数据缓冲区指针
* @param len 写入的数据长度
*/
void Inf_W25Q32_PageProgram(uint8_t block,uint8_t sector,uint8_t page,uint8_t data[],uint8_t len);
/**
* 从W25Q32闪存芯片的指定位置读取数据
* @param block 块地址
* @param sector 扇区地址
* @param page 页面地址
* @param buff 存储读取数据的缓冲区指针
* @param len 读取的数据长度
*/
void Inf_W25Q32_ReadData(uint8_t block,uint8_t sector,uint8_t page,uint8_t buff[],uint8_t len);
#endif //SPI_W25Q32_H
W25Q32.c
//
// Created by seven on 2024/9/2.
//
#include "W25Q32.h"
void W25Q32_Init(void){
MX_SPI1_Init();
}
void w25q32_Stop(void){
SPI_Stop();
}
void W25Q32_ReadId(uint8_t *sid,uint16_t *mid){
// 1. 片选启动
SPI_Start();
// 2. 发送指令
SPI_SwapByte(0x9f);
// 3. 接收id
// 3.1 首先接收制造商id
*sid = SPI_SwapByte(0xff);
// 3.2 接收设备id
*mid = SPI_SwapByte(0xff) << 8;
*mid |= SPI_SwapByte(0xff) << 0;
// 4. 片选关闭
SPI_Stop();
}
// 等待忙状态
// 原理是调用05h指令 可以循环读取8位的状态寄存器 -> 第0位表示忙状态
// 当第0位变为0的时候表示它不忙
void Inf_W25Q32_WaitNotBusy(void){
SPI_Start();
SPI_SwapByte(0x05);
while(SPI_SwapByte(0xff) & 0x01){
}
SPI_Stop();
}
// 打开写使能
void Inf_W25Q32_WriteEnable(void){
SPI_Start();
SPI_SwapByte(0x06);
SPI_Stop();
}
// 关闭写使能
void Inf_W25Q32_WriteDisable(void){
SPI_Start();
SPI_SwapByte(0x04);
SPI_Stop();
}
// 擦除方法
void Inf_W25Q32_SectorErase(uint8_t block,uint8_t sector){
Inf_W25Q32_WaitNotBusy();
Inf_W25Q32_WriteEnable();
SPI_Start();
SPI_SwapByte(0x20);
uint32_t address = (block << 16) | (sector << 8);
SPI_SwapByte(address >> 16);
SPI_SwapByte(address >> 8);
SPI_SwapByte(address >> 0);
SPI_Stop();
Inf_W25Q32_WriteDisable();
}
// 写入数据
void Inf_W25Q32_PageProgram(uint8_t block,uint8_t sector,uint8_t page,uint8_t data[],uint8_t len){
Inf_W25Q32_WaitNotBusy();
Inf_W25Q32_WriteEnable();
SPI_Start();
SPI_SwapByte(0x02);
uint32_t address = (block << 16) | (sector << 8) | (page << 0);
SPI_SwapByte(address >> 16);
SPI_SwapByte(address >> 8);
SPI_SwapByte(address >> 0);
for(int i = 0; i < len; i++){
SPI_SwapByte(data[i]);
}
SPI_Stop();
Inf_W25Q32_WriteDisable();
}
// 读数据 -> 不需要使能 但需要等待忙状态
void Inf_W25Q32_ReadData(uint8_t block,uint8_t sector,uint8_t page,uint8_t buff[],uint8_t len){
Inf_W25Q32_WaitNotBusy();
SPI_Start();
SPI_SwapByte(0x03);
uint32_t address = (block << 16) | (sector << 8) | (page << 0);
SPI_SwapByte(address >> 16);
SPI_SwapByte(address >> 8);
SPI_SwapByte(address >> 0);
for(int i = 0; i < len; i++){
buff[i] = SPI_SwapByte(0xff);
}
SPI_Stop();
}
基于官方HAL库spi额外添加的函数
在spi.c
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
内添加下面代码
/**
* @brief SPI通信启动函数
*
* 该函数通过将片选(CS)信号置低来启动SPI通信。
*/
void SPI_Start(void)
{
// 将CS(片选)信号置低,启动SPI通信
HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET);
}
/**
* @brief SPI通信停止函数
*
* 该函数通过将片选(CS)信号置高来结束SPI通信。
*/
void SPI_Stop(void)
{
// 将CS(片选)信号置高,结束SPI通信
HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET);
}
/**
* @brief SPI通信中字节交换函数
*
* 该函数在SPI通信中交换一个字节,并返回接收到的字节。
*
* @param byte 要发送和交换的字节
* @return uint8_t 返回从SPI通信接收到的字节
*/
uint8_t SPI_SwapByte(uint8_t byte)
{
uint8_t rByte = 0;
// 通过SPI通信发送字节并接收回应字节
HAL_SPI_TransmitReceive(&hspi1, &byte, &rByte, 1, 1000);
return rByte;
}
在spi.h的
/* USER CODE BEGIN Prototypes */
/* USER CODE END Prototypes */
内添加下面代码
// 初始化SPI通信
void SPI_Start(void);
// 停止SPI通信
void SPI_Stop(void);
// 数据位交换
// 将输入的8位数据进行位交换,例如0x01交换后变为0x80
uint8_t SPI_SwapByte(uint8_t byte);