当前位置:   article > 正文

SPI_FLASH(W25X16)擦除、读数据以及写数据等操作_spi flash 擦除

spi flash 擦除

SPI_FLASH(W25X16)擦除、读数据以及写数据等操作

串行FLASH_W25X16简介

FLASH的特性

FLSAH 存储器又称闪存,它与 EEPROM 都是掉电后数据不丢失的存储器,但 FLASH存储器容量普遍大于 EEPROM,现在基本取代了它的地位。我们生活中常用的 U盘、SD卡、SSD 固态硬盘以及我们 STM32 芯片内部用于存储程序的设备,都是 FLASH 类型的存储器。在存储控制上,最主要的区别是FLASH 芯片只能一大片一大片地擦写,而EEPROM可以单个字节擦写。

SPI_Flash W25X16简介

W25X16有8192个可编程页,每页256字节。用“页编程指令”每次就可以编程256个字节。用扇区擦除指令每次可以擦除16页,用块擦除指令每次可以擦除256页,用整片擦除指令即可以擦除整个芯片。W25X16有512个可擦除扇区或32个可擦除块。

W25X16硬件连线

CS:片选引脚,低电平有效,连接到STM32-PH2管脚

SO:连接到STM32-PB4管脚(MISO)

SI:连接到STM32-PB5管脚(MOSI)

CLK:连接到STM32-PA5管脚(CLK)

WP:写保护管脚,低电平有效,有效时禁止写入数据。接电源未使用

HOLD: HOLD 引脚可用于暂停通讯,该引脚为低电平时,通讯暂停,未使用

W25X16控制指令

W25X16操作方式

        我们需要了解如何对 FLASH 芯片进行读写。FLASH 芯片自定义了很多指令,我们通过控制 STM32利用 SPI总线向 FLASH 芯片发送指令,FLASH芯片收到后就会执行相应的操作。

        而这些指令,对主机端(STM32)来说,只是它遵守最基本的 SPI通讯协议发送出的数据,但在设备端(FLASH 芯片)把这些数据解释成不同的意义,所以才成为指令。

        注:可查看FLASH 芯片的数据手册《W25X16》,可了解各种它定义的各种指令的功能及指令格式

cubemx配置

w25x16.h文件

  1. #ifndef __W25X16_H
  2. #define __W25X16_H
  3. #include "stm32f4xx_hal.h"
  4. #define W25X_ManufactDeviceID 0x90 /* Read identification */
  5. #define sFLASH_CMD_WREN 0x06 /* Write enable instruction */
  6. #define sFLASH_CMD_RDSR 0x05 /* Read Status Register instruction */
  7. #define sFLASH_CMD_SE 0x20 /* Sector Erase instruction */
  8. #define sFLASH_CMD_WRITE 0x02 /* Write to Memory instruction */
  9. #define sFLASH_CMD_READ 0x03 /* Read from Memory instruction */
  10. #define sFLASH_DUMMY_BYTE 0x00
  11. #define sFLASH_BUSY_FLAG 0x01
  12. #define sFLASH_SPI_PAGESIZE 0x100
  13. /* 选中芯片: 拉低片选 */
  14. #define sFLASH_CS_LOW() HAL_GPIO_WritePin(GPIOH,GPIO_PIN_2,GPIO_PIN_RESET)
  15. /* 释放芯片: 拉高片选 */
  16. #define sFLASH_CS_HIGH() HAL_GPIO_WritePin(GPIOH,GPIO_PIN_2,GPIO_PIN_SET)
  17. uint8_t sFLASH_SendByte(uint8_t byte);
  18. uint16_t sFLASH_ReadID(void);
  19. void sFLASH_EraseSector(uint32_t SectorAddr);
  20. void sFLASH_WriteBuffer(uint8_t* pBuffer, uint32_t WriteAddr, uint32_t NumByteToWrite);
  21. void sFLASH_WritePage(uint8_t* pBuffer, uint32_t WriteAddr, uint32_t NumByteToWrite);
  22. void sFLASH_ReadBuffer(uint8_t* pBuffer, uint32_t ReadAddr, uint32_t NumByteToRead);

 以下函数皆在w25x16.c文件中编写

  1. #include "w25x16.h"
  2. extern SPI_HandleTypeDef hspi1;
  3. /*读写一个字节函数*/
  4. uint8_t sFLASH_SendByte(uint8_t byte)
  5. {
  6. uint8_t TX_DATA = byte;
  7. uint8_t RX_DATA = 0;
  8. HAL_SPI_TransmitReceive(&hspi1, &TX_DATA ,&RX_DATA , 1, 1000);
  9. return RX_DATA;
  10. }

