当前位置: 首页 > news >正文

STM32 SPI Flash读写实验手册

实验名称

基于SPI Flash的断电状态保存系统


实验目的

  1. 掌握SPI Flash的基本读写操作

  2. 实现设备状态断电保存功能

  3. 学习STM32 HAL库的SPI驱动开发


硬件要求

  1. STM32开发板

  2. 按键

  3. SPI Flash模块

  4. 杜邦线若干

  5. 面包板


硬件连接

SPI Flash引脚STM32引脚
CLKSPI_SCK (PA5)
DISPI_MISO (PA7)
DOSPI_MOSI (PA6)
CSNSS(PA4)
VCC3.3V
GNDGND

按键连接:

  • 按键一端接PA0,另一端接GND(上拉模式)


软件配置(STM32CubeMX)

  1. SPI配置

  • 选择SPI1

  • Mode: Full-Duplex Master
  • Hardware NSS: Disabled

  • 其他参数设置

  1. GPIO配置

  • CS引脚:PA4,输出推挽模式,初始高电平

  • 用户按键:PA0,输入模式,上拉

  • 板载LED:PC13,输出开漏模式

  • 时钟配置

    系统时钟设置为系统配置的8分(因实验硬件限制)

按钮及其灯亮代码实现

uint8_t pre = 1;//按钮按下的前标志
uint8_t cur = 1;//按钮按下的后标志
uint8_t ledState = 0;//板载LED的状态标志

//读取flsh数据并进行判断,函数实现在fish读数据实现
ledState = LoadledState();
	
	if(ledState == 1){
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);
	}else{
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);
	}


  while (1)
  {
		pre = cur;将前后标志重置为1,既按钮未被按下的状态
		
		if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_SET){//获取按钮引脚电平
			cur = 1;//引脚高电平
		}else{
			cur = 0;//引脚低电平
		}
		
		if(pre !=  cur){//按钮有动作
			HAL_Delay(10);//延迟消抖
			if(cur == 0){//按下按钮(图2)
			
			}else{//按钮弹起(图3)
				if(ledState == 1){
					HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);//板载LED熄灭
					ledState = 0;//设置LED灯状态为0
				}else{
					HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);//板载LED点亮
					ledState = 1;//设置LED灯状态为1
				}

                    SaveLEDState(ledState);//flsh写入数据的方法在后面实现
			}
		}

未按下

                                                          图1


按钮按下 

                                                          图2


 按钮弹起

                                                           图3


SPI Flash读写原理

读写函数


TypeDef HAL_SPI_Transmit

HAL_SPI_Transmit函数通过SPI接口以阻塞(轮询)模式发送指定长度的数据。

HAL_StatusTypeDef HAL_SPI_Transmit(
    SPI_HandleTypeDef *hspi, 
    uint8_t *pData, 
    uint16_t Size, 
    uint32_t Timeout
);

参数说明

参数类型说明
hspiSPI_HandleTypeDef*指向SPI外设句柄的指针,包含SPI配置信息(如SPI1、SPI2等)
pDatauint8_t*待发送数据的缓冲区指针
Sizeuint16_t待发送数据的字节数
Timeoutuint32_t超时时间(单位:毫秒),若超时,函数返回HAL_TIMEOUT

返回值

返回值说明
HAL_OK传输成功完成
HAL_ERROR发生错误(如SPI未初始化、DMA错误等)
HAL_BUSYSPI外设正忙(上一次传输未完成)
HAL_TIMEOUT传输超时(未在指定时间内完成)


HAL_SPI_Receive

HAL_SPI_Receive通过SPI接口以阻塞(轮询)模式接收指定长度的数据。

HAL_StatusTypeDef HAL_SPI_Receive(
    SPI_HandleTypeDef *hspi, 
    uint8_t *pData, 
    uint16_t Size, 
    uint32_t Timeout
);

参数说明

参数类型说明
hspiSPI_HandleTypeDef*指向SPI外设句柄的指针,包含SPI配置信息(如SPI1、SPI2等)
pDatauint8_t*接收数据的缓冲区指针(用于存储从设备返回的数据)
Sizeuint16_t待接收数据的字节数
Timeoutuint32_t超时时间(单位:毫秒),若超时,函数返回HAL_TIMEOUT

返回值

返回值说明
HAL_OK接收成功完成
HAL_ERROR发生错误(如SPI未初始化、DMA错误等)
HAL_BUSYSPI外设正忙(上一次传输未完成)
HAL_TIMEOUT接收超时(未在指定时间内完成)


HAL_SPI_TransmitReceive

全双工同步通信:在发送数据的同时接收数据,每个发送字节对应一个接收字节。高效数据交换,适用于需要高速双向数据交换的场景(如传感器数据读取、通信协议交互)。

HAL_StatusTypeDef HAL_SPI_TransmitReceive(
    SPI_HandleTypeDef *hspi,
    uint8_t *pTxData,
    uint8_t *pRxData,
    uint16_t Size,
    uint32_t Timeout
);

参数说明

参数类型说明
hspiSPI_HandleTypeDef*指向SPI外设句柄的指针
pTxDatauint8_t*发送数据缓冲区指针(需发送的数据)
pRxDatauint8_t*接收数据缓冲区指针(用于存储接收的数据)
Sizeuint16_t发送/接收数据的字节数(需确保发送和接收数据长度一致)
Timeoutuint32_t超时时间(单位:毫秒)

返回值

