通过SPI读写SD卡的protel仿真学习

原理图:
在这里插入图片描述
SPI简介:
SPI,是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI接口主要应用在 EEPROM,FLASH,实时时钟,AD转换器等芯片,还有数字信号处理器和数字信号解码器之间。
SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议,比如AT91RM9200。

SPI连接图
在这里插入图片描述
在这里插入图片描述
#SPI总线四种工作方式
SPI 模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响。
CPOL
时钟极性选择,为0时SPI总线空闲为低电平,为1时SPI总线空闲为高电平
CPHA 时钟相位选择,为0时在SCK第一个跳变沿采样,为1时在SCK第二个跳变沿采样
Mode 0 SCLK 空闲是为低电平,数据在上升沿有效
Mode 1 SCLK 空闲是为低电平,数据在下降沿有效
Mode 2 SCLK 空闲是为高电平,数据在下降沿有效
Mode 3 SCLK 空闲是为高电平,数据在上升沿有效

在这里插入图片描述

//定义SD卡需要的4根信号线
sbit SD_CLK = P1^4;
sbit SD_DI  = P1^6;
sbit SD_DO  = P1^5;
sbit SD_CS  = P1^7;

//写一字节到SD卡,模拟SPI总线方式
void SdWrite(unsigned char n)
{
    unsigned char i;
    for(i=8;i;i--){
        SD_CLK=0;
        SD_DI=(n&0x80);//上升沿写入数据
        n<<=1;
        SD_CLK=1;
     }
     SD_DI=1; 
} 

//从SD卡读一字节,模拟SPI总线方式
unsigned char SdRead()
{
    unsigned char n,i;
    for(i=8;i;i--)
    {
        SD_CLK=0;
        SD_CLK=1; //上升沿读取数据
        n<<=1;
        if(SD_DO) n|=1;
    }
    return n;
}




原文链接:https://blog.csdn.net/ming1006/article/details/7281597
SPI模式的SD卡
SD卡的命令格式如下,6字节共48位,传输时最高位(MSB)先传输:
在这里插入图片描述
SD卡的command(命令)占6 bit,一般叫CMDx或ACMDx,比如CMD1就是1,CMD13就是13,ACMD41就是41,依此类推。Command Argument(命令参数)占4 byte,并不是所有命令都有参数,没有参数的话该位一般就用置0。最后一个字节由7 bit CRC校验位和1 bit停止位组成。在SPI模式下,CRC是被忽略的,可以都置1或置0.但是发送CMD0时要记得加上CRC,即最后1字节为0x95(因为发送CMD0时还未进入SPI模式,PS:CMD8也要,但一般大家都把发送CMD8省略了)。

每次发送完一次命令后,SD卡都会有回应。SD卡的回应有多种格式,1字节的R1,2字节的R2等,不过一般在SPI模式中我们只用到R1,下面介绍R1的格式:
在这里插入图片描述
关于SD卡SPI和command的发送要注意以下几点:
1.SD卡的SPI总线,在读入数据时SD卡的SPI是CLK的上升沿输入锁存,输出数据也是在上升沿。
2.向SD卡写入一个CMD或者ACMD指令的过程是这样的: 首先使CS为低电平,SD卡使能;其次在SD卡的Din写入指令;写入指令后还要附加8个填充时钟,是SD卡完成内部操作;之后在SD卡的Dout上接受回应;回应接受完毕使CS为低电平,再附加8个填充时钟。
3.在SD卡的Din没有数据写入时,应使Din保持高电平。

SD卡初始化复位方法:
1.拉高CS,发送至少74个clk周期来使SD卡达到正常工作电压和进行同步
2.选低CS,发送CMD0,需要收到回应0x01表示成功进入idle状态, 复位成功后,SD卡就进入了SPI模式,接着应该进行初始化。
3、发送CM1进行初始化

在这里插入图片描述
在这里插入图片描述

