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