SPI flash是什么,关于SPI FLASH的读写问题

电子说

1.3w人已加入

描述

  本文主要是关于SPI flash的相关介绍,并着重对SPI flash的原理及其读写进行了相近的阐述。

  SPI flash

  SPI一种通信接口。那么严格的来说SPI Flash是一种使用SPI通信的Flash,即,可能指NOR也可能是NAND。但现在大部分情况默认下人们说的SPI Flash指的是SPI NorFlash。早期Norflash的接口是parallel的形式,即把数据线和地址线并排与IC的管脚连接。但是后来发现不同容量的Norflash不能硬件上兼容(数据线和地址线的数量不一样),并且封装比较大,占用了较大的PCB板位置,所以后来逐渐被SPI(串行接口)Norflash所取代。同时不同容量的SPI Norflash管脚也兼容封装也更小。,至于现在很多人说起NOR flash直接都以SPI flash来代称。

  SPI

  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板的空间。

  今天主要说下SPI NorFlash。

  二、有毛用啊

  节省成本,减小封装,存储数据。

  三、怎么用啊

  怎么用说白了对于Flash就是读写擦,也就是实现flash的驱动。先简单了解下spi flash的物理连接。

  之前介绍SPI的时候说过,SPI接口目前的使用是多种方式(具体指的是物理连线有几种方式),Dual SPI、Qual SPI和标准的SPI接口(这种方式肯定不会出现在连接外设是SPI Flash上,这玩意没必要全双工),对于SPI Flash来说,主要就是Dual和Qual这两种方式。具体项目具体看了,理论上在CLK一定的情况下, 线数越多访问速度也越快。我们项目采用的Dual SPI方式,即两线。

  当前涉及到具体的SPI flash芯片类型了,所以必须也得参考flash的datasheet手册了。我们以W25Q64JVSSIQ为例。

  这是基本信息的介绍,然后看下具体IO的定义

  这个是WSON封装的管脚定义,其他详细信息参考datasheet。

  硬件驱动的话也是和芯片强相关的,因为读写擦都是和硬件时序相关的,所以必须得参考硬件datasheet手册。

  上面的datasheet都详细说明了每个操作的时序周期发送的命令。上图中,第一列是指令名称,第二列是指令编码,第三列及以后的指令功能与对应的指令有关。带括号的字节内容为flash向主机返回的字节数据,不带括号则是主机向flash发送字节数据。

  A0~A23:flash内部存储器地址;MID0~MID7:制造商ID;ID0~ID15:flash芯片ID;D0~D7:flash内部存储的数据;dummy:指任意数据。

  比如获取deviceID:

  表示该命令由这四个字节组成,其中dummy意为任意编码,即这三个字节必须得发数据,但这些数据是任意的,上图命令列表中带括号的字节数据表示由FLASH返回给主机的响应,可以看到deviceID命令的第5个字节为从机返回的响应,(ID7~ID0),即返回设备的ID号。

  代码如下:

  uint32_t Get_Flash_DeviceID(void)

  {

  uint8_t deviceID= 0x00;

  spiflashReset();

  spi_write( 0xAB);

  spi_write( Dummy);

  spi_write( Dummy);

  spi_write( Dummy);

  deviceID = spi_write( Dummy);

  spi_write( Dummy);

  spiflashSet() ;

  return deviceID;

  }

  SPI Flash的擦写注意事项

  SPI flash一般支持3种擦写方式:按sector擦写,按block擦写,整片chip擦写。

  以KH25L3255E为例,

  • Serial Peripheral Interface compatible -- Mode 0 and Mode 3

  • 33,554,432 x 1 bit structure or 16,777,216 x 2 bits (two I/O mode) structure or 8,388,608 x 4 bits (four I/O

  mode) structure

  • 1024 Equal Sectors with 4K bytes each

  - Any Sector can be erased individually

  • 128 Equal Blocks with 32K bytes each

  - Any Block can be erased individually

  • 64 Equal Blocks with 64K bytes each

  - Any Block can be erased individually

  • Power Supply Operation

  - 2.7 to 3.6 volt for read, erase, and program operations

  • Latch-up protected to 100mA from -1V to Vcc +1V

  由此可以看出,KH25L3255E的sector大小是4K,block大小是32K/64K.因此,可以一次性擦除4K/32K/64K.

  三种擦写方式的性能比较:

  - Fast erase time: 30ms (typ.)/sector (4K-byte per sector) ; 0.25s(typ.) /block (64K-byte per block); 10s(typ.) /

  chip

  从上述数据看,擦除同样大小的情况下,执行时间 sector》block》chip

  按sector擦写,擦完64K需要0.48s,而按block一次性擦写64K只需要0.25s;按block擦写,擦完整片chip需要16s,而按chip擦写只需要10s;按sector擦写完整片chip需要30s。

  从linux/drivers/mtd/devices/m25p80.c看,m25p_probe函数在选择擦写单位时,是倾向于选择小单位的,会优先选择4K,如下:

  /* prefer “small sector” erase if possible */

  if (info-》flags & SECT_4K) {

  flash-》erase_opcode = OPCODE_BE_4K;

  flash-》mtd.erasesize = 4096;

  } else {

  flash-》erase_opcode = OPCODE_SE;

  flash-》mtd.erasesize = info-》sector_size;

  }

  这在在用户空间体现在,使用mtd工具或者ioctl读取erasesize时读到的是sector(4K),如下:

  # mtd_debug info /dev/mtd0

  mtd.type = MTD_NORFLASH

  mtd.flags = MTD_CAP_NORFLASH

  mtd.size = 2883584 (2M)

  mtd.erasesize = 4096 (4K)

  mtd.writesize = 1

  mtd.oobsize = 0

  regions = 0

  # mtdinfo /dev/mtd0

  mtd0

  Name: flash0.bolt

  Type: nor

  Eraseblock size: 4096 bytes, 4.0 KiB

  Amount of eraseblocks: 704 (2883584 bytes, 2.8 MiB)

  Minimum input/output unit size: 1 byte

  Sub-page size: 1 byte

  Character device major/minor: 90:0

  Bad blocks are allowed: false

  Device is writable: true

  若想要将erasesize改成block大小,需要修改驱动,例如,将上述m25p80.c中的配置成SECT_4K的相关地方改成0,驱动就会使用block大小。

  另外,需要注意的是,m25p80.c中“sector”和“block”的概念搞反了,里面的“block”才是规格书中的sector,一般是4K,请见OPCODE_BE_4K和OPCODE_SE两个宏定义处。

  mtd.erasesize的大小会对应用上有一些影响,比较重要的一点是在制作某些嵌入式文件系统的镜像文件时,需要设置擦除单位,如果设置的擦除单位和驱动中的不符,会导致文件系统无法正常工作。

  另外,porting层设置spi的擦写单位时,也需要注意当前实际的erasesize。

  案例(解决方法就是修改驱动将spi-mtd的erasesize改成block大小,使其和jffs2的擦写单位一致):

  http://bbs.csdn.net/topics/390299487/

  《制作JFFS2时的erase_block与SPI FLASH中的erase_sector的关系?》

  如题, 在制作JFFS2时 需要用到-e 参数(。/mkfs.jffs2 -b --pad=524288 -s 256 -e 65536 -d userfs/ -o usrjffs2.img)help命令中,对其的解释为: -e, --eraseblock=SIZE Use erase block size SIZE (default: 64KiB)。

  在SPI FLASH(winbond 8M)的datasheet中写到:其erase_sector只能为4KB,erase_block可为32/64kB,

  目前在SPI的驱动中,设置block_size为 64KB, erase_sector为 4KB。

  问题来了!

  将制作好的512K FLASH挂载到系统上后,会报如下错误:

  jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x00022034: 0x5771 instead

  Further such events for this erase block will not be printed

  Node at 0x00022f60 with length 0x000000f6 would run over the end of the erase block

  Perhaps the file system was created with the wrong erase size?

  jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x00023000: 0x69c2 instead

  进入系统后,发现jffs2中的文件有缺失。

  根据报错提示以及网上资料, 原因应该为: 制作JFFS2时的erase_block 与系统的不符~。

  但系统这块的block_size(这个应该也就是erase_block的大小吧?)已经是设置成64KB的了。

  更改SPI驱动中,erase_sector的大小为64KB,则JFFS2报错问题解决, 但由于该SPI FLASH的

  erase_sector是固定死的4K,贸然使用64KB(32KB同样)会造成再擦写FLASH时,极大概率出现某

  一erase_secotr大小的块中数据为空的情况,即系统不能启动。

  另,在制作JFFS2时,将-e参数设置为4096,以配合FLASH的erase_sector, 但悲催的是mkfs.jffs2

  所支持-e 最小为8KB。

  请问现在这个报错问题应该如何解决???

  一点细节: 尝试添加了 -s 参数,其大小为FLASH page的大小256B,但问题依旧。

  当JFFS2中文件较少时,不知为何就不会出现上方的报错。

  关于SPI FLASH的读写问题

  spi flash W25Q128会偶尔出现写入错误的情况,会发现读出的值和写入的值不一致,需加入2次读出比较判断。

  21W25QXX_Read(&temp_date_count,0x000000,1);

  //W25QXX_Write((u8*)&temp_date,0x400000,135);

  //W25QXX_Read((u8*)&temp_data_test,0x400000,135);

  W25QXX_Write((u8*)&temp_date,(temp_date_count*135+1),135);

  W25QXX_Read((u8*)&temp_data_test,(temp_date_count*135+1),135);

  if(memcmp(&temp_date,&temp_data_test,135)!=0)

  {

  W25QXX_Write((u8*)&temp_date,(temp_date_count*135+1),135);

  W25QXX_Read((u8*)&temp_data_test,(temp_date_count*135+1),135);

  if(memcmp(&temp_date,&temp_data_test,135)!=0)

  {

  W25QXX_Write((u8*)&temp_date,(temp_date_count*135+1),135);

  }

  }

  temp_date_count=temp_date_count+1;//Each time a structure is written

  if(temp_date_count==60)

  {

  temp_date_count=0;

  }

  W25QXX_Write(&temp_date_count,0,1);

  结语

  关于SPI flash的相关介绍就到这了,如有不足之处欢迎指正。

相关阅读推荐:揭开Zynq Z-7000从SPI接口挂载的flash启动的神秘面纱

相关阅读推荐:基于FPGA 的SPI Flash 控制器设计及验证

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

全部0条评论

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

×
20
完善资料,
赚取积分