unsigned char SdInit(void)
{
    int delay=0, trials=0;
    unsigned char i;
    unsigned char response=0x01;
    SD_CS=1;
    for(i=0;i<=9;i++)
    SdWrite(0xff);
    SD_CS=0;
    //Send Command 0 to put MMC in SPI mode
    SdCommand(0x00,0,0x95);
    response=SdResponse();
    if(response!=0x01)
    {
        return 0;
    } 
    while(response==0x01)
    {
        SD_CS=1;
        SdWrite(0xff);
        SD_CS=0;
        SdCommand(0x01,0x00ffc000,0xff);
        response=SdResponse();
    } 
    SD_CS=1;
    SdWrite(0xff);
    return 1; 
}

读单块和多块:

SD卡读单块和多块的命令分别为CMD17和CMD18,他们的参数即要读的区域的开始地址。因为考虑到一般SD卡的读写要求地址对齐,所以一般我们都将地址转为块,并以扇区(块)(512Byte)为单位进行读写,比如读扇区0参数就为0,读扇区1参数就为1<<9(即地址512),读扇区2参数就为2<<9(即地址1024),依此类推。

读单块方法:
1.发送CMD17,收到0x00表示成功
2.连续读直到读到开始字节0xFE
3.读512个字节
4.读两个CRC字节

读多块方法:
1.发送CMD18读,收到0x00表示成功
2.连续读直到读到开始字节0xFE
3.读512字节
4.读两个CRC字节
5.如果还想读下一扇区,重复2-4
6.发送CMD12来停止读多块操作
//从SD卡指定地址读取数据,一次最多512字节,这个是读单块
unsigned char SdReadBlock(unsigned char *Block, unsigned long address,int len)
{
    unsigned int count=0;
    SD_CS=0;
    SdCommand(0x11,address,0xff);
    if(SdResponse()==00){
        while(SdRead()!=0xfe);
        for(count=0;count<len&&count<512;count++) *Block++=SdRead(); 
        for(;count<512;count++) SdRead();
        SdRead();
        SdRead();
        SD_CS=1;
        SdRead();
        return 1;
    }
    return 0;
}
写单块方法:
1.发送CMD24,收到0x00表示成功
2.发送若干时钟
3.发送写单块开始字节0xFE
4.发送512个字节数据
5.发送2字节CRC(可以均为0xff)
6.连续读直到读到XXX00101表示数据写入成功
7.继续读进行忙检测(读到0x00表示SD卡正忙),当读到0xff表示写操作完成

写多块方法:
1.发送CMD25,收到0x00表示成功
2.发送若干时钟
3.发送写多块开始字节0xFC
4.发送512字节数据
5.发送两个CRC(可以均为0xff)
6.连续读直到读到XXX00101表示数据写入成功
7.继续读进行忙检测,直到读到0xFF表示写操作完成
8.如果想读下一扇区重复2-7步骤
9.发送写多块停止字节0xFD来停止写操作
10.进行忙检测直到读到0xFF

在这里插入图片描述

//往SD卡指定地址写数据,一次最多512字节
unsigned char SdWriteBlock(unsigned char *Block, unsigned long address,int len)
{
    unsigned int count;
    unsigned char dataResp;
    SD_CS=0;
    SdCommand(0x18,address,0xff);
    if(SdResponse()==00)
    {
        SdWrite(0xff);
        SdWrite(0xff);
        SdWrite(0xff);
        SdWrite(0xfe);
        for(count=0;count<len&&count<512;count++) SdWrite(*Block++);
        for(;count<512;count++) SdWrite(0);
        SdWrite(0xff); //两字节CRC校验, 为0XFFFF 表示不考虑CRC
        SdWrite(0xff);
        dataResp=SdRead();     
        while(SdRead()==0);
        dataResp=dataResp&0x0f; //mask the high byte of the DATA RESPONSE token
        SD_CS=1;
        SdWrite(0xff);
        if(dataResp==0x0b){
        	return 0;
        }
        if(dataResp==0x05)
        return 1;

        return 0;
    }
    return 0;
}

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值