专注电子技术学习与研究
当前位置:单片机教程网 >> MCU设计实例 >> 浏览文章

自编基于51单片机的sD卡程序

作者:佚名   来源:本站原创   点击数:  更新时间:2012年01月08日   【字体:
#include<reg52.h>

#include<intrins.h>

#define uint unsigned int

#define uchar unsigned char

    //错误码定义//

#define cmd0_error 0x01

#define cmd1_error 0x02

#define write_error 0x03

#define read_error 0x04

    /*位定义*/

sbit so=P1^0; //定义主机接收位

sbit clk=P1^1;    //定义时钟位

sbit si=P1^2; //定义主机发送数据位

sbit cs=P1^3; //定义片选位

uchar xdata shuju[512]={0};     //定义数据缓冲数组

uchar flag_time;         

//定义标志时间,因为当sd卡进行初始化时需要降低

//通信速度,所以通过该标志来写1来降低速度

void delay(uint x)       //通信延时函数

{

    while(x--)

    _nop_();

}

void delay1(uint a)     

{

    uint i,j;

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

        for(j=0;j<120;j++);

}

//写一字节数据//

void write_sd(uchar date)  

{

    uchar i;

    CY=0;

    clk=1;

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

    {

        clk=0;

        date=date<<1;

        si=CY;

        if(flag_time==1)  //用来判断是否处于初始化,如果是降低通信速度

        delay(10);

        _nop_();   //用来让io口数据更稳定,也可以省略

        clk=1;

        _nop_();

        if(flag_time==1)

        delay(10);

    }

}

//读取sd卡一个字节数据//

uchar read_sd()      

{

    uchar i,temp=0;

    so=1;          //一定要先将其置1否则会出现错误

    //因为如果先置0单片机io口寄存器相应位电平为低当

    //当接收到高电平后可能sd卡电平不足使其io变为高电平

    clk=1;

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

    {

        clk=0;

        if(flag_time==1)

        delay(10);

        temp<<=1;

        temp=temp|so;

        _nop_();

        clk=1;

        _nop_();

        if(flag_time==1)

        delay(10);

    }

    return temp;

}

//向sd卡写命令//

uchar write_cmd(uchar *cmd)

{

    uchar i,time,temp;  

    si=1;

    for(i=0;i<6;i++)  //发送六字节命令

    {

        write_sd(cmd[i]);

    }

    time=0;

    do

    {  

        temp=read_sd();

        time++;

    }

    while((temp==0xff)&&(time<100));

    //判断命令是否写入成功,当读取到so不为0xff时命令写入成功

    //当temp==0xff时为真且没发送100次为真继续执行

    //但是又不能无限制等待所以让命令写入100次结束

    return temp;      //返回读取的数据

}

//复位函数//

uchar sd_reset()

{

    uchar i,temp=0xff,time;

    uchar table[]={0x40,0x00,0x00,0x00,0x00,0x95};

    flag_time=1;

    cs=1;

    for(i=0;i<0x0f;i++)      //复位时最少写入74个时钟周期

    {

        write_sd(0xff);

    }  

    cs=0;

    time=0;//打开片选

    do

    {

        temp=write_cmd(table);   //写入cmd0

        time++;

        if(time==100)

        return(cmd0_error);

    }

    while(temp!=0x01);       //等待命令CMD0的响应

    cs=1;                  //关闭片选

    write_sd(0xff);           //补偿8个时钟

    return 0;

}

//初始化函数此函数决定SD卡的工作模式 选择SPI还是SD模式//

uchar sd_init()

{

    uchar time=0,temp=0xff;

    uchar table[]={0x41,0x00,0x00,0x00,0x00,0xff};       //命令码

    flag_time=1;

    cs=0;

    time=0;

    do

    {

        temp=write_cmd(table);

        time++;

        if(time==100)

        return 0x02;

    }

    while(temp!=0x00);       //等待命令CMD1响应

    flag_time=0;

    cs=1;

    write_sd(0xff);           //补偿8个时钟

    return 0;

}

//写sd卡扇区//

uchar xie_sd_shanqu(unsigned long int add,uchar *buffer)

{

    uchar temp,time;

    uint i;

    uchar table[]={0x58,0x00,0x00,0x00,0x00,0xff};

    add=add<<9;       //add=add*512

    //由于sd卡操作一次性只能写一个扇区也就是512个字节

    //所以这里通过将长整型地址左移九位来将地址乘上512

    //用于地址操作

    table[1]=((add&0xff000000)>>24);

    table[2]=((add&0x00ff0000)>>16);

    table[3]=((add&0x0000ff00)>>8);

    cs=0;

    time=0;

    do

    {

        temp=write_cmd(table);       //写入写扇区命令

        time++;

        if(time==100)

        {

            return(write_error);

        }

    }

    while(temp!=0x00);               //判断命令是否写入成功成功时返回0x00

    for(i=0;i<20;i++)     //补充若干时钟

    {

        write_sd(0xff);

    }

    write_sd(0xfe);       //写入开始字节0xfe,后面要写入512字节数据

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

    {

        write_sd(buffer[i]);

    }

    write_sd(0xff);  

    write_sd(0xff);           //两字节奇偶校验

 

    temp=read_sd();           //读取返回值

    if((temp&0x1f)!=0x05)        //如果返回值是 xxx00101 说明数据已经被写入

    {

        cs=1;

        return(write_error);

    }

    while(read_sd()!=0xff);          //等待sd卡不忙 数据写入成功

    cs=1;                  //关闭片选

    write_sd(0xff);           //补偿8 个时钟

    return 0;

}

//读取sd卡扇区//

uchar duqushanqu(unsigned long add,uchar *buffer)

{

    uchar temp,time=0;

    uint i;

    uchar  table[]={0x51,0x00,0x00,0x00,0x00,0xff};

    add=add<<9;

    table[1]=((add&0xff000000)>>24);

    table[2]=((add&0x00ff0000)>>16);

    table[3]=((add&0x0000ff00)>>8);

    cs=0;                          //打开片选

    do

    {

        temp=write_cmd(table);       //写命令

        time++;

        if(time==100)

        {

            return read_error;

        }

    }

    while(temp!=0);

    write_sd(0xff);               //补偿8个时钟

    while(read_sd()!=0xfe);      //一直读取等待0xfe

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

    {

        buffer[i]=read_sd();

    }

    write_sd(0xff);           //两字节奇偶校验位

    write_sd(0xff);

    cs=1;

    write_sd(0xff);           //补偿8个时钟

    return 0;

}

/*在P0上接八个发光二极管用来显示读取到的数据

首先在数组(shuju)里面放入i用于显示,再将其

写入SD卡扇区,然后在读取出SD卡里的数据*/

void main()

{

    uint i=0;

    P2=0x00;

    P0=0xff;

    sd_reset();  

    sd_init();     ///初始化sd卡

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

    {

        shuju[i]=i;           //向数据数组里面写入数据

    }

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

    {

        xie_sd_shanqu(1,shuju);      //将数据数组里面的数据写入sd卡

    }

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

    {

        shuju[i]=0;           //清零数据数组用来存储从sd卡读取到的数据

    }         

    duqushanqu(1,shuju);     //读取扇区数据

    while(1)

    {

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

        {

            P0=shuju[i];      //显示扇区数据

            delay1(200);

        }

    }

}
关闭窗口

相关文章