浅析FLASH读写----SPI原理及应用

电子说

1.3w人已加入

描述

  SPI Flash
 
  首先它是个Flash,Flash是什么东西就不多说了(非易失性存储介质),分为NOR和NAND两种(NOR和NAND的区别本篇不做介绍)。SPI一种通信接口。那么严格的来说SPI Flash是一种使用SPI通信的Flash,即,可能指NOR也可能是NAND。但现在大部分情况默认下人们说的SPI Flash指的是SPI NorFlash。早期Norflash的接口是parallel的形式,即把数据线和地址线并排与IC的管脚连接。但是后来发现不同容量的Norflash不能硬件上兼容(数据线和地址线的数量不一样),并且封装比较大,占用了较大的PCB板位置,所以后来逐渐被SPI(串行接口)Norflash所取代。同时不同容量的SPI Norflash管脚也兼容封装也更小。,至于现在很多人说起NOR flash直接都以SPI flash来代称。
 
  NorFlash根据数据传输的位数可以分为并行(Parallel,即地址线和数据线直接和处理器相连)NorFlash和串行(SPI,即通过SPI接口和处理器相连)NorFlash;区别主要就是:1、SPI NorFlash每次传输一bit位的数据,parallel连接的NorFlash每次传输多个bit位的数据(有x8和x16bit两种); 2、SPI NorFlash比parallel便宜,接口简单点,但速度慢。
 
  NandFlash是地址数据线复用的方式,接口标准统一(x8bit和x16bit),所以不同容量再兼容性上基本没什么问题。但是目前对产品的需求越来越小型化以及成本要求也越来越高,所以SPI NandFlash渐渐成为主流,并且采用SPI NANDFlash方案,主控也可以不需要传统NAND控制器,只需要有SPI接口接口操作访问,从而降低成本。另外SPI NandFlash封装比传统的封装也小很多,故节省了PCB板的空间。
 
  怎么用说白了对于Flash就是读写擦,也就是实现flash的驱动。先简单了解下spi flash的物理连接。
 
  之前介绍SPI的时候说过,SPI接口目前的使用是多种方式(具体指的是物理连线有几种方式),Dual SPI、Qual SPI和标准的SPI接口(这种方式肯定不会出现在连接外设是SPI Flash上,这玩意没必要全双工),对于SPI Flash来说,主要就是Dual和Qual这两种方式。具体项目具体看了,理论上在CLK一定的情况下, 线数越多访问速度也越快。我们项目采用的Dual SPI方式,即两线。
SPI

  应用环境如下: 控制器 STM32F103

  FLASH M25P64

  读写方式 SPI

  编程环境 MDK

  以SPI方式读写FLASH的基本流程如下:

  (1)设置SPI的工作模式。

  (2)flash初始化。

  (3)SPI写一个字节、写使能函数、写数据函数,读数据函数等编写。

  (4)主函数编写。

  一 设置SPI工作模式。

  宏定义

  #define SPI_FLASH_CS_LOW() GPIO_ResetBits(GPIOA,GPIO_Pin_4)

  #define SPI_FLASH_CS_HIGH() GPIO_SetBits(GPIOA,GPIO_Pin_4)

  /* M25P64 SPI Flash supported commands */

  #define WRSR 0x01 /* Write Status Register instruction */

  #define WREN 0x06 /* Write enable instruction */

  #define WRDI 0x04 /* Write disable instruction */

  #define READ 0x03 /* Read from Memory instruction */

  #define RDSR 0x05 /* Read Status Register instruction */

  #define RDID 0x9F /* Read identification */

  #define FAST_READ 0x0B /* Fast read Status Register instruction */

  #define SE 0xD8 /* Sector Erase instruction */

  #define BE 0xC7 /* Bulk Erase instruction */

  #define PP 0x02 /* Page prigrame instruction */

  #define RES 0xAB /* Sector Erase instruction */

  #define WIP_FLAG 0x01 /* Write In Progress (WIP) flag */

  #define DUMMY_BYTE 0xA5

  #define SIZE sizeof(TEXT_Buffer)

  #define SPI_FLASH_PAGESIZE 0x100

  #define FLASH_WriteAddress 0x000000

  #define FLASH_ReadAddress FLASH_WriteAddress

  #define FLASH_SectorToErase FLASH_WriteAddress

  #define M25P64_FLASH_ID 0x202017

  #define countof(a) (sizeof(a) / sizeof(*(a)))

  #define BufferSize (countof(Tx_Buffer)-1)

  SPI初始化

  void Init_SPI1(void)

  {

  SPI_InitTypeDef SPI_InitStructure;

  GPIO_InitTypeDef GPIO_InitStructure;

  /* Enable SPI and GPIO clocks */

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1 | RCC_APB2Periph_GPIOA , ENABLE);

  /* Configure SPI pins: SCK, MISO and MOSI */

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_Init(GPIOA, &GPIO_InitStructure);

  /* Configure I/O for Flash Chip select */

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

  GPIO_Init(GPIOA, &GPIO_InitStructure);

  /* Deselect the FLASH: Chip Select high */

  SPI_FLASH_CS_HIGH();

  /* SPI configuration */

  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //双工模式

  SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //SPI主模式

  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //8bit数据

  SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //CLK空闲时为高电平

  SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //CLK上升沿采样,因为上升沿是第二个边沿动作,所以也可以理解为第二个边沿采样

  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //片选用软件控制

  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; //SPI频率:72M/4 = 18M

  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //高位在前

  SPI_InitStructure.SPI_CRCPolynomial = 7; //crc7,stm32spi带硬件ecc

  SPI_Init(SPI1, &SPI_InitStructure);

  /* Enable the SPI */

  SPI_Cmd(SPI1, ENABLE);

  SPI_FLASH_SendByte(0xFF); // 启动传输

  }

  二 FLASH初始化

  void Init_FLASH(void)

  {

  GPIO_InitTypeDef GPIO_InitStructure;

  /* Enable GPIO clocks */

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOA , ENABLE);

  /* PA0--SPI_FLASH_HOLD */

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_Init(GPIOA, &GPIO_InitStructure);

  /* PC4-- SPI_FLASH_WP */

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_Init(GPIOC, &GPIO_InitStructure);

  GPIO_SetBits(GPIOA,GPIO_Pin_0);

  GPIO_ResetBits(GPIOC,GPIO_Pin_4);

  Init_SPI1();

  }

  三 函数编写

  /* 通过SPIx发送一个数据,同时接收一个数据*/

  u8 SPI_FLASH_SendByte(u8 byte)

  {

  /* Loop while DR register in not emplty */

  while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);//如果发送寄存器数据没有发送完,循环等待

  /* Send byte through the SPI1 peripheral */

  SPI_I2S_SendData(SPI1, byte); //往发送寄存器写入要发送的数据

  /* Wait to receive a byte */

  while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);//如果接收寄存器没有收到数据,循环

  /* Return the byte read from the SPI bus */

  return SPI_I2S_ReceiveData(SPI1);

  }

  /* brief Enables the write access to the FLASH. */

  void SPI_FLASH_WriteEnable(void)

  {

  /* Select the FLASH: Chip Select low */

  SPI_FLASH_CS_LOW();

  /* Send “Write Enable” instruction */

  SPI_FLASH_SendByte(WREN);

  /* Deselect the FLASH: Chip Select high */

  SPI_FLASH_CS_HIGH();

  }

  /*brief Erases the specified FLASH sector. */

  void SPI_FLASH_SectorErase(u32 SectorAddr)

  {

  /* Send write enable instruction */

  SPI_FLASH_WriteEnable();

  /* Sector Erase */

  /* Select the FLASH: Chip Select low */

  SPI_FLASH_CS_LOW();

  /* Send Sector Erase instruction */

  SPI_FLASH_SendByte(SE);

  /* Send SectorAddr high nibble address byte */

  SPI_FLASH_SendByte((SectorAddr & 0xFF0000) 》》 16);

  /* Send SectorAddr medium nibble address byte */

  SPI_FLASH_SendByte((SectorAddr & 0xFF00) 》》 8);

  /* Send SectorAddr low nibble address byte */

  SPI_FLASH_SendByte(SectorAddr & 0xFF);

  /* Deselect the FLASH: Chip Select high */

  SPI_FLASH_CS_HIGH();

  }

  /*brief Writes more than one byte to the FLASH with a single WRITE cycle */

  void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)

  {

  /* Enable the write access to the FLASH */

  SPI_FLASH_WriteEnable();

  /* Select the FLASH: Chip Select low */

  SPI_FLASH_CS_LOW();

  /* Send “Write to Memory ” instruction */

  SPI_FLASH_SendByte(PP);

  /* Send WriteAddr high nibble address byte to write to */

  SPI_FLASH_SendByte((WriteAddr & 0xFF0000) 》》 16);

  /* Send WriteAddr medium nibble address byte to write to */

  SPI_FLASH_SendByte((WriteAddr & 0xFF00) 》》 8);

  /* Send WriteAddr low nibble address byte to write to */

  SPI_FLASH_SendByte(WriteAddr & 0xFF);

  while(NumByteToWrite--)

  {

  SPI_FLASH_SendByte(*pBuffer);

  pBuffer++;

  }

  /* Deselect the FLASH: Chip Select high */

  SPI_FLASH_CS_HIGH();

  }

  /*brief Writes block of data to the FLASH. In this function, the number of */

  void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)

  {

  u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;

  Addr = WriteAddr % SPI_FLASH_PAGESIZE;

  count = SPI_FLASH_PAGESIZE - Addr;

  NumOfPage = NumByteToWrite / SPI_FLASH_PAGESIZE;

  NumOfSingle = NumByteToWrite % SPI_FLASH_PAGESIZE;

  if (Addr == 0) /* WriteAddr is SPI_FLASH_PAGESIZE aligned */

  {

  if (NumOfPage == 0) /* NumByteToWrite 《 SPI_FLASH_PAGESIZE */

  {

  SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);

  }

  else /* NumByteToWrite 》 SPI_FLASH_PAGESIZE */

  {

  while (NumOfPage--)

  {

  SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PAGESIZE);

  WriteAddr += SPI_FLASH_PAGESIZE;

  pBuffer += SPI_FLASH_PAGESIZE;

  }

  SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);

  }

  }

  else /* WriteAddr is not SPI_FLASH_PAGESIZE aligned */

  {

  if (NumOfPage == 0) /* NumByteToWrite 《 SPI_FLASH_PAGESIZE */

  {

  if (NumOfSingle 》 count) /* (NumByteToWrite + WriteAddr) 》 SPI_FLASH_PAGESIZE */

  {

  temp = NumOfSingle - count;

  SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);

  WriteAddr += count;

  pBuffer += count;

  SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);

  }

  else

  {

  SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);

  }

  }

  else /* NumByteToWrite 》 SPI_FLASH_PAGESIZE */

  {

  NumByteToWrite -= count;

  NumOfPage = NumByteToWrite / SPI_FLASH_PAGESIZE;

  NumOfSingle = NumByteToWrite % SPI_FLASH_PAGESIZE;

  SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);

  WriteAddr += count;

  pBuffer += count;

  while (NumOfPage--)

  {

  SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PAGESIZE);

  WriteAddr += SPI_FLASH_PAGESIZE;

  pBuffer += SPI_FLASH_PAGESIZE;

  }

  if (NumOfSingle != 0)

  {

  SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);

  }

  }

  }

  }

  /*brief Reads a block of data from the FLASH. */

  void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead)

  {

  /* Select the FLASH: Chip Select low */

  SPI_FLASH_CS_LOW();

  /* Send “Read from Memory ” instruction */

  SPI_FLASH_SendByte(READ);

  /* Send ReadAddr high nibble address byte to read from */

  SPI_FLASH_SendByte((ReadAddr & 0xFF0000) 》》 16);

  /* Send ReadAddr medium nibble address byte to read from */

  SPI_FLASH_SendByte((ReadAddr& 0xFF00) 》》 8);

  /* Send ReadAddr low nibble address byte to read from */

  SPI_FLASH_SendByte(ReadAddr & 0xFF);

  while (NumByteToRead--) /* while there is data to be read */

  {

  /* Read a byte from the FLASH */

  *pBuffer = SPI_FLASH_SendByte(DUMMY_BYTE);

  /* Point to the next location where the byte read will be saved */

  pBuffer++;

  }

  /* Deselect the FLASH: Chip Select high */

  SPI_FLASH_CS_HIGH();

  }

  /*brief Reads FLASH identification. */

  u32 SPI_FLASH_ReadID(void)

  {

  u32 Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;

  /* Select the FLASH: Chip Select low */

  SPI_FLASH_CS_LOW();

  /* Send “RDID ” instruction */

  SPI_FLASH_SendByte(0x9F);

  /* Read a byte from the FLASH */

  Temp0 = SPI_FLASH_SendByte(DUMMY_BYTE);

  /* Read a byte from the FLASH */

  Temp1 = SPI_FLASH_SendByte(DUMMY_BYTE);

  /* Read a byte from the FLASH */

  Temp2 = SPI_FLASH_SendByte(DUMMY_BYTE);

  /* Deselect the FLASH: Chip Select high */

  SPI_FLASH_CS_HIGH();

  Temp = (Temp0 《《 16) | (Temp1 《《 8) | Temp2;

  return Temp;

  }

  四 主函数

  void SPI1_Test(void)

  {

  /* Get SPI Flash ID */

  FLASH_ID = SPI_FLASH_ReadID();

  FLASH_ID = SPI_FLASH_ReadID();

  if (FLASH_ID == M25P64_FLASH_ID)

  {

  /* Write Tx_Buffer data to SPI FLASH memory */

  SPI_FLASH_BufferWrite(Tx_Buffer, FLASH_WriteAddress, BufferSize);

  delay_ms(20);

  DbgOutputstr(“Write message is ok!\r\n”);

  /* Read data from SPI FLASH memory */

  memset(Rx_Buffer,0,sizeof(Rx_Buffer));

  SPI_FLASH_BufferRead(Rx_Buffer, FLASH_ReadAddress, BufferSize);

  DbgOutputstr(“the message you write is:\r\n”);

  DbgOutputstr((char *)Rx_Buffer);

  DbgOutputstr(“\r\n”);//插入换行

  /* Erase SPI FLASH Sector to write on */

  SPI_FLASH_SectorErase(FLASH_SectorToErase);

  delay_ms(10);

  /* Read data from SPI FLASH memory No data -----Erase is ok */

  memset(Rx_Buffer,0,sizeof(Rx_Buffer));

  SPI_FLASH_BufferRead(Rx_Buffer, FLASH_ReadAddress, BufferSize);

  DbgOutputstr(“the message you write is:\r\n”);

  DbgOutputstr((char *)Rx_Buffer);

  DbgOutputstr(“\r\n”);//插入换行

  DbgOutputstr(“\r\n”);//插入换行

  delay_ms(1000);

  }

  }


打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分