使用的芯片是STC15系列的IAP15F2K61S2,开发板是51的xl2000。把芯片座上的51芯片拔掉换成STC的,混合模式。(配置够低吧)
源码由51开发板例程的实验26修改而来。
浏览了网上的教程,目前SD卡基本有以下几种,对应着不同的初始化和读写方式:
一般来说刚上电时,
SD卡不支持CMD1而支持CMD55,
MMC卡不支持CMD55而支持CMD1.
通过CMD8来区分SD1.1和SD2.0卡,
通过ACMD41来区分SD2.0 SDSC or SDHC.
我手头有六张1~16GB的不同容量的SD卡,1G/2G/8G/16G各一张,4G两张。经过测试,已经可以成功地向其中的5张SD卡读写数据块,另有一张class6的4G卡没有读写成功。在这过程中,初始化的过程与大佬们前述的结果并不一样,例如协议1.0与2.0的卡在CMD8命令下均返回0x01,SD2.0 SDSC卡初始化可以返回正常值但不能读出写入,等等。在此列出1G,2G,4G及以上SD卡不同的初始化代码以及测试代码。
其中只有HAL.c的SdInit函数不一样,其他代码是共用的。
SD卡模块
HAL.h
#include "config.h"
sbit SD_CS =P1^1;
sbit SD_DI =P1^2;
sbit SD_CLK =P1^3;
sbit SD_DO =P1^4;
unsigned char SdInit(void);
unsigned char SdReadBlock(unsigned char *Block, unsigned long address,int len);
unsigned char SdWriteBlock(unsigned char *Block, unsigned long address,int len);
unsigned char SdWriteBlock2(unsigned char *Block, unsigned long address,int len);
void ComSendByte(unsigned char c);
HAL.c
1G卡:CMD0+CMD8+CMD1
#include "HAL.H"
char csd[16];
//============================================================
//写一字节到SD卡,模拟SPI总线方式
void SdWrite(unsigned char n)
{
unsigned char i;
for(i=8;i;i--)
{
SD_CLK=0;
SD_DI=(n&0x80);
n<<=1;
SD_CLK=1;
}
SD_DI=1;
}
//================================================================
//从SD卡读一字节,模拟SPI总线方式
unsigned char SdRead()
{
unsigned char n,i;
for(i=8;i;i--)
{
SD_CLK=0;
SD_CLK=1;
n<<=1;
if(SD_DO) n|=1;
}
return n;
}
//================================================================
//检测SD卡的响应
unsigned char SdResponse()
{
unsigned char i=0,response;
while(i<=8)
{
response = SdRead();
if(response==0x00)
break;
if(response==0x01)
break;
i++;
}
return response;
}
//================================================================
//发命令到SD卡
void SdCommand(unsigned char command, unsigned long argument, unsigned char CRC)
{
SdWrite(command|0x40);
SdWrite(((unsigned char *)&argument)[0]);
SdWrite(((unsigned char *)&argument)[1]);
SdWrite(((unsigned char *)&argument)[2]);
SdWrite(((unsigned char *)&argument)[3]);
SdWrite(CRC);
}
//================================================================
//初始化SD卡
unsigned char SdInit(void)
{
int delay=0, trials=0;
unsigned char i;
unsigned char response=0x01;
SD_CS=1;
for (i=0;i<0x0f;i++)
{
SdWrite(0xff);
}
SD_CS=0;
//Send Command 0 to put MMC in SPI mode
do{
SdCommand(0x00,0,0x95);
response=SdResponse();
trials++;
if(trials>=200){
return 0xfe;
}
}while(response!=0x01);
trials=0;
do{
SdCommand(0x08,0x1AA,0x87);
response=SdResponse();
trials++;
if(trials>=200){
return 0xfd;
}
}while(response!=0x01);
trials = 0;
do{
SD_CS=1;
SdWrite(0xff);
SD_CS=0;
SdCommand(0x01,0x00ffc000,0xff);
response=SdResponse();
trials++;
if(trials>=200){
return 0xfa;
}
} while(response==0x01);
SD_CS=1;
SdWrite(0xff);
return 1;
}
//================================================================
//往SD卡指定地址写数据,一次最多512字节
unsigned char SdWriteBlock(unsigned char *Block, unsigned long address,int len)
{
unsigned int count;
unsigned char dataResp;
//Block size is 512 bytes exactly
//First Lower SS
SD_CS=0;
//Then send write command
SdCommand(0x18,address,0xff);
if(SdResponse()==0x00)
{
SdWrite(0xff);
SdWrite(0xff);
SdWrite(0xff);
//command was a success - now send data
//start with DATA TOKEN = 0xFE
SdWrite(0xfe);
//now send data
for(count=0;count<len;count++) SdWrite(*Block++);
for(;count<512;count++) SdWrite(0);
//data block sent - now send checksum
SdWrite(0xff); //两字节CRC校验, 为0XFFFF 表示不考虑CRC
SdWrite(0xff);
//Now read in the DATA RESPONSE token
dataResp=SdRead();
//Following the DATA RESPONSE token
//are a number of BUSY bytes
//a zero byte indicates the MMC is busy
while(SdRead()==0);
dataResp=dataResp&0x0f; //mask the high byte of the DATA RESPONSE token
SD_CS=1;
SdWrite(0xff);
if(dataResp==0x0b)
{
//P0=0x0b;
//printf("DATA WAS NOT ACCEPTED BY CARD -- CRC ERROR\n");
return 0;
}
if(dataResp==0x05){
//P0=0x05;
return 1;
}
//printf("Invalid data Response token.\n");
//P0=0x02;
return 0;
}
//P0=0x01;
//printf("Command 0x18 (Write) was not received by the MMC.\n");
return 0;
}
//=======================================================================
//从SD卡指定地址读取数据,一次最多512字节
unsigned char SdReadBlock(unsigned char *Block, unsigned long address,int len)
{
unsigned int count;
int trials=0;
//Block size is 512 bytes exactly
//First Lower SS
// printf("MMC_read_block\n");
SD_CS=0;
//Then send write command
SdCommand(0x11,address,0xff);
if(SdResponse()==0x00)
{
//command was a success - now send data
//start with DATA TOKEN = 0xFE
while(SdRead()!=0xfe);
for(count=0;count<len;count++) *Block++=SdRead();
for(;count<512;count++) SdRead();
//data block sent - now send checksum
SdRead();
SdRead();
//Now read in the DATA RESPONSE token
SD_CS=1;
SdRead();
return 1;
}
// printf("Command 0x11 (Read) was not received by the MMC.\n");
return 0;
}
HAL.c
2G卡:CMD0+CMD8+CMD55+CMD1
#include "HAL.H"
char csd[16];
//============================================================
//写一字节到SD卡,模拟SPI总线方式
void SdWrite(unsigned char n)
{
unsigned char i;
for(i=8;i;i--)
{
SD_CLK=0;
SD_DI=(n&0x80);
n<<=1;
SD_CLK=1;
}
SD_DI=1;
}
//================================================================
//从SD卡读一字节,模拟SPI总线方式
unsigned char SdRead()
{
unsigned char n,i;
for(i=8;i;i--)
{
SD_CLK=0;
SD_CLK=1;
n<<=1;
if(SD_DO) n|=1;
}
return n;
}
//================================================================
//检测SD卡的响应
unsigned char SdResponse()
{
unsigned char i=0,response;
while(i<=8)
{
response = SdRead();
if(response==0x00)
break;
if(response==0x01)
break;
i++;
}
return response;
}
//================================================================
//发命令到SD卡
void SdCommand(unsigned char command, unsigned long argument, unsigned char CRC)
{
SdWrite(command|0x40);
SdWrite(((unsigned char *)&argument)[0]);
SdWrite(((unsigned char *)&argument)[1]);
SdWrite(((unsigned char *)&argument)[2]);
SdWrite(((unsigned char *)&argument)[3]);
SdWrite(CRC);
}
//================================================================
//初始化SD卡
unsigned char SdInit(void)
{
int delay=0, trials=0;
unsigned char i;
unsigned char response=0x01;
SD_CS=1;
for (i=0;i<0x0f;i++)
{
SdWrite(0xff);
}
SD_CS=0;
//Send Command 0 to put MMC in SPI mode
do{
SdCommand(0x00,0,0x95);
response=SdResponse();
trials++;
if(trials>=200){
return 0xfe;
}
}while(response!=0x01);
trials=0;
do{
SdCommand(0x08,0x1AA,0x87);
response=SdResponse();
trials++;
if(trials>=200){
return 0xfd;
}
}while(response!=0x01);
SdCommand(55, 0, 0);
response=SdResponse();
trials = 0;
do{
SD_CS=1;
SdWrite(0xff);
SD_CS=0;
SdCommand(0x01,0x00ffc000,0xff);
response=SdResponse();
trials++;
if(trials>=200){
return 0xfa;
}
} while(response==0x01);
SD_CS=1;
SdWrite(0xff);
return 1;
}
//================================================================
//往SD卡指定地址写数据,一次最多512字节
unsigned char SdWriteBlock(unsigned char *Block, unsigned long address,int len)
{
unsigned int count;
unsigned char dataResp;
//Block size is 512 bytes exactly
//First Lower SS
SD_CS=0;
//Then send write command
SdCommand(0x18,address,0xff);
if(SdResponse()==0x00)
{
SdWrite(0xff);
SdWrite(0xff);
SdWrite(0xff);
//command was a success - now send data
//start with DATA TOKEN = 0xFE
SdWrite(0xfe);
//now send data
for(count=0;count<len;count++) SdWrite(*Block++);
for(;count<512;count++) SdWrite(0);
//data block sent - now send checksum
SdWrite(0xff); //两字节CRC校验, 为0XFFFF 表示不考虑CRC
SdWrite(0xff);
//Now read in the DATA RESPONSE token
dataResp=SdRead();
//Following the DATA RESPONSE token
//are a number of BUSY bytes
//a zero byte indicates the MMC is busy
while(SdRead()==0);
dataResp=dataResp&0x0f; //mask the high byte of the DATA RESPONSE token
SD_CS=1;
SdWrite(0xff);
if(dataResp==0x0b)
{
//P0=0x0b;
//printf("DATA WAS NOT ACCEPTED BY CARD -- CRC ERROR\n");
return 0;
}
if(dataResp==0x05){
//P0=0x05;
return 1;
}
//printf("Invalid data Response token.\n");
//P0=0x02;
return 0;
}
//P0=0x01;
//printf("Command 0x18 (Write) was not received by the MMC.\n");
return 0;
}
//=======================================================================
//从SD卡指定地址读取数据,一次最多512字节
unsigned char SdReadBlock(unsigned char *Block, unsigned long address,int len)
{
unsigned int count;
int trials=0;
//Block size is 512 bytes exactly
//First Lower SS
// printf("MMC_read_block\n");
SD_CS=0;
//Then send write command
SdCommand(0x11,address,0xff);
if(SdResponse()==0x00)
{
//command was a success - now send data
//start with DATA TOKEN = 0xFE
while(SdRead()!=0xfe);
for(count=0;count<len;count++) *Block++=SdRead();
for(;count<512;count++) SdRead();
//data block sent - now send checksum
SdRead();
SdRead();
//Now read in the DATA RESPONSE token
SD_CS=1;
SdRead();
return 1;
}
// printf("Command 0x11 (Read) was not received by the MMC.\n");
return 0;
}
HAL.c
4G以上:CMD0+CMD8+CMD55+CMD41
#include "HAL.H"
//============================================================
//写一字节到SD卡,模拟SPI总线方式
void SdWrite(unsigned char n)
{
unsigned char i;
for(i=8;i;i--)
{
SD_CLK=0;
SD_DI=(n&0x80);
n<<=1;
SD_CLK=1;
}
SD_DI=1;
}
//================================================================
//从SD卡读一字节,模拟SPI总线方式
unsigned char SdRead()
{
unsigned char n,i;
for(i=8;i;i--)
{
SD_CLK=0;
SD_CLK=1;
n<<=1;
if(SD_DO) n|=1;
}
return n;
}
//================================================================
//检测SD卡的响应
unsigned char SdResponse()
{
unsigned char i=0,response;
while(i<=8)
{
response = SdRead();
if(response==0x00)
break;
if(response==0x01)
break;
i++;
}
return response;
}
//================================================================
//发命令到SD卡
void SdCommand(unsigned char command, unsigned long argument, unsigned char CRC)
{
SdWrite(command|0x40);
SdWrite(((unsigned char *)&argument)[0]);
SdWrite(((unsigned char *)&argument)[1]);
SdWrite(((unsigned char *)&argument)[2]);
SdWrite(((unsigned char *)&argument)[3]);
SdWrite(CRC);
}
//================================================================
//初始化SD卡
unsigned char SdInit(void)
{
int delay=0, trials=0;
unsigned char i;
unsigned char response=0x01;
SD_CS=1;
for (i=0;i<0x0f;i++)
{
SdWrite(0xff);
}
SD_CS=0;
//Send Command 0 to put MMC in SPI mode
do{
SdCommand(0x00,0,0x95);
response=SdResponse();
trials++;
if(trials>=200){
return 0xfe;
}
}while(response!=0x01);
////////////////////USB2.0
trials=0;
do{
SdCommand(0x08,0x1AA,0x87);
response=SdResponse();
trials++;
if(trials>=200){
return 0xfd;
}
}while(response!=0x01);
SD_CS=1;
SdWrite(0xff);
SD_CS=0;
trials =0;
do
{
SdRead();SdRead();SdRead();SdRead();
SdCommand(55, 0, 0);
response=SdResponse();
if(response!=0x01)
{
return 0xfb;
}
SdCommand(41, 0x40000000, 0);
response=SdResponse();
trials++;
if(trials>=200){
return 0xf7;
}
}while(response!=0x00);
SD_CS=1;
SdWrite(0xff);
return 1;
}
//================================================================
//往SD卡指定地址写数据,一次最多512字节
unsigned char SdWriteBlock(unsigned char *Block, unsigned long address,int len)
{
unsigned int count;
unsigned char dataResp;
//Block size is 512 bytes exactly
//First Lower SS
SD_CS=0;
//Then send write command
SdCommand(0x18,address,0xff);
if(SdResponse()==0x00)
{
SdWrite(0xff);
SdWrite(0xff);
SdWrite(0xff);
//command was a success - now send data
//start with DATA TOKEN = 0xFE
SdWrite(0xfe);
//now send data
for(count=0;count<len;count++) SdWrite(*Block++);
for(;count<512;count++) SdWrite(0);
//data block sent - now send checksum
SdWrite(0xff); //两字节CRC校验, 为0XFFFF 表示不考虑CRC
SdWrite(0xff);
//Now read in the DATA RESPONSE token
dataResp=SdRead();
//Following the DATA RESPONSE token
//are a number of BUSY bytes
//a zero byte indicates the MMC is busy
while(SdRead()==0);
dataResp=dataResp&0x0f; //mask the high byte of the DATA RESPONSE token
SD_CS=1;
SdWrite(0xff);
if(dataResp==0x0b)
{
//P0=0x0b;
//printf("DATA WAS NOT ACCEPTED BY CARD -- CRC ERROR\n");
return 0;
}
if(dataResp==0x05){
//P0=0x05;
return 1;
}
//printf("Invalid data Response token.\n");
//P0=0x02;
return 0;
}
//P0=0x01;
//printf("Command 0x18 (Write) was not received by the MMC.\n");
return 0;
}
//=======================================================================
//从SD卡指定地址读取数据,一次最多512字节
unsigned char SdReadBlock(unsigned char *Block, unsigned long address,int len)
{
unsigned int count;
int trials=0;
//Block size is 512 bytes exactly
//First Lower SS
// printf("MMC_read_block\n");
SD_CS=0;
//Then send write command
SdCommand(0x11,address,0xff);
if(SdResponse()==0x00)
{
//command was a success - now send data
//start with DATA TOKEN = 0xFE
while(SdRead()!=0xfe);
for(count=0;count<len;count++) *Block++=SdRead();
for(;count<512;count++) SdRead();
//data block sent - now send checksum
SdRead();
SdRead();
//Now read in the DATA RESPONSE token
SD_CS=1;
SdRead();
return 1;
}
// printf("Command 0x11 (Read) was not received by the MMC.\n");
return 0;
}
main.c
略有改动,以确认读出的数据确实是之前写入。
#include <string.h>
#include "HAL.H"
char buf[16];
char buf2[16]={0xAA,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0};
//=======================================================
//延时子程序
void delay()
{
unsigned int i;
for(i=0;i<0x8000;i++);
}
//=======================================================
//主程序
char code SST516[3] _at_ 0x003b;
main()
{
unsigned char rsp;
CLK_DIV=0x03;
P0M0=0xff;// 00000000 11111111 高阻 下同
P0M1=0xff;//
P1M0=0xff;// 11111111 11111111
P1M1=0xff;//
P2M0=0xff;// 11111111 111111111
P2M1=0xff;//常规
P3M0=0xff;
P3M1=0xff;//TXD推挽RXD常规 11111111 11111111
P4M0=0xff;
P4M1=0xff;//11111111 11111111
P5M0=0xff;
P5M1=0xff;// 00000000 00000010
P1M0=0xef;// 11111111 11110001
P1M1=0xe1;// P11 推挽 P12推挽 P13推挽 P14常规
/*
sbit SD_CS =P1^1; //SD/MMC接口定义 qinyg@163.net
sbit SD_DI =P1^2;
sbit SD_CLK =P1^3;
sbit SD_DO =P1^4;
*/
delay(); //上电延时
delay();
delay();
delay();
delay(); //上电延时
delay();
delay();
delay();
SdInit();
while(1)
{
rsp=SdInit();
//P0=rsp;
if(rsp==1) //初始化SD卡
{
SdReadBlock(buf2,1024,16); //从SD卡地址为1024的地方读16字节数据到BUFFER, 最多一次只能读512字节
//buf2[0]+=2;
P0 = buf2[0]; //只用第一字节, 送到P0口
buf[0]++; //自加1
delay();
//rewrapBlock();
//delay(); //延时
SdWriteBlock(buf,1024,sizeof(buf));
//写回SD卡
}
delay();
//延时
}
}
测试成功,得出判断是所有的卡都支持2.0,其中1G卡很可能是SD SC卡,2G卡类型还不确定,4G及以上都是SD HC卡。
很可惜,还有一张class6的4G卡虽然初始化返回正确但无法读写数据(成功的那张是class4)。尝试了不同的读写速度也无济于事。留待日后琢磨。
不像51必须用IO口模拟时序,STC15自带SPI机制。所以接下去打算利用芯片的SPI功能改写读写逻辑,以及学习利用znFTP或CH375/376实现文件访问。