查看: 900  |  回复: 0
基于FPGA的SD卡的数据读写实现(SD NAND FLASH)

主题

回复
发表于2022-12-15 18:16:11 | 显示全部楼层
1# 电梯直达

                     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        [31:0]  wr_sec_addr   ,  //写数据扇区地址

input        [15:0]  wr_data       ,  //写数据

output               wr_busy       ,  //写数据忙信号

output               wr_req        ,  //写数据请求信号

//用户读SD卡接口

input                rd_start_en   ,  //开始读SD卡数据信号

input        [31:0]  rd_sec_addr   ,  //读数据扇区地址

output               rd_busy       ,  //读数据忙信号

output               rd_val_en     ,  //读数据有效信号

output       [15:0]  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年,专业提供小容量存储解决方案。




主题

回复
  • 温馨提示: 标题不合格、重复发帖、发布广告贴,将会被删除帖子或禁止发言。 详情请参考: 社区发帖规则
  • 您当前输入了 0 个文字。还可以输入 8000 个文字。 已添加复制上传图片功能,该功能目前仅支持chrome和火狐

禁言/删除

X
请选择禁言时长:
是否清除头像:
禁言/删除备注:
昵 称:
 
温馨提示:昵称只能设置一次,设置后无法修改。
只支持中文、英文和数字。

举报

X
请选择举报类型:
请输入详细内容:

顶部