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提供了大量能使我们快速便捷地处理数据的函数和方法。

    作者:林叔叔336

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32与W25Q数据存储技术深度解析

    发表回复