📄 sdcard.c
字号:
//创建日期:2007/5/5
//作者:龚志
//
#include "SDCard.h"
//int1 HaveActSd = 0;
void delay8ms()
{
unsigned char i,j;
for(i=0;i<100;i++)
for(j=0;j<30;j++)
;
}
//函数说明
//参数
//返回
void Assert_SPI_CS(char value)
{
SD_CS = value?1:0;
}
//函数说明 写一个字节到spi
//参数 写数据
//返回 成功返回NO_ERROR 失败返回超时
char WriteToSPI(unsigned char databyte)
{
unsigned char i;
unsigned char temp;
temp = databyte;
for(i=0;i<8;i++)
{
SD_SCK = 0; //SCK为低的时候准备传输的数据,为高的时候发送数据
if((temp & 0x80) != 0)
SD_MOSI = 1;
else
SD_MOSI = 0;
temp = temp << 1;
SD_SCK = 1;
}
SD_MOSI = 1;
return NO_ERROR;
}
//函数说明 从spi读一个数据
//参数 接受数据的指针
//返回 成功返回NO_ERROR 失败返回超时
char RdFromSPI(unsigned char *pdatabyte)
{
int i;
bit bt;
unsigned char temp = 0;
for(i=0;i<8;i++)
{
SD_MISO = 1; //上跳沿读数据
SD_SCK = 0;
SD_SCK = 1;
temp = temp << 1;
bt = SD_MISO;
if(bt == 1) temp = temp | 0x1;
else temp = temp & 0xfe;
}
*pdatabyte = temp;
return NO_ERROR;
}
void ConvertInt4ToChar4(unsigned long length,unsigned char *para)
{
*para = (unsigned char)length;
*(para+1) = (unsigned char)(length>>8);
*(para+2) = (unsigned char)(length>>16);
*(para+3) = (unsigned char)(length>>24);
}
//函数说明 设置块长度
//参数 块长度
//返回 统一
char SetBlockLen(unsigned int length)
{
char ret;
unsigned char para[4];
unsigned char respond;
SD_CS = 0 ; // 置CS低
ConvertInt4ToChar4(length,para); //把长度转化为四字节参数
ret = SendCmd(16,para,R1,&respond); //发送CMD16即设置块长度命令
if(ret != NO_ERROR) //出错处理
{
SD_CS = 1;
return ERROR;
}
if(respond != 0) {SD_CS = 1;return ERROR;} //命令响应不为0表示有错误
SD_CS = 1;
return NO_ERROR;
}
//函数说明 读数据块
//参数
//返回值正确返回NO_ERROR ,不正确返回ERROR
char RDBlock(unsigned long addr, unsigned char *rbuf)
{
char ret;
unsigned char temp;
int i;
char respond, para[4];
Assert_SPI_CS(0);
ConvertInt4ToChar4(addr,para); //参数转化
ret = SendCmd(17,para,R1,&respond); //发送读命令
if(ret != NO_ERROR) {Assert_SPI_CS(1);return ERROR;} //出错处理
if(respond != 0) {Assert_SPI_CS(1);return ERROR;} //响应错误
ret = RdFromSPI(&temp); //从spi读同步字节
while(temp == 0xff) //为FF表示SD忙
{
RdFromSPI(&temp);
}
if(temp != 0xfe) //同步字节错误后就重启SD卡
{
delay8ms();
Assert_SPI_CS(1);
InitialSD();
return ERROR;
}
for(i=0; i<=512-1; i++) //读512字节数据,数据出来的顺序 注意数据的顺序(应该是先低位后高位)
{
RdFromSPI(&rbuf[i]);
}
RdFromSPI(&temp); //读两字节的校验
RdFromSPI(&temp);
Assert_SPI_CS(1);
return NO_ERROR;
}
//函数说明 写一512字节到sd卡
//返回值 统一
//参数说明 addr地址 wbuf数据指针
char WriteBlock(unsigned long addr, unsigned char *wbuf)
{
char temp;
unsigned int j=0 ;
char ret;
int i;
char respond;
unsigned char para[4];
//print("addr:");
// printhex(addr);
//print("\r\n");
delay8ms();
Assert_SPI_CS(0);
ConvertInt4ToChar4(addr,para); //参数转化
ret = SendCmd(24,para,R1,&respond); //发送写命令
if(ret != NO_ERROR) {delay8ms();delay8ms();Assert_SPI_CS(1);InitialSD();return ERROR;} //注意:这两个时间延迟是需要的
if(respond != 0x0 ) {Assert_SPI_CS(1);InitialSD();return ERROR;}
WriteToSPI(0xff); //写8个高脉冲
WriteToSPI(0xfe); //写同步字节
for(i=0; i<512; i++) //写512字节数据 注意写数据的顺序(应该是先低后高)
{
ret = WriteToSPI(wbuf[i]);
}
ret = WriteToSPI(0xff);
ret = WriteToSPI(0xff);
RdFromSPI(&temp); //数据回应正确值为OX1F
if((temp & 0x1f) == 0xb) //后面都为错处处理
{
delay8ms();
WriteToSPI(0xff);
Assert_SPI_CS(1);
InitialSD();
return ERROR;
}
else if((temp & 0x1f) == 0xd)
{
delay8ms();
WriteToSPI(0xff);
Assert_SPI_CS(1);
InitialSD();
return ERROR;
}
else if((temp & 0x1f) != 0x5)
{
delay8ms();
WriteToSPI(0xff);
Assert_SPI_CS(1);
InitialSD();
return ERROR;
}
j = 0;
do
{
RdFromSPI(&temp); //读忙标志 为OXFF表示SD卡忙
j++;
}
while((j<1000) && (temp == 0xff)) ;
Assert_SPI_CS(1);
if((j >= 1000) || (temp == 0xff)) {/*print("over time\r\n");*/return ERROR;}
else {/*print("wait:");printhex(j);print("\r\n");*/return NO_ERROR;}
}
// 函数说明 向sd卡发送一个命令
//参数:cmdNo命令码,para命令参数,paralen参数长度,respondtype响应类型,respond响应内容指针
//返回值:无错的情况下返回NO_ERROR 有错的情况下有两种返回值
char SendCmd(unsigned char cmdNo, unsigned char *para, unsigned char respondtype,unsigned char *respond)
{
char i, respondlength, ret;
unsigned char temp;
int retry=0;
//print("in cmd\r\n");
ret = WriteToSPI((cmdNo & 0x3F) | 0x40); //发送命令字节 置命令码的最高两位为“01”
for(i=3;i>=0;i--) //发送参数 先发高字节 4字节参数每个命令必须有
{
ret = WriteToSPI(para[i]);
}
temp = GenerateCRC((cmdNo & 0x3F) | 0x40,para);
ret = WriteToSPI(temp); //发送校验字节
switch (respondtype) /* 根据不同的命令,得到不同的响应长度 */
{
case R1:
case R1b: respondlength = 1; break;
case R2: respondlength = 2; break;
case R3: respondlength = 5; break;
default: respondlength = 0; break; /* 返回命令响应类型错误 return error of command response type */
}
if(respondlength == 0)
{
return ERROR; //命令参数中返回类型给错
}
RdFromSPI(&temp);
while((temp & 0x80) != 0)// 等待命令响应,响应的开始位为0
{
RdFromSPI(&temp);
retry++;
if(retry >= 200) break;
}
if((retry >= 200) && ((temp & 0x80) != 0))
{
//print("over 200\r\n");
return ERROR;
}
respond[0] = temp;
return NO_ERROR;
}
//函数说明 激活sd卡
//参数
//返回 返回值统一
char Activate_SD()
{
char ret;
unsigned char temp, para[4];
unsigned char i;
for(i=0;i<4;i++)
{
para[i] = 0;
}
Assert_SPI_CS(0);
do
{
ret = SendCmd(1, para, R1,&temp); //发送CMD1
if((ret == NO_ERROR) && (temp == 0x00)) //出错处理
{Assert_SPI_CS(1);return NO_ERROR;}
i++;
}
while(i<200);
Assert_SPI_CS(1);
return ERROR;
}
//函数说明 允许校验与否
//参数
//返回
char Enable_CRC(unsigned char bEnable)
{
unsigned char ret, temp, para[4];
// print("in enable crc\r\n");
if (bEnable == 1)
para[0] = 1; /* 使能crc enable crc */
else
para[1] = 0;
Assert_SPI_CS(0);
ret = SendCmd(59, para, R1,&temp); //发送CMD59
//printhex(temp);
if(ret == ERROR) {Assert_SPI_CS(1);return ERROR;}
if(temp != 0x00) {Assert_SPI_CS(1);return ERROR;}
Assert_SPI_CS(1);
return NO_ERROR;
}
//函数说明 计算命令的校验码
//参数
//返回
char GenerateCRC(unsigned char cmdNo, unsigned char *para)
{
unsigned char i, j;
unsigned char reg = 0;
unsigned char array[5];
array[0] = cmdNo;
for (i = 1; i < 5; i++) //将参数的顺序重新排列
array[i] = para[4 - i];
for (i = 0; i < 5; i++) /// 计算5个字节的CRC7
{
for (j = 0; j < 8; j++)
{
reg <<= 1;
reg ^= ((((array[i] << j) ^ reg) & 0x80) ? 0x9 : 0);
}
}
return ((reg << 1) + 0x01) ; // 计算结果的CRC7左移一位,并将最低位置1
}
//函数说明 复位sd卡
//参数
//返回 返回值统一
char Reset_SD()
{
char ret;
int i;
unsigned char para[4];
char respond;
Assert_SPI_CS(0);
for(i=0;i<4;i++)
{
para[i] = 0;
}
for(i=0;i<100;i++)
{
ret = SendCmd(0,¶[0],R1,&respond); //发送CMD0命令
if((ret == NO_ERROR) && (respond == 0x01))
{Assert_SPI_CS(1);return NO_ERROR;} //出错处理
}
Assert_SPI_CS(1);
return ERROR;
}
//初始化SD卡为SPI模式
char InitSDToSpi()
{
char ret;
unsigned char x;
Assert_SPI_CS(1); //置SD卡的CS线为高
for(x=0;x<10;x++) //写80个正脉冲到SD卡 ,要求发送74个
WriteToSPI(0xff);
Assert_SPI_CS(0); //CS=0;
ret = Reset_SD(); //复位SD卡,此时CS = 0,因此进入SPI模式
if(ret != NO_ERROR) {Assert_SPI_CS(1);return ERROR;}
else {Assert_SPI_CS(1);return NO_ERROR;}
}
//初始化SD卡
char InitialSD()
{
while(1)
{
if(InitSDToSpi() == NO_ERROR) break; //初始化为SPI模式
}
while(1)
{
if(Activate_SD() == NO_ERROR) break; //激活SD卡
}
while(1)
{
if( Enable_CRC(0) == NO_ERROR) break; //不使用校验
}
while(1)
{
if( SetBlockLen(512) == NO_ERROR) break; //设置读写块长度为512字节
}
return NO_ERROR;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -