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