当前位置:   article > 正文

基于STM32+SPI+FLASH芯片跨页读写擦除的实现_芯片的擦除,覆盖,写入

芯片的擦除,覆盖,写入

前言     


        本次我们学习一下STM32F103关于SPI对存储芯片的读写,针对flash芯片不能跨页写的限制,写一个扇区写入的函数,不需要自己手动跨页,使用更方便,为字库,图片烧入做铺垫。

本篇博客大部分是自己收集和整理,如有侵权请联系我删除。

本次实验板子使用的是正点原子精英版,芯片是STM32F103ZET6,需要资料可以@我拿取。

交流群:717237739

如果觉得有用点赞关注收藏三连,多谢支持

本博客内容原创,创作不易,转载请注明

没有初步认识SPI协议的,可以先看看我之前的博客:SPI协议介绍
没有初步认识W25QXX芯片的,可以先看看我之前的博客:w25QXX芯片读写函数

FLASH 驱动手册自取

一. FLASH 跨页读写,分区和注意事项

1. 内部区域写入划分等级

W25Q16,W25Q32 ,W25Q64 系列的FLASH存储器分别有8192,16384,32768可编程页,每页256个字节。芯片内部分为了块,扇区,页的编程指令和擦除指令,下面来看看他们之间的关系。
内部区分是从 块 -> 扇区 -> 页->页地址字节写入

2.FLASH 注意事项

写入操作解读:

1.写使能的操作:相当于FLASH芯片做的保护措施,防止误操作写入其他数据,意义和手机的解锁差不多,其实芯片内部的FLASH也是需要解锁关锁操作的,芯片类型都一样。

2.每个数据位只能由1改为0,是因为FLASH芯片自身的限制决定,它没有完全任意修改的能力,所以芯片内部无数据的时候默认为0XFF,表示为空。

3.针对第二个限制,所以我们在每次写入flash的时候,都是需要保证芯片内部是0XFF,就需要先擦除在写入,这样数据就不会出错了。

4.擦除不能针对指定地址擦除,在W25QXX芯片介绍中就有说明,最小只能扇区擦除,所以每次都是需要擦除最小4K的扇区。

5.每次写入最多写入255个字节,芯片规定不能跨页写,写入的数据超过之后就会重新从该页的首地址写入,数据就会出错,针对这一现象等会我们分解数据进行写入。

6.写入结束之后,芯片会进入busy状态,在这个状态下不能进行读写操作。每次写完之后我们都可以判断一下这个标志位,防止出错。

7.busy状态位下,不能进行读写,此处该注意。

SPI读写函数:(可以查看之前的文章)

  1. //SPIx 读写一个字节
  2. //TxData:要写入的字节
  3. //返回值:读取到的字节
  4. u8 SPI2_ReadWriteByte(u8 TxData)
  5. {
  6. u8 retry=0;
  7. while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位
  8. {
  9. retry++;
  10. if(retry>200)return 0;
  11. }
  12. SPI_I2S_SendData(SPI2, TxData); //通过外设SPIx发送一个数据
  13. retry=0;
  14. while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) //检查指定的SPI标志位设置与否:接受缓存非空标志位
  15. {
  16. retry++;
  17. if(retry>200)return 0;
  18. }
  19. return SPI_I2S_ReceiveData(SPI2); //返回通过SPIx最近接收的数据
  20. }

二. FLASH 芯片ID 读取

读取芯片ID的意义是什么?

1.首先识别芯片的型号:不同型号的 Flash 存储器可能有不同的特性,包括容量、擦写速度、寿命等。这有助于系统在运行时适应不同型号的 Flash 存储器。

2.适应对应配置:针对不同的 Flash 存储器,可能需要特定的配置参数,如擦写和写入时的超时时间、擦写和写入的命令序列等。通过读取 Flash ID,系统可以在运行时动态配置这些参数,以确保与连接的 Flash 存储器的兼容性。

