资源描述
对SD卡进行操作首先要对SD卡进行初始化,初始化的过程中设置SD卡工作在SPI模式,其流程图如图3所示。
在复位成功之后可以通过CMD55和ACMD41判断当前电压是否在工作范围内。主机还可以继续通过CMD10读取SD卡的CID寄存器,通过CMD16设置数据Block长度,通过CMD9读取卡的CSD寄存器。从CSD寄存器中,主机可获知卡容量,支持的命令集等重要参数。SD卡初始化的C语言程序如下:
unsigned char SD_Init(void)
{ unsigned char retry,temp;
unsigned char i;
for (i=0;i<0x0f;i++)
{ SPI_TransferByte(0xff); //延迟74个以上的时钟
}
SD_Enable(); //开片选
SPI_TransferByte(SD_RESET); //发送复位命令
SPI_TransferByte(0x00);
SPI_TransferByte(0x00);
SPI_TransferByte(0x00);
SPI_TransferByte(0x00);
SPI_TransferByte(0x95);
SPI_TransferByte(0xff);
SPI_TransferByte(0xff);
retry=0;
do{ temp="Write"_Command_SD(SD_INIT,0);
//发送初始化命令
retry++;
if(retry==100) //重试100次
{SD_Disable(); //关片选
return(INIT_CMD1_ERROR);
//如果重试100次失败返回错误号
}
}while(temp!=0);
SD_Disable(); //关片选
return(TRUE); //返回成功
}
数据块的读写
完成SD卡的初始化之后即可进行它的读写操作。SD卡的读写操作都是通过发送SD卡命令完成的。SPI总线模式支持单块(CMD24)和多块(CMD25)写操作,多块操作是指从指定位置开始写下去,直到SD卡收到一个停止命令CMD12才停止。单块写操作的数据块长度只能是512字节。单块写入时,命令为CMD24,当应答为0时说明可以写入数据,大小为512字节。SD卡对每个发送给自己的数据块都通过一个应答命令确认,它为1个字节长,当低5位为00101时,表明数据块被正确写入SD卡。
在需要读取SD卡中数据的时候,读SD卡的命令字为CMD17,接收正确的第一个响应命令字节为0xFE,随后是512个字节的用户数据块,最后为2个字节的CRC验证码。
可见,读写SD卡的操作都是在初始化后基于SD卡命令和响应完成操作的,写、读SD卡的程序流程图如图4和图5所示。
结束语
实验结果表明单片机使用12MHz的晶体振荡器时,读写速度和功耗都基本令人满意,可以应用于对读写速度要求不高的情况下。本文详细阐述了用AT89C52单片机对SD卡进行操作的过程,提出了一种不带SD卡控制器,MCU读写SD卡的方法,实现了SD卡在电能监测及无功补偿数据采集系统中的用途。
本文的实现程序把SPI总线读写功能集成在一起,传递的val变量既是向SPI写的数据,也是从SPI读取的数据。具体程序如下:(程序是在Keil uVision2的编译环境下编写)
sbit CS="P3"^5;
sbit CLK= P1^5;
sbit DataI="P1"^7;
sbit DataO="P1"^6;
#define SD_Disable() CS="1" //片选关
#define SD_Enable() CS="0" //片选开
unsigned char SPI_TransferByte(unsigned char val)
{
unsigned char BitCounter;
for(BitCounter=8; BiCounter!=0; BitCounter--)
{ CLK="0";
DataI=0; // write
if(val&0x80) DataI="1";
val<<=1;
CLK=1;
if(DataO)val|=1; // read
}
CLK=0;
return val;
SD卡读写子程序:
硬件平台:atmega8L最小系统
硬spi(sd卡的初始化采用了io口模拟时序,因为在实验中发现要使用较低的速率才能稳定的初始化)
软件开发平台:ICC-AVR version 6.31a
硬件配置:atmega8L 内部8m时钟
sandisk 128m sd卡
几个基本的子程序及其介绍:
1.io口模拟spi,实现数据发送,在初始化时使用
void iodatatransfer(unsigned char iodata)
{
unsigned char num,b;
num=0x80;
for (b=0;b0x01)
num=num>>1;
}
}
2.io口模拟spi,实现数据读取,在初始化时使用
unsigned char iodataread(void)
{
unsigned char data,temp,b;
data=0;
temp=0;
for (b=0;b100)
{
break;
}
}
WDR();//feed the dog
return(temp);//the respone of the byte_write_operation
}
4.硬spi读数据
unsigned char Read_Byte_SD(void)
{
char Byte;
//SD_Enable();
SPDR=0xff;
while(!(SPSR&(1100)
{
break;
}
}
//for some reason we need to delay here
//delay_1ms();
return(tmp);//the respone of the byte_write_operation
}
7.初始化
unsigned char SDInit(void)
{
unsigned char a,b,retry,erroetoken;
unsigned char CMD[]={0x40,0x00,0x00,0x00,0x00,0x95};//cmd0
// Set certain pins to inputs and others to outputs
// Only SPI_DI (data in) is an input
//SD_Direction_REG==ddrb
SD_Direction_REG&=~(1200)
{ //fail and return
return 1;
}
}
//return 0;///////////
//Send the 2nd command
retry=0;
CMD[0]=0x41;
CMD[5]=0xFF;
while(erroetoken=iocmd(CMD)!=0x00)
{
WDR();
if (retry++>200)
{
return 2;
}
}
//Set the SPI bus to full speed
SPCR=0x50;
SPSR|=0x01;
//Raise Chip Select
SD_Disable();
return 0;
}
8.设置每次读的字节数
char SD_set_length(unsigned int length)
{
unsigned char retry;
//Command to set the block length;
char CMD[]={0x50,0x00,0x00,0x00,0x00,0xFF}; //cmd16
CMD[3]=((length&0xFF00)>>8);//
CMD[4]= (length&0x00FF);
while(Write_Command_SD(CMD)!=0)//
{
WDR();
if (retry++>200)
{
return 1;
}
}
SD_Disable();
return 0;
}
9.write 512 bytes to a given sector from a Byte_byte_long Buffer
unsigned char SD_write_sector(unsigned long addr,unsigned char *Buffer,unsigned int Bytes)
{
unsigned int a;
unsigned char retry,temp;
//Command to read a block;
char CMD[]={0x58,0x00,0x00,0x00,0x00,0xFF};//cmd24
CMD[1]=((addr&0xFF000000)>>24);
CMD[2]=((addr&0x00FF0000)>>16);
CMD[3]=((addr&0x0000FF00)>>8);
CMD[4]=(addr&0x000000FF);
//Send the write command
while(Write_Command_SD(CMD)!=0)
{
if (retry++>50)
{
return 1;
}
}
//Send the start byte
Write_Byte_SD(0xfe);
//Read off all the bytes in the block
for(a=0;a>24);
CMD[2]=((addr&0x00FF0000)>>16);
CMD[3]=((addr&0x0000FF00)>>8);
CMD[4]=(addr&0x000000FF);
//Send the read command
while(Write_Command_SD(CMD)!=0)
{
WDR();//feed the dog
if (retry++>200)
{
return 1;
}
}
//Send the start byte
while(Read_Byte_SD()!=0xfe)
{
WDR();//feed the dog
}
//Read off all the bytes in the block
for(a=0;a
{
WDR();//feed the dog
*Buffer=Read_Byte_SD();
//serial(*Buffer);
Buffer++;
}
//Read CRC byte
Read_Byte_SD();
Read_Byte_SD();
// Set SD_Chip_Select to high
SD_Disable();
//SEI(); //re-enable interrupts
return 0;
}
/*
//read xx bytes no matter of misalignment!!
*/
unsigned char read_antimisaliment(unsigned long addr_temp,unsigned char *p_buffer, unsigned int length)
{
unsigned int _length=0x0000;
SD_Enable();
while(SD_read_sector(addr_temp,p_buffer,length))
{
SD_Enable();//
length-=0x0001;//to find a suuitable length to avoid misalignment
_length+=0x0001;// _length+length==xx
SD_set_length(length);
}
///
if(_length==0x0000)
{
return 0;
}
///
addr_temp+=length;
SD_Enable();//
SD_set_length(_length);
SD_Enable();//
while(SD_read_sector(addr_temp,p_buffer,_length))
{
SD_Enable();
}
SD_Enable();//
SD_set_length(length+_length);//to read the rest bytes of the xx bytes
return 0;
}
展开阅读全文