#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; //定義主機發(fā)送數(shù)據(jù)位 sbit cs=P1^3; //定義片選位 uchar xdata shuju[512]={0}; //定義數(shù)據(jù)緩沖數(shù)組 uchar flag_time; //定義標志時間,因為當(dāng)sd卡進行初始化時需要降低 //通信速度,所以通過該標志來寫1來降低速度 void delay(uint x) //通信延時函數(shù) { while(x--) _nop_(); } void delay1(uint a) { uint i,j; for(i=0;i<a;i++) for(j=0;j<120;j++); } //寫一字節(jié)數(shù)據(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口數(shù)據(jù)更穩(wěn)定,也可以省略 clk=1; _nop_(); if(flag_time==1) delay(10); } } //讀取sd卡一個字節(jié)數(shù)據(jù)// uchar read_sd() { uchar i,temp=0; so=1; //一定要先將其置1否則會出現(xiàn)錯誤 //因為如果先置0單片機io口寄存器相應(yīng)位電平為低當(dāng) //當(dāng)接收到高電平后可能sd卡電平不足使其io變?yōu)楦唠娖? 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++) //發(fā)送六字節(jié)命令 { write_sd(cmd[i]); } time=0; do { temp=read_sd(); time++; } while((temp==0xff)&&(time<100)); //判斷命令是否寫入成功,當(dāng)讀取到so不為0xff時命令寫入成功 //當(dāng)temp==0xff時為真且沒發(fā)送100次為真繼續(xù)執(zhí)行 //但是又不能無限制等待所以讓命令寫入100次結(jié)束 return temp; //返回讀取的數(shù)據(jù) } //復(fù)位函數(shù)// 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++) //復(fù)位時最少寫入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的響應(yīng) cs=1; //關(guān)閉片選 write_sd(0xff); //補償8個時鐘 return 0; } //初始化函數(shù)此函數(shù)決定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響應(yīng) flag_time=0; cs=1; write_sd(0xff); //補償8個時鐘 return 0; } //寫sd卡扇區(qū)// 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卡操作一次性只能寫一個扇區(qū)也就是512個字節(jié) //所以這里通過將長整型地址左移九位來將地址乘上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); //寫入寫扇區(qū)命令 time++; if(time==100) { return(write_error); } } while(temp!=0x00); //判斷命令是否寫入成功成功時返回0x00 for(i=0;i<20;i++) //補充若干時鐘 { write_sd(0xff); } write_sd(0xfe); //寫入開始字節(jié)0xfe,后面要寫入512字節(jié)數(shù)據(jù) for(i=0;i<512;i++) { write_sd(buffer[i]); } write_sd(0xff); write_sd(0xff); //兩字節(jié)奇偶校驗 temp=read_sd(); //讀取返回值 if((temp&0x1f)!=0x05) //如果返回值是 xxx00101 說明數(shù)據(jù)已經(jīng)被寫入 { cs=1; return(write_error); } while(read_sd()!=0xff); //等待sd卡不忙 數(shù)據(jù)寫入成功 cs=1; //關(guān)閉片選 write_sd(0xff); //補償8 個時鐘 return 0; } //讀取sd卡扇區(qū)// 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); //兩字節(jié)奇偶校驗位 write_sd(0xff); cs=1; write_sd(0xff); //補償8個時鐘 return 0; } /*在P0上接八個發(fā)光二極管用來顯示讀取到的數(shù)據(jù) 首先在數(shù)組(shuju)里面放入i用于顯示,再將其 寫入SD卡扇區(qū),然后在讀取出SD卡里的數(shù)據(jù)*/ void main() { uint i=0; P2=0x00; P0=0xff; sd_reset(); sd_init(); ///初始化sd卡 for(i=0;i<512;i++) { shuju[i]=i; //向數(shù)據(jù)數(shù)組里面寫入數(shù)據(jù) } for(i=0;i<512;i++) { xie_sd_shanqu(1,shuju); //將數(shù)據(jù)數(shù)組里面的數(shù)據(jù)寫入sd卡 } for(i=0;i<2;i++) { shuju[i]=0; //清零數(shù)據(jù)數(shù)組用來存儲從sd卡讀取到的數(shù)據(jù) } duqushanqu(1,shuju); //讀取扇區(qū)數(shù)據(jù) while(1) { for(i=0;i<512;i++) { P0=shuju[i]; //顯示扇區(qū)數(shù)據(jù) delay1(200); } } }