3.针对固件更新检测:在一些应用中,可能需要进行固件更新。读取 Flash ID 可以帮助系统确认连接的 Flash 存储器是否与新固件兼容。这有助于防止在错误的 Flash 存储器上执行固件更新,从而避免不可逆的系统问题。

4.错误检测和恢复:Flash 存储器可能会受到环境因素或硬件问题的影响,导致数据损坏或存储器出现错误。读取 Flash ID 可以用于检测存储器是否正常工作,并在可能的情况下采取适当的措施,如进行错误检测和修复。

  1. //读取芯片ID
  2. //返回值如下:
  3. //0XEF13,表示芯片型号为W25Q80
  4. //0XEF14,表示芯片型号为W25Q16
  5. //0XEF15,表示芯片型号为W25Q32
  6. //0XEF16,表示芯片型号为W25Q64
  7. //0XEF17,表示芯片型号为W25Q128
  8. u16 W25QXX_ReadID(void)
  9. {
  10. u16 Temp = 0;
  11. W25QXX_CS=0;
  12. SPI2_ReadWriteByte(0x90);//发送读取ID命令
  13. SPI2_ReadWriteByte(0x00);
  14. SPI2_ReadWriteByte(0x00);
  15. SPI2_ReadWriteByte(0x00);
  16. Temp|=SPI2_ReadWriteByte(0xFF)<<8;
  17. Temp|=SPI2_ReadWriteByte(0xFF);
  18. W25QXX_CS=1;
  19. return Temp;
  20. }

三 . FLASH 扇区读写实现

注意:因为一些写使能,写禁止的一些函数在其他文章已经封装了,这些函数不懂的可以看看主页的W25QXX芯片介绍,这些就不多介绍了,下面主要实现扇区跨页写的实现流程:

1.在指定页写入少于256个字节的数据函数,此处不做擦除,是因为在扇区函数做了擦除,这里如果想单独写入,也可以在写入之前加一个读取地址下的数据判断是否需要擦除。

  1. //无检验写SPI FLASH
  2. //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
  3. //具有自动换页功能
  4. //在指定地址开始写入指定长度的数据,但是要确保地址不越界!
  5. //pBuffer:数据存储区
  6. //WriteAddr:开始写入的地址(24bit)
  7. //NumByteToWrite:要写入的字节数(最大65535)
  8. //CHECK OK
  9. void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
  10. {
  11. u16 pageremain;
  12. pageremain=256-WriteAddr%256; //单页剩余的字节数 关键先求出这个页剩余的空字节位置
  13. if( NumByteToWrite<=pageremain ) //写入的字节小于剩下的字节数
  14. {
  15. pageremain=NumByteToWrite;//不大于256个字节,直接获取写入的字节数
  16. }
  17. while(1)
  18. {
  19. W25QXX_Write_Page(pBuffer,WriteAddr,pageremain); //在对应的地址下写入数据,长度不超过该页的终地址
  20. //上面的写入,如果小于就正常写入,如果大于该页,再做判断是否需要跨页
  21. if( NumByteToWrite==pageremain )
  22. {
  23. break;//写入结束了 直接退出while循环,写入完成
  24. }
  25. else //NumByteToWrite>pageremain
  26. {
  27. pBuffer+=pageremain; //传入的数据,往写入完成的数据作偏移
  28. WriteAddr+=pageremain; //传入的地址,往写入完成的做地址偏移
  29. NumByteToWrite-=pageremain; //减去已经写入了的字节数
  30. if(NumByteToWrite>256)
  31. {
  32. pageremain=256; //一次可以写入256个字节
  33. }
  34. else
  35. {
  36. pageremain=NumByteToWrite; //不够256个字节了,就直接等于当前字节数
  37. }
  38. }
  39. }
  40. }