返回值说明
HAL_OK全双工传输成功完成
HAL_ERROR参数错误或SPI初始化失败
HAL_BUSYSPI外设正忙(上一次操作未完成)
HAL_TIMEOUT超时未完成传输

flsh结构

存储单元大小操作特性
页(Page)256字节最小编程单位
扇区(Sector)4KB最小擦除单位
块(Block)64KB大容量擦除单位
整片(Chip)8MB(64Mbit)全片擦除操作

存储特性

  • 写入前必须擦除(1→0需要擦除,0→1可直接写)

  • 每个位只能从1变为0,擦除操作会将整个扇区恢复为全1(0xFF)

  • 典型擦写寿命:10万次(需注意磨损均衡)

flsh写入数据过程

根据数据写入过程函数设计如下

static void SaveLEDState(uint8_t ledState){
    //写使能
    //扇区删除
    HAL_Delay(100)
    //写使能
    //扇区删除
    HAL_Delay(10)
}

写使能

操作时序:
CS拉低 → 发送0x06 → CS拉高
作用:开启写操作许可(每次写操作前必须执行)

实现代码

uint8_t writeEnableCmd[] = {0x06};

HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);

HAL_SPI_Transmit(&hspil,writeEnableCmd,1,HAL_MAX_DELAY);

HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);

扇区擦除

操作时序:
CS拉低 → 
发送0x20 → 
发送24位地址(仅使用高16位确定扇区)→ 
CS拉高
擦除时间:典型值400ms

实现代码

uint8_t sectorEraseCmd[] = {0x20,0x00,0x00,0x00};

HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);

HAL_SPI_Transmit(&hspil1,sectorEraseCmd,4,HAL_MAX_DELAY);

HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);

页编程

操作时序:
CS拉低 → 
发送0x02 → 
发送24位地址(A23-A0)→ 
发送数据(最多256字节)→ 
CS拉高
注意:地址必须页对齐(如0x000000, 0x000100等)

实现代码

uint8_t pageProgCmd[5];
pageProgCmd[0] = 0x02;

pageProgCmd[1] = 0;
pageProgCmd[2] = 0;
pageProgCmd[3] = 0;

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);

实现代码

static void SaveLEDState(uint8_t ledState){
//写使能
	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);
//扇区擦除
	uint8_t sectorEraseCmd[] = {0x20,0x00,0x00,0x00};

	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);
	HAL_SPI_Transmit(&hspi1,sectorEraseCmd,4,HAL_MAX_DELAY);
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
//延迟
	HAL_Delay(100);
//写使能
	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);
//页编程
	uint8_t pageProgCmd[5];
	pageProgCmd[0] = 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);
}

上述方法加到判断按钮状态的if语句中去。

flsh读数据

操作时序:
CS拉低 → 
发送0x03 → 
发送24位地址 → 
连续读取数据 → 
CS拉高
特点:支持跨页连续读取

实现代码

static uint8_t LoadledState(){

    uint8_t readDateCmd[] = {0x03,0x00,0x00,0x00};
    uint8_t ledState = 0xff;

    HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);
    HAL_SPI_Transmit(&hspi1,readDateCmd,4,HAL_MAX_DELAY);
    HAL_SPI_Receive(&hspi1,&ledState ,1,HAL_MAX_DELAY);
    HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);

    return ledState ;
} 

读取flsh内容函数要在程序加载前执行并对返回的数据进行判断并进行亮灯或没灯操作。


实验步骤

  1. 按照硬件连接图接线

  2. 使用STM32CubeMX生成工程

  3. 实现主程序逻辑

  4. 编译下载程序到开发板

  5. 测试流程:

    • 首次上电:LED默认状态

    • 按下按键 → LED状态切换并保存

    • 断电后重新上电 → 恢复上次保存的状态

    • 再次按键 → 状态切换并保存

相关文章:

  • KubeKey一键安装部署k8s集群和KubeSphere详细教程
  • 前端js进阶,ES6语法,包详细
  • ViT 模型介绍(一)——综述
  • VS2022配置FFMPEG库基础教程
  • 使用大语言模型(Deepseek)构建一个基于 SQL 数据的问答系统
  • DeepSeek行业应用实践报告-智灵动力【112页PPT全】
  • Layer2 扩容解决方案详解
  • 人工智能(AI):科技新纪元的领航者
  • 关于使用带elementplus前缀图标的步骤
  • vmvare kali如何配置桥接模式进行上网
  • vscode无法预览Markdown在线图片链接
  • 【机器学习】【KMeans聚类分析实战】用户分群聚类详解——SSE、CH 指数、SC全解析,实战电信客户分群案例
  • CSS笔记一
  • 0_关闭防火墙、设置静态IP
  • C++/JavaScript ⭐算法OJ⭐ 链表相交
  • C++ 游戏开发:从零到英雄的进阶之旅
  • [ComfyUI]Recraft贴图开源方案,实现服装印花自由
  • Debezium 报错:“The db history topic is missing” 的处理方法
  • Fences 5深度解析:一键打造超高效整洁桌面
  • QT中经常出现的用法:组合
  • 自己的服务器做网站域名解析/百度怎么找人工客服
  • 王者做网站/百度升级最新版本下载安装
  • 看24小时b站直播/网络营销方法和手段
  • 提高网站访问速度/软文广告经典案例分析
  • 酒泉网站建设有限公司/培训机构连锁加盟
  • 个人网站介绍怎么写/大型网站seo课程