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

stm32 W25Q数据存储

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、cubemx配置
  • 二、keil中文件修改与配置
  • 三、几个重要函数的说明
  • 四、DMA方式传输(待写)
  • 总结


前言

W25Q128 容量为128位 128/8 = 16 也就是16M
在这里插入图片描述
在这里插入图片描述
擦除之前必须写使能
在这里插入图片描述
在这里插入图片描述
写数据的存储单元必须是被擦除过的也就是必须是0XFF,不过不是则写入无效。

在这里插入图片描述
此图来源于


一、cubemx配置

全双工SPI
不使用硬件NSS
w25q虽然数据手册写可以达到30mbit/s但是还是建议用10以下的,高了偶尔会出错。

这里参考b站的视频
视频中用的spi3模式,我用的1模式
spi配置
在这里插入图片描述
片选引脚配置
初始化为high 保证上电以后未被片选
在这里插入图片描述

二、keil中文件修改与配置

使用到的代码为b站视频中的链接1 链接2

.h文件这里进行了修改
在这里插入图片描述
在这里插入图片描述
这里注意
前两个 值对任意W25Q都是一样的,不需要修改,4096 也并未用到,目前暂时不修改

//===========Flash存储芯片W25Q128的存储容量参数================
#define		FLASH_PAGE_SIZE			256		//一个Page是256字节
#define		FLASH_SECTOR_SIZE		4096	//一个Sector是4096字节
#define		FLASH_SECTOR_COUNT		4096	//总共4096个 Sector

三、几个重要函数的说明

w25q必须以页为单位来写入,写入前必须要擦除,擦除必须以扇区进行擦除
如果写入前没有擦除,虽然不会报错,但是写入是无效的。
重要函数
Flash_EraseChip();//擦除整个芯片大概20多秒
Flash_EraseBlock64K(globalAddr);//擦除一个块
Flash_EraseSector(memAddress1);//擦除一个扇区 这个是擦除的最单位了
Flash_Addr_byBlockSectorPage(uint8_t BlockNo, uint8_t SubSectorNo, uint8_t SubPageNo) 这个函数是用来得到 块 扇区 页 的那个绝对地址的。

举例
擦除一个扇区

	uint32_t memAddress1 = Flash_Addr_byBlockSectorPage(0, 0, 1);Flash_EraseSector(memAddress1);

向两个页分别写数据

void Flash_TestWrite() {uint8_t BlockNo = 0;uint8_t SubSectorNo = 0;uint8_t SubPageNo = 0;uint32_t memAddress = 0;memAddress = Flash_Addr_byBlockSectorPage(BlockNo, SubSectorNo, SubPageNo);uint8_t bufStr1[30] = "Hello222444";Flash_WriteInPage(memAddress, bufStr1, strlen(bufStr1) + 1);printf("Write in Page0:0\n");printf( "%s",bufStr1);uint8_t bufStr2[30] = "Hello111333";Flash_WriteInPage(memAddress + 100, bufStr2, strlen(bufStr2) + 1);printf( "Write in Page0:100\n");printf( "%s", bufStr2);uint8_t bufPage[FLASH_PAGE_SIZE];for (uint16_t i = 0; i < FLASH_PAGE_SIZE; ++i) {bufPage[i] = 0;}SubPageNo = 1;memAddress = Flash_Addr_byBlockSectorPage(BlockNo, SubSectorNo, SubPageNo);Flash_WriteInPage(memAddress, bufPage, FLASH_PAGE_SIZE);printf("Write 0~255 in Page1");
}

向两个页分别读数据

void Flash_TestRead() {uint8_t BlockNo = 0;uint8_t SubSectorNo = 0;uint8_t SubPageNo = 0;uint32_t memAddress = Flash_Addr_byBlockSectorPage(BlockNo, SubSectorNo, SubPageNo);uint8_t bufStr[50];Flash_ReadBytes(memAddress, bufStr, 50);printf( "Read in Page0:0 ");printf( "%s", bufStr);Flash_ReadBytes(memAddress + 100, bufStr, 50);printf( "Read in Page0:100 ");printf( "%s", bufStr);SubPageNo = 1;memAddress = Flash_Addr_byBlockSectorPage(BlockNo, SubSectorNo, SubPageNo);uint8_t randData12 = Flash_ReadOneByte(memAddress + 12);uint8_t randData136 = Flash_ReadOneByte(memAddress + 136);uint8_t randData210 = Flash_ReadOneByte(memAddress + 210);uint8_t tempStrRandData[30];sprintf(tempStrRandData, "Page1[12]=%d,[136]=%d,[210]=%d",randData12, randData136, randData210);printf( "%s", tempStrRandData);
}

有了 上面两个函数我们就可以做一些小实验了。
第一次使用的时候先对芯片进行整体的擦除
让后调用Flash_TestWrite();函数,此时调用Flash_TestRead() ;函数来读取是正常的
修改代码,不对w25q进行任何擦除直接把Flash_TestWrite();中的bufPage[i] = 0;写成bufPage[i] = i;
这时候读取函数读到的值就是上一次的,说明虽然没有报错,但是并未写入成功
如果对芯片进行该扇区的擦除,再写入即可在读取中获取新的写入。记住写入是按照页来写,擦除是按照扇区擦,一个扇区里面有16个页。