2.在扇区内连续跨页写入数据,先擦除后写入。

  1. //写SPI FLASH
  2. //在指定地址开始写入指定长度的数据
  3. //该函数带擦除操作!
  4. //pBuffer:数据存储区
  5. //WriteAddr:开始写入的地址(24bit)
  6. //NumByteToWrite:要写入的字节数(最大65535)
  7. u8 W25QXX_BUFFER[4096];
  8. void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
  9. {
  10. u32 secpos;
  11. u16 secoff;
  12. u16 secremain;
  13. u16 i;
  14. u8 * W25QXX_BUF;
  15. W25QXX_BUF=W25QXX_BUFFER; //指向首地址
  16. secpos=WriteAddr/4096;//扇区地址,第几个扇区
  17. secoff=WriteAddr%4096;//在扇区内的偏移地址
  18. secremain=4096-secoff;//扇区剩余空间大小
  19. //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用
  20. if(NumByteToWrite<=secremain) //不大于4096个字节 写不完一个扇区内的空间大小,即不跨扇区
  21. {
  22. secremain=NumByteToWrite; //假设一个扇区原来已经写了3K的数据,后来再写入0.5K,结果还有0.5K空间没写,
  23. //这样就不用全部擦除剩余的1K空间,只要擦除要写入的0.5K的空间就可以了
  24. }
  25. while(1)
  26. {
  27. W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容,保存到W25QXX_BUF
  28. for(i=0;i<secremain;i++)//校验数据,对数组的数据进行遍历
  29. {
  30. if(W25QXX_BUF[secoff+i]!=0XFF)break; //需要擦除,从secoff的位置开始 读出来的内容如果不是0xff,需要擦除,手册上有说明P38
  31. }
  32. if(i<secremain)//需要擦除 假设写入10个数,而写到第8个的时候,上面的校验出错,那么就要擦除了
  33. {
  34. W25QXX_Erase_Sector(secpos); //擦除这个扇区,这时候这个扇区的内容已经被存放在W25QXX_BUF,故可以擦除
  35. for(i=0;i<secremain;i++) //复制
  36. {
  37. W25QXX_BUF[i+secoff]=pBuffer[i]; //之前存放的数据在上面也已经存放到 //W25QXX_BUF中了,所以从 secoff 处开始存放数据(要写入的数据),
  38. //这样就把之前存放的数据和后来写入的数据都存入W25QXX_BUF中了。
  39. }
  40. W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区
  41. }else
  42. W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写入整个扇区 secpos*4096就是地址
  43. if(NumByteToWrite==secremain)
  44. {
  45. break;//写入结束了
  46. }
  47. else//写入未结束 写满了一个扇区,开始往第二个扇区写数据
  48. {
  49. secpos++;//扇区地址增1 已经是下一个扇区了,所以++
  50. secoff=0;//偏移位置为0 已经是一个新的扇区了,所以为0
  51. pBuffer+=secremain; //源数据指针偏移 已经写了secremain个数据,所以需要偏移
  52. WriteAddr+=secremain; //写地址偏移
  53. NumByteToWrite-=secremain; //字节数递减
  54. //已经写了secremain个数据 故减去secremain个数据
  55. if(NumByteToWrite>4096) //下一个扇区还是写不完
  56. {
  57. secremain=4096; //下一下扇区的内容就是4096
  58. }
  59. else
  60. {
  61. secremain=NumByteToWrite; //下一个扇区可以写完了
  62. }
  63. }
  64. }
  65. }

总结:

      以上就是关于SPI对FLASH芯片跨页写入的方法了,难度不是很大,其实就是一种代码逻辑的实现,主要就是注意对各种函数的调用和做一些芯片限制的判断,这样我们就能用一条函数实现我们需要的功能了。下一篇博客主要介绍字库和图片的写入存储,大家如果对我的博客有疑问或者错误,可以@我修改,大家相互交流。

交流群:717237739

如果觉得有用点赞关注收藏三连,多谢支持

本博客内容原创,创作不易,转载请注明

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/429251
推荐阅读
相关标签
  

闽ICP备14008679号