tag 标签: NAND Flash

相关帖子
相关博文
  • 2024-12-17 17:37
    165 次阅读|
    0 个评论
      前言   作为一名电子专业的学生,半导体存储显然是绕不过去的一个坎,今天聊一聊关于Nand Flash的一些小知识。   这里十分感谢深圳雷龙发展有限公司为博主提供的两片CS创世SD NAND的存储芯片,同时也给大家推荐该品牌的相关产品。   一、定义   存储芯片根据断电后是否保留存储的信息可分为易失性存储芯片(RAM)和非易失性存储芯片(ROM)。   非易失性存储器芯片在断电后亦能持续保存代码及数据,分为闪型存储器 (Flash Memory)与只读存储器(Read-OnlyMemory),其中闪型存储器是主流,而闪型存储器又主要是NAND Flash 和NOR Flash。   NAND Flash 存储单元尺寸更小,存储密度更高,单位容量成本更低,块擦/写速度快, 具有更长的寿命,多应用于大容量数据存储,如智能手机、PC、平板电脑、U 盘、固态硬盘、服务器等领域。   NOR Flash 读取速度更快,具备可在芯片内执行程序(XIP)的特点,在传输效率、稳定性和可靠性方面更具优势,通常用于小容量数据存储,适宜中等容量代码存储(通常在 1Mb~1Gb),在计算机、消费电子(智能手机、TV、TWS 耳机、可 穿戴设备)、安防设备、汽车电子(ADAS、车窗控制、仪表盘)等领域均有应用。   二、NAND Flash   NAND Flash的存储单元是数据存储的最小单位,目前闪存已经由数千亿个存储单元组成,通过将电子移入和移出封闭在绝缘体中的电荷存储膜来存储数据。   NAND Flash存储器使用浮栅晶体管,它能在没有电源的情况下存储信息。所有的电路都依赖于某种能量来使整个电池的电荷产生差异,这种能量迫使电子穿过栅极,Nand闪存的浮动栅极系统通过使用第二个栅极在电子穿过电池时收集和捕获一些电子, 这使得粘在浮栅上的电子在没有电压的情况下保持原位,在这一过程中不管是否有电源连 接,芯片都能继续存储下一个值。   NAND Flash 为大容量数据存储的实现提供了廉价有效的解决方案,是目前全球市场大容量非易失存储的主流技术方案。   三、NAND Flash分类   NAND闪存卡的主要分类以NAND闪存颗粒的技术为主,NAND闪存颗粒根据存储原理分为SLC、MLC、TLC和QLC,从结构上又可分为2D、3D两大类。   Flash按技术主要分为SLC、MLC、TLC和QLC四大类,对应不同的空间结构,这四类技术可又分为2D结构和3D结构两大类;2D结构的存储单元仅布置在芯片的XY平面中,为了提高存储密度,制造商开发了3D NAND或V-NAND(垂直NAND)技术,该技术将Z平面中的存储单元堆叠在同一晶圆上。   3D NAND, 即立体堆叠技术,如果把2D NAND看成平房,那么3D NAND就是高楼大厦,建筑面积成倍扩增,理论上可以无限堆叠,可以摆脱对先进制程工艺的束缚,同时也不依赖于极紫外光刻(EUV)技术。   与2D NAND缩小Cell提高存储密度不同的是,3D NAND只需要提高堆栈层数,目前多种工艺架构并存。从2013年三星推出了第一款24层SLC/MLC 3D V-NAND,到现在层数已经迈进200+层,并即将进入300+层阶段。目前,三星/西部数据/海力士/美光/铠 侠等几乎垄断了所有市场份额,并且都具有自己的特殊工艺架构,韩系三星/海力士的CTF,美系镁光/英特尔的FG,国内长江存储的X-tacking。   随着堆栈层数的增加,工艺也面临越来越多的挑战,对制造设备和材料也提出了更多的要求。主要包括以下几个方面:   1)ONON薄膜应力:随着器件层数增加,薄膜应力问题越发凸显,会影响后续光刻对准精度;   2)高深宽比通孔刻蚀:随着深宽比增加,刻蚀难度会显著增加,容易出现刻蚀不完全、通孔结构扭曲等问题;   3)WL台阶的设计与刻蚀:垂直管状环栅结构的器件需要刻蚀出精确的台阶结构,保障CT能打到对应位置,而随着层数增加, 工艺难度加大,需要重新设计WL台阶结构。   四、品牌推荐——雷龙发展代理CS创世SD NAND   对于电子这一专业来说,仅仅从书面上了解一款电子元件是远远不够格的,上手实践才是第一要义。   这里十分感谢深圳雷龙发展有限公司为博主提供的两片SD NAND的存储芯片,同时也给大家推荐该品牌的相关产品。   博主拿到手上的芯片型号为:CSNP4GCR01-AMW,其性能如下   这款存储芯片作为博主正在完成的物联网项目中表现优异,性能良好,是作为存储工具的不二选择。
  • 热度 2
    2024-6-21 18:03
    274 次阅读|
    0 个评论
      在此介绍的是使用FPGA实现SD NAND FLASH的读写操作,以雷龙发展提供的CS创世SD NAND FLASH样品为例,分别讲解电路连接、读写时序与仿真和实验结果。 目录   1 视频讲解   2 SD NAND FLASH背景介绍   3 样品申请   4 电路结构与接口协议   4.1 SD NAND   4.2 SD NAND测试板 4.3 FPGA开发 板   5 SD卡协议与时序流程   5.1 SD卡协议   5.2 SD卡2.0版本初始化步骤   5.3 SD卡的读步骤   5.4 SD卡的写步骤   6 模块代码   6.1 sd_card_top   6.2 sd_card_cmd   6.3 sd_card_sec_read_write   6.4 spi_master   6.5 其余代码   6.5.1 sd_card_test   6.5.2 ax_debounce   6.5.3 seg_decoder   6.5.4 seg_scan   7 实验结果   8 参考资料   使用FPGA讲解SD NAND FLASH的文章网上也有很多比较详实的内容,本文的部分思路也是参考了其他博主的博客思路。  1 视频讲解   为了便于更加清晰地讲解内容,本文也将文章的对应部分以视频的形式进行了录制:   (后期正在紧锣密鼓制作ing)  2 SD NAND FLASH背景介绍   目前市面上主流的存储芯片,分为了EEPROM、NOR FLASH、NAND FLASH三种,其中后两种是市面上主要的非易失闪存技术,他们分别具有不同的特点:   1.EEPROM   EEPROM (Electrically Erasable Programmable read only memory)是指带电可擦可编程只读存储器。是一种掉电后数据不丢失的存储芯片。 EEPROM 可以在电脑上或专用设备上擦除已有信息,重新编程。一般用在即插即用设备中。   相较于EEPROM计数,下文提到的FLASH技术,具有更快的速度,工艺上可以分为NOR FLASH和NAND FLASH两种   2.NOR FLASH   NOR FLASH是一种非易失闪存技术。其特点是芯片内执行 (XIP),应用程序可以直接在存储芯片内运行,不必再把代码读到系统 RAM 中。其传输效率较高高,在 1~4MB 的小容量时具有很高的成本效益。   3.NAND FLASH   NAND FLASH内部采用非线性宏单元模式,这种结构能提供极高的单元密度,并且写入和擦除的速度很快。作为当前最热门的存储芯片,目前生活中常见的电子产品都会使用到这种存储芯片,例如数码相机、U盘等等。   由于NAND FLASH在大容量应用中的便利性,因此作为今天介绍的主角~   什么是SD NAND呢(以下省略FLASH)?下面的内容是从雷龙发展官网的介绍中得到:   SD NAND俗称贴片式TF卡,尽管与TF卡名称类似,但是有较大的区别:   相比常见的TF卡,SD NAND是专门为内置存储进行设计,焊接在PCB板上以供工业级产品的应用。因此对品质稳定性、一致性、以及尺寸都有较高的要求。   下图中左侧即为SD NAND、右侧是常见的TF卡。 3 样品申请   本文所使用的CS创世SD NAND是从深圳雷龙发展申请获得,可以在官网中最上面找到申请样品的入口:   深圳市雷龙发展有限公司创立于2008年,专注NAND Flash设计研发13年。创始人均为步步高/华为技术背景出身。是一家专注于存储元器件代理分销商。 如果有一些技术问题也可以和其公司人员进行沟通,相关的工作人员非常专业和热心。   下图是我收到的测试样品:  4 电路结构与接口协议   4.1 SD NAND   本文所使用的产品是CSNP4GCR01-AMW,是雷龙的第二代产品,产品如下图所示:   数据手册可以在立创商城进行下载,其封装与连接的电路原理参考图如下图所示:   芯片共包含8个引脚,包括4根数据线(6、7、1、2);2根电源线(4、8);1根时钟线(3);1根命令控制线(5)   手册中提供了SD NAND的两种使用模式,分别为SD MODE 以及 SPI MODE。他们所对应的引脚定义,如下图所示:   对于两种模式的切换,官方给出了初始化的方式。下文在代码的时序部分也会涉及到相关内容。   在对SD卡数据读写速度要求不高的情况下,选用SPI通信模式可以说是一种最佳方案。因为在该模式下,同只需要通过四根线就是可以完成所有的数据交换,可以为我们节省出宝贵的FPGA I/O资源。下图给出了SPI一对一通信时,主设备与从设备之间的连接关系。   (注:SPI协议详解传送门)   因此本文主要介绍SPI MODE下各个引脚的功能:   确定了通讯模式后,也就便于我们后文中,利用这种通讯模式按照SD卡的读写时序进行读写操作。 4.2 SD NAND测试板   单独的SD NAND不便于我们使用FPGA进行读写测试,好在官方提供了测试板,如下图所示:   有了它就可以轻松实现SD NAND与我们常见的FPGA开发板上的Micro SD插槽进行连接与测试了。   适用产品:LGA8,6x8mm 封装的SD NAND产品。   测试板尺寸:长度6.22厘米,宽度2.49厘米,接口长度2.53厘米。   使用方法:将芯片焊接至测试板上,可在原有的Micro SD卡座上直接调试和测试。   准备工具:热风枪,锡膏,镊子。温度要求:将热风枪温度调至300摄氏度℃即可焊接。  4.3 FPGA开发板   本文所使用的是黑金的AX301开发板,上面装有一个 Micro SD 卡座, FPGA 通过 SPI 数据总线访问 Micro SD 卡,SD 卡座和 FPGA 的硬件电路连接如下:   借由硬件电路的连接,FPGA可以直接与我们的SD NAND进行通信了。   至此,我们已经实现了SD NANDSPI通信方式方案的确定以及基于此的硬件电路连接,下一步就是根据SD卡的读写时序讲通信方式初始化为SPI模式,并按照SD卡协议进行读写操作。  5 SD卡协议与时序流程  5.1 SD卡协议   以下内容来自黑金的实验手册:   SD 卡的协议是一种简单的命令/响应的协议。全部命令由主机发起, SD 卡接收到命令后并返   回响应数据。根据命令的不同,返回的数据内容和长度也不同。 SD 卡命令是一个 6 字节组成的命   令包,其中第一个字节为命令号, 命令号高位 bit7 和 bit6 为固定的“01“,其它 6 个 bit 为具体   的命令号。第 2 个字节到第 5 个字节为命令参数。第 6 个字节为 7 个 bit 的 CRC 校验加 1 个 bit 的结束位。 如果在 SPI 模式的时候, CRC 校验位为可选。 如下图所示, Command 表示命令,通常使用十进制表示名称,例如 CMD17,这个时候 Command 就是十进制的 17。   对于详细的SD卡协议内容,可以参考传送门中的相关内容,给出了比较具体的解释。   SD 卡对每个命令会返回一个响应,每个命令有一定的响应格式。响应的格式跟给它的命令号   有关。在 SPI 模式中,有三种响应格式: R1, R2, R3。   在进行SD NAND的SPI模式读写操作时,主要使用到了以下几种SD卡命令,下面的表格进行简单介绍,这里可以找到完整版: 5.2 SD卡2.0版本初始化步骤   上电后延时至少 74clock,等待 SD 卡内部操作完成   片选 CS 低电平选中 SD 卡   发送 CMD0,需要返回 0x01,进入 Idle 状态   为了区别 SD 卡是 2.0 还是 1.0,或是 MMC 卡,这里根据协议向上兼容的,首先发送只有SD2.0 才有的命令 CMD8,如果 CMD8 返回无错误,则初步判断为 2.0 卡,进一步循环发送命令 CMD55+ACMD41,直到返回 0x00,确定 SD2.0 卡   如果 CMD8 返回错误则判断为 1.0 卡还是 MMC 卡,循环发送 CMD55+ACMD41,返回无错误,则为 SD1.0 卡,到此 SD1.0 卡初始成功,如果在一定的循环次数下,返回为错误,则进一步发送 CMD1 进行初始化,如果返回无错误,则确定为 MMC 卡,如果在一定的次数下,返回为错误,则不能识别该卡,初始化结束。 (通过 CMD16 可以改变 SD 卡一次性读写的长度)   CS 拉高 5.3 SD卡的读步骤   发送 CMD17(单块)或 CMD18(多块)读命令,返回 0X00   接收数据开始令牌 fe(或 fc) +正式数据 512Bytes + CRC 校验 2Bytes(默认正式传输的数据长度是 512Bytes) 5.4 SD卡的写步骤   发送 CMD24(单块)或 CMD25(多块)写命令,返回 0X00   发送数据开始令牌 fe(或 fc) +正式数据 512Bytes + CRC 校验 2Bytes   6 模块代码   本代码所实现的功能,是基于黑金AX301B,实现对SD NAND FLASH的数据写入与读取,并显示在开发板的数码管上。当按下开发板上的按键时,会自动将数据加一操作,并进行同步显示。   前文介绍的是SD NAND的协议以及初始化、读写操作的流程,下面介绍代码的组成部分,整个工程主要由以下部分模块构成:   sd_card_test(top模块)   ax_debounce:ax_debounce_m0(按键消抖模块)   sd_card_top:sd_card_top_m0(SD卡top模块)   sd_card_cmd:sd_card_cmd_m0(SD卡指令)   sd_card_sec_read_write:sd_card_sec_read_write_m0(SD卡读写)   spi_master:spi_master_m0(SPI一个字节读写)   seg_decoder:seg_decoder_m0(数码管控制)   seg_decoder:seg_decoder_m1(数码管控制)   seg_scan:seg_scan_m0(数码管控制)   下面主要介绍上述四个加粗的模块以及其功能  6.1 sd_card_top   本模块是SD card的top模块,用来实现不同子模块之间的连接。 // // // // // // Author: meisq // // msq@qq.com // // ALINX(shanghai) Technology Co.,Ltd // // heijin // // WEB: http://www.alinx.cn/ // // BB S: http://www.heijin.org/ // // // // // // // Copyright (c) 2017,ALINX(shanghai) Technology Co.,Ltd // // All rights reserved // // // // This source file may be used and distributed without restriction provided // // that this copyright statement is not removed from the file and that any // // derivative work contains the original copyright notice and the associated // // disclaimer. // // // // //========================================================================== // Revision History: // Date By Revision Change Description //-------------------------------------------------------------------------- // 2017/6/21 meisq 1.0 Original //*************************************************************************/ module sd_card_top #( parameter SPI_LOW_SPEED_DIV = 248, // SD card low speed mode frequency division parameter,spi clk speed = clk speed /((SPI_LOW_SPEED_DIV + 2) * 2 ) parameter SPI_HIGH_SPEED_DIV = 0 // SD card high speed mode frequency division parameter,spi clk speed = clk speed /((SPI_HIGH_SPEED_DIV + 2) * 2 ) ) ( input clk, input rst, output SD_nCS, //SD card chip select (SPI mode) output SD_DCLK, //SD card clock output SD_MOSI, //SD card controller data output input SD_MISO, //SD card controller data input output sd_init_done, //SD card initialization is complete input sd_sec_read, //SD card sector read input sd_sec_read_addr, //SD card sector read address output sd_sec_read_data, //SD card sector read data output sd_sec_read_data_valid, //SD card sector read data valid output sd_sec_read_end, //SD card sector read end input sd_sec_write, //SD card sector write input sd_sec_write_addr, //SD card sector write address input sd_sec_write_data, //SD card sector write data output sd_sec_write_data_req, //SD card sector write data next clock is valid output sd_sec_write_end //SD card sector write end ); wire spi_clk_div; //SPI module clock division parameter wire cmd_req; //SD card command request wire cmd_req_ack; //SD card command request response wire cmd_req_error; //SD card command request error wire cmd; //SD card command wire cmd_r1; //SD card expect response wire cmd_data_len; //SD card command read data length wire block_read_req; //SD card sector data read request wire block_read_valid; //SD card sector data read data valid wire block_read_data; //SD card sector data read data wire block_read_req_ack; //SD card sector data read response wire block_write_req; //SD card sector data write request wire block_write_data; //SD card sector data write data next clock is valid wire block_write_data_rd; //SD card sector data write data wire block_write_req_ack; //SD card sector data write response wire nCS_ctrl; //SPI module chip select control wire spi_wr_req; //SPI module data sending request wire spi_wr_ack; //SPI module data request response wire spi_data_in; //SPI module send data wire spi_data_out; //SPI module data returned wire clk_div; sd_card_sec_read_write #( .SPI_LOW_SPEED_DIV(SPI_LOW_SPEED_DIV), .SPI_HIGH_SPEED_DIV(SPI_HIGH_SPEED_DIV) ) sd_card_sec_read_write_m0( .clk (clk ), .rst (rst ), .sd_init_done (sd_init_done ), .sd_sec_read (sd_sec_read ), .sd_sec_read_addr (sd_sec_read_addr ), .sd_sec_read_data (sd_sec_read_data ), .sd_sec_read_data_valid (sd_sec_read_data_valid ), .sd_sec_read_end (sd_sec_read_end ), .sd_sec_write (sd_sec_write ), .sd_sec_write_addr (sd_sec_write_addr ), .sd_sec_write_data (sd_sec_write_data ), .sd_sec_write_data_req (sd_sec_write_data_req ), .sd_sec_write_end (sd_sec_write_end ), .spi_clk_div (spi_clk_div ), .cmd_req (cmd_req ), .cmd_req_ack (cmd_req_ack ), .cmd_req_error (cmd_req_error ), .cmd (cmd ), .cmd_r1 (cmd_r1 ), .cmd_data_len (cmd_data_len ), .block_read_req (block_read_req ), .block_read_valid (block_read_valid ), .block_read_data (block_read_data ), .block_read_req_ack (block_read_req_ack ), .block_write_req (block_write_req ), .block_write_data (block_write_data ), .block_write_data_rd (block_write_data_rd ), .block_write_req_ack (block_write_req_ack ) ); sd_card_cmd sd_card_cmd_m0( .sys_clk (clk ), .rst (rst ), .spi_clk_div (spi_clk_div ), .cmd_req (cmd_req ), .cmd_req_ack (cmd_req_ack ), .cmd_req_error (cmd_req_error ), .cmd (cmd ), .cmd_r1 (cmd_r1 ), .cmd_data_len (cmd_data_len ), .block_read_req (block_read_req ), .block_read_req_ack (block_read_req_ack ), .block_read_data (block_read_data ), .block_read_valid (block_read_valid ), .block_write_req (block_write_req ), .block_write_data (block_write_data ), .block_write_data_rd (block_write_data_rd ), .block_write_req_ack (block_write_req_ack ), .nCS_ctrl (nCS_ctrl ), .clk_div (clk_div ), .spi_wr_req (spi_wr_req ), .spi_wr_ack (spi_wr_ack ), .spi_data_in (spi_data_in ), .spi_data_out (spi_data_out ) ); spi_master spi_master_m0( .sys_clk (clk ), .rst (rst ), .nCS (SD_nCS ), .DCLK (SD_DCLK ), .MOSI (SD_MOSI ), .MISO (SD_MISO ), .clk_div (clk_div ), .CPOL (1'b1 ), .CPHA (1'b1 ), .nCS_ctrl (nCS_ctrl ), .wr_req (spi_wr_req ), .wr_ack (spi_wr_ack ), .data_in (spi_data_in ), .data_out (spi_data_out ) ); endmodule 6.2 sd_card_cmd   sd_card_cmd 模块主要实验 sd 卡基本命令操作,还有上电初始化的 88 个周期的时钟,数据   块的读写,状态机如下:   代码如下: // // // // // // Author: meisq // // msq@qq.com // // ALINX(shanghai) Technology Co.,Ltd // // heijin // // WEB: http://www.alinx.cn/ // // BBS: http://www.heijin.org/ // // // // // // // Copyright (c) 2017,ALINX(shanghai) Technology Co.,Ltd // // All rights reserved // // // // This source file may be used and distributed without restriction provided // // that this copyright statement is not removed from the file and that any // // derivative work contains the original copyright notice and the associated // // disclaimer. // // // // //========================================================================== // Revision History: // Date By Revision Change Description //-------------------------------------------------------------------------- // 2017/6/21 meisq 1.0 Original //*************************************************************************/ module sd_card_cmd( input sys_clk, input rst, input spi_clk_div, //SPI module clock division parameter input cmd_req, //SD card command request output cmd_req_ack, //SD card command request response output reg cmd_req_error, //SD card command request error input cmd, //SD card command input cmd_r1, //SD card expect response input cmd_data_len, //SD card command read data length input block_read_req, //SD card sector data read request output reg block_read_valid, //SD card sector data read data valid output reg block_read_data, //SD card sector data read data output block_read_req_ack, //SD card sector data read response input block_write_req, //SD card sector data write request input block_write_data, //SD card sector data write data next clock is valid output block_write_data_rd, //SD card sector data write data output block_write_req_ack, //SD card sector data write response output nCS_ctrl, //SPI module chip select control output reg clk_div, output reg spi_wr_req, //SPI module data sending request input spi_wr_ack, //SPI module data request response output spi_data_in, //SPI module send data input spi_data_out //SPI module data returned ); parameter S_IDLE = 0; parameter S_WAIT = 1; parameter S_INIT = 2; parameter S_CMD_PRE = 3; parameter S_CMD = 4; parameter S_CMD_DATA = 5; parameter S_READ_WAIT = 6; parameter S_READ = 7; parameter S_READ_ACK = 8; parameter S_WRITE_TOKEN = 9; parameter S_WRITE_DATA_0 = 10; parameter S_WRITE_DATA_1 = 11; parameter S_WRITE_CRC = 12; parameter S_WRITE_ACK = 13; parameter S_ERR = 14; parameter S_END = 15; reg state; reg CS_reg; reg byte_cnt; reg send_data; wire data_recv; reg wr_data_cnt; assign cmd_req_ack = (state == S_END); assign block_read_req_ack = (state == S_READ_ACK); assign block_write_req_ack= (state == S_WRITE_ACK); assign block_write_data_rd = (state == S_WRITE_DATA_0); assign spi_data_in = send_data; assign data_recv = spi_data_out; assign nCS_ctrl = CS_reg; always@(posedge sys_clk or posedge rst) begin if(rst == 1'b1) begin CS_reg <= 1'b1; spi_wr_req <= 1'b0; byte_cnt <= 16'd0; clk_div <= 16'd0; send_data <= 8'hff; state <= S_IDLE; cmd_req_error <= 1'b0; wr_data_cnt <= 10'd0; end else case(state) S_IDLE: begin state <= S_INIT; clk_div <= spi_clk_div; CS_reg <= 1'b1; end S_INIT: begin //send 11 bytes on power(at least 74 SPI clocks) if(spi_wr_ack == 1'b1) begin = 16'd10) begin byte_cnt <= 16'd0; spi_wr_req <= 1'b0; state <= S_WAIT; end begin byte_cnt <= byte_cnt + 16'd1; end end else begin spi_wr_req <= 1'b1; send_data <= 8'hff; end end S_WAIT: begin cmd_req_error <= 1'b0; wr_data_cnt <= 10'd0; //wait for instruction if(cmd_req == 1'b1) state <= S_CMD_PRE; else if(block_read_req == 1'b1) state <= S_READ_WAIT; else if(block_write_req == 1'b1) state <= S_WRITE_TOKEN; clk_div <= spi_clk_div; end S_CMD_PRE: begin //before sending a command, send an byte 'ff',provide some clocks if(spi_wr_ack == 1'b1) begin state <= S_CMD; spi_wr_req <= 1'b0; byte_cnt <= 16'd0; end else begin spi_wr_req <= 1'b1; CS_reg <= 1'b1; send_data <= 8'hff; end end S_CMD: begin if(spi_wr_ack == 1'b1) begin if((byte_cnt == 16'hffff) || (data_recv != cmd_r1 && data_recv == 1'b0)) begin state <= S_ERR; spi_wr_req <= 1'b0; end else if(data_recv == cmd_r1) begin spi_wr_req <= 1'b0; if(cmd_data_len != 16'd0) begin state <= S_CMD_DATA; byte_cnt <= 16'd0; end else state <= S_END; end else byte_cnt <= byte_cnt + 16'd1; end else begin spi_wr_req <= 1'b1; CS_reg <= 1'b0; if(byte_cnt == 16'd0) send_data <= (cmd | 8'h40); else if(byte_cnt == 16'd1) send_data <= cmd ; else if(byte_cnt == 16'd2) send_data <= cmd ; else if(byte_cnt == 16'd3) send_data <= cmd ; else if(byte_cnt == 16'd4) send_data <= cmd ; else if(byte_cnt == 16'd5) send_data <= cmd ; else send_data <= 8'hff; end end S_CMD_DATA: begin if(spi_wr_ack == 1'b1) begin if(byte_cnt == cmd_data_len - 16'd1) begin state <= S_END; spi_wr_req <= 1'b0; byte_cnt <= 16'd0; end else begin byte_cnt <= byte_cnt + 16'd1; end end else begin spi_wr_req <= 1'b1; send_data <= 8'hff; end end S_READ_WAIT: begin if(spi_wr_ack == 1'b1 && data_recv == 8'hfe) begin spi_wr_req <= 1'b0; state <= S_READ; byte_cnt <= 16'd0; end else begin spi_wr_req <= 1'b1; send_data <= 8'hff; end end S_READ: begin if(spi_wr_ack == 1'b1) begin if(byte_cnt == 16'd513) begin state <= S_READ_ACK; spi_wr_req <= 1'b0; byte_cnt <= 16'd0; end else begin byte_cnt <= byte_cnt + 16'd1; end end else begin spi_wr_req <= 1'b1; send_data <= 8'hff; end end S_WRITE_TOKEN: if(spi_wr_ack == 1'b1) begin state <= S_WRITE_DATA_0; spi_wr_req <= 1'b0; end else begin spi_wr_req <= 1'b1; send_data <= 8'hfe; end S_WRITE_DATA_0: begin state <= S_WRITE_DATA_1; wr_data_cnt <= wr_data_cnt + 10'd1; end S_WRITE_DATA_1: begin if(spi_wr_ack == 1'b1 && wr_data_cnt == 10'd512) begin state <= S_WRITE_CRC; spi_wr_req <= 1'b0; end else if(spi_wr_ack == 1'b1) begin state <= S_WRITE_DATA_0; spi_wr_req <= 1'b0; end else begin spi_wr_req <= 1'b1; send_data <= block_write_data; end end S_WRITE_CRC: begin if(spi_wr_ack == 1'b1) begin if(byte_cnt == 16'd2) begin state <= S_WRITE_ACK; spi_wr_req <= 1'b0; byte_cnt <= 16'd0; end else begin byte_cnt <= byte_cnt + 16'd1; end end else begin spi_wr_req <= 1'b1; send_data <= 8'hff; end end S_ERR: begin state <= S_END; cmd_req_error <= 1'b1; end S_READ_ACK,S_WRITE_ACK,S_END: begin state <= S_WAIT; end default: state <= S_IDLE; endcase end always@(posedge sys_clk or posedge rst) begin if(rst == 1'b1) block_read_valid <= 1'b0; else if(state == S_READ && byte_cnt < 16'd512) block_read_valid <= spi_wr_ack; else block_read_valid <= 1'b0; end always@(posedge sys_clk or posedge rst) begin if(rst == 1'b1) block_read_data <= 8'd0; else if(state == S_READ && spi_wr_ack == 1'b1) block_read_data <= data_recv; end endmodule  6.3 sd_card_sec_read_write   sd_card_sec_read_write 模块继续完成 SD 卡初始化,然后等待扇区读写指令,并完成扇区的   读写操作。 下图为模块的状态机转换图,首先发送 CMD0 命令,然后发送 CMD8 命令,再发送   CMD55,接着发送 ACMD41,如果应答正常, sd 卡初始化完成,等待扇区的读写。   代码如下: // // // // // // Author: meisq // // msq@qq.com // // ALINX(shanghai) Technology Co.,Ltd // // heijin // // WEB: http://www.alinx.cn/ // // BBS: http://www.heijin.org/ // // // // // // // Copyright (c) 2017,ALINX(shanghai) Technology Co.,Ltd // // All rights reserved // // // // This source file may be used and distributed without restriction provided // // that this copyright statement is not removed from the file and that any // // derivative work contains the original copyright notice and the associated // // disclaimer. // // // // //=============================================================================== // Revision History: // Date By Revision Change Description //------------------------------------------------------------------------------- // 2017/6/21 meisq 1.0 Original //*******************************************************************************/ module sd_card_sec_read_write #( parameter SPI_LOW_SPEED_DIV = 248, // spi clk speed = clk speed /((SPI_LOW_SPEED_DIV + 2) * 2 ) parameter SPI_HIGH_SPEED_DIV = 0 // spi clk speed = clk speed /((SPI_HIGH_SPEED_DIV + 2) * 2 ) ) ( input clk, input rst, output reg sd_init_done, input sd_sec_read, input sd_sec_read_addr, output sd_sec_read_data, output sd_sec_read_data_valid, output sd_sec_read_end, input sd_sec_write, input sd_sec_write_addr, input sd_sec_write_data, output sd_sec_write_data_req, output sd_sec_write_end, output reg spi_clk_div, output reg cmd_req, input cmd_req_ack, input cmd_req_error, output reg cmd, output reg cmd_r1, output reg cmd_data_len, output reg block_read_req, input block_read_valid, input block_read_data, input block_read_req_ack, output reg block_write_req, output block_write_data, input block_write_data_rd, input block_write_req_ack ); reg read_data; reg timer; localparam S_IDLE = 0; localparam S_CMD0 = 1; localparam S_CMD8 = 2; localparam S_CMD55 = 3; localparam S_CMD41 = 4; localparam S_CMD17 = 5; localparam S_READ = 6; localparam S_CMD24 = 7; localparam S_WRITE = 8; localparam S_ERR = 14; localparam S_WRITE_END = 15; localparam S_READ_END = 16; localparam S_WAIT_READ_WRITE = 17; localparam S_CMD16 = 18; reg state; reg sec_addr; assign sd_sec_read_data_valid = (state == S_READ) && block_read_valid; assign sd_sec_read_data = block_read_data; assign sd_sec_read_end = (state == S_READ_END); assign sd_sec_write_data_req = (state == S_WRITE) && block_write_data_rd; assign block_write_data = sd_sec_write_data; assign sd_sec_write_end = (state == S_WRITE_END); always@(posedge clk or posedge rst) begin if(rst == 1'b1) begin state <= S_IDLE; cmd_req <= 1'b0; cmd_data_len <= 16'd0; cmd_r1 <= 8'd0; cmd <= 48'd0; spi_clk_div <= SPI_LOW_SPEED_DIV ; block_write_req <= 1'b0; block_read_req <= 1'b0; sec_addr <= 32'd0; sd_init_done <= 1'b0; end else case(state) S_IDLE: begin state <= S_CMD0; sd_init_done <= 1'b0; spi_clk_div <= SPI_LOW_SPEED_DIV ; end S_CMD0: begin if(cmd_req_ack & ~cmd_req_error) begin state <= S_CMD8; cmd_req <= 1'b0; end else begin cmd_req <= 1'b1; cmd_data_len <= 16'd0; cmd_r1 <= 8'h01; cmd <= {8'd0,8'h00,8'h00,8'h00,8'h00,8'h95}; end end S_CMD8: begin if(cmd_req_ack & ~cmd_req_error) begin state <= S_CMD55; cmd_req <= 1'b0; end else begin cmd_req <= 1'b1; cmd_data_len <= 16'd4; cmd_r1 <= 8'h01; cmd <= {8'd8,8'h00,8'h00,8'h01,8'haa,8'h87}; end end S_CMD55: begin if(cmd_req_ack & ~cmd_req_error) begin state <= S_CMD41; cmd_req <= 1'b0; end else begin cmd_req <= 1'b1; cmd_data_len <= 16'd0; cmd_r1 <= 8'h01; cmd <= {8'd55,8'h00,8'h00,8'h00,8'h00,8'hff}; end end S_CMD41: begin if(cmd_req_ack & ~cmd_req_error) begin state <= S_CMD16; cmd_req <= 1'b0; sd_init_done <= 1'b1; spi_clk_div <= SPI_HIGH_SPEED_DIV ; end else if(cmd_req_ack) begin state <= S_CMD55; end else begin cmd_req <= 1'b1; cmd_data_len <= 16'd0; cmd_r1 <= 8'h00; cmd <= {8'd41,8'h40,8'h00,8'h00,8'h00,8'hff}; end end S_CMD16: begin if(cmd_req_ack & ~cmd_req_error) begin state <= S_WAIT_READ_WRITE; cmd_req <= 1'b0; sd_init_done <= 1'b1; spi_clk_div <= SPI_HIGH_SPEED_DIV ; end else if(cmd_req_ack) begin state <= S_CMD55; end else begin cmd_req <= 1'b1; cmd_data_len <= 16'd0; cmd_r1 <= 8'h00; cmd <= {8'd16,32'd512,8'hff}; end end S_WAIT_READ_WRITE: begin if(sd_sec_write == 1'b1) begin state <= S_CMD24; sec_addr <= sd_sec_write_addr; end else if(sd_sec_read == 1'b1) begin state <= S_CMD17; sec_addr <= sd_sec_read_addr; end spi_clk_div <= 16'd0; end S_CMD24: begin if(cmd_req_ack & ~cmd_req_error) begin state <= S_WRITE; cmd_req <= 1'b0; end else begin cmd_req <= 1'b1; cmd_data_len <= 16'd0; cmd_r1 <= 8'h00; cmd <= {8'd24,sec_addr,8'hff}; end end S_WRITE: begin if(block_write_req_ack == 1'b1) begin block_write_req <= 1'b0; state <= S_WRITE_END; end else block_write_req <= 1'b1; end S_CMD17: begin if(cmd_req_ack & ~cmd_req_error) begin state <= S_READ; cmd_req <= 1'b0; end else begin cmd_req <= 1'b1; cmd_data_len <= 16'd0; cmd_r1 <= 8'h00; cmd <= {8'd17,sec_addr,8'hff}; end end S_READ: begin if(block_read_req_ack) begin state <= S_READ_END; block_read_req <= 1'b0; end else begin block_read_req <= 1'b1; end end S_WRITE_END: begin state <= S_WAIT_READ_WRITE; end S_READ_END: begin state <= S_WAIT_READ_WRITE; end default: state <= S_IDLE; endcase end endmodule  6.4 spi_master   这一模块用来完成SPI一个字节的读写。   spi master 状态机设计, 主要完成一个字节 spi 数据的读写,由于是全双工的,写一个字节的   同时也读一个字节。 首先空闲状态“IDLE”接收到写请求后进入“DCLK_IDLE”状态,这个状态为   spi 时钟沿变化保持一定的时间,用来控制 spi 时钟的周期,然后进入 spi 时钟沿的变化状态,一   个字节上升沿和下降沿一共 16 个数据沿。 在最后一个数据沿进入“LAST_HALF_CYCLE”状态,为   让最后一个沿也保持一定的时间,再进入应答状态,完成一次写请求。spi_master 模块中模拟了一个 spi 时钟,在状态机进入到‘DCLK_EDGE’时进行翻转。状态机图示如下:   代码如下: // // // // // // Author: meisq // // msq@qq.com // // ALINX(shanghai) Technology Co.,Ltd // // heijin // // WEB: http://www.alinx.cn/ // // BBS: http://www.heijin.org/ // // // // // // // Copyright (c) 2017,ALINX(shanghai) Technology Co.,Ltd // // All rights reserved // // // // This source file may be used and distributed without restriction provided // // that this copyright statement is not removed from the file and that any // // derivative work contains the original copyright notice and the associated // // disclaimer. // // // // //========================================================================== // Revision History: // Date By Revision Change Description //-------------------------------------------------------------------------- // 2017/6/19 meisq 1.0 Original //*************************************************************************/ module spi_master ( input sys_clk, input rst, output nCS, //chip select (SPI mode) output DCLK, //spi clock output MOSI, //spi data output input MISO, //spi input input CPOL, input CPHA, input nCS_ctrl, input clk_div, input wr_req, output wr_ack, input data_in, output data_out ); localparam IDLE = 0; localparam DCLK_EDGE = 1; localparam DCLK_IDLE = 2; localparam ACK = 3; localparam LAST_HALF_CYCLE = 4; localparam ACK_WAIT = 5; reg DCLK_reg; reg MOSI_shift; reg MISO_shift; reg state; reg next_state; reg clk_cnt; reg clk_edge_cnt; assign MOSI = MOSI_shift ; assign DCLK = DCLK_reg; assign data_out = MISO_shift; assign wr_ack = (state == ACK); assign nCS = nCS_ctrl; always@(posedge sys_clk or posedge rst) begin if(rst) state <= IDLE; else state <= next_state; end always@(*) begin case(state) IDLE: if(wr_req == 1'b1) next_state <= DCLK_IDLE; else next_state <= IDLE; DCLK_IDLE: //half a SPI clock cycle produces a clock edge if(clk_cnt == clk_div) next_state <= DCLK_EDGE; else next_state <= DCLK_IDLE; DCLK_EDGE: //a SPI byte with a total of 16 clock edges if(clk_edge_cnt == 5'd15) next_state <= LAST_HALF_CYCLE; else next_state <= DCLK_IDLE; //this is the last data edge LAST_HALF_CYCLE: if(clk_cnt == clk_div) next_state <= ACK; else next_state <= LAST_HALF_CYCLE; //send one byte complete ACK: next_state <= ACK_WAIT; //wait for one clock cycle, to ensure that the cancel request signal ACK_WAIT: next_state <= IDLE; default: next_state <= IDLE; endcase end always@(posedge sys_clk or posedge rst) begin if(rst) DCLK_reg <= 1'b0; else if(state == IDLE) DCLK_reg <= CPOL; else if(state == DCLK_EDGE) DCLK_reg <= ~DCLK_reg;//SPI clock edge end //SPI clock wait counter always@(posedge sys_clk or posedge rst) begin if(rst) clk_cnt <= 16'd0; else if(state == DCLK_IDLE || state == LAST_HALF_CYCLE) clk_cnt <= clk_cnt + 16'd1; else clk_cnt <= 16'd0; end //SPI clock edge counter always@(posedge sys_clk or posedge rst) begin if(rst) clk_edge_cnt <= 5'd0; else if(state == DCLK_EDGE) clk_edge_cnt <= clk_edge_cnt + 5'd1; else if(state == IDLE) clk_edge_cnt <= 5'd0; end //SPI data output always@(posedge sys_clk or posedge rst) begin if(rst) MOSI_shift <= 8'd0; else if(state == IDLE && wr_req) MOSI_shift <= data_in; else if(state == DCLK_EDGE) if(CPHA == 1'b0 && clk_edge_cnt == 1'b1) MOSI_shift <= {MOSI_shift ,MOSI_shift }; else if(CPHA == 1'b1 && (clk_edge_cnt != 5'd0 && clk_edge_cnt == 1'b0)) MOSI_shift <= {MOSI_shift ,MOSI_shift }; end //SPI data input always@(posedge sys_clk or posedge rst) begin if(rst) MISO_shift <= 8'd0; else if(state == IDLE && wr_req) MISO_shift <= 8'h00; else if(state == DCLK_EDGE) if(CPHA == 1'b0 && clk_edge_cnt == 1'b0) MISO_shift <= {MISO_shift ,MISO}; else if(CPHA == 1'b1 && (clk_edge_cnt == 1'b1)) MISO_shift <= {MISO_shift ,MISO}; end endmodule  6.5 其余代码   6.5.1 sd_card_test // // // // // // Author: meisq // // msq@qq.com // // ALINX(shanghai) Technology Co.,Ltd // // heijin // // WEB: http://www.alinx.cn/ // // BBS: http://www.heijin.org/ // // // // // // // Copyright (c) 2017,ALINX(shanghai) Technology Co.,Ltd // // All rights reserved // // // // This source file may be used and distributed without restriction provided // // that this copyright statement is not removed from the file and that any // // derivative work contains the original copyright notice and the associated // // disclaimer. // // // // //================================================================================ // Revision History: // Date By Revision Change Description //-------------------------------------------------------------------------------- // 2017/6/19 meisq 1.0 Original //*******************************************************************************/ module sd_card_test( input clk, input rst_n, input key1, output SD_nCS, output SD_DCLK, output SD_MOSI, input SD_MISO, output seg_sel, output seg_data ); parameter S_IDLE = 0; parameter S_READ = 1; parameter S_WRITE = 2; parameter S_END = 3; reg state; wire sd_init_done; reg sd_sec_read; wire sd_sec_read_addr; wire sd_sec_read_data; wire sd_sec_read_data_valid; wire sd_sec_read_end; reg sd_sec_write; wire sd_sec_write_addr; reg sd_sec_write_data; wire sd_sec_write_data_req; wire sd_sec_write_end; reg wr_cnt; reg rd_cnt; wire button_negedge; reg read_data; ax_debounce ax_debounce_m0 ( .clk (clk), .rst (~rst_n), .button_in (key1), .button_posedge (), .button_negedge (button_negedge), .button_out () ); wire seg_data_0; seg_decoder seg_decoder_m0( .bin_data (read_data ), .seg_data (seg_data_0) ); wire seg_data_1; seg_decoder seg_decoder_m1( .bin_data (read_data ), .seg_data (seg_data_1) ); seg_scan seg_scan_m0( .clk (clk), .rst_n (rst_n), .seg_sel (seg_sel), .seg_data (seg_data), .seg_data_0 ({1'b1,7'b1111_111}), .seg_data_1 ({1'b1,7'b1111_111}), .seg_data_2 ({1'b1,7'b1111_111}), .seg_data_3 ({1'b1,7'b1111_111}), .seg_data_4 ({1'b1,seg_data_1}), .seg_data_5 ({sd_init_done,seg_data_0}) ); always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) wr_cnt <= 10'd0; else if(state == S_WRITE) begin if(sd_sec_write_data_req == 1'b1) wr_cnt <= wr_cnt + 10'd1; end else wr_cnt <= 10'd0; end always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) rd_cnt <= 10'd0; else if(state == S_READ) begin if(sd_sec_read_data_valid == 1'b1) rd_cnt <= rd_cnt + 10'd1; end else rd_cnt <= 10'd0; end always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) read_data <= 8'd0; else if(state == S_READ) begin if(sd_sec_read_data_valid == 1'b1 && rd_cnt == 10'd0) read_data <= sd_sec_read_data; end else if(state == S_END && button_negedge == 1'b1) read_data <= read_data + 8'd1; end always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) sd_sec_write_data <= 8'd0; else if(sd_sec_write_data_req) sd_sec_write_data <= read_data + wr_cnt ; end always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) begin state <= S_IDLE; sd_sec_read <= 1'b0; sd_sec_write <= 1'b0; end else if(sd_init_done == 1'b0) begin state <= S_IDLE; end else case(state) S_IDLE: begin state <= S_READ; end S_WRITE: begin if(sd_sec_write_end == 1'b1) begin sd_sec_write <= 1'b0; state <= S_READ; end else sd_sec_write <= 1'b1; end S_READ: begin if(sd_sec_read_end == 1'b1) begin state <= S_END; sd_sec_read <= 1'b0; end else begin sd_sec_read <= 1'b1; end end S_END: begin if(button_negedge == 1'b1) state <= S_WRITE; end default: state <= S_IDLE; endcase end sd_card_top sd_card_top_m0( .clk (clk ), .rst (~rst_n ), .SD_nCS (SD_nCS ), .SD_DCLK (SD_DCLK ), .SD_MOSI (SD_MOSI ), .SD_MISO (SD_MISO ), .sd_init_done (sd_init_done ), .sd_sec_read (sd_sec_read ), .sd_sec_read_addr (sd_sec_read_addr ), .sd_sec_read_data (sd_sec_read_data ), .sd_sec_read_data_valid (sd_sec_read_data_valid ), .sd_sec_read_end (sd_sec_read_end ), .sd_sec_write (sd_sec_write ), .sd_sec_write_addr (sd_sec_write_addr ), .sd_sec_write_data (sd_sec_write_data ), .sd_sec_write_data_req (sd_sec_write_data_req ), .sd_sec_write_end (sd_sec_write_end ) ); endmodule 6.5.2 ax_debounce // // // // // // Author: meisq // // msq@qq.com // // ALINX(shanghai) Technology Co.,Ltd // // heijin // // WEB: http://www.alinx.cn/ // // BBS: http://www.heijin.org/ // // // // // // // Copyright (c) 2017,ALINX(shanghai) Technology Co.,Ltd // // All rights reserved // // // // This source file may be used and distributed without restriction provided // // that this copyright statement is not removed from the file and that any // // derivative work contains the original copyright notice and the associated // // disclaimer. // // // // //================================================================================ // Revision History: // Date By Revision Change Description //-------------------------------------------------------------------------------- // 2017/5/3 meisq 1.0 Original //*******************************************************************************/ `timescale 1 ns / 100 ps module ax_debounce ( input clk, input rst, input button_in, output reg button_posedge, output reg button_negedge, output reg button_out ); ---------------- internal constants -------------- parameter N = 32 ; // debounce timer bitwidth parameter FREQ = 50; //model clock :Mhz parameter MAX_TIME = 20; //ms localparam TIMER_MAX_VAL = MAX_TIME * 1000 * FREQ; ---------------- internal variables --------------- reg q_reg; // timing regs reg q_next; reg DFF1, DFF2; // input flip-flops wire q_add; // control flags wire q_reset; reg button_out_d0; ------------------------------------------------------ contenious assignment for counter control assign q_reset = (DFF1 ^ DFF2); // xor input flip flops to look for level chage to reset counter assign q_add = ~(q_reg == TIMER_MAX_VAL); // add to counter when q_reg msb is equal to 0 combo counter to manage q_next always @ ( q_reset, q_add, q_reg) begin case( {q_reset , q_add}) 2'b00 : q_next <= q_reg; 2'b01 : q_next <= q_reg + 1; default : q_next <= { N {1'b0} }; endcase end Flip flop inputs and q_reg update always @ ( posedge clk or posedge rst) begin if(rst == 1'b1) begin DFF1 <= 1'b0; DFF2 <= 1'b0; q_reg <= { N {1'b0} }; end else begin DFF1 <= button_in; DFF2 <= DFF1; q_reg <= q_next; end end counter control always @ ( posedge clk or posedge rst) begin if(rst == 1'b1) button_out <= 1'b1; else if(q_reg == TIMER_MAX_VAL) button_out <= DFF2; else button_out <= button_out; end always @ ( posedge clk or posedge rst) begin if(rst == 1'b1) begin button_out_d0 <= 1'b1; button_posedge <= 1'b0; button_negedge <= 1'b0; end else begin button_out_d0 <= button_out; button_posedge <= ~button_out_d0 & button_out; button_negedge <= button_out_d0 & ~button_out; end end endmodule   6.5.3 seg_decoder // // // // // // Author: meisq // // msq@qq.com // // ALINX(shanghai) Technology Co.,Ltd // // heijin // // WEB: http://www.alinx.cn/ // // BBS: http://www.heijin.org/ // // // // // // // Copyright (c) 2017,ALINX(shanghai) Technology Co.,Ltd // // All rights reserved // // // // This source file may be used and distributed without restriction provided // // that this copyright statement is not removed from the file and that any // // derivative work contains the original copyright notice and the associated // // disclaimer. // // // // //========================================================================== // Revision History: // Date By Revision Change Description //-------------------------------------------------------------------------- // 2017/6/19 meisq 1.0 Original //*************************************************************************/ module seg_decoder ( input bin_data, // bin data input output reg seg_data // seven segments LED output ); always@(*) begin case(bin_data) 4'd0:seg_data <= 7'b100_0000; 4'd1:seg_data <= 7'b111_1001; 4'd2:seg_data <= 7'b010_0100; 4'd3:seg_data <= 7'b011_0000; 4'd4:seg_data <= 7'b001_1001; 4'd5:seg_data <= 7'b001_0010; 4'd6:seg_data <= 7'b000_0010; 4'd7:seg_data <= 7'b111_1000; 4'd8:seg_data <= 7'b000_0000; 4'd9:seg_data <= 7'b001_0000; 4'ha:seg_data <= 7'b000_1000; 4'hb:seg_data <= 7'b000_0011; 4'hc:seg_data <= 7'b100_0110; 4'hd:seg_data <= 7'b010_0001; 4'he:seg_data <= 7'b000_0110; 4'hf:seg_data <= 7'b000_1110; default:seg_data <= 7'b111_1111; endcase end endmodule   6.5.4 seg_scan // // // // // // Author: meisq // // msq@qq.com // // ALINX(shanghai) Technology Co.,Ltd // // heijin // // WEB: http://www.alinx.cn/ // // BBS: http://www.heijin.org/ // // // // // // // Copyright (c) 2017,ALINX(shanghai) Technology Co.,Ltd // // All rights reserved // // // // This source file may be used and distributed without restriction provided // // that this copyright statement is not removed from the file and that any // // derivative work contains the original copyright notice and the associated // // disclaimer. // // // // //========================================================================== // Revision History: // Date By Revision Change Description //-------------------------------------------------------------------------- // 2017/6/19 meisq 1.0 Original //*************************************************************************/ module seg_scan( input clk, input rst_n, output reg seg_sel, //digital led chip select output reg seg_data, //eight segment digital tube output,MSB is the decimal point input seg_data_0, input seg_data_1, input seg_data_2, input seg_data_3, input seg_data_4, input seg_data_5 ); parameter SCAN_FREQ = 200; //scan frequency parameter CLK_FREQ = 50000000; //clock frequency parameter SCAN_COUNT = CLK_FREQ /(SCAN_FREQ * 6) - 1; reg scan_timer; //scan time counter reg scan_sel; //Scan select counter always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) begin scan_timer <= 32'd0; scan_sel <= 4'd0; end = SCAN_COUNT) begin scan_timer <= 32'd0; if(scan_sel == 4'd5) scan_sel <= 4'd0; else scan_sel <= scan_sel + 4'd1; end else begin scan_timer <= scan_timer + 32'd1; end end always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) begin seg_sel <= 6'b111111; seg_data <= 8'hff; end else begin case(scan_sel) //first digital led 4'd0: begin seg_sel <= 6'b11_1110; seg_data <= seg_data_0; end //second digital led 4'd1: begin seg_sel <= 6'b11_1101; seg_data <= seg_data_1; end //... 4'd2: begin seg_sel <= 6'b11_1011; seg_data <= seg_data_2; end 4'd3: begin seg_sel <= 6'b11_0111; seg_data <= seg_data_3; end 4'd4: begin seg_sel <= 6'b10_1111; seg_data <= seg_data_4; end 4'd5: begin seg_sel <= 6'b01_1111; seg_data <= seg_data_5; end default: begin seg_sel <= 6'b11_1111; seg_data <= 8'hff; end endcase end end endmodule  7 实验结果   下载实验程序后,可以看到数码管显示一个数字,这个数字是存储在 sd 卡中第一扇区的第一   个数据,数据是随机的,这个时候按键 KEY1 按下,数字加一,并写入了 sd 卡,再次下载程序,   可以看到直接显示更新后的数据。
  • 热度 3
    2024-6-14 16:28
    288 次阅读|
    0 个评论
    文章目录   一、简介   二、速度测试   最近比较忙,也一直没空发什么文章,这算是新年第一篇吧,正好最近收到了一个雷龙的flash芯片,先拿来玩一下吧。   有兴趣的小伙伴可以去雷龙官网找小姐姐领取一个免费试用。 一、简介   大概样子就是上面这样,使用LGA-8封装,实际上驱动也是通用SD卡的驱动,相比与SD卡可以直接贴片到嵌入式设备中,并且体积更小,数据存储和SD卡存储一样。   我使用的型号是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便宜,   样品免费试用。   实际应用场景   新一代SD NAND主要应用领域   •5G   •机器人   •智能音箱   •智能面板(HMI)   •移动支付   •智能眼镜(AR)   •智能家居   •医疗设备   •轨道交通   •人脸识别   •3D打印机 二、速度测试   手里正好还有一张内存卡,那么就做下对比测试:   先理解下下面这四种测试的含义:   SEQ1M|Q8T1表示顺序读写,位深1024K,1线程8队列的测试速度   SEQ1M|Q1T1表示顺序读写,位深1024K,1线程1队列测试速度   RND4K|Q32T16表示随机读写,位深10244K,16线程32队列的测试速度   RND4K|Q1T1表示随机读写,位深10244K,一线程一队列的测试速度   那么由于CSNP1GCR01-AOW是512M的,那么就统一使用256M的随机读写来测试   CSNP1GCR01-AOW读写速度: ------------------------------------------------------------------------------ CrystalDiskMark 8.0.4 x64 (C) 2007-2021 hiyohiyo Crystal Dew World: https://crystalmark.info/ ------------------------------------------------------------------------------ * MB/s = 1,000,000 bytes/s * KB = 1000 bytes, KiB = 1024 bytes SEQ 1MiB (Q= 8, T= 1): 18.242 MB/s SEQ 1MiB (Q= 1, T= 1): 18.409 MB/s RND 4KiB (Q= 32, T= 1): 4.807 MB/s RND 4KiB (Q= 1, T= 1): 4.215 MB/s SEQ 1MiB (Q= 8, T= 1): 7.326 MB/s SEQ 1MiB (Q= 1, T= 1): 7.549 MB/s RND 4KiB (Q= 32, T= 1): 2.453 MB/s RND 4KiB (Q= 1, T= 1): 2.029 MB/s Profile: Default Test: 256 MiB (x5) Mode: Time: Measure 5 sec / Interval 5 sec Date: 2024/01/15 11:35:30 OS: Windows 10 Professional (x64)   内存卡读写速度: ------------------------------------------------------------------------------ CrystalDiskMark 8.0.4 x64 (C) 2007-2021 hiyohiyo Crystal Dew World: https://crystalmark.info/ ------------------------------------------------------------------------------ * MB/s = 1,000,000 bytes/s * KB = 1000 bytes, KiB = 1024 bytes SEQ 1MiB (Q= 8, T= 1): 17.579 MB/s SEQ 1MiB (Q= 1, T= 1): 18.236 MB/s RND 4KiB (Q= 32, T= 1): 5.105 MB/s RND 4KiB (Q= 1, T= 1): 4.622 MB/s SEQ 1MiB (Q= 8, T= 1): 1.676 MB/s SEQ 1MiB (Q= 1, T= 1): 7.962 MB/s RND 4KiB (Q= 32, T= 1): 0.018 MB/s RND 4KiB (Q= 1, T= 1): 0.015 MB/s Profile: Default Test: 256 MiB (x5) Mode: Time: Measure 5 sec / Interval 5 sec Date: 2024/01/15 11:53:09 OS: Windows 10 Professional (x64)
  • 热度 3
    2024-5-21 17:40
    521 次阅读|
    0 个评论
    目录   引言   SD卡的发展   SD NAND卡的特性与优势   二代SD NAND五大优点   SD NAND六大主要优势   现有产品分类   实际应用场景   SD NAND芯片推荐线路连接:   CSNP4GCR01-AMW的介绍   基础使用例程   例程环境简介   硬件设备及电路   项目创建流程   代码   例程结果   对比市场现有产品   创世半导体(CS)是全球首家推出SD NAND FLASH产品的厂商,SD NAND的出现大大降低了使用 NAND FLASH 的技术难度。   ———————————————— 引言   随着科技的发展,数据的存储需求也在日益增长。在这个信息爆炸的时代,一款高效、稳定、便携的存储设备显得尤为重要。新品SD卡——SD NAND,应运而生,为我们的数据存储带来了新的革命。 SD卡的发展   SD卡自问世以来,其体积不断缩小,容量逐步增大,速度也在不断提升。   开始。   1997年11月,闪迪和英飞凌联合推出了MMC(MultiMediaCard)存储卡。   1999年8月,闪迪又联合松下、东芝推出了SD(Secure Digital)存储卡。SD卡拥有与MMC卡相同的长宽尺寸,略厚一些(2.1mm VS. 1.4mm)。早期SD卡设备/读卡器都能同时兼容MMC卡。   2000年1月,SD卡协会正式成立,当年推出了最大容量64MB、传输速度约12.5MB/s的产品。   2003年3月,闪迪展示了面向手机等移动设备的miniSD卡(目前已退出市场)。   2004年2月,闪迪又和摩托罗拉发布了更小巧的microSD卡(也称为TransFlash或TF卡)。   2005年7月,SD卡协会确认了microSD卡规范,传输速度也提升到了约25MB/s。   2006年1月,SD 2.0带来了采用FAT32文件系统、最大容量32GB的SDHC(包括miniSDHC、microSDHC)卡。而最初版本的SD卡采用FAT12/FAT16文件系统,最大容量为2GB。   2010年5月,SD 3.01带来了采用exFAT文件系统、最大容量提升到2TB的SDXC(包括microSDXC)卡;以及UHS-I高速总线,最大传输速度为104MB/s。   2011年6月,SD 4.0带来了UHS-II总线。这种SD卡(包括microSD卡)具有两排触点,可以实现全双工156MB/s、半双工312MB/s的传输速度。   2016年2月,SD 5.0带来了视频速度等级规范,包括V30、V60、V90。   2016年11月,SD 5.1增加了针对App运行性能的A1标准。在满足10MB/s持续读写的基础上,增加了随机读取1500IOPS、随机写入500IOPS的要求。   2017年2月,SD 6.0带来了全双工312MB/s、全双工624MB/s的UHS-III总线以及随机读取4000IOPS、随机写入2000IOPS的A2标准。UHS-III向下兼容UHS-II,但到目前为止都没有看到任何样品。   2016年11月,SD 5.1增加了针对App运行性能的A1标准。在满足10MB/s持续读写的基础上,增加了随机读取1500IOPS、随机写入500IOPS的要求。   2017年2月,SD 6.0带来了全双工312MB/s、全双工624MB/s的UHS-III总线以及随机读取4000IOPS、随机写入2000IOPS的A2标准。UHS-III向下兼容UHS-II,但到目前为止都没有看到任何样品。   2019年初,闪迪推出“UHS-I超频卡”,突破了104MB/s的速度瓶颈。之后,金士顿、雷克沙等厂商也追加了类似规格的产品。   2020年5月,SD 8.0引入了PCIe 3.0×2、PCIe 4.0×1和PCIe 4.0×2,将最高速度提升至接近4GB/s(3938MB/s)。   2022年5月,SD 9.0增加了快速启动和安全启动特性,为SD卡创造了半嵌入式应用场景。   内存卡在近年来的发展主要集中在提高容量和读写速度上。例如,现在市场上已经出现了容量达到1TB的MicroSD卡,读写速度也不断提高,以满足用户对存储容量和速度的需求。   同时,一些新型内存卡如CFexpress和SD Express也正在逐渐普及,它们支持更快的数据传输速度和更大的容量。随着手机互联网的发展,云存储也逐渐在吞食存储卡的市场。过去需要通过内存卡扩展手机存储空间,现在则可以通过云服务,把数据存储在云端。 SD NAND的特性与优势   以CSNP4GCR01-AMW为例。   不用写驱动程序自带坏块管理的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/2GB/4GB/8GB,   比TF卡稳定,比eMMC便宜。 二代SD NAND五大优点   •尺寸小巧   •简单易用   •兼容性强   •稳定可靠   •固件可定制  SD NAND六大主要优势   •LGA-8封装,机贴手贴都方便。   •尺寸小巧5(6*8mm),助力产品颜值提升。   •容量适宜(1Gb/4Gb/32Gb)帮助客户降低成本。   •擦写寿命长(内置SLC晶圆,擦写寿命可达5-10万次,专为嵌式而生)。   •免驱动(即贴即用)直连SD/SPI接口即可使用,已内置Flash管理程序。   •稳定可靠:已通过10k次随机掉电高低温冲击测试。内置FW包含平均读写,坏块管理,垃圾回收等处理机制。   SD NAND 与 TF卡的区别:(看图表) 现有产品分类   本篇示例代码采用工业级CSNP4GCR01-AMW。容量为512MB。 实际应用场景   新一代SD NAND主要应用领域   •5G   •机器人   •智能音箱   •智能面板(HMI)   •移动支付   •智能眼镜(AR)   •智能家居   •医疗设备   •轨道交通   •人脸识别   •3D打印机  SD NAND芯片推荐线路连接:  CSNP4GCR01-AMW的介绍   不用写驱动程序自带坏块管理的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/2GB/4GB/8GB,   比TF卡稳定,比eMMC便宜。 基础使用例程   例程环境简介   项目环境:   使用开发板为正点原子探索者STM32F407ZG;   STM32CubeMX;   Keil;   SD NAND:芯片型号 CSNP4GCR01-AMW;芯片转接板(将芯片引脚引出为TF 卡)  硬件设备及电路   SD NAND原理图:   探索者TF 卡槽:   STM32线路连接   使用SDIO模式,   D0接PC8; D1接PC9; D2接PC10; D3接PC11;   信号   SDIO信号“4线模式”   CLK:HOST给DEVICE的时钟信号。   VDD:电源信号。   VSS:Ground信号。   DAT0-DAT3:4条数据线   CMD:用于HOST发送命令和DEVICE回复响应。 项目创建流程   基础时钟配置:   SDIO模式配置:   FATFS配置:   更改缓存区大小:   完成项目其他基础配置。 代码   while(1)之前: FATFS fs; /* FatFs 文件系统对象 */ FIL file; /* 文件对象 */ FRESULT f_res; /* 文件操作结果 */ UINT fnum; /* 文件成功读写数量 */ BYTE ReadBuffer = {0}; /* 读缓冲区 */ BYTE WriteBuffer[] = /* 写缓冲区 */ "This is STM32 working with FatFs \r\n STM32的FATFS文件系统测试 \r\n "; // 在外部 SD 卡挂载文件系统,文件系统挂载时会对 SD 卡初始化 // note:必须先要保证SD卡正常拥有FAT文件系统,如果没有会失败。 f_res = f_mount(&fs, "0:", 1); /*----------------------- 文件系统测试:写测试 -----------------------------*/ /* 打开文件,如果文件不存在则创建它 */ f_res = f_open(&file, "0:FatFs STM32cube.txt", FA_CREATE_ALWAYS | FA_WRITE); if(f_res == FR_OK) { /* 将指定存储区内容写入到文件内 */ f_res = f_write(&file, WriteBuffer, sizeof(WriteBuffer), &fnum); /* 不再读写,关闭文件 */ f_close(&file); } /*------------------- 文件系统测试:读测试 ------------------------------------*/ f_res = f_open(&file, "0:FatFs STM32cube.txt", FA_OPEN_EXISTING | FA_READ); if(f_res == FR_OK) { f_res = f_read(&file, ReadBuffer, sizeof(ReadBuffer), &fnum); } /* 不再读写,关闭文件 */ f_close(&file); /* 不再使用文件系统,取消挂载文件系统 */ f_mount(NULL, "0:", 1); /* 操作完成,停机 */   MX_SDIO_SD_Init()函数中加入 if (HAL_SD_Init(&hsd) != HAL_OK) { Error_Handler(); } if (HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK) { Error_Handler(); } void MX_SDIO_SD_Init(void) { /* USER CODE BEGIN SDIO_Init 0 */ /* USER CODE END SDIO_Init 0 */ /* USER CODE BEGIN SDIO_Init 1 */ /* USER CODE END SDIO_Init 1 */ hsd.Instance = SDIO; hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING; hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE; hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE; hsd.Init.BusWide = SDIO_BUS_WIDE_1B; hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_ENABLE; hsd.Init.ClockDiv = 34; /* USER CODE BEGIN SDIO_Init 2 */ if (HAL_SD_Init(&hsd) != HAL_OK) { Error_Handler(); } if (HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK) { Error_Handler(); } /* USER CODE END SDIO_Init 2 */ } 例程结果   新建了一个 STM32cube.txt 文件   写入内容如下图所示。   Keil 调试 :成功读取文件内容,暂存至数组中。内容如下   代码解释 对比市场现有产品   目前再嵌入式中使用最多的存储情况:   EEPROM:   只能存放字节类型的数据:芯片为AT24CXX;采用IIC通信,存储内容类型,大小有限。   U盘 :   存放文件格式多样;采用USB接口;占用空间大;可以热拔插;   关于MCU的存储方面,以前基本上用内置的E2PROM,或者是外置的NOR Flash 就可以了。但随着物联网的兴起,MCU的应用越来越广泛了,逐渐的MCU会涉及到大容量的存储需求,用来存储音频,图片(GUI)、视频缓存、协议栈等等。   那传统的E2PROM和NOR Flash就不够用了。这个时候MCU可能就需要用到NAND Flash了。但MCU采用大容量存储芯片NAND Flash,面临着新的挑战。   每个产品都有自己的优缺点。再存储器件选取上,都是考虑项目本身的需求,个产品性能综合考虑最优存储产品。
  • 2023-11-15 18:16
    1 次阅读|
    0 个评论
    前言:  很感谢深圳雷龙发展有限公司为博主提供的两片SD NAND的存储芯片,在这里博主记录一下自己的使用过程以及部分设计。  深入了解该产品:  拿到这个产品之后,我大致了解了下两款芯片的性能。CSNP4GCR01-AMW是一种基于NAND闪存和SD控制器的4Gb密度嵌入式存储;而CSNP32GCR01-AOW是一种基于NAND闪存和SD控制器的32Gb密度嵌入式存储。与原始NAND相比其具有嵌入式坏块管理和更强的嵌入式ECC。即使在异常断电,它仍然可以安全地保存数据。作为一个存储芯片,它确实做到了小巧,LGA-8的封装对比我之前用到过的TF卡,只占到了其面积的三分之一,这样对于一些嵌入式的设计就方便了很多。 编辑 ​  雷龙官方还很贴心的提供了样品的测试板,在这款测试板上,我焊接了4GB的CSNP4GCR01-AMW上去,并且跑了一下分,对于一款小的存储芯片而言,实在难得。 编辑 ​  (上图为测试板焊接图)  博主日前在设计基于H616与NB-IOT的嵌入式智能储物柜的时候考虑过存储方面的问题,当时在SD NAND和EMMC与TF卡中徘徊,以下是几个存储类型的对比。 编辑 ​ 编辑 ​  经过多方对比,本着不需要频繁更换的原则,同时也为了更好的防水和成本考虑,最终决定使用雷龙公司的SD NAND 作为设计样品的存储部分。  此外,SD NAND还具有不用写驱动程序自带坏块管理的NAND FLASH(贴片式TF卡),不标准的SDIO接口,也同时兼容SPI/SD接口,10万次的SLC晶圆擦写寿命,通过一万次的随机掉电测试耐高低温,经过跑分测得,速度级别Class10。标准的SD2.0协议,普通的SD卡可以直接驱动,支持TF卡启动的SOC都可以用SD NAND,而且雷龙官方还贴心的提供了STM32参考例程和原厂技术支持,这对于刚上手的小白而言,十分友好。  设计理念:  使用H616作为主控CPU并搭配NB-IOT来向申请下来的云端传输数据,当WIFI正常时,储物数据每搁两小时向云端传输一次,当有人取出物品时再次向云端发送一次数据(不保留在SD NAND中);一旦系统检测到WIFI出现问题,储物数据转而存储到SD NAND中,取物时输入的物品ID和取出时间一并放入SD NAND中(我也是看中了SD NAND与原始NAND相比其具有嵌入式坏块管理和更强的嵌入式ECC。即使在异常断电,它仍然可以安全地保存数据这一点)。  部分SD NAND的参考设计  根据官方数据手册提供的SD NAND参考设计,只占用8个GPIO,对于H616来说,确实很友好 编辑 ​  这里为了不泄露他人的劳动成果,我也就不粘PCB设计了。 编辑 ​  采用H616驱动SD NAND的示例代码  下面是关于H616驱动SD NAND的示例代码,这里记录一下自己当初的学习过程(注:这个代码不能直接拿过来就用,而是要根据自己的需求修改) #include #include #include #include #include "h616_sdio.h" // 定义SDIO引脚 #define SDIO_CMD_PIN 0 #define SDIO_CLK_PIN 1 #define SDIO_D0_PIN 2 #define SDIO_D1_PIN 3 #define SDIO_D2_PIN 4 #define SDIO_D3_PIN 5 // 定义NAND芯片命令 #define CMD_READ 0x00 #define CMD_WRITE 0x80 #define CMD_ERASE 0x60 #define CMD_STATUS 0x70 #define CMD_RESET 0xff // 定义NAND芯片状态 #define STATUS_READY 0x40 #define STATUS_ERROR 0x01 // 初始化SDIO控制器 void sdio_init() { // 设置SDIO引脚模式和速率 h616_sdio_set_pin_mode(SDIO_CMD_PIN, H616_SDIO_PIN_MODE_SDIO); h616_sdio_set_pin_mode(SDIO_CLK_PIN, H616_SDIO_PIN_MODE_SDIO); h616_sdio_set_pin_mode(SDIO_D0_PIN, H616_SDIO_PIN_MODE_SDIO); h616_sdio_set_pin_mode(SDIO_D1_PIN, H616_SDIO_PIN_MODE_SDIO); h616_sdio_set_pin_mode(SDIO_D2_PIN, H616_SDIO_PIN_MODE_SDIO); h616_sdio_set_pin_mode(SDIO_D3_PIN, H616_SDIO_PIN_MODE_SDIO); h616_sdio_set_clock(H616_SDIO_CLOCK_FREQ_25MHZ); // 初始化SDIO控制器 h616_sdio_init(); } // 发送NAND芯片命令 void nand_send_cmd(uint8_t cmd) { // 设置SDIO控制器传输模式和命令码 h616_sdio_set_transfer_mode(H616_SDIO_TRANSFER_MODE_WRITE); h616_sdio_set_command_code(cmd); // 发送命令 h616_sdio_send_command(); } // 发送NAND芯片地址 void nand_send_addr(uint32_t addr) { // 设置SDIO控制器传输模式和地址 h616_sdio_set_transfer_mode(H616_SDIO_TRANSFER_MODE_WRITE); h616_sdio_set_address(addr); // 发送地址 h616_sdio_send_address(); } // 读取NAND芯片数据 void nand_read_data(uint8_t *data, uint32_t size) { // 设置SDIO控制器传输模式 h616_sdio_set_transfer_mode(H616_SDIO_TRANSFER_MODE_READ); // 读取数据 h616_sdio_read_data(data, size); } // 写入NAND芯片数据 void nand_write_data(const uint8_t *data, uint32_t size) { // 设置SDIO控制器传输模式 h616_sdio_set_transfer_mode(H616_SDIO_TRANSFER_MODE_WRITE); // 写入数据 h616_sdio_write_data(data, size); } // 读取NAND芯片状态 uint8_t nand_read_status() { uint8_t status; // 发送读取状态命令 nand_send_cmd(CMD_STATUS); // 读取状态 nand_read_data(&status, 1); return status; } // 等待NAND芯片准备就绪 void nand_wait_ready() { uint8_t status; // 循环读取状态,直到NAND芯片准备就绪 do { status = nand_read_status(); } while ((status & STATUS_READY) == 0); } // 读取NAND芯片数据 void nand_read(uint32_t page, uint32_t column, uint8_t *data, uint32_t size) { // 发送读取命令和地址 nand_send_cmd(CMD_READ); nand_send_addr(column | (page << 8)); // 等待NAND芯片准备就绪 nand_wait_ready(); // 读取数据 nand_read_data(data, size); } // 写入NAND芯片数据 void nand_write(uint32_t page, uint32_t column, const uint8_t *data, uint32_t size) { // 发送写入命令和地址 nand_send_cmd(CMD_WRITE); nand_send_addr(column | (page << 8)); // 写入数据 nand_write_data(data, size); // 等待NAND芯片准备就绪 nand_wait_ready(); } // 擦除NAND芯片块 void nand_erase(uint32_t block) { // 发送擦除命令和地址 nand_send_cmd(CMD_ERASE); nand_send_addr(block << 8); // 等待NAND芯片准备就绪 nand_wait_ready(); } // 复位NAND芯片 void nand_reset() { // 发送复位命令 nand_send_cmd(CMD_RESET); // 等待NAND芯片准备就绪 nand_wait_ready(); } // 示例程序入口 int main() { uint8_t data ; memset(data, 0x5a, sizeof(data)); // 初始化SDIO控制器 sdio_init(); // 复位NAND芯片 nand_reset(); // 擦除第0块 nand_erase(0); // 写入第0页 nand_write(0, 0, data, sizeof(data)); // 读取第0页 nand_read(0, 0, data, sizeof(data)); return 0; }