• “Flash闪存”基础 及 “SD NAND Flash”产品的测试



    本篇除了对flash闪存进行简单介绍外,另给读者推荐一种我本人也在用的小容量闪存

    自带坏块管理的SD NAND Flash(贴片式TF卡),尺寸小巧,简单易用,兼容性强,稳定可靠,标准SDIO接口,兼容SPI,兼容拔插式TF卡/SD卡,可替代普通TF卡/SD卡,尺寸6.2x8mm毫米,内置平均读写算法,读取速度23.5MB/S写入速度12.3MB/S,标准的SD 2.0协议使得用户可以直接移植标准驱动代码,省去了驱动代码编程环节。

    SD NAND Flash 产品由“深圳市雷龙发展有限公司”提供,获取更多学习资料可移步“雷龙发展”

    下面我也将以该公司下的“二代 CSNP32GCR01-AOW”为例展开介绍

    一、“FLASH闪存”是什么?

    1. 简介

    FLASH闪存是属于内存器件的一种,“Flash”。闪存则是一种非易失性( Non-Volatile )内存,在没有电流供应的条件下也能够长久地保持数据,其存储特性相当于硬盘,这项特性正是闪存得以成为各类便携型数字设备的存储介质的基础。

    各类 DDR 、 SDRAM 或者 RDRAM 都属于挥发性内存,只要停止电流供应内存中的数据便无法保持,因此每次电脑开机都需要把数据重新载入内存。

    闪存则是一种非易失性( Non-Volatile )内存,在没有电流供应的条件下也能够长久地保持数据,其存储特性相当于硬盘,这项特性正是闪存得以成为各类便携型数字设备的存储介质的基础。

    2. 分类

    NORNAND是市场上两种主要的非易失闪存技术。

    在1984年,东芝公司的发明人舛冈富士雄首先提出了快速闪存存储器(此处简称闪存)的概念。与传统电脑内存不同,闪存的特点是NVM,其记录速度也非常快。

    Intel是世界上第一个生产闪存并将其投放市场的公司。1988年,公司推出了一款256K bit闪存芯片。它如同鞋盒一样大小,并被内嵌于一个录音机里。後来,Intel发明的这类闪存被统称为NOR闪存。它结合EPROM和EEPROM两项技术,并拥有一个SRAM接口。

    第二种闪存称为NAND闪存。它由日立公司于1989年研制,并被认为是NOR闪存的理想替代者。NAND闪存的写周期比NOR闪存短90%,它的保存与删除处理的速度也相对较快。NAND的存储单元只有NOR的一半,在更小的存储空间中NAND获得了更好的性能。鉴于NAND出色的表现,它常常被应用于诸如CompactFlash、SmartMedia、 SD、 MMC、 xD、 and PC cards、USB sticks等存储卡上。

    NAND 闪存的存储单元采用串行结构,存储单元的读写是以页和块为单位来进行(一页包含若干字节,若干页则组成储存块, NAND 的存储块大小为 8 到 32KB ),这种结构最大的优点在于容量可以做得很大,超过 512MB 容量的 NAND 产品相当普遍, NAND 闪存的成本较低,有利于大规模普及。

    3. 特点

    性能
    flash闪存是非易失存储器,可以对称为块的存储器单元块进行擦写和再编程。任何flash器件的写入操作只能在空或已擦除的单元内进行,所以大多数情况下,在进行写入操作之前必须先执行擦除。NAND器件执行擦除操作是十分简单的,而NOR则要求在进行擦除前先要将目标块内所有的位都写为1。

    由于擦除NOR器件时是以64~128KB的块进行的,执行一个写入/擦除操作的时间为5s,与此相反,擦除NAND器件是以8~32KB的块进行的,执行相同的操作最多只需要4ms。

    执行擦除时块尺寸的不同进一步拉大了NOR和NADN之间的性能差距,统计表明,对于给定的一套写入操作(尤其是更新小文件时),更多的擦除操作必须在基于NOR的单元中进行。这样,当选择存储解决方案时,设计师必须权衡以下的各项因素。
    ● NOR的读速度比NAND稍快一些。
    ● NAND的写入速度比NOR快很多。
    ● NAND的4ms擦除速度远比NOR的5s快。
    ● 大多数写入操作需要先进行擦除操作。
    ● NAND的擦除单元更小,相应的擦除电路更少。

    可靠性
    采用flash介质时一个需要重点考虑的问题是可靠性。对于需要扩展MTBF的系统来说,Flash是非常合适的存储方案。可以从寿命(耐用性)、位交换和坏块处理三个方面来比较NOR和NAND的可靠性。

    耐用性
    在NAND闪存中每个块的最大擦写次数是一百万次,而NOR的擦写次数是十万次。NAND存储器除了具有10比1的块擦除周期优势,典型的NAND块尺寸要比NOR器件小8倍,每个NAND存储器块在给定的时间内的删除次数要少一些。

    易于使用
    可以非常直接地使用基于NOR的闪存,可以像其他存储器那样连接,并可以在上面直接运行代码。
    由于需要I/O接口,NAND要复杂得多。各种NAND器件的存取方法因厂家而异。
    在使用NAND器件时,必须先写入驱动程序,才能继续执行其他操作。向NAND器件写入信息需要相当的技巧,因为设计师绝不能向坏块写入,这就意味着在NAND器件上自始至终都必须进行虚拟映射。

    其他作用
    驱动还用于对DiskOnChip产品进行仿真和NAND闪存的管理,包括纠错、坏块处理和损耗平衡。

    4. 虚拟化

    FLASH闪存是一种内存技术,与RAM不同,在断电时它仍旧可以保留所存储的信息。尽管FLASH闪存在执行读写操作时并不像RAM那样快,但性能远远高于典型的硬盘。更为重要的是,FLASH闪存访问数据时几乎不存在任何时间延迟。FLASH闪存技术非常适合随机I/O,而虚拟服务器环境中恰恰存在大量的随机I/O。

    对FLASH闪存主要的关注点之一是其执行写操作的方式。FLASH闪存可以执行的写操作次数有限,这意味着FLASH闪存厂商需要开发复杂的控制器技术,对写入FLASH闪存模块的方式进行管理,确保每个FLASH闪存单元接收相同的写请求。

    目前有三种类型的FLASH闪存,耐久性各不相同。单阶存储单元(SLC)FLASH闪存在每个单元写一位数据,耐久性最好。多阶存储单元(MLC)FLASH闪存在每个单元写多位数据,耐久性排名第二。三阶存储单元(TLC)在每个单元写三位数据,耐久性最差。每个单元写入的数据位越多意味着每个单元的容量越高,每GB的成本越低,同样意味着平均寿命更短。

    SLC是数据中心标准,但控制器技术的不断优化使得MLC被大多数用例所接受。尤其是在采用了某种方式的数据保护,比如镜像或者RAID或者使用了FLASH闪存层时。

    二、SD NAND Flash

    这里我以贴片式TF卡“CSNP32GCR01-AOW”型号为例介绍

    在这里插入图片描述

    1. 概述

    CSNP32GCR01-AOW是基于NAND闪存和SD控制器的32Gb密度嵌入式存储。该产品与原始NAND相比,它有许多优点,包括嵌入式坏块管理和更强的嵌入式ECC。即使在异常断电的情况下,它仍然可以安全地保存数据。

    2. 特点

    • 接口:标准SD规范2.0版,带有1-I/O和4-I/O。
    • 电源:Vcc=2.7V-3.6V
    • 默认模式:可变时钟频率0-25 MHz,最高12.5 MB/秒接口速度(使用4条并行数据线)
    • 高速模式:可变时钟频率0-50 MHz,最高25 MB/秒接口速度(使用4条并行数据线)
    • 工作温度:-25°C至+85°C
    • 储存温度:-40°C至+85°C
    • 备用电流:<250uA
    • 开关功能命令支持高速、电子商务和未来功能
    • 内存字段错误的纠正
    • 内容保护机制-符合SDMI标准的最高安全性。
    • SD NAND的密码保护(CMD42-锁定和解锁)
    • 使用机械开关的写保护功能
    • 内置写保护功能(永久和临时)
    • 特定于应用程序的命令

    3. 引脚分配

    在这里插入图片描述

    4. 数据传输模式

    在这里插入图片描述

    5. SD NAND寄存器

    SDNAND接口中定义了六个寄存器:OCR、CID、CSD、RCA、DSR和SCR。这些信息只能通过
    相应的命令。OCR、CID、CSD和SCR寄存器携带SDNAND/内容特定信息,而RCA、DSR寄存器是存储实际配置参数的配置寄存器(这里选取俩个寄存器进行展示)。

    CID register
    在这里插入图片描述
    SCR register
    在这里插入图片描述

    6. 通电图

    在这里插入图片描述
    通电时间
    在这里插入图片描述

    7. 参考设计

    在这里插入图片描述
    在这里插入图片描述

    Tips: RDAT和RCMD(10K~100 kΩ)是上拉电阻器,当SDNAND处于a状态时,保护CMD和DAT线路不受总线浮动的影响;在高阻抗模式,即使主机仅在SD模式下使用SDNAND作为1位模式,主机也应通过RDAT上拉所有DAT0-3线。它是建议VCC上有2.2uF电容。RCLK参考0~120Ω。

    三、STM32测试例程

    1. 初始化

    SD_Error SD_Init(void)
    {
      uint32_t i = 0;
    
      /*!< Initialize SD_SPI */
      GPIO_Configuration(); 
    
      /*!< SD chip select high */
      SD_CS_HIGH();
    
      /*!< Send dummy byte 0xFF, 10 times with CS high */
      /*!< Rise CS and MOSI for 80 clocks cycles */
      for (i = 0; i <= 9; i++)
      {
        /*!< Send dummy byte 0xFF */
        SD_WriteByte(SD_DUMMY_BYTE);
      } 
    	
    	//获取卡的类型,最多尝试10次
    	i=0;
    	do
    	{		
    		/*------------Put SD in SPI mode--------------*/
    		/*!< SD initialized and set to SPI mode properly */
    		SD_GoIdleState();
    
    		/*Get card type*/
    		SD_GetCardType();
    		
    	}while(SD_Type == SD_TYPE_NOT_SD && i++ >10);
    	
    	//不支持的卡
    	if(SD_Type == SD_TYPE_NOT_SD)
    		return SD_RESPONSE_FAILURE;
    			
    	return SD_GetCardInfo(&SDCardInfo);	
    	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    2. 但数据块测试

    void SD_SingleBlockTest(void)
    {  
      /*------------------- Block Read/Write --------------------------*/
      /* Fill the buffer to send */
      Fill_Buffer(Buffer_Block_Tx, BLOCK_SIZE, 0x320F);
    
      if (Status == SD_RESPONSE_NO_ERROR)
      {
        /* Write block of 512 bytes on address 0 */
        Status = SD_WriteBlock(Buffer_Block_Tx, 0x00, BLOCK_SIZE);
        /* Check if the Transfer is finished */
      }
    
      if (Status == SD_RESPONSE_NO_ERROR)
      {
        /* Read block of 512 bytes from address 0 */
        Status = SD_ReadBlock(Buffer_Block_Rx, 0x00, BLOCK_SIZE);
    
      }
    
      /* Check the correctness of written data */
      if (Status == SD_RESPONSE_NO_ERROR)
      {
        TransferStatus1 = Buffercmp(Buffer_Block_Tx, Buffer_Block_Rx, BLOCK_SIZE);
      }
      
      if(TransferStatus1 == PASSED)
      {
        LED2_ON;
        printf("Single block 测试成功!\n");
    
      }
      else
      {
    		LED1_ON;
        printf("Single block 测试失败,请确保SD卡正确接入开发板,或换一张SD卡测试!\n");
        
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    3. 多数据块测试

    void SD_MultiBlockTest(void)
    {  
      /*--------------- Multiple Block Read/Write ---------------------*/
      /* Fill the buffer to send */
      Fill_Buffer(Buffer_MultiBlock_Tx, MULTI_BUFFER_SIZE, 0x0);
    
      if (Status == SD_RESPONSE_NO_ERROR)
      {
        /* Write multiple block of many bytes on address 0 */
        Status = SD_WriteMultiBlocks(Buffer_MultiBlock_Tx, 0x00, BLOCK_SIZE, NUMBER_OF_BLOCKS);
        /* Check if the Transfer is finished */
      }
    
      if (Status == SD_RESPONSE_NO_ERROR)
      {
        /* Read block of many bytes from address 0 */
        Status = SD_ReadMultiBlocks(Buffer_MultiBlock_Rx, 0x00, BLOCK_SIZE, NUMBER_OF_BLOCKS);
        /* Check if the Transfer is finished */
      }
    
      /* Check the correctness of written data */
      if (Status == SD_RESPONSE_NO_ERROR)
      {
        TransferStatus2 = Buffercmp(Buffer_MultiBlock_Tx, Buffer_MultiBlock_Rx, MULTI_BUFFER_SIZE);
      }
      
      if(TransferStatus2 == PASSED)
      {
    		LED2_ON;
        printf("Multi block 测试成功!");
    
      }
      else
      {
    		LED1_ON;
        printf("Multi block 测试失败,请确保SD卡正确接入开发板,或换一张SD卡测试!");
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    4. 状态缓冲

    TestStatus Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint32_t BufferLength)
    {
      while (BufferLength--)
      {
        if (*pBuffer1 != *pBuffer2)
        {
          return FAILED;
        }
    
        pBuffer1++;
        pBuffer2++;
      }
    
      return PASSED;
    }
    
    void Fill_Buffer(uint8_t *pBuffer, uint32_t BufferLength, uint32_t Offset)
    {
      uint16_t index = 0;
    
      /* Put in global buffer same values */
      for (index = 0; index < BufferLength; index++)
      {
        pBuffer[index] = index + Offset;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
  • 相关阅读:
    沉睡者 - 大流量站项目
    一篇弄懂二分算法
    LeetCode1572.矩阵对角线元素的和
    【Maven学习】3.3 实验三:执行 Maven 的构建命令
    你不应该依赖CSS 100vh,这就是原因
    Java如何使用实时流式计算处理?
    vben admin配置详解(Table, Form)
    NC14745 Hungry!
    猿创征文|基于鲁棒控制理论的微电网优化调度(Matlab代码实现)
    融合语言模型中的拓扑上下文和逻辑规则实现知识图谱补全11.18
  • 原文地址:https://blog.csdn.net/Dustinthewine/article/details/127639719