在使用 W25Q 系列的 SPI Flash 存储器时,确实存在写入操作以页为单位(通常为256字节),而擦除操作以扇区为单位(通常为4KB)的限制。这意味着如果你只想修改某个页的数据,而不影响同一扇区内的其他数据,必须采取一些策略来避免丢失扇区中其他页的数据。
解决方法
通常有两种常见的解决方案来应对这个问题:
1扇区读出、修改和写回
步骤:
读出整个扇区:在你需要修改某一页的数据时,先读取整个扇区的数据到内存中。
修改页数据:在内存中修改目标页的数据。
擦除扇区:执行扇区擦除操作。
写回数据:将修改后的数据重新写回该扇区,包括未修改的页数据和修改后的页数据
2使用缓存(缓存区)
步骤
在内存中保留一个缓冲区:该缓冲区的大小等同于一个扇区大小。
管理缓存:每次写入时,先更新缓存区中的数据,然后定期或在缓存区满时写回 SPI Flash。
擦除与写回:当要写入到 Flash 时,执行上述 “扇区读出、修改和写回” 的操作。
优点:可以减少对 Flash 的擦写次数,延长 Flash 的寿命。
总结
无论选择哪种方式,主要思想都是避免直接修改 Flash 中的数据,而是通过在内存中暂存整个扇区的数据,再进行更新和写回操作。这种方式能够有效避免因为写入新数据而导致同一扇区内其他数据的丢失问题。

1扇区读出、修改和写回参考代码

#define SECTOR_SIZE 4096  // W25Q扇区大小
#define PAGE_SIZE 256     // W25Q页大小uint8_t sectorBuffer[SECTOR_SIZE];void updatePageInSector(uint32_t sectorAddr, uint16_t pageOffset, uint8_t* data, uint16_t length) {// 1. 读出整个扇区W25Q_Read(sectorBuffer, sectorAddr, SECTOR_SIZE);// 2. 修改指定页的数据memcpy(&sectorBuffer[pageOffset * PAGE_SIZE], data, length);// 3. 擦除扇区W25Q_EraseSector(sectorAddr);// 4. 将修改后的数据写回整个扇区W25Q_Write(sectorBuffer, sectorAddr, SECTOR_SIZE);
}

注意:W25Q_ReadW25Q_Write 是 SPI Flash 的读写函数;W25Q_EraseSector 是擦除扇区的函数。这里假设你要修改的页在 sectorAddr 的偏移量为 pageOffset 页。

四、DMA方式传输(待写)


总结

提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • C语言的结构体类型
  • Rust Windows下编译 静态链接VCRuntime140.dll
  • 华为 HCIP 认证费用和报名资格
  • 【5G QoS】详解5G QoS端到端工作机制
  • Linux tr命令
  • CMake构建学习笔记16-使用VS进行CMake项目的开发
  • [论文笔记]ChatQA: Surpassing GPT-4 on Conversational QA and RAG
  • 【LLM多模态】文生视频评测基准VBench
  • django-admin自定义功能按钮样式
  • 数据结构之栈和队列的应用
  • 【物联网技术大作业】设计一个智能家居的应用场景
  • 树莓派Pico开发板简介
  • 【网络】高级IO——阻塞IO和非阻塞IO的实现
  • 【项目一】基于pytest的自动化测试框架———解读requests模块
  • 【App】React Native
  • Android Volley源码解析
  • - C#编程大幅提高OUTLOOK的邮件搜索能力!
  • create-react-app项目添加less配置
  • Druid 在有赞的实践
  • es的写入过程
  • JAVA_NIO系列——Channel和Buffer详解
  • javascript 总结(常用工具类的封装)
  • javascript面向对象之创建对象
  • Java-详解HashMap
  • js继承的实现方法
  • Map集合、散列表、红黑树介绍
  • MySQL用户中的%到底包不包括localhost?
  • Quartz实现数据同步 | 从0开始构建SpringCloud微服务(3)
  • Vue官网教程学习过程中值得记录的一些事情
  • Work@Alibaba 阿里巴巴的企业应用构建之路
  • 发布国内首个无服务器容器服务,运维效率从未如此高效
  • 工作手记之html2canvas使用概述
  • 构建工具 - 收藏集 - 掘金
  • 关于extract.autodesk.io的一些说明
  • 微服务入门【系列视频课程】
  • 微信开源mars源码分析1—上层samples分析
  • AI又要和人类“对打”,Deepmind宣布《星战Ⅱ》即将开始 ...
  • 阿里云ACE认证之理解CDN技术
  • #gStore-weekly | gStore最新版本1.0之三角形计数函数的使用
  • #LLM入门|Prompt#2.3_对查询任务进行分类|意图分析_Classification
  • #Z2294. 打印树的直径
  • #我与Java虚拟机的故事#连载07:我放弃了对JVM的进一步学习
  • (javascript)再说document.body.scrollTop的使用问题
  • (二)延时任务篇——通过redis的key监听,实现延迟任务实战
  • (免费领源码)Python#MySQL图书馆管理系统071718-计算机毕业设计项目选题推荐
  • (十六)一篇文章学会Java的常用API
  • (四)JPA - JQPL 实现增删改查
  • (五)MySQL的备份及恢复
  • (转)视频码率,帧率和分辨率的联系与区别
  • .net core docker部署教程和细节问题
  • .NET Core 通过 Ef Core 操作 Mysql
  • .net wcf memory gates checking failed
  • .net 开发怎么实现前后端分离_前后端分离:分离式开发和一体式发布
  • .net 生成二级域名
  • .NET简谈设计模式之(单件模式)