tag 标签: nand

相关博文
  • 热度 2
    2024-1-11 17:24
    300 次阅读|
    0 个评论
    stm32 CubeMx 实现SD卡/sd nand FATFS读写测试。   材料:stm32F407ZGT6开发板、雷龙公司的SD_NAND 测试板(CSNP1GCR01-AOW)。(一开始是使用 Nandflash的操作起来不太方便而且 stm32cubemx自带的 fatfs还没有磨损平衡算法,很是难受。)   CSNP1GCR01-AOW的优势:   不用写驱动程序自带坏块管理的NAND Flash(贴片式TF卡),尺寸小巧,简单易用,兼容性强,稳定可靠,固件可定制,LGA-8封装,标准SDIO接口,兼容SPI/SD接口,兼容各大MCU平台,可替代普通TF卡/SD卡,尺寸6x8mm毫米,内置SLC晶圆擦写寿命10万次,通过1万次随机掉电测试耐高低温,支持工业级温度-40°~+85°,机贴手贴都非常方便,速度级别Class10(读取速度23.5MB/S写入速度12.3MB/S)标准的SD 2.0协议使得用户可以直接移植标准驱动代码,省去了驱动代码编程环节。支持TF卡启动的SOC都可以用SD NAND,提供STM32参考例程及原厂技术支持,主流容量:128MB/512MB/4GB/8GB,比TF卡稳定,比eMMC便宜,样品免费试用(可到官网找客服小姐姐领取样品哦)。雷龙官网   话不多说开始正文:   stm32cubeMX 版本:6.6.1   MDK5 版本5.35   开始配置STM32   1、 配置时钟:   系统时钟树配置(我这里直接拉满,实际使用根据功耗要求作相应的调整)   2、 配置调试接口   注意DEBUG这个一定要配置,如果是默认的那下载一次程序之后第二次就下载不进去了.   3、配置SDIO:   (我这里还是用了DMA 减少mcu的资源开销)   配置完成之后随便选一个IO口作为SD_NAND的插入检测引脚(没有检测脚的也选上不然在生成代码的时候会有警告,看着很不舒服,我这里选的是 PE4 引脚)   4、配置SDIO的DMA   5、添加文件系统  6、配置堆栈大小(稍微调大一点,不然在读写大一点的数据的时候可能会出错)   7、生成代码   8、生成代码后在 bsp_driver_sd.c这个文件中将这三行代码注释(这是检测SD卡是否接入的引脚 如果不注释在挂载sdnand的时候会提示 not_ready)   9、在main.c中 添加测试代码 */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "dma.h" #include "fatfs.h" #include "sdio.h" #include "gpio.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ FATFS fs; /* FatFs 文件系统对象 */ FIL file; /* 文件对象 */ FRESULT f_res; /* 文件操作结果 */ UINT fnum; /* 文件成功读写数量 */ BYTE ReadBuffer = {0}; /* 读缓冲区 */ BYTE WriteBuffer */ void* work, /* Pointer to working buffer */ UINT len /* Size of working buffer */ )  f_mkfs 这个函数有五个参数,老版本的只有三个参数   所以在格式化的时候得这么来操作 f_res = f_mkfs("0:/",FM_FAT|FM_SFD,0,&ReadBuffer,sizeof(ReadBuffer));
  • 2023-10-13 16:55
    0 个评论
    前言 嵌入式项目中,比较常见的存储扩展方案是使用TF/SD卡或者EMMC或者RAW NAND,各种方案都有其优缺点,而SD NAND相对于上述方案具备很多优势,是目前嵌入式项目中存储扩展方案的一个非常不错的选择,正好一个项目在选择NAND存储方案, 恰巧论坛中联系到雷龙公司申请到了两片SD NAND,所以就进行一下测试评估。 SD NAND是什么? 为什么选择SD NAND? 其与TF/SD卡,EMMC,RAW NAND比有什么优势, 我们可以从深圳市雷龙发展有限公司的官网找到答案http://www.longsto.com/news/。 该公司SD NAND 二代产品介绍可以参考如下地址http://longsto.com/product/list-39.html,有1Gb,4Gb,32Gb,64Gb的容量可选,我这里申请到的是两片32Gb的芯片和测试板. 焊接后如下: 测试过程 申请样品时官方提供了野火版的STM32测试程序,但是手里只有精英STM32F103开发板 V2开发板, 稍微改以下即可使用。 使用工程SDIO模式SD测试例程\\SDIO-HAL库代码\\36-SDIO—SD卡读写测试\\Project\\Fire_F103VE.uvprojx 修改下芯片型号 修改下编译器,我这里使用的是新版本的MDK所以是AC6 选择下对应的仿真器 User\\key\\bsp_key.h中修改下按键,按照开发板原理图改为PE3 #define KEY1_PIN GPIO_PIN_3 #define KEY1_GPIO_PORT GPIOE #define KEY1_GPIO_CLK_ENABLE __HAL_RCC_GPIOE_CLK_ENABLE 由于AC6只能使用UTF-8编码,而原来的文件使用GB2312编码,所以需要修改下main.c的编码,用VSCODE打开main.c Save with Encodeing选择UTF8,这样就改为UTF-8格式了。 编译下载按Key1按键, 串口打印测试结果如下 实际上CS SD NAND是完全兼容SD/TF卡的,所以直接使用精英STM32F103开发板 V2开发板的SD卡测试程序也是可以的 使用工程 【正点原子】精英STM32F103开发板 V2-资料盘(A盘)\\4,程序源码\\2,标准例程-HAL库版本.zip\\2,标准例程-HAL库版本\\实验34 SD卡实验 编译下载运行,可以看到串口打印如下 以上测试可以看出,用SD卡的程序完全可以不做任何修改应用于CS SD NAND, 软件可以无缝切换。 总结: CS创世SD NAND相对于TF卡的优势 焊接在PCB稳定,LGA-8封装容易焊接 尺寸小 结构更容易做到防水 内置SLC 擦写寿命长 一致性好 SD NAND与eMMC比具备以下优劣 尺寸小 方便焊接 容量可选多,适合实际应用 PIN脚少 功耗低 擦写寿命长 性能稳定 使用SLC/MLC CS创世 SD NAND相对于Raw NAND具有以下优势 免驱动使用 性能更稳定。 尺寸更小。 SD NAND可选容量更多 SD NAND的读写速度更快 在使用上程序完全兼容TF/SD卡,原来的程序无需任何修改即可使用,无缝切换. 而SD NAND又相对于其他方案具备上述诸多优势,所以是一个非常不错的选择. 综上,还在使用TF卡吗,是时候考虑用SD NAND替换了。
  • 热度 8
    2023-8-29 18:02
    507 次阅读|
    0 个评论
    文章目录 FATFS文件系统详解 1. 简介 2. 基础概念 3. FAT文件系统组成介绍 4. FAT文件系统分析 4.1 采用FAT格式格式化SD nand/sd卡 4.2 引导扇区分析 4.3 分区偏移及大小计算 4.4 FAT子类型确认 4.4 访问FAT条目 4.5 文件与簇之间的关系 4.6 FSInfo扇区结构及备份引导扇区 4.7 FAT目录 4.7.1 SFN 短文件名目录 4.7.2 LFN长文件名 4.7.3 LFN系统对于SFN的兼容 5. 分区分析 5.1 保留分区分析 5.2 FAT区分析 5.3 根目录区分析 5.4 数据区分析 5.5 新增文件测试 6. 总结 1. 简介 在早期计算机刚发展的时候,那时候硬盘大小、flash设备容量都比较小,随着技术的不断迭代更新,硬盘容量越来越大。在早期,面对小容量的硬盘/flash,往往采用对应地址存放对应数据的方案,由于数据量不大,操作起来尚还可以。但是发展到今天,随着硬盘/flash容量不断增大,存储的数据也越来越多,早期单一的对应地址存放对应数据的方案已经无法满足我们的需求,操作硬盘/flash会变得异常的困难复杂。 因此针对上述问题,一群大佬们便开始设计文件系统这样一个东西,用来管理硬盘/flash上的数据信息,像我们电脑上打开文件夹,访问里面的文件,这其实就是基于文件系统访问电脑硬盘上数据的一个操作。 发展至今,文件系统已有众多版本,本文主要分享 关于FAT文件系统的详细设计, FAT文件系统适用于嵌入式设备,如SD卡、SD nand、spi nor flash等众多存储设备,同时基于此文件系统的文件亦能被电脑正常读取。 2. 基础概念 在研究文件系统之前,我们需要首先弄清楚关于内存这块的几个基本概念: 2.1.区分 扇区、块、簇的概念 扇区(sector):flash可操作的最小单元,通常指我们擦除的最小单元大小,以sd nand举例,通常最小为512Byte 块(block) 以及 簇(cluster):其实这是两个相同的概念,只是由于历史原因,在不同系统上的不同称呼,在windows中称簇,而在linux中称块。一个簇/块由多个扇区组成,由于一个扇区的空间较小,因此文件系统通过会将多个扇区组合在一起形成一个簇,并以簇为单位进行读写操作! 一个簇通常可以由 2、4、8、… 、2的n次方个扇区组成。 2.2.FAT文件系统总共由FAT12、FAT16以及FAT32三个版本,这是由于随着存储技术不断发展,FAT文件系统迭代导致,数字越大,版本越新,新版本对老版本完全兼容! 3. FAT文件系统组成介绍 Fat文件系统官方文档: http://elm-chan.org/docs/fat_e.html FAT文件系统在flash上的布局如下图所示,总共由四个区域组成: 保留区 FAT区 根目录区 (FAT32类型不包含此区域) 数据区 编辑 ​ 编辑 ​ 接下来,我们对一张格式化为FAT格式的SD卡进行分析,理解FAT文件系统的实现细节: 4. FAT文件系统分析 4.1 采用FAT格式格式化SD nand/sd卡 1.使用win10格式化一张118.5M的SD nand / sd卡,我这里用的是手上的一颗 创世CS 家的sd nand加一块转接板,和SD卡完全没有区别,且SD nand在稳定性上比SD卡具有优势。 编辑 ​ 编辑 ​ 此处由于SD nand(SD卡)大小原因,默认采用FAT16进行了格式化!因此在下文中我们先以FAT16进行分析,之后再重新格式化为FAT32进行分析,就很容易懂了! 4.2 引导扇区分析 1.使用 winhex 工具打开对应磁盘,注意需使用管理员权限运行 编辑 ​ 2.打开后我们可以以二进制的格式查看SD卡上所有数据,首先看到第一个扇区,也就是对应的引导扇区 boot sector,注意引导扇区位于保留区! 编辑 ​ 3.接下来我们根据 官方文档 对引导扇区进行分析 注意,FAT文件系统数据均采用小端格式! a) 首先是FAT12/16/32公共部分,(偏移值 0 - 35): EB 3C 90:BS_JmpBoot,跳转指令 4D 53 44 4F 53 35 2E 30:BS_OEMName,MSDOS 5.0,一个名字,指示创建此卷的操作系统,无其他作用 00 02:BPB_BytsPerSec,扇区大小 512 字节 04:BPB_SecPerClus,每次操作的最小扇区数,簇 Cluster,4 (与格式化时选择的大小匹配 2048 = 512 * 4) 06 00:BPB_RsvdSecCnt,保留区的扇区数,6 (通过此可计算,FAT区起始地址为 6 * 512 = 0xC00) 02:BPB_NumFATs,FATs的个数,2(一般此值为2,多一个用来做冗余备份,解决系统异常导致第一个损坏时,增大恢复的可能性,表示FAT区有两个FATs备份) 00 02:BPB_RootEntCnt,512,在FAT12/16系统中,此字段表示根目录中32字节目录条目数量,设置此值时需注意对齐,为了最大的兼容性,FAT16系统上此值应设置为512,FAT32系统上此值应设置为0 65536,所以此字段为0) F8:BPB_Media 媒体类型 ED 00:BPB_FATSz16,237,一个FAT占用的扇区数,此字段仅在FAT12/16系统使用;FAT32系统,此字段必须为0,使用BPB_FATSz32字段替代。FAT区总大小等于 BPB_FATSz?? * BPB_NumFATs 扇区(2372512=242688=0x3B400,由此可推算根目录区起始地址:0x3B400+0xC00=0x3C000)。 3F 00:BPB_SecPerTrk,每个磁道的扇区数,此字段仅与具有几何形状且仅用于 IBM PC 的磁盘 BIOS 的介质相关,不用管。 FF 00:BPB_NumHeads,头数量,此字段仅与具有几何形状且仅用于 IBM PC 的磁盘 BIOS 的介质相关,不用管。 00 00 00 00:BPB_HiddSec,0,FAT 卷之前的隐藏物理扇区数(当磁盘被分区之后,当前分区并不一定是从扇区头开始的) 00 B4 03 00:BPB_TotSec32,242688,32位大小区域描述FAT卷扇区总数(整个卷空间大小)。 FAT12/16系统,扇区总数小于0x10000时,此字段必须为0,真实值存放在BPB_FATSz16;FAT32系统,此字段一直有效。(118.5M = 512 * 242688) b) 接下来是FAT12/16特有字段(偏移值36) 80:BS_DrvNum,IBM PC 的磁盘 BIOS 使用的驱动器号,00h代表软盘,80h代表固定磁盘 00:BS_Reserved,保留字段,0 29:BS_BootSig,扩展引导签名,表示以下存在三个字段 83 3E 07 E4:BS_VolID,与 BS_VolLab 一起构成卷序列号,一般在格式化的时候结合时间生成 4E 4F 20 4E 41 4D 45 20 20 20 20:(解析为:"NO NAME “),BS_VolLab,11byte卷标,当卷标不存在时,此值应设置为"NO NAME” 46 41 54 31 36 20 20 20:(解析为:"FAT16 "),BS_FilSysType文件系统类型,支持字段有:"FAT12 ", "FAT16 " or "FAT ",注意很多人认为是通过此字段区分FAT12/16/32系统类型,实际是错误的,文件系统类型实际上是根据磁盘大小确定的,官方文档 “Determination of FAT sub-type” 章节或本博文后文有描述,不过为了最大的兼容性考虑,此字段应设置为对应文件系统的名字。 33 C9 ~ CB D8:BS_BootCode,引导启动程序,与平台有关,不使用时填充为0 55 AA:BS_BootSign,0xAA55,引导签名,指示这是一个有效的引导扇区当扇区大小大于512字节时,剩余的字段应全部使用0x0填充。 c) 如果是FAT32,则采用FAT32特有字段解析(偏移值和FAT12/16特有字段一致为36) 虽然此处我们的是FAT16格式,不过此处也将FAT的字段进行描述,方便理解。 BPB_FATSz32:一个FAT占用的扇区数,此字段仅在FAT32系统有效。FAT区总大小等于 BPB_FATSz?? * BPB_NumFATs 扇区。 BPB_ExtFlags:扩展标识字段,bit7=0,表示所有FAT都是镜像的和活跃的;bit7=1,表示只有bit3-0表示的FAT是有效的。 BPB_FSVer:FAT32版本,高字节是主版本号,低字节是次版本号。 BPB_RootClus:根目录的第一个簇号,此值通常为2,因为前两个簇一般用于保留。 BPB_FSInfo:FSInfo结构扇区与FAT32卷顶部的偏移扇区值。此值通常为1,因为其通常位于引导扇区旁边。 BPB_BkBootSec:备份引导扇区与FAT32卷顶部的偏移扇区值。此值通常为6,考虑最大的兼容性,此值不建议为其他值。 BPB_Reserved:保留 BS_DrvNum:含义与FAT12/16字段一样 BS_Reserved:含义与FAT12/16字段一样 BS_BootSig:含义与FAT12/16字段一样 BS_VolID:含义与FAT12/16字段一样 BS_VolLab:含义与FAT12/16字段一样 BS_FilSysType:始终为"FAT32 ",对FAT类型的确定没有任何影响。 BS_BootCode32:引导启动程序,与平台有关,不使用时填充为0 BS_BootSign:0xAA55,引导签名,指示这是一个有效的引导扇区当扇区大小大于512字节时,剩余的字段应全部使用0x0填充。 以上就是引导扇区内容的详细分析了,通过引导扇区的内容,我们即可知道FAT文件系统依赖的硬件存储空间大小、簇大小、扇区大小以及以及FAT系统版本等重要信息。 同时通过引导扇区的内容,我们便可计算出对应的FAT的四个区域的大小及起始偏移位置等重要信息,接下来计算FAT四个分区的起始位置及大小。 4.3 分区偏移及大小计算 FAT卷总共分为以下四个区域: 保留区 1.第一个扇区为引导扇区,存放BPB(BIOS Parameter Block)数据,存放的是FAT卷的配置参数。 2.上述参数中以 BPB_ 命名的字段都是 BPB 的一部分,而以 BS_ 标题命名的字段都不是 BPB 的一部分,而只是引导扇区的一部分 FAT区(分区表装载区) 根目录区 数据区 各分区偏移地址及大小如下: 编辑 ​ 此外,关于FAT区,通常存在一个以上的FAT,如此处所格式化的sd卡便存在两个FAT,对应的偏移地址和大小如下: 编辑 ​ 4.4 FAT子类型确认 关于FAT的类型是FAT12/16/32确认:FAT类型由数据区内簇的数量决定,除此之外无其他办法! 当一个卷,簇的数量 ≤4085 时,为FAT12 当一个卷,簇的数量 ≥4086 且 ≤65525 时,为FAT16 当一个卷,簇的数量 ≥65526 时,为FAT32 簇的数量计算公式:CountofClusters = DataSectors / BPB_SecPerClus; 如我们这里:CountofClusters = 242176 / 4 = 60544,所以为 FAT16! 当簇的大小从 512 ~ 32768字节的各种条件下,不同类型FAT对应卷的大小范围如下: 编辑 ​ 4.4 访问FAT条目 FAT区由一条条FAT条目构成,关于FAT 对应的条目具体位置计算如下: 编辑 ​ FAT16: FAT32: 编辑 ​ 格外需要注意的是,不同格式,对应的FAT条目的长度和格式不一样: 此外对于FAT32格式,高4位是保留位,只有低28位有效! 具体如下图所示: 编辑 ​ 4.5 文件与簇之间的关系 那么文件和簇之间的相互关系又是怎样的呢?我们又是如何准确的找到存放在flash上的文件的呢?接下来让我们看下文件与簇之间的关系映射。 在FAT卷上文件通过目录管理,目录是一个32字节数组组成的目录条目结构,此目录结构包含:文件名、文件大小、时间戳以及文件所在的第一个簇号。 簇号为0和1的簇被保留,由参数BPB_RootClus可知,有效簇从第2号簇开始。FAT (2号簇)对应数据区的第一个簇。 因此第N个簇的位置计算公式如下: FirstSectorofCluster = DataStartSector + (N - 2) * BPB_SecPerClus 每个条目所在的位置,对应一个簇。当文件长度大于一个簇长度时,条目内的值为下一个条目的索引,直到文件所在的最后一个簇,由此构成簇链!文件所在的最有一个簇所对应的FAT条目内的值由一个特殊的值(EOC)组成,它永远不会匹配任何有效的簇号,如下: FAT12: 0xFF8 - 0xFFF (typically 0xFFF) FAT16: 0xFFF8 - 0xFFFF (typically 0xFFFF) FAT32: 0x0FFFFFF8 - 0x0FFFFFFF (typically 0x0FFFFFFF) 存在一些特殊的值被用来做损坏簇的标记,如下: FAT12: 0xFF7 FAT16:0xFFF7 FAT32:0xFFFFFFF7 不过此处需要注意,在FAT12/16系统上,上述特殊值绝不会和任何有效簇匹配,但是在FAT32上有可能,因此为了防止混淆,你在创建FAT32系统的时候应该避免这种情况发生!因此FAT32系统上簇的上限为268435445(大于256M个簇) FAT条目初始化的时候,FAT 及以后的数据应被初始化为0,指示未被使用处于空闲状态,如果值不为0,则意味着簇被损坏或被使用状态。在FAT12/16系统上,空闲簇的数量未被记录,而在FAT32系统上,FAT32支持FSInfo结构体,里面记录了空闲簇的数量。 关于FAT 和FAT : 此两个保留的条目,没有与任何簇有联系;不过具有其他意义,如下: FAT12: FAT = 0xF??; FAT = 0xFFF; FAT16: FAT = 0xFF??; FAT = 0xFFFF; FAT32: FAT = 0xFFFFFF??; FAT = 0xFFFFFFFF; FAT 中的?? 与 BPB_Media 相同; FAT 记录了错误历史记录:卷脏标志(FAT16:bit15、FAT32:bit31),系统在启动的时候清除此位,正常关闭的时候恢复。 如果此位已经清除,表明上次未被正常关闭,可能存在逻辑卷错误;硬件错误标志(FAT16:bit14、FAT32:bit30)当出现无法恢复的读写错误时清除,表明需要进行全面检查。 关于FAT区域,有两个重点注意事项: 第一个是FAT的最后一个扇区可能没有被完全使用。在大多数情况下,FAT在扇区的中间结束。FAT驱动程序不应该对未使用的区域做出任何假设。在格式化卷时,应该用零填充它,并且在此之后不应更改它。 另一个是BPB_FATSz16/32可以指示比卷需要的值大的值。换句话说,未使用的扇区可以跟随每个FAT。这可能是数据区域对齐或其他原因导致的。同时,在格式化时这些扇区也会被用零填充。 下表展示了不同FAT类型中FAT值所对应的含义解释: 编辑 ​ 4.6 FSInfo扇区结构及备份引导扇区 此部分内容只在FAT32系统上存在,对于FAT12系统FAT区域大小最大6KB,对于FAT16系统FAT区域最大128KB,但是在FAT32系统上FAT区域通常上达数MB,这是因为FAT32系统支持FSInfo数据结构。 在FAT32系统上新增FSInfo数据结构的原因是:在FAT12/16系统上,想要知道flash上剩余的簇数需要扫描整个FAT区才能计算出来,但随着flash容量的不断扩大,扫描花费的时长越来越长,为了避免扫描浪费过多的时间,因此在FAT32系统上增加了FSInfo结构,用于记录flash上剩余的簇数。 FSInfo数据结构如下: 编辑 ​ 注意:当扇区大小大于512字节时, 剩余空间采用0x00填充 4.7 FAT目录 FAT目录分为长文件名目录(LFN)以及短文件名目录(SFN),长文件目录是在短文件名目录上的一个扩展,具体采用长文件名还是短文件名由读取FAT文件系统的操作系统决定,如windows;设置长文件名时短文件名也被设置,具有兼容性。 此外,有一个很重要的概念:在FAT文件系统上目录也是一个文件,只是此文件的属性不一样而已。 在所有目录中,有一个比较特殊的是根目录,且根目录作为顶层目录必须存在。 在FAT12/16系统中,根目录不是一个文件,且放在根目录区,根目录的最大条目数由 BPB_RootEntCnt 参数指示; 在FAT32系统中,根目录与子目录没有什么区别,且根目录的起始簇由 BPB_RootClus 参数指示。 根目录与子目录的另外一个区别是,根目录不包含 . .. 此两个点目录,且它可以包含卷标(具有ATTR_VOLUME_ID属性的条目) 4.7.1 SFN 短文件名目录 目录条目结构如下: 编辑 ​ 关于目录结构的第一个字段 DIR_Name 的第一个元素 DIR_Name 在目录表中有着特殊作用,如下: 当此值为 0xE5 时,代表此目录条目未被使用(或已废弃) 当此值为 0x00 时,也代表此目录条目未被使用;此外还提示后续目录条目也未被使用,因为后续的目录条目 DIR_Name 都会是 0x00 如果文件名的第一个字符为 0xE5 这个特殊值,则使用 0x05 替代。 这么设计的意义是什么呢?将 DIR_Name 用作特殊字符,其目录在于方便文件删除!当我们删除一个文件的时候,文件系统并不会将此文件所对应的数据全部删除,因为那样太费时间了,也没有必要,而是直接将对应文件的目录项中的 DIR_Name 修改为 0xE5 即可! 关于文件名字段 DIR_Name,在FAT文件系统中还有如下规定: DIR_Name 字段的11字节的文件名分为两个部分:8 字节的主文件名 + 3字节的扩展名; 文件名中主文件名与扩展名中间的 . 被省略,不在此记录 如果主文件名长度不够,小于8字节,则使用 0x20 空格填充 用于设置文件名的字符也有限制,支持的字符有 0~9 A~Z ! # $ % & ’ ( ) - @ ^ _ ` { } ~ 主文件名和扩展名中的(a~z)ASCII字符都会被转化成大写字符保存 以下为文件名存储示例: 编辑 ​ 4.7.2 LFN长文件名 长文件名是文件名的另外一种存储方式,由于SFN短文件名具有长度、字符等限制,在一些场景下不能很好的满足需求,因此就需要使用到长文件名,关于长文件名的具体内容如下: 长文件名是一个具有特殊属性的目录条目。长文件名目录属性 DIR_Attr 字段的值 ATTR_LONG_NAME = (ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID) = 0x0F; 编辑 ​ 关于长文件名的目录属性如下: 编辑 ​ 关于长文件名,有以下几点重要概念: 一个文件一定有短文名SFN,但不一定有长文件名LFN 长文件名LFN字段中仅包含文件名信息,不包含其他内容,其他内容需要通过短文件名SFN查看 如果一个文件既有长文件名也有短文件名,则长文件名是其主要名字,而短文件名则为附加名字 长文件名LFN条目在对应的短文件名SFN条目前面 一个文件的长文件名最长255字符,对应最多20个长文件名LFN条目 长文件名简单理解起始就是存储一个字符串,因此没有类似SFN的限制,允许有空格、支持大小写、允许多个.符号等 LFN条目文件名长度不够,仍然采用0x20填充 下图是官方关于一个名为 “MultiMediaCard System Summary.pdf” 的长文件名在flash上的长文件名条目,如下所示,一眼没看明白也没关系,后文有实例说明,对长文件名有概念了就行! 编辑 ​ 关于长文件名的checksum字段和计算,算法如下: uint8_t create_sum (const DIR* entry) { int i; uint8_t sum; for (i = sum = 0; i < 11; i++) { /* Calculate sum of DIR_Name ; } return sum; } 4.7.3 LFN系统对于SFN的兼容 在使用LFN长文件名的系统中,会自动生成SFN短文件名已确保此文件在短文件名的文件系统中可使用。同时为了防止生成的短文件名冲突,SFN的生成采用 名称+数字后缀+扩展 的格式,同时采用以下规则生成SFN: 小写自动转大写 如果存在空格,则删去空格,设置有损转换标识 已.开头的文件删除头部的.,并设置有损转换标识 存在多个.的文件名,仅保留最后一个作为文件名与扩展的分隔,并设置有损转换标识 其他不支持的字符,采用_代替,并设置有损转换标识 文件名如果是Unicode编码,则转化为ANSI/OEM编码;不能转换的字符采用_代替,并设置有损转换标识 长度超过8字节的部分,截断,并设置有损转换标识 扩展名字段超过3字节的,截断,并设置有损转换标识 有损转转换标识为:~,ASCII值为0x7E,十进制126 示例如下: 编辑 ​ 至此,FAT文件系统的理论部分已经描述完了,接下来我们继续使用winhex对数据进行分析。 5. 分区分析 继续回顾我们一开始的这张布局图 编辑 ​ 5.1 保留分区分析 保留分区为第一个分区,其中引导扇区位于保留分区的第一个扇区。 根据 4.3 章节计算结果可知,保留分区起始地址为 0x00,大小 0xC00 保留分区数据如下,保留分区内最重要的内容即为引导扇区,除引导扇区外,其他剩余空间全部保留,采用0x00覆盖。关于引导扇区已在 4.2 章节详细分析,此处不再做介绍。 编辑 ​ 5.2 FAT区分析 根据 4.3 章节描述,FAT区的起始地址为0xC00,大小为0x3B400,此外存在两个FAT区,FAT1和FAT2,起始地址分别为:0xC00、0x1E600,对应地址数据如下: FAT1 数据: 编辑 ​ FAT2 数据如下: 编辑 ​ 由于此处采用FAT16格式,所以每个FAT条目占据两个字节! 根据上述数据进行分析: 确认 FAT2 为 FAT1 的备份; 存在5个FAT条目其中 FAT 和 FAT 为保留条目,FAT 的内容与 BPB_Media 媒体类型字段一致,FAT 用来记录错误历史记录 (详见 4.5 章节描述) 根据4.5章节描述,FAT (2号簇)对应数据区的第一个簇,又FAT 、FAT 、FAT 数据均为 0xFF,表明存在三个文件,且每个文件的大小小于等于一个簇的空间;且分别存放在数据区第1到第3个簇上! 此处可能大家会由疑问,刚刚格式化的sd卡为什么会存在文件内,其实这个是系统文件,格式化后自带的,默认是隐藏的,只有使用winhex才能看到,也就是对应的System Volume Information文件夹。 5.3 根目录区分析 注意,根目录区只有 FAT12 / FAT16 系统上存在,在FAT32系统上不存在此区域。 根目录区用来记录根目录下的文件内容,根据 4.3 章节计算可知,根目录区起始地址为:0x3C000,大小为0x4000,数据内容如下: 编辑 ​ 以下是对数据字段进行分析后的内容,如下图所示: 编辑 ​ 格式化之后,默认会生成一个System Volume Infomation的系统文件夹,同时此文件夹是根目录下唯一的一个文件,因此在根目录的数据如上图所示。 此文件夹为目录属性,是隐藏的系统目录 长文件名为System Volume Information,短文件名为SYSTEM~1 此目录指向存放的数据在2号簇(对应数据区第一个簇),文件大小字段,由于此文件为目录属性,此字段无意义,因此强制为0 至此,根目录区分析完了,同时根目录区的 System Volume Information文件指向数据区第一个簇(2号簇),接下来我们便进入数据区进行分析。 5.4 数据区分析 根据 4.3 章节计算可知,数据区起始地址为:0x40000,大小为242176 * 512 = 0x764 0000,数据内容如下: 编辑 ​ 对应数据字段分析如下: 编辑 ​ System Volume Information 目录下存在两个文件,分别是IndexerVolumeGuid 和 WPSettings.dat。根据上述分析可知: IndexerVolumeGuid文件的数据存放在 FAT ,3号簇上,即数据区的第3个簇(数据区的第1个簇为2号簇); WPSettings.dat 文件的数据存放在 FAT ,4号簇上,即数据区的第2个簇(数据区的第1个簇为2号簇); 首先,我们跳转到4号簇上查看IndexerVolumeGuid的数据,对应地址计算方式为: FirstSectorofCluster = DataStartSector + (N - 2) * BPB_SecPerClus = 512 + (4 - 2) * 4 = 520; 对应地址为: FirstSectorofCluster * BPB_BytsPerSec = 520 * 512 = 0x0004 1000 编辑 ​ 接着跳转到3号簇上查看WPSettings.dat的数据,对应地址计算方式为: FirstSectorofCluster = DataStartSector + (N - 2) * BPB_SecPerClus = 512 + (3 - 2) * 4 = 516; 对应地址为: FirstSectorofCluster * BPB_BytsPerSec = 520 * 512 = 0x0004 0800 编辑 ​ 5.5 新增文件测试 1.在根目录下新增 test 目录,使用winhex更新磁盘数据,观察各数据区变化 保留区无变化 FAT区变化如下: 编辑 ​ 编辑 ​ 根目录区变化如下: 编辑 ​ 数据区变化: 编辑 ​ 2.新增long file test文件夹,里面存入一个长度为 2050 Byte(占据两个簇的空间)的test.txt文件,使用winhex重新打开磁盘进行分析。 编辑 ​ 保留区无变化 FAT区变化如下: 编辑 ​ 编辑 ​ 根目录区变化如下: 数据区变化如下: long file test目录数据指向6号簇,跳转至6号簇,地址DataStartSector + (N - 2) * BPB_SecPerClus = 0x40000 + (6-2) * 4 * 512 = 0x420000 编辑 ​ test.txt文件指向 7号簇,跳转至7号簇,地址DataStartSector + (N - 2) * BPB_SecPerClus = 0x40000 + (7-2) * 4 * 512 = 0x428000,均为test.txt的实际有效数据,如下: 编辑 ​ 编辑 ​ 6. 总结 以上便是关于FAT文件系统的全部分析了,通过上述分析,外加新增文件辅助理解,对于文件在FAT文件系统下如何管理、存储,相信已经有了非常深入的了解。 FAT文件系统分为四个区: 保留区最重要的是里面包含引导扇区,引导扇区内存放着BIOS参数信息,通过此参数可以知道FAT文件系统的flash布局,以及flash大小,fat块大小、簇大小等关键信息; FAT区,记录了文件所占用簇的情况,以及对于文件大小大于一个簇的文件,在FAT区内形成簇链,记录文件由哪几个簇组成 根目录区,只有FAT12/16系统所有,记录了根目录下的文件/目录条目信息 数据区,记录数据分为两个部分,第一部分为目录信息,除根目录外,每个文件夹需要占据一个及以上的簇描述对应目录下的文件情况;第二部分为具体文件数据。两部分数据通过短文件名SFN字段进行关联! 以上就是FAT文件系统的简单概括,由于本文使用的是FAT16文件系统作为实例分析,关于FAT32文件系统,在下一篇博文中进行补充,敬请关注!
  • 热度 6
    2023-1-6 17:30
    748 次阅读|
    0 个评论
    文章目录 0、前言 1、目标 2、图片的预处理 3、SD NAND的预处理 4、FPGA实现 4.1、详细设计 4.2、仿真 4.3、实验结果 ·前言 在上一篇文章《基于FPGA的SD卡的数据读写实现(SD NAND FLASH)》中,我们了解到了SD NAND Flash的相关知识,并在FPGA平台上实现了对SD NAND的读写测试。SD NAND的读写测试可能会有点简单和枯燥,所以本文我们来搞点有乐趣性的----将存储在SD NAND内的两张图片通过FPGA读取,并通过VGA的方式在显示器上轮回显示。 1、目标 使用 SD NAND数据读写控制器读取事先存储在 SD NAND的图片数据,将读取的图片数据通过SDRAM 数据读写控制器暂存在 SDRAM 芯片中,通过 VGA 显示器将暂存在 SDRAM 的图片显示出来。 SD 卡内存储两张图片,其交替显示在 VGA 显示器上,分辨率为 640*480。 SD NAND在SD2.0版本协议下,SPI模式的理论最大传输速率为50Mbps,加上命令号以及等待返回响应信号的时间,实际上的传输速率还会下降。对于采用分辨率为640*480@60Hz 的显示器来说,一幅图像的数据量达到640*480*16bit = 4915200bit = 4800Kbit(1Kbit=1024bit), 每秒钟刷新60次,那么每秒钟需要传输的数据量达到4800Kbit*60 = 288000Kbit =281.25Mbit (1Mbit=1024Kbit)。由此可以看出,SD卡的读写速度完全跟不上VGA的数据发送速度,因此必须先缓存一幅图像到内部或外部存储器,再通过VGA接口显示。FPGA的片内存储资源较少,对于缓存如此大量的数据,只能使用SDRAM或DDR3缓存数据。 2、图片的预处理 首先选取要显示的图片两张,使用 Window 系统自带的画图工具对图片进行处理,将图片处理为分辨率 640*480。 VGA的显示格式为16位RGB565格式,为了使SD NAND读出的数据可以直接在VGA上显示,需要将图片通过 “ IMG2LCD ” 上位机软件转成16位的RGB565格式的bin文件,再将bin文件导入SD NAND中。 使用 “ IMG2LCD ” 上位机软件打开两张图片,按如下设置相关参数,然后点击保存,就生成了两个图片的二进制文件(像素值)。 3、SD NAND的预处理 SD NAND在经过多次存放数据与删除数据之后,存入的文件有可能不是按照连续的扇区地址存储的,为了避免图片显示错误,我们将bin文件导入SD NAND之前,需要对SD NAND进行一个格式化处理。 首先得找个读卡器,再把所用到的SD NAND开发板插到读卡器上边,通过USB接口与PC建立链接。 本次实验我依然选用的是 深圳雷龙公司 的一款SD NAND产品----CSNP32GCR01-AOW。 可以看到这款SD NAND开发板设计得很巧妙,把对外接口设计成了通用的micro接口,兼容性非常强,不管是插读卡器还是直接插FPGA开发板,都是即插即用,十分方便。 接着说回来对SD NAND的初始化处理。插上读卡器后,选择对应的磁盘,点击“格式化”,并点击“开始” 格式化完成后,将前面生成的两张图片对应的bin文件存入对应的SD NAND磁盘中: SD NAND内部的存储资源是以扇区的形式进行划分的,为了将图片的bin数据从SD NAND中读取出来,我们需要找到图片存储对应的扇区地址。扇区地址可以用“WinHex 软件”来查看。 以管理员身份运行软件 WinHex 软件,点击“工具 ”,然后点击“打开磁盘”: 双击打开对应的SD NAND,记录下两个 bin文件的第一扇区地址: 此时查询到的扇区地址就是bin文件存放的起始扇区地址,我们只需要按照这个起始扇区地址,按顺序读出SD NAND中的数据即可,直到读完一张图片中的所有数据。SD NAND中一个扇区存放512个字节,也就是256个16位数据,对于分辨率为640*480的图片来说,共需要读出1200(640*480/256)个扇区数据。 4、FPGA实现 先说下总体思路: · SD NAND中存有两幅图片,一副为雷龙公司的官网截图,另一幅则是本博客的头像 · FPGA从SD NAND中读取这两幅图片的像素信息,并缓存到SDRAM中 · 将SDRAM中的数据(两幅图片的像素信息)通过VGA接口显示在显示器上 根据这个思路,可以对应的画对应的系统框图: FPGA顶层模块例化了以下五个模块:PLL时钟模块、SD NAND读取图片控制模块、SD NAND控制器模块、SDRAM控制器模块和VGA驱动模块。 4.1、详细设计 (1) 顶层模块 顶层模块:顶层模块主要完成对其余各模块的例化,实现各模块之间的数据交互。需要注意的是,系统初始化完成是在SD NAND以及SDRAM都初始化完成后才开始拉高的,该信号控制着SD NAND读取图片控制模块的复位信号,因此SD NAND读取图片控制模块是在系统初始化完成后才工作的,防止因SD NAND或者SDRAM初始化未完成导致数据错误。 此部分代码如下: module top_sd_photo_vga( input sys_clk , //系统时钟 input sys_rst_n , //系统复位,低电平有效 //SD NAND接口 input sd_miso , //SD NANDSPI串行输入数据信号 output sd_clk , //SD NANDSPI时钟信号 output sd_cs , //SD NANDSPI片选信号 output sd_mosi , //SD NANDSPI串行输出数据信号 //SDRAM接口 output sdram_clk , //SDRAM 时钟 output sdram_cke , //SDRAM 时钟有效 output sdram_cs_n , //SDRAM 片选 output sdram_ras_n , //SDRAM 行有效 output sdram_cas_n , //SDRAM 列有效 output sdram_we_n , //SDRAM 写有效 output sdram_ba , //SDRAM Bank地址 output sdram_dqm , //SDRAM 数据掩码 output sdram_addr , //SDRAM 地址 inout sdram_data , //SDRAM 数据 //VGA接口 output vga_hs , //行同步信号 output vga_vs , //场同步信号 output vga_rgb //红绿蓝三原色输出 ); //parameter define parameter PHOTO_H_PIXEL = 24'd640 ; //设置SDRAM缓存大小 parameter PHOTO_V_PIXEL = 24'd480 ; //设置SDRAM缓存大小 //wire define wire clk_100m ; //100mhz时钟,SDRAM操作时钟 wire clk_100m_shift ; //100mhz时钟,SDRAM相位偏移时钟 wire clk_50m ; wire clk_50m_180deg ; wire clk_25m ; wire rst_n ; wire locked ; wire sys_init_done ; //系统初始化完成 wire sd_rd_start_en ; //开始写SD NAND数据信号 wire sd_rd_sec_addr ; //读数据扇区地址 wire sd_rd_busy ; //读忙信号 wire sd_rd_val_en ; //数据读取有效使能信号 wire sd_rd_val_data ; //读数据 wire sd_init_done ; //SD NAND初始化完成信号 wire wr_en ; //sdram_ctrl模块写使能 wire wr_data ; //sdram_ctrl模块写数据 wire rd_en ; //sdram_ctrl模块读使能 wire rd_data ; //sdram_ctrl模块读数据 wire sdram_init_done ; //SDRAM初始化完成 //***************************************************** //** main code //***************************************************** assign rst_n = sys_rst_n & locked; assign sys_init_done = sd_init_done & sdram_init_done; //SD NAND和SDRAM都初始化完成 assign wr_en = sd_rd_val_en; assign wr_data = sd_rd_val_data; //锁相环 pll_clk u_pll_clk( .areset (1'b0 ), .inclk0 (sys_clk ), .c0 (clk_100m ), .c1 (clk_100m_shift ), .c2 (clk_50m ), .c3 (clk_50m_180deg ), .c4 (clk_25m ), .locked (locked ) ); //读取SD NAND图片 sd_read_photo u_sd_read_photo( .clk (clk_50m), //系统初始化完成之后,再开始从SD NAND中读取图片 .rst_n (rst_n & sys_init_done), .rd_busy (sd_rd_busy), .rd_start_en (sd_rd_start_en), .rd_sec_addr (sd_rd_sec_addr) ); //SD NAND顶层控制模块 sd_ctrl_top u_sd_ctrl_top( .clk_ref (clk_50m), .clk_ref_180deg (clk_50m_180deg), .rst_n (rst_n), //SD NAND接口 .sd_miso (sd_miso), .sd_clk (sd_clk), .sd_cs (sd_cs), .sd_mosi (sd_mosi), //用户写SD NAND接口 .wr_start_en (1'b0), //不需要写入数据,写入接口赋值为0 .wr_sec_addr (32'b0), .wr_data (16'b0), .wr_busy (), .wr_req (), //用户读SD NAND接口 .rd_start_en (sd_rd_start_en), .rd_sec_addr (sd_rd_sec_addr), .rd_busy (sd_rd_busy), .rd_val_en (sd_rd_val_en), .rd_val_data (sd_rd_val_data), .sd_init_done (sd_init_done) ); //SDRAM 控制器顶层模块,封装成FIFO接口 //SDRAM 控制器地址组成: {bank_addr ,row_addr ,col_addr } sdram_top u_sdram_top( .ref_clk (clk_100m), //sdram 控制器参考时钟 .out_clk (clk_100m_shift), //用于输出的相位偏移时钟 .rst_n (rst_n), //系统复位 //用户写端口 .wr_clk (clk_50m), //写端口FIFO: 写时钟 .wr_en (wr_en), //写端口FIFO: 写使能 .wr_data (wr_data), //写端口FIFO: 写数据 .wr_min_addr (24'd0), //写SDRAM的起始地址 .wr_max_addr (PHOTO_H_PIXEL*PHOTO_V_PIXEL),//写SDRAM的结束地址 .wr_len (10'd512), //写SDRAM时的数据突发长度 .wr_load (~rst_n), //写端口复位: 复位写地址,清空写FIFO //用户读端口 .rd_clk (clk_25m), //读端口FIFO: 读时钟 .rd_en (rd_en), //读端口FIFO: 读使能 .rd_data (rd_data), //读端口FIFO: 读数据 .rd_min_addr (24'd0), //读SDRAM的起始地址 .rd_max_addr (PHOTO_H_PIXEL*PHOTO_V_PIXEL),//读SDRAM的结束地址 .rd_len (10'd512), //从SDRAM中读数据时的突发长度 .rd_load (~rst_n), //读端口复位: 复位读地址,清空读FIFO //用户控制端口 .sdram_read_valid (1'b1), //SDRAM 读使能 .sdram_pingpang_en (1'b0), //SDRAM 乒乓操作使能 .sdram_init_done (sdram_init_done), //SDRAM 初始化完成标志 //SDRAM 芯片接口 .sdram_clk (sdram_clk), //SDRAM 芯片时钟 .sdram_cke (sdram_cke), //SDRAM 时钟有效 .sdram_cs_n (sdram_cs_n), //SDRAM 片选 .sdram_ras_n (sdram_ras_n), //SDRAM 行有效 .sdram_cas_n (sdram_cas_n), //SDRAM 列有效 .sdram_we_n (sdram_we_n), //SDRAM 写有效 .sdram_ba (sdram_ba), //SDRAM Bank地址 .sdram_addr (sdram_addr), //SDRAM 行/列地址 .sdram_data (sdram_data), //SDRAM 数据 .sdram_dqm (sdram_dqm) //SDRAM 数据掩码 ); //VGA驱动模块 vga_driver u_vga_driver( .vga_clk (clk_25m), .sys_rst_n (rst_n), .vga_hs (vga_hs), .vga_vs (vga_vs), .vga_rgb (vga_rgb), .pixel_data (rd_data), .data_req (rd_en), //请求像素点颜色数据输入 .pixel_xpos (), .pixel_ypos () ); endmodule (2) PLL时钟模块 PLL时钟模块:PLL时钟模块通过调用锁相环(PLL)IP核实现,总共输出五个时钟,频率分别为100Mhz、100Mhz(相位偏移-180度)、50Mhz、50Mhz(相位偏移180度)和25Mhz。 两个100Mhz的时钟用于为SDRAM控制器模块提供驱动时钟;两个50Mhz的时钟用于为SD NAND控制器模块提供驱动时钟;25Mhz用于为VGA驱动模块提供驱动时钟。 (3) SD NAND读取图片控制模块 SD NAND读取图片控制模块:SD NAND读取图片控制模块通过控制SD NAND控制器的读接口,从SD NAND中读取图像数据,并在读完一张图片后延时一段时间,再去读取另一张图片数据,实现两张图片的循环切换读取。 此部分代码: module sd_read_photo( input clk , //时钟信号 input rst_n , //复位信号,低电平有效 input rd_busy , //SD NAND读忙信号 output reg rd_start_en , //开始写SD NAND数据信号 output reg rd_sec_addr //读数据扇区地址 ); //parameter define parameter PHOTO_SECCTION_ADDR0 = 32'd16640; //第一张图片扇区起始地址 parameter PHOTO_SECTION_ADDR1 = 32'd17856; //第二张图片扇区起始地址 //640*480/256 = 1200 parameter RD_SECTION_NUM = 11'd1200 ; //单张图片总共读出的次数 //reg define reg rd_flow_cnt ; //读数据流程控制计数器 reg rd_sec_cnt ; //读扇区次数计数器 reg rd_addr_sw ; //读两张图片切换 reg delay_cnt ; //延时切换图片计数器 reg rd_busy_d0 ; //读忙信号打拍,用来采下降沿 reg rd_busy_d1 ; //wire define wire neg_rd_busy ; //SD NAND读忙信号下降沿 //***************************************************** //** main code //***************************************************** assign neg_rd_busy = rd_busy_d1 & (~rd_busy_d0); //对rd_busy信号进行延时打拍,用于采rd_busy信号的下降沿 always @(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) begin rd_busy_d0 <= 1'b0; rd_busy_d1 <= 1'b0; end else begin rd_busy_d0 <= rd_busy; rd_busy_d1 <= rd_busy_d0; end end //循环读取SD NAND中的两张图片(读完之后延时1s再读下一个) always @(posedge clk or negedge rst_n) begin if(!rst_n) begin rd_flow_cnt <= 2'd0; rd_addr_sw <= 1'b0; rd_sec_cnt <= 11'd0; rd_start_en <= 1'b0; rd_sec_addr <= 32'd0; end else begin rd_start_en <= 1'b0; case(rd_flow_cnt) 2'd0 : begin //开始读取SD NAND数据 rd_flow_cnt <= rd_flow_cnt + 2'd1; rd_start_en <= 1'b1; rd_addr_sw <= ~rd_addr_sw; //读数据地址切换 if(rd_addr_sw == 1'b0) rd_sec_addr <= PHOTO_SECCTION_ADDR0; else rd_sec_addr <= PHOTO_SECTION_ADDR1; end 2'd1 : begin //读忙信号的下降沿代表读完一个扇区,开始读取下一扇区地址数据 if(neg_rd_busy) begin rd_sec_cnt <= rd_sec_cnt + 11'd1; rd_sec_addr <= rd_sec_addr + 32'd1; //单张图片读完 if(rd_sec_cnt == RD_SECTION_NUM - 11'b1) begin rd_sec_cnt <= 11'd0; rd_flow_cnt <= rd_flow_cnt + 2'd1; end else rd_start_en <= 1'b1; end end 2'd2 : begin delay_cnt <= delay_cnt + 26'd1; //读取完成后延时1秒 if(delay_cnt == 26'd50_000_000 - 26'd1) begin //50_000_000*20ns = 1s delay_cnt <= 26'd0; rd_flow_cnt <= 2'd0; end end default : ; endcase end end endmodule (4)SD NAND控制器模块 SD NAND控制器模块:SD NAND控制器模块负责驱动SD NAND,该模块将SD NAND的读写操作封装成方便用户使用的接口。关于SD NAND读写控制器模块在上一篇文章中已经详细说明了,可参考: 基于FPGA的SD卡的数据读写实现(SD NAND FLASH) (5)SDRAM读写控制模块 SDRAM读写控制模块:SDRAM读写控制器模块负责驱动SDRAM存储器,缓存图像数据。该模块将SDRAM复杂的读写操作封装成类似FIFO的用户接口, 非常方便用户的使用。关于此部分,有详尽的系列文章供参考:相信我,SDRAM真的不难----汇总篇 (6)VGA驱动模块 VGA驱动模块根据VGA时序参数输出行、场同步信号;同时它还要输出数据请求信号用于读取SDRAM中的图片数据,并将图片通过VGA接口在显示器上显示。关于此部分,有详尽的文章供参考:如何用VGA接口乳法? 4.2、仿真 一般的测试中,我们都需要先进行仿真来观察时序等测试行为。此次实验由于找不到好的SD NAND的Verilog模型,所以仿真测试略。 4.3、实验结果 编译工程,把程序下载到FPGA开发板,通过VGA接口连接VGA线到显示器,如下: 接着观察显示器是否会交替显示我们事先保存的两幅图片。实验现象果然与预期一致: 第1幅图片: 深圳市雷龙发展有限公司 第2幅图片:本博客图像(星爷yyds) 好啦,本次实验就做完啦。 如果屏幕前的你也有存储产品方面的需求的话,你都可以试试雷龙公司的 SD NAND 产品哦。 这是一家专业做存储产品的公司,NAND Flash是其主要产品。 该公司专注NAND Flash设计研发13年,在这一块可以说是相当专业。如果你对 NAND Flash 仍有疑惑的问题,或者你想在你的设计中使用NAND Flash产品,都可以直接联系: 深圳市雷龙发展有限公司 术业有专攻,闻道有先后,专业的事就交给专业的人处理。如果你有这方面的设计需求都可以直接找他们要免费样品哦。 ———————————————— 【本文转载自CSDN,作者:孤独的单刀】
  • 热度 10
    2022-12-15 18:09
    712 次阅读|
    0 个评论
    文章目录 1、存储芯片分类 2、NOR Flash 与NAND Flash的区别 3、什么是SD卡? 4、什么是SD NAND? 5、SD NAND的控制时序 6、FPGA实现SD NAND读写 6.1、设计思路 6.2、仿真结果 6.3、实验结果 1、存储芯片分类 目前市面上的存储芯片,大致可以将其分为3大类: ① EEPROM EEPROM (Electrically Erasable Programmable read only memory)是指带电可擦可编程只读存储器,是一种掉电后数据不丢失的存储芯片。EEPROM 可以在电脑上或专用设备上擦除已有信息,重新编程。 这类产品容量小,读取速度慢,且无法在应用过程中写入数据,十分不便。目前多存在于一些MCU内部,如遥控器,电风扇等各类低端、低速消费类产品。相信你读大学如果玩单片机的话,那么应该是不陌生。 ② NOR Flash NOR Flash由Intel在1988年发明,是市场上两种主要的非易失闪存技术之一。NOR Flash 技术的出现,彻底改变了原先由EPROM和EEPROM一统天下的局面。 NOR Flash可能是目前应用领域最广泛的一种存储芯片了,基本上主流的电子产品里都有使用,比如手机摄像头内部,或者屏幕驱动电路板。主要用来存储代码和一些比较小的数据文件。NOR Flash架构决定了它的容量不能做大,而且读取速度比较慢。好处在于简单易用,其接口可以实现地址寻址,也就意味着可以做到直接对某个地址直接操作,而不需要建立额外的文件系统。 ③ NAND Flash 虽然很多人平常说的Flash 存储器一半默认其就是NOR Flash ,但这无疑是不严谨的。FLASH产品可以分为两个大类:NOR Flash 和 NAND Flash 。 NAND Flash 是市场上两种主要的非易失闪存技术之一,由东芝公司在1989年发明。其强调降低每比特的成本,并拥有更高的性能,可以像磁盘一样可以通过接口轻松升级。 NAND Flash应该是目前最热门的存储芯片了。因为我们生活中经常使用的电子产品都会涉及到它。比如你买手机,肯定会考虑64GB,还是256GB?买笔记本是买256GB,还是512GB容量的硬盘呢?(目前电脑大部分采用了基于NAND Flash产品的固态硬盘) 2、NOR Flash 与 NAND Flash的区别 Flash 闪存是非易失存储器,可以对称为块的存储器单元块进行擦写和再编程。任何Flash 器件的写入操作都只能在空或已擦除的单元内进行,所以大多数情况下,在进行写入操作之前必须先执行擦除。NAND器件执行擦除操作是十分简单的,而NOR则要求在进行擦除前先要将目标块内所有的位都写为0。 执行擦除时块尺寸的不同进一步拉大了NOR和NAND之间的性能差距,统计表明,对于给定的一套写入操作(尤其是更新小文件时),更多的擦除操作必须在基于NOR的单元中进行。这样,当选择存储解决方案时,用户必须权衡以下因素: NOR Flash支持随机访问,所以支持XIP(execute In Place),NAND Flash需要按块进行读取,所以不支持XIP NAND FLASH理论读取速度与NOR Flash相近,实际情况会根据接口不同有些差异 NOR 与 NAND 写入前都需要先擦除,NOR在擦除时以64~128KB的块进行,执行一个写入/擦除操作的时间约5s,NAND在擦除时以8~32KB的块进行,执行一个写入/擦除操作的时间约4ms NAND 理论最大擦除次数比NOR多 NOR 驱动比NAND简单,NAND FLASH需要通过专门的NFI(NAND FLASH Interface)与Host端进行通信,驱动相对复杂 所有Flash 都会有位反转的问题,NAND 位反转概率要比NOR高,NAND Flash 必须要使用ECC NAND的单元尺寸几乎是NOR器件的一半,所以NAND成本更低   NOR 与 NAND 各有特点,应用场景与应用难度也不同,一般来讲,NOR适用于小容量、略低速且需要直接对地址块进行操作的应用,而NADN则适用于大容量的高速应用。SD NAND 在保留了NAND架构优质特性的同时改进了不足之处,内置的控制器能自行管理NAND Flash,无需在外部处理ECC和进行坏块管理,免去了MTD层,不需要写繁琐的驱动代码。 3、什么是SD卡? ①概述 SD卡的英文全称是Secure Digital Card,即安全数字卡(又叫安全数码卡),是在MMC 卡(Multimedia Card,多媒体卡)的基础上发展而来,主要增加了两个特色:更高的安全性和更快的读写速度。 ②容量标准和速度等级 若按照容量 对 SD 卡进行等级划分,SD 卡可分为 4 个等级,SD(Secure Digital Card,安全数字卡) 卡、SDHC 卡(Secure Digital High Capacity,高容量安全数字卡)、SDXC 卡( SD eXtended Capacity,容量扩大化的安全数字卡)和 SDUC(Secure Digital Ultra Capacity,超 容量安全数字卡)。现今,市场的主流 SD 产品是 SDHC 和 SDXC 这两种较大容量的存储 卡,SD 卡因容量过小,已逐渐被市场淘汰,SDUC 则是容量太大,预计会出现在未来市 场。SD 卡的四种容量标准,具体见下图: 不同品牌和厂商生产的 SD 卡在对存取速度上的定义标准不同,这会使用户在选择 SD卡时产生困扰。所以 SD 协会根据视频匀速写入到 SD 卡的最低持续速度来划分不同等级, 每个等级的速率是以每秒传输多少 MB 来衡量的,单位为 MB/S。 SD 协会定义了三种速度等级:速度等级、UHS 速度等级与视频速度等级。三种速度等级的具体传输速度如下图: 4、什么是SD NAND? 上文中提到的SD卡其实更应该叫做TF卡,在日常生活中最常见的应用就是数码相机的存储卡。因为它是可拆卸的,所以这类SD卡最大的优点就是便携方便,但同时也有容易丢失和接触不良等毛病,所以多用于消费类产品。 在工业级应用中,更多见的是一类贴片式的SD NAND产品,俗称贴片式T卡或贴片式SD卡。虽然SD NAND 和TF卡称呼上有些类似,但是SD NAND和TF卡有着本质上的区别: 为什么SD NAND和 TF卡 之间有这么大区别呢?因为 SD NAND是为内置存储而生,是焊接在PCB板上的,是针对工业级应用的产品,所以品质稳定、一致性高、使用稳定性高、同时尺寸也小。而TF卡主要是针对普通消费者,品质和一致性、使用稳定性等相对要求不高,再加上国内鱼龙混杂的市场环境,导致内置存储用TF卡的品质风险高。   NAND Flash产品的一个特质就是它的品质并不是0和1这么简单,有些品质隐患是使用一段时间之后才被发现,如果这个产品已经销往海外,处理起来会变得异常麻烦。使用SD NAND可以为客户产品带来整体品质的提升,提供确定性,是客户产品良好品牌和口碑的稳定基石。而使用TF卡时,产品整机不可控因素会增高,比如常见的卡座老化松动、TF触点氧化、TF卡遗失、抗震性能减退等等。综上所述,虽然SD NAND与TF卡使用的协议相同,但从外观到内在都有区别。正在使用TF卡的客户需要提升产品稳定性及耐用性时,SD NAND 是绝佳选择。 尽管 SD NAND和 TF卡之间有着这么大的区别,但具体到实际应用,其对外接口和驱动程序都是一样了,这说明可移植性非常好。 ① 物理结构 SD NAND从物理结构看包括 5 个部分,分别为存储单元、存储单元接口、电源检测、卡及接口控制器和对外接口,具体见下图。 存储单元是存储数据部件,存储单元通过存储单元接口与卡控制单元进行数据传输 存储单元接口是接口控制器与存储单元的数据交互通道 电源检测单元保证 SD NAND工作在合适的电压下,如出现掉电或上电状态时,它会使控制单元和存储单元接口复位 卡及接口控制单元控制 SDNAND的运行状态,它包括 8 个寄存器 对外接口单元控制 SD NAND引脚的输入输出,一般包含SDIO接口和SPI接口 ② 对外接口 SD NAND共有 9 个引脚,其中包括 3 根电源线、1 根时钟线、1 根命令线和 4根数据线。如下: CLK:同步时钟线,由主机产生,即由主控制器(FPGA)输出; 使用 SPI 模式 时,该引脚与 SPI 总线的 SCK 时钟信号相连 CMD:命令控制线, SDIO 主机通过该线发送命令控制 SD NAND,如果命令要求 SD NAND 提供应答(响应), SD NAND也是通过该线传输应答信息; 使用 SPI 模式时,该引脚与 SPI总线的 MOSI 信号相连, SPI 主机通过它向 SD NAND发送命令及数据,但因为 SPI 总线 的 MOSI 仅用于主机向从机输出信号,所以 SD NAND返回应答信息时不使用该信号线; DAT0-3:在 SDIO 模式下,它们均为数据线,传输读写数据, SD NAND可将 D0 拉低表 示忙状态; 在 SPI 模式下, DAT0 与 SPI 总线的 MISO 信号相连, SD NAND通过该信号线向主机发送数据或响应, DAT3 与总线的 CS 信号相连, SPI 主机通过该信号线选择要通讯的 SD NAND。 VDD、VSS1、VSS2:电源和地信号。 ③ 工作模式 SD NAND有两种工作模式:SDIO 模式与SPI模式 在 SDIO 模式下,SD NAND共使用到 CLK、CMD、DAT 6根信号线;SDIO 总线与多个 SD NAND连接时,可以共用 CLK 时钟信号线,对于 CMD、DAT 信号线,每个 SD NAND都要独立连接。SDIO 总线与 SD 卡连接方式,具体见下图。此模式使用IO引脚多,但传输速度高。 在 SPI 模式下,SD NAND共使用到 CS(DAT )、CLK、MISO(DAT )、MOSI(CMD) 4根信号线;SPI 总线与多个 SD NAND连接时,除 CS 片选信号线不可共用外,其他信号均可公用。SPI 总线与 SD NAND连方式,具体见下图。此模式使用IO引脚少,但传输速度慢。 ④ 内部寄存器 SD NAND总共有 8 个寄存器,用于设定或表示 SD NAND信息,寄存器描述具体见下图。 这些寄存器只能通过对应的命令访问,对 SD NAND的控制操作是通过命令来执行的, SD NAND定义了 64 个命令(部分命令不支持 SPI 模式) ,每个命令都有特殊意义,可以实现某一特定功能, SD NAND接收到命令后,根据命令要求对 SD NAND内部寄存器进行修改,程序控制中只需要发送组合命令就可以实现 SD NAND的控制以及读写操作。 内部寄存器就不展开讲了,我们用FPGA实现读写测试也不需要了解那么多寄存器。 5、SD NAND的控制时序 ① 命令与读写时序 SD NAND的通信是基于命令和数据传输的。通讯由一个起始位(“0”)开始,由一个停止位(“1”)终止。SD NAND通信一般是主机发送一个命令(Command),从设备在接收到命令后作出响应(Response),如有需要会有数据(Data)传输参与。SD NAND的基本交互是命令与响应交互, 见下图: SD NAND数据是以块(Block)形式传输的,SDHC 规格数据块长度一般为 512 字节,数据可以从主机到芯片,也可以是从芯片到主机。数据块需要 CRC 位来保证数据传输成功,CRC 位由 SD NAND系统硬件生成。单个数据块的读、写时序分别见下2图: 读写操作都是由主机发起的,主机发送不同的命令表示读或写, SD NAND接收到命令后先针对命令返回响应。在读操作中, SD NAND返回一个数据块,数据块中包含 CRC校验码;在写操作中,主机接收到命令响应后需要先发送一个标志(TOKEN)然后紧跟一个要写入的数据块,SD NAND接收完数据块后会返回一个数据响应及忙碌标志,当 SD NAND把接收到的数据写入到内部存储单元完成后,会停止发送忙碌标志,主机确认 SD NAND空闲后,才可以发送下一个命令。 SD NAND数据传输支持单块和多块读写,它们分别对应不同的操作命令, 结束多块读写时需要使用命令来停止操作。 ② 命令格式 SD NAND命令由主机发出,命令格式固定为 48bit,通过 CMD 信号线连续传输。SD NAND命令格式,具体见下图: 起始位和终止位:命令的主体包含在起始位与终止位之间,它们都只包含一个数据位,起始位为 0,终止位为 1。 传输标志:用于区分传输方向,该位为 1 时表示命令,方向为主机传输到 SD NAND,该位为 0 时表示响应,方向为 SD NAND传输到主机。命令主体内容包括命令、地址信息/参数和 CRC 校验三个部分。 命令号:它固定占用 6bit,所以总共有 64 个命令,每个命令都有特定的用途,部分命令不适用于 SPI 总线,或不适用于 SD NAND操作,只是专门用于 MMC 卡或者 SD I/O卡。 地址/参数:每个命令有 32bit 地址信息/参数用于命令附加内容,例如,广播命令没有地址信息,这 32bit 用于指定参数,而寻址命令这 32bit 用于指定目标 SD NAND的地址, 当使用 SDIO 驱动多个 SD NAND时,通过地址信息区分控制不同的SD NAND,使用 SPI 总线驱动时,通过片选引脚来选择不同的SD NAND,所以使用这些命令时地址可填充任意值。 CRC7 校验:长度为 7bit 的校验位用于验证命令传输内容正确性,如果发生外部干扰 导致传输数据个别位状态改变将导致校准失败,也意味着命令传输失败, SD NAND不执行命令。 使用 SDIO 驱动时,命令中必须包含正确的 CRC7 校验值;而使用 SPI 驱动时,命令中的 CRC7 校验默认是关闭的,即这 CRC7 校验位中可以写入任意值而不影响通讯,仅在发送 CMD0 命令时需要强制带标准的 CRC7 校验。 ③ 命令内容 SD NAND命令可分为标准命令 (如 CMD0)和特殊应用命令 (如 ACMD41),其中特殊应用命令只有在先写入 CMD55 命令后才能被识别。按照指令类型又可将 SD NAND命令分为基本命令、数据块写命令、数据块读命令、擦除命令等 12 种(class0 ~ class11)。 本次实验将会使用 SPI 模式实现 SD NAND的数据读写操作,所以接下来只列举 SPI 模式下常用的 SD 卡命令,具体见下表: SPI 模式下,上述各命令中,命令 CMD0 的 CRC7 校验为固定的 1001_010;命令CMD8 的 CRC7 校验为固定的 1000_011;其他命令的 CRC7 校验在 SPI 模式下无作用,赋值为 1111_111 即可。 ④ 响应格式 当 SD NAND接收到命令时,会向 SD NAND回传命令响应。SD NAND有 5 种类型的命令响应:R1、R1b、R2、R3、R7;SDIO NAND还支持另外两种命令响应:R4、R5。下文只对部分响应做介绍。 R1 响应格式,具体见下图: in idle state:当该位为 1 时,表示 SD NAND处于空闲状态 erase reset:因为接收到无需擦除操作的命令,擦除操作被复位 illegal command:接收到一个无效的命令代码 com crc error:接收到的上一个命令的 CRC 校验错误 erase sequence error:擦除命令的控制顺序错误 address error:读写的数据地址不对齐(数据地址需要按块大小对齐) parameter error:命令的参数错误 R3 响应格式,具体见下图: R3 响应包括 5 个字节,首先返回的第 1 个字节内容为 R1,剩下的其余字节为 OCR( Operation Conditions Register, 操作条件寄存器)寄存器的内容。 R7 响应格式,具体见下图: R7 响应包括 5 个字节,首先返回的第 1 个字节内容为 R1,R7 位为命令版本,R7 为保留位,R7 为反馈的电压范围,最后 1 个字节为检查模式。 6、FPGA实现SD NAND读写 接下来编写FPGA的Verilog代码实现向SD NAND的指定扇区中写入512个字节的数据,写完后将数据读出,并通过指示灯的方式验证数据是否被正确读写。需要说明的是,后文的读写操作均采用SPI模式。 6.1、设计思路 ① 上电时序 SD NAND同其他的许多芯片一样上电后需要保持一定的时间以便维持电压稳定,这个时间通常是74+个时钟周期,一般实际应用中可设置参数为74~100。只有经过这个过渡时间后,才可以执行后续的SD NAND初始化操作。 ② 初始化时序 SD NAND在正常读写操作之前,必须先对SD NAND进行初始化,使其工作在预期的工作模式。初始化流程如下: 1.SD NAND完成上电后,主机FPGA先对从机SD NAND发送至少74个以上的同步时钟,在上电同步期间,片选CS引脚和MOSI引脚必须为高电平(MOSI引脚除发送命令或数据外,其余时刻都为高电平); 2.拉低片选CS引脚,发送命令CMD0(0x40)复位SD NAND,命令发送完成后等待SD NAND返回响应数据; 3.SD NAND返回响应数据后,先等待8个时钟周期再拉高片选CS信号,此时判断返回的响应数据。如果返回的数据为复位完成信号0x01,在接收返回信息期间片选CS为低电平, 此时SD NAND进入SPI模式,并开始进行下一步,如果返回的值为其它值,则重新执行第2步; 4.拉低片选CS引脚,发送命令CMD8(0x48)查询SD NAND的版本号,只有SD2.0版本才支持此命令,命令发送完成后等待SD NAND返回响应数据; 5.SD NAND返回响应数据后,先等待8个时钟周期再拉高片选CS信号,此时判断返回的响应数据。如果返回的电压范围为4’b0001即2.7V~3.6V,说明2.0版本,进行下一步,否则重新执行第4步; 6.拉低片选CS引脚,发送命令CMD55(0x77)告诉SD NAND下一次发送的命令是应用相关命令,命令发送完成后等待SD NAND返回响应数据; 7.SD NAND返回响应数据后,先等待8个时钟周期再拉高片选CS信号,此时判断返回的响应数据。如果返回的数据为空闲信号0x01,开始进行下一步,否则重新执行第6步。 8.拉低片选CS引脚,发送命令ACMD41(0x69)查询SD NAND是否初始化完成,命令发送完成后等待SD NAND返回响应数据; 9.SD NAND返回响应数据后,先等待8个时钟周期再拉高片选CS信号,此时判断返回的响应数据。如果返回的数据为0x00,此时初始化完成,否则重新执行第6步。 ③ 写操作时序 至此,SD NAND完成了复位以及初始化操作,进入到SPI模式的读写操作。SD NAND读写一次的数据量必须为512字节的整数倍,即对SD NAND读写操作的最少数据量为512 个字节。我们可以通过命令CMD16来配置单次读写操作的数据长度,以使每次读写的数据量为 (n*512)个字节(n≥1),本次SD NAND的读写操作使用默认配置,即单次读写操作的数据量为512个字节。 SD NAND的写操作时序图如下图所示: 拉低片选信号 CS_N,向 SD NAND写入命令 CMD24,命令号为 0x58,携带参数为 4字节的 SD NAND写扇区地址,CRC 校验字节未使用直接写入 0xFF,命令发送完成后 等待 SD NAND返回响应数据 若 SD NAND返回正确响应数据 R1 为 0x00,等待 8 个时钟周期,向 SD NAND写入令牌0xFE,紧随其后写入 512 个字节的数据 数据发送完成后,再向 SD NAND写入 2 个字节的 CRC 校验字节。SPI 模式下不对数据进行 CRC 校验,直接写入两个字节的 0xFF 校验数据发送完成后, SD NAND会有响应数据返回,随后 SD NAND将 Miso 信号拉低进入写忙状态 MISO 信号再次拉高后 SD NAND退出写忙状态,等待 8 个时钟周期后拉高片选信号,SD NAND数据写操作完成,可以执行其它操作 ④ 读操作时序 SD NAND的读操作时序图如下图所示: 1.拉低片选信号 CS_N, 向 SD NAND写入命令 CMD17,命令号为 0x51,携带参数为 4字节的 SD NAND读扇区地址,CRC 校验字节未使用直接写入 0xFF,命令发送完成后 等待 SD NAND返回响应数据 2.若 SD NAND返回正确响应数据 R1 为 0x00,以 SD NAND返回的数据头 0xFE 为标志,接收自 SD NAND读出的 512 字节数据和 2 字节的 CRC 校验字节 3.解析到数据头 0xFE 后,接下来接收 SD NAND返回的 512 个字节的数据 4.数据解析完成后,接下来接收2个字节的 CRC 校验值。 由于 SPI 模式下不对数据进行 CRC 校验,可直接忽略这两个字节 5.CRC 校验字节接收完毕,等待 8 个时钟周期,拉高片选信号 CS_N,一次数据读操作完成 ⑤ 程序设计 通过前面介绍的SD NAND初始化、写操作以及读操作可知,SD NAND的这3个操作是相互独立且不能同时进行的,因此我们可以将SD NAND的初始化、写操作以及读操作分别划分为3个独立的模块,最后将这三个模块例化在SD NAND的控制器模块中,便于在其它工程项目中使用。 下图是系统框图,PLL时钟模块(PLL)为各个模块提供驱动时钟,SD NAND测试数据产生模块产生测试数据写入SD NAND,写完后从SD NAND中读出数据,最终读写测试结果由LED显示模块通过控制LED灯的显示状态来指示。 顶层模块:顶层模块完成了对其它四个模块的例化,SD NAND测试数据产生模块产生的开始写入信号及数据连接至SD NAND控制器模块,数据写完后从SD NAND控制器中读出数据, 并验证数据的正确性,将验证的结果连接至LED显示模块。 PLL时钟模块:PLL时钟模块通过调用锁相环(PLL)IP核来实现,总共输出2个时钟,频率都是50Mhz,但两个时钟相位相差180度。我们知道,SD卡的SPI通信模式为CPOL=1, CPHA=1;即SPI_CLK在空闲时为高电平,数据发送是在时钟的第一个边沿,也就是SPI_CLK由高 电平到低电平的跳变,所以数据采集是在上升沿,数据发送是在下降沿。为了在程序代码中统 一使用上升沿,我们使用两个相位相差180度的时钟来对SD NAND进行操作。 SD NAND测试数据产生模块:SD NAND测试数据产生模块产生的开始写入信号和数据写入SD NAND控制器模块中,数据写完后从SD NAND控制器中读出数据,并验证数据的正确性,将验证的结果发送给LED显示模块。 SD NAND控制器模块:SD NAND控制器模块例化了SD NAND初始化模块、 SD NAND写数据模块和SD NAND读数据模块。SD NAND初始化模块完成对SD NAND的上电初始化操作;SD NAND写数据模块完成对SD NAND的写操作;SD NAND读数据模块完成对SD NAND的读操作。 由于这三个模块都操作了SD NAND的引脚信号,且这三个模块在同一时间内不会同时操作,所以此模块实现了对其它三个模块的例化以及选择SD NAND的引脚连接至其中某一个模块。 LED显示模块:LED显示模块将SD NAND测试数据产生模块输出的验证结果值, 通过控制LED灯的显示状态来指示。 SD NAND控制器部分代码如下: module sd_ctrl_top( input clk_ref , //时钟信号 input clk_ref_180deg, //时钟信号,与clk_ref相位相差180度 input rst_n , //复位信号,低电平有效 //SD卡接口 input sd_miso , //SD卡SPI串行输入数据信号 output sd_clk , //SD卡SPI时钟信号 output reg sd_cs , //SD卡SPI片选信号 output reg sd_mosi , //SD卡SPI串行输出数据信号 //用户写SD卡接口 input wr_start_en , //开始写SD卡数据信号 input wr_sec_addr , //写数据扇区地址 input wr_data , //写数据 output wr_busy , //写数据忙信号 output wr_req , //写数据请求信号 //用户读SD卡接口 input rd_start_en , //开始读SD卡数据信号 input rd_sec_addr , //读数据扇区地址 output rd_busy , //读数据忙信号 output rd_val_en , //读数据有效信号 output rd_val_data , //读数据 output sd_init_done //SD卡初始化完成信号 ); //wire define wire init_sd_clk ; //初始化SD卡时的低速时钟 wire init_sd_cs ; //初始化模块SD片选信号 wire init_sd_mosi ; //初始化模块SD数据输出信号 wire wr_sd_cs ; //写数据模块SD片选信号 wire wr_sd_mosi ; //写数据模块SD数据输出信号 wire rd_sd_cs ; //读数据模块SD片选信号 wire rd_sd_mosi ; //读数据模块SD数据输出信号 //***************************************************** //** main code //***************************************************** //SD卡的SPI_CLK assign sd_clk = (sd_init_done==1'b0) ? init_sd_clk : clk_ref_180deg; //SD卡接口信号选择 always @(*) begin //SD卡初始化完成之前,端口信号和初始化模块信号相连 if(sd_init_done == 1'b0) begin sd_cs = init_sd_cs; sd_mosi = init_sd_mosi; end else if(wr_busy) begin sd_cs = wr_sd_cs; sd_mosi = wr_sd_mosi; end else if(rd_busy) begin sd_cs = rd_sd_cs; sd_mosi = rd_sd_mosi; end else begin sd_cs = 1'b1; sd_mosi = 1'b1; end end //SD卡初始化 sd_init u_sd_init( .clk_ref (clk_ref), .rst_n (rst_n), .sd_miso (sd_miso), .sd_clk (init_sd_clk), .sd_cs (init_sd_cs), .sd_mosi (init_sd_mosi), .sd_init_done (sd_init_done) ); //SD卡写数据 sd_write u_sd_write( .clk_ref (clk_ref), .clk_ref_180deg (clk_ref_180deg), .rst_n (rst_n), .sd_miso (sd_miso), .sd_cs (wr_sd_cs), .sd_mosi (wr_sd_mosi), //SD卡初始化完成之后响应写操作 .wr_start_en (wr_start_en & sd_init_done), .wr_sec_addr (wr_sec_addr), .wr_data (wr_data), .wr_busy (wr_busy), .wr_req (wr_req) ); //SD卡读数据 sd_read u_sd_read( .clk_ref (clk_ref), .clk_ref_180deg (clk_ref_180deg), .rst_n (rst_n), .sd_miso (sd_miso), .sd_cs (rd_sd_cs), .sd_mosi (rd_sd_mosi), //SD卡初始化完成之后响应读操作 .rd_start_en (rd_start_en & sd_init_done), .rd_sec_addr (rd_sec_addr), .rd_busy (rd_busy), .rd_val_en (rd_val_en), .rd_val_data (rd_val_data) ); endmodule SD NAND控制器模块输出的sd_init_done(SD NAND初始化完成信号)连接至SD NAND测试数据产生模块,只有在SD NAND初始化完成之后(sd_init_done为高电平),才能对SD NAND进行读写测试。SD NAND控制器模块将SD NAND的初始化以及读写操作封装成方便用户调用的接口,SD NAND测试数据产生模块只需对SD NAND控制器模块的用户接口进行操作即可完成对SD NAND的读写操作。 6.2、仿真结果 一般的测试中,我们都需要先进行仿真来观察时序等测试行为。此次实验由于找不到好的SD NAND的Verilog模型,所以仿真测试略。 6.3、实验结果 上文已经说了常用的相机中的TF卡与工业级的SD NAND(贴片式T卡)的区别,所以本次实验我选用的是深圳雷龙公司的一款SD NAND产品----CSNP32GCR01-AOW。 这是一家专业做存储产品的公司,NAND Flash是其主要产品。 该公司专注NAND Flash设计研发13年,在这一块可以说是相当专业。如果你对NAND Flash仍有疑惑的问题,或者你想在你的设计中使用NAND Flash产品,都可以直接联系: 深圳市雷龙发展有限公司 实验结果其实没什么好看的,LED灯常量表明说明从SD NAND读出的512个字节(256个16位数据) 与写入的数据相同,SD NAND读写测试程序下载验证成功。 PS:有个小细节可以说下,雷龙公司的SD NAND开发板还是蛮用心的,封装都是标准的Micro SD的封装,只要你的FPGA开发板上有SD卡座,就可以直接插上使用了,即插即用十分方便,有心了。 ———————————————— 【本文转载自CSDN,作者:孤独的单刀】   亲爱的卡友们,如果看完文章之后还是有疑惑或不懂的地方,请联系我们,自己去理解或猜答案是件很累的事,请把最麻烦的事情交给我们来处理,术业有专攻,闻道有先后, 深圳市雷龙发展 专注存储行业13年,专业提供小容量存储解决方案。
相关资源