特别说明:以下所涉及代码参考网络、相关开发板厂家。只用于自己学习、理解和感悟。
SD存储卡( Secure Digital Memory Card)是一种基于半导体快闪存储器的新一代高速存储设备。SD 卡是现在嵌入式设备重要的存储模块。通常SD卡中都有文件系统,可以安装文件名和目录路径来读写文件。SD卡结构如下图所示:
- SD卡协议简介
数明:SD卡命令是6字节组成的命令包。
a) 命令码:
命令码由1个字节组成,第7位和第6位固定为“01”。
b) 校验与结束码:
校验与结束码由7位CRC校验和1位结束码组成。
c) 命令参数码:
2. 响应:
SD卡对每个命令多有响应,在SPI模式下有三种响应模式:R1、R2、R3。
-
SD卡2.0版的初始化步骤:
a.上电后延时至少 74clock,等待 SD 卡内部操作完成。
b. 片选 CS 低电平选中 SD 卡。
c. 发送 CMD0,需要返回 0x01,进入 Idle 状态。
d. 为了区别 SD 卡是 2.0 还是 1.0,或是 MMC 卡,这里根据协议向上兼容的,首先发送只有 SD2.0 才有的命令 CMD8,如果 CMD8 返回无错误,则初步判断为 2.0 卡,进一步循环发送命令CMD55+ACMD41,直到返回 0x00,确定 SD2.0 卡。
e. 如果 CMD8 返回错误则判断为 1.0 卡还是 MMC 卡,循环发送 CMD55+ACMD41,返回无错误,
则为 SD1.0 卡,到此 SD1.0 卡初始成功,如果在一定的循环次数下,返回为错误,则进一步发
送 CMD1 进行初始化,如果返回无错误,则确定为 MMC 卡,如果在一定的次数下,返回为错
误,则不能识别该卡,初始化结束。(通过 CMD16 可以改变 SD 卡一次性读写的长度)。
f. CS 拉高。 -
SD卡的读步骤:
a. 发送 CMD17(单块)或 CMD18(多块)读命令,返回 0X00。
b. 接收数据开始令牌 fe(或 fc)+正式数据 512Bytes + CRC 校验 2Bytes 默认正式传输的数据长度是 512Bytes。 -
SD 卡的写步骤:
a 发送 CMD24(单块)或 CMD25(多块)写命令,返回 0X00。
b. 发送数据开始令牌 fe(或 fc)+正式数据 512Bytes + CRC 校验 2Bytes。
相关代码如下:
- spi_master.v
module spi_master(
input sys_clk,
input rst,
output nCS,
output DCLK,
output MOSI,
input MISO,
input CPOL,
input CPHA,
input nCS_ctrl,
input[15:0] clk_div,
input wr_req,
output wr_ack,
input[7:0] data_in,
output[7:0] 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[7:0] MOSI_shift;
reg[7:0] MISO_shift;
reg[2:0] state;
reg[2:0] next_state;
reg[15:0] clk_cnt;
reg[4:0] clk_edge_cnt;
assign MOSI = MOSI_shift[7];
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:
if(clk_cnt == clk_div)
next_state <= DCLK_EDGE;
else
next_state <= DCLK_IDLE;
DCLK_EDGE:
if(clk_edge_cnt == 5’d15)
next_state <= LAST_HALF_CYCLE;
else
next_state <= DCLK_IDLE;
LAST_HALF_CYCLE:
if(clk_cnt == clk_div)
next_state <= ACK;
else
next_state <= LAST_HALF_CYCLE;
ACK:
next_state <= ACK_WAIT;
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;
end
always @(posedge sys_clk or posedge rst)
begin
if(rst)
clk_cnt <= 16’d0;
else if(s