读制造商/设备ID(90)

        该指令通常在调试程序的时候用到,判断SPI通信是否正常。该指令通过主器件拉低/CS片选使能器件开始传输,首先通过DI线传输“90H”指令,接着传输000000H的24位地址(A23-A0),之后从器件会通过DO线返回制造商ID(EFH)和设备ID。

(注:SPI为数据交换通信,主器件在发送“90H”指令时也会接收到一个字节FFH,但此数据为无效数据)

  1. /*读取设备ID*/
  2. uint16_t sFLASH_ReadID(void)
  3. {
  4. uint16_t FLASH_ID;
  5. uint8_t temp0,temp1;
  6. sFLASH_CS_LOW();
  7. sFLASH_SendByte(W25X_ManufactDeviceID);
  8. sFLASH_SendByte(sFLASH_DUMMY_BYTE);
  9. sFLASH_SendByte(sFLASH_DUMMY_BYTE);
  10. sFLASH_SendByte(sFLASH_DUMMY_BYTE);
  11. temp0 = sFLASH_SendByte(sFLASH_DUMMY_BYTE);
  12. temp1 = sFLASH_SendByte(sFLASH_DUMMY_BYTE);
  13. sFLASH_CS_HIGH();
  14. FLASH_ID = (temp0 << 8) | temp1;
  15. return FLASH_ID;
  16. }

写使能命令(06H)

在向 FLASH 芯片存储矩阵写入数据前,首先要使能写操作,通过“WriteEnable”命令即可写使能

  1. /*写使能*/
  2. void sFLASH_WriteEnable(void)
  3. {
  4. sFLASH_CS_LOW();
  5. sFLASH_SendByte(sFLASH_CMD_WREN);
  6. sFLASH_CS_HIGH();
  7. }

扇区擦除(20H)

        由于 FLASH 存储器的特性决定了它只能把原来为“1”的数据位改写成“0”,而原来为“0”的数据位不能直接改写为“1”。所以这里涉及到数据“擦除”的概念。

        在写入前,必须要对目标存储矩阵进行擦除操作,把矩阵中的数据位擦除为“1”,在数据写入的时候,如果要存储数据“1”,那就不修改存储矩阵,在要存储数据“0”时,才更改该位。

  1. /*擦除扇区*/
  2. void sFLASH_EraseSector(uint32_t SectorAddr)
  3. {
  4. sFLASH_WriteEnable(); //开启写使能
  5. sFLASH_CS_LOW();
  6. sFLASH_SendByte(sFLASH_CMD_SE);
  7. sFLASH_SendByte( (SectorAddr>>16) & 0xff); //传送高8位
  8. sFLASH_SendByte( (SectorAddr>>8) & 0xff); //传送中8位
  9. sFLASH_SendByte( (SectorAddr>>0) & 0xff); //传送低8位
  10. sFLASH_CS_HIGH();
  11. /*等待擦除完成*/
  12. sFLASH_WaitForEnd();
  13. }

读状态寄存器(05H)

        FLASH 芯片向内部存储矩阵写入数据需要消耗一定的时间,并不是在总线通讯结束的一瞬间完成的,所以在写操作后需要确认FLASH 芯片“空闲”。我们只需要读取状态寄存器SRP的S0即可(当这个位为“1”时,表明FLASH芯片处于忙碌状态,它可能正在对内部的存储矩阵进行“擦除”或“数据写入”的操作)

  1. /*等待擦除或者写数据完成*/
  2. void sFLASH_WaitForEnd(void)
  3. {
  4. uint8_t sr_value = 0;
  5. sFLASH_CS_LOW();
  6. sFLASH_SendByte(sFLASH_CMD_RDSR);
  7. do{
  8. sr_value = sFLASH_SendByte(sFLASH_DUMMY_BYTE);
  9. }while( sr_value & sFLASH_BUSY_FLAG);
  10. sFLASH_CS_HIGH();
  11. }

