现在的位置: 首页 > 综合 > 正文

【原创】基于DE0的SD卡读写(无文件系统版)

2012年12月19日 ⁄ 综合 ⁄ 共 2784字 ⁄ 字号 评论关闭

  年前准备做个数码相册,VGA的驱动写好后,卡在了SD卡这里。在网上找了很多资料,看的是稀里糊涂。有个视频教程,好像是什么欣世纪的阿迪老师录制的,下来一看,不是清晰的,清晰版是要钱的,当时挺不爽的,感觉这就是在耍人嘛。于是下定决心把SD卡搞定并且开源,免费给大家使用。掏出SD卡官方手册,纯英文的,一页一页啃。这期间很多事发生,耽搁了不少时间。这几天心烦,熬夜搞,进度很不错,裸卡读写已经没问题了,现在正在搞FAT32文件系统。岁月如梭,废话少说,进入正题。

  DE0读取SD卡采用软件模拟SPI工作模式。SD卡工程的电压为3.3V。在SOPC里建立4个PIO,三个为输出,一个为输入。分别对应SD_CLK,SD_CMD,SD_CS_N和SD_DATA_IN。硬件的搭建还是很简单的。


  下面扯一扯SD卡的协议。SPI协议规定,在卡初始化之前,要置片选无效,然后发送至少74个时钟,则会使为了使卡有足够时间进入到工作电压范围内,64个时钟后的时钟是用来同步的。初始化的时候,时钟不能太快,一般clk=0clk=1之间延时至少50微秒。74个时钟发送完以后,开始发送初始化命令。注意,所有的命令都要求片选有效。

  初始化SD卡进入SPI模式,初始化顺序为:发送CMD0,回应0x01;发送CMD1,回应0x00,则卡已经进入就绪状态,初始化完毕。我发现miniSD卡和我买的2G金士顿SD卡都可以用上述两个命令完成初始化,而对于cmd55,两种卡都不响应,而对于CMD55之后发送的ACMD41,都回应为0x00。那么为了不发生初始化失败,我只使用CMD1而不用CMD55+ACMD41。还得再扯个蛋,SD卡2G以下的才能用这种方式初始化,我用8G的卡试了一下,完全不行,后来查了资料才知道,2G以上的卡叫SDHC,就是高容量SD卡,高容量SD卡的命令跟普通的卡不太一样,初始化过程也不太一样,关于SDHC卡,可以去看SD卡手册的2.0版。

  软件模拟SPI协议,SD卡为上升沿锁存数据,同时在上升沿将数据发出。发送时应该先将时钟拉低,然后在发送口上写入数据,然后等待50微秒后,将时钟拉高产生上升沿使卡锁存数据。与此同时,从卡的输出引脚读入一位数据。卡是在上升沿输出数据,所以最好是在产生上升沿后再读取数据。、我曾尝试clk=0;mosi=0x01;然后读取somi引脚的数据,再clk=1;产生上升沿,读回的数据也是对的,但我觉得不太可靠。我猜测SD卡的输出也是

Reg 
out;

always@posedge clk

Begin

Out<=data;

End

这种类型的,那么在上升沿之后读取数据,比较稳妥。

SD卡的命令封装成48位6字节的命令,高位在前,起始位为0,结束位为1.所有的数据传输都是高位在前,即MSB first。而卡的应答常用的有3种,R1,R2,R3,R1使用的频率最高。R1为8位一个字节。我的程序设计中,SPI发送的最小单位就是一个字节。发送和接收同时进行,入过只需要接收而不用发送,那么就发送0xff即可。这是因为在无命令发送时,CMD线必须为高电平。相当于发送0xff。函数如下:

#Define uc unsigned char

uc send_byte(uc sda)

{

    uc cnt=0,rda=0;

    SD_CLK->DATA=1;

    SD_CMD->DIRECTION=1;

    for(cnt=0;cnt<8;cnt++)

    {

       SD_CLK->DATA=0;

        SD_CMD->DATA=0;

       if(sda & 0x80)

           SD_CMD->DATA=1;

 

       usleep(50);

 

       sda=sda<<1;

       rda=rda<<1;

       SD_CLK->DATA=1;

 

       if(SD_DA->DATA)

       {

 

           rda=rda|0x01;

       }

//     SD_CLK->DATA=1;

 

    }

 

    SD_CLK->DATA=1;

    return rda;

}

这里没有用库操作方式,而是用纯C的方式实现,那个库函数用起来就是不爽,又长又记不住。关于这部分的结构体,是跟马瑞哥学的,详细解释见《nios2那些事》的第六章。我这里只给头文件不解释了。头文件如下:

#ifndef SOPC_H_

#define SOPC_H_

#include "system.h"

#define _LED

typedef struct

{

    unsigned long int DATA;

    unsigned long int DIRECTION;

    unsigned long int INTERRUPT_MASK;

    unsigned long int EDGE_CAPTURE;

}PIO_STR;

#ifdef _LED

#define SD_DA ((PIO_STR *)SD_DA_BASE)

#define SD_CMD ((PIO_STR *)SD_CMD_BASE)

#define SD_CS ((PIO_STR *)SD_CS_N_BASE)

#define SD_CLK ((PIO_STR *)SD_CLK_BASE)

#define LED ((PIO_STR *)LED_BASE)

#define _SD

#endif

#ifdef _SD

#define data (SD_DA->DATA)

#define cmd (SD_CMD->DATA)

#define cs (SD_CS->DATA)

#define clk (SD_CLK->DATA)

#endif

#define uc unsigned char

#endif /* SOPC_H_ */

初始化时发送至少74个时钟的程序:

SD_CS->DATA=1;

    for(i=0;i<100;i++)

    {

       send_byte(0xff);

    }

注意此时片选是无效的。再无数据发送时,CMD引脚应该为高。所以产生时钟就发送0xff

初始化的命令为CMD0(应答为0x01)和CMD1(应答为0x00),为了保证初始化成功,可重复发送,知道收到正确的应答。这两个命令被正确应答后,就可以尽享 其他操作了,读取CSD寄存器(CMD9)和CID寄存器(CMD10),设置读取块大小(CMD16)等,然后初始化就完成了。

  初始化完成后,就可以对卡进行读写了。单块读命令为CMD17,单块写命令为CMD24。

  可以run hardwire 将读取的内容打印出来在控制台查看并与WinHex软件对比。

完整的代码待会上传上来。工程太大,没法一次传上来,硬件很简单,就不传了。这是下载地址:

http://www.cnblogs.com/lamapig/admin/Files.aspx


抱歉!评论已关闭.