#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);
}
}
}