读数据(03H)

        读数据指令可从存储器依次一个或多个数据字节,该指令通过主器件拉低/CS电平使能设备开始传输,然后传输“03H”指令,接着通过DI管脚传输24位地址,从器件接到地址后,寻址存储器中的数据通过DO引脚输出。每传输一个字节地址自动递增,所以只要时钟继续传输,可以不断读取存储器中的数据。

  1. /*读数据*/
  2. void sFLASH_ReadBuffer(uint8_t* pBuffer, uint32_t ReadAddr, uint32_t NumByteToRead)
  3. {
  4. sFLASH_CS_LOW();
  5. sFLASH_SendByte(sFLASH_CMD_READ);
  6. sFLASH_SendByte( (ReadAddr>>16) & 0xff); //传送高8位
  7. sFLASH_SendByte( (ReadAddr>>8) & 0xff); //传送中8位
  8. sFLASH_SendByte( (ReadAddr>>0) & 0xff); //传送低8位
  9. while(NumByteToRead--)
  10. {
  11. * pBuffer = sFLASH_SendByte(sFLASH_DUMMY_BYTE);
  12. pBuffer++;
  13. }
  14. sFLASH_CS_HIGH();
  15. }

写数据—页编程(02H)

        页编程指令可以在已擦除的存储单元中写入256个字节。该指令先拉低/CS引脚电平,接着传输“02H”指令和24位地址。后面接着传输至少一个数据字节,最多256字节。

注:当数据写到一个新的扇区的时候,需要重新发起一个页编程信号才能继续写入数据。

  1. /*页写入,只能在页头开始写且不能超过页大小*/
  2. void sFLASH_WritePage(uint8_t* pBuffer, uint32_t WriteAddr, uint32_t NumByteToWrite)
  3. {
  4. if(NumByteToWrite > sFLASH_SPI_PAGESIZE )
  5. {
  6. NumByteToWrite = sFLASH_SPI_PAGESIZE;
  7. printf("写数据量太大,超过一页的大小\n");
  8. }
  9. sFLASH_WriteEnable(); //开启写使能
  10. sFLASH_CS_LOW();
  11. sFLASH_SendByte(sFLASH_CMD_WRITE);
  12. sFLASH_SendByte( (WriteAddr>>16) & 0xff); //传送高8位
  13. sFLASH_SendByte( (WriteAddr>>8) & 0xff); //传送中8位
  14. sFLASH_SendByte( (WriteAddr>>0) & 0xff); //传送低8位
  15. while(NumByteToWrite--)
  16. {
  17. sFLASH_SendByte(* pBuffer);
  18. pBuffer++;
  19. }
  20. sFLASH_CS_HIGH();
  21. /*等待写完成*/
  22. sFLASH_WaitForEnd();
  23. }

改进后可以任意位置写入任意大小

  1. void sFLASH_WriteBuffer(uint8_t* pBuffer, uint32_t WriteAddr, uint32_t NumByteToWrite)
  2. {
  3. uint16_t NumOfPage, NumOfBytes, count, offset;
  4. offset = WriteAddr % sFLASH_SPI_PAGESIZE;
  5. count = sFLASH_SPI_PAGESIZE - offset;
  6. /* 处理页不对齐的情况*/
  7. if(offset && (NumByteToWrite > count ))
  8. {
  9. sFLASH_WritePage(pBuffer,WriteAddr,count);
  10. NumByteToWrite -= count;
  11. pBuffer += count;
  12. WriteAddr += count;
  13. }
  14. NumOfPage = NumByteToWrite / sFLASH_SPI_PAGESIZE;
  15. NumOfBytes = NumByteToWrite % sFLASH_SPI_PAGESIZE;
  16. if(NumOfPage)
  17. {
  18. while(NumOfPage--)
  19. {
  20. sFLASH_WritePage(pBuffer,WriteAddr,sFLASH_SPI_PAGESIZE);
  21. pBuffer += sFLASH_SPI_PAGESIZE;
  22. WriteAddr += sFLASH_SPI_PAGESIZE;
  23. }
  24. }
  25. if(NumOfBytes)
  26. {
  27. sFLASH_WritePage(pBuffer,WriteAddr,NumOfBytes);
  28. }
  29. }

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

闽ICP备14008679号