从零开始,基于FPGA实现SD卡高效读写操作(二)

引言:上一篇博文介绍了SD卡的理论知识,本文我们介绍如何利用FPGA实现SD卡的读写操作。

1.硬件电路

开发板上装有一个 Micro SD 卡座,FPGA 通过 SPI 数据总线访问 Micro SD 卡, SD 卡座和 FPGA的硬件电路连接如图1所示。

图片

图1:SD卡硬件原理图设计

本文通过SD卡SPI模式进行读写操作,SPI模式管脚定义如图1所示。

2. SD卡协议简介

SD卡的协议是一种简单的命令/响应的协议。全部命令由主机发起,SD卡接收到命令后并返回响应数据。根据命令的不同,返回的数据内容和长度也不同。

SD卡命令是一个6字节组成的命令包,其中第一个字节为命令号,命令号高位bit7和bit6为固定的“01“,其它6个bit为具体的命令号。第2个字节到第5个字节为命令参数。第6个字节为7个bit的CRC校验加1个bit的结束位。如果在SPI模式的时候,CRC校验位为可选。如图2所示,Command表示命令,通常使用十进制表示名称,例如CMD17,这个时候 Command就是十进制的17。

图片

图2:SD卡命令格式

SD卡对每个命令会返回一个响应,每个命令有一定的响应格式。响应的格式跟给它的命令号有关。在 SPI 模式中,有三种响应格式:R1,R2,R3。

图片

图3:SPI模式响应R1格式

图片

图4:SPI模式响应R2格式

图片

图5:SPI模式响应R3格式

2.2.1 SD卡2.0版的初始化步骤

(1)上电后延时至少74clock,等待SD卡内部操作完成;

(2) 片选CS低电平选中SD卡;

(3)发送CMD0,需要返回0x01,进入Idle状态;

(4)为了区别SD卡是2.0还是1.0,或是MMC卡,这里根据协议向上兼容的,首先发送只有SD2.0才有的命令CMD8,如果CMD8返回无错误,则初步判断为2.0卡,进一步循环发送命令CMD55+ACMD41,直到返回0x00,确定 SD2.0卡;

(5)如果CMD8返回错误则判断为1.0卡还是MMC卡,循环发送 CMD55+ACMD41,返回无错误,则为SD1.0卡,到此SD1.0卡初始成功,如果在一定的循环次数下,返回为错误,则进一步发送CMD1进行初始化,如果返回无错误,则确定为MMC卡,如果在一定的次数下,返回为错误,则不能识别该卡,初始化结束。(通过CMD16可以改变SD卡一次性读写的长度);

(6) CS 拉高。

2.2.2 SD卡的读步骤

(1)发送 CMD17(单块)或 CMD18(多块)读命令,返回 0X00;

(2)接收数据开始令牌fe(或fc)+正式数据512Bytes+CRC校验2Bytes,默认正式传输的数据长度是 512Bytes。

图片

图 6:单块读操作

2.2.3 SD卡的写步骤

(1)发送 CMD24(单块)或 CMD25(多块)读命令,返回 0X00;

(2)接收数据开始令牌fe(或fc)+正式数据512Bytes+CRC校验2Bytes。

图片

图 7:单块写操作

3.FPGA实现

(1)软件实现
        本文要利用FPGA通过SPI接口实现SD卡读写操作。具体功能如下:

(1)SD卡SPI接口驱动设计;

(2)SD卡读写操作;

(3)读出数据显示在LED灯上;

(4)按键消抖功能。

软件功能模块划分如图8所示。

图片

图8:软件功能模块框图

各个模块功能:

(1)sd_card_test.v模块为顶层模块,实现模块间互联;

(2)spi_master.v实现SPI总线接口对外通信功能;

(3)sd_card_cmd.v实现SD卡命令协议功能;

(4)sd_card_sec_read_write.v实现SD卡读写命令状态机操作功能;

(5)sd_card_top.v实现SD卡读写功能顶层互联;

(6)KeyJitters.v实现按键操作消抖功能;

(7)sd_test_ctrl.v实现SD卡测试数据读写状态机控制。

 (2)测试结果

软件下载至电路板,SD卡读出数据正常显示在LED上,如图9所示,显示读写操作正确。

图片

图9:SD卡读出数据

图片

图10:SD卡读出数据显示在LED灯上

FPGA读写SD测试实验 Verilog逻辑源码Quartus工程文件+文档说明,FPGA型号Cyclone4E系列中的EP4CE6F17C8,Quartus版本17.1。 // 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 [5:0] seg_sel, output [7:0] seg_data ); parameter S_IDLE = 0; parameter S_READ = 1; parameter S_WRITE = 2; parameter S_END = 3; reg[3:0] state; wire sd_init_done; reg sd_sec_read; wire[31:0] sd_sec_read_addr; wire[7:0] sd_sec_read_data; wire sd_sec_read_data_valid; wire sd_sec_read_end; reg sd_sec_write; wire[31:0] sd_sec_write_addr; reg [7:0] sd_sec_write_data; wire sd_sec_write_data_req; wire sd_sec_write_end; reg[9:0] wr_cnt; reg[9:0] rd_cnt; wire button_negedge; reg[7:0] read_data; ax_debounce ax_debounce_m0 ( .clk (clk), .rst (~rst_n), .button_in (key1), .button_posedge (), .button_negedge (button_negedge), .button_out () ); wire[6:0] seg_data_0; seg_decoder seg_decoder_m0( .bin_data (read_data[3:0]), .seg_data (seg_data_0) ); wire[6:0] seg_data_1; seg_decoder seg_decoder_m1( .bin_data (read_data[7:4]), .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_WR
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值