单片机:STC12C5A60S2 晶振:24MHZ
51单片机读sd卡资料:http://download.youkuaiyun.com/detail/sparkstrike/7982195
sd卡引脚
一.IO接口
- sbit CLK = P3^4;//同步时钟
- sbit DI = P3^5;//Cmd/DataIn
- sbit DO = P3^6;//DataOut
- sbit CS = P3^7;//片选
二.模拟SPI
注:spi相关见http://blog.youkuaiyun.com/sparkstrike/article/details/39609235
- //***********模拟spi写函数
- void SPI_W(unsigned char Data){
- unsigned char i;
- for(i = 0; i<8; i++){
- Data <<= 1;
- CLK = 0;
- DI = CY;
- CLK = 1;
- };
- DI = 1;
- }
- //***********模拟spi读函数
- unsigned char SPI_R(){
- unsigned char Data,i;
- DO = 1;//设置DO接口为输入状态
- for(i = 0; i<8; i++){
- Data <<= 1;
- CLK = 0;
- CLK = 1;
- Data |= DO;
- };
- return Data;
- }
三.等待SD卡的回应(在写入一些命令后,sd卡会回应一些东西)
- //**************读sd卡回应
- unsigned char SD_Response(){
- unsigned char i,Response;
- for(i = 0; i<10; i++){
- Response = SPI_R();
- if(Response == 0x00)
- break;
- if(Response == 0x01)
- break;
- };
- return Response;
- }
Cmd为命令,命令有复位命令、读命令、写命令等,4位的Arguement为地址,CRC为校验码
- //***************向SD发命令
- void SD_Cmd(unsigned char Cmd, unsigned long Argument, unsigned char CRC){
- unsigned char arg[4];
- arg[0] = (unsigned char)Argument;
- arg[1] = (unsigned char)(Argument >> 8);
- arg[2] = (unsigned char)(Argument >> 16);
- arg[3] = (unsigned char)(Argument >> 24);
- SPI_W(Cmd | 0x40);
- SPI_W(arg[3]);
- SPI_W(arg[2]);
- SPI_W(arg[1]);
- SPI_W(arg[0]);
- SPI_W(CRC);
- }
五.初始化SD卡
SD卡有两种读写模式:SD模式和SPI模式,默认的读写模式为SD模式,单片机用SPI模式比较方便,要使用SPI模式需要在SD卡上电是对它写入CMD0命令和CMD1命令
- /*************SD卡初始化,设置SPI模式
- unsigned char SD_Init(){
- unsigned int delay = 0;
- unsigned char i;
- unsigned char Response = 0xff;
- CS = 1;
- for(i = 0; i<10; i++){
- SPI_W(0xff);//上电后给74个以上的时间脉冲
- };
- CS = 0;
- SD_Cmd(0x00, 0, 0x95);//命令CMD0,复位SD卡
- //等待复位成功
- i = 0;
- while(SD_Response() != 0x01){//等待SD卡回应信号
- i++;
- if(i > 100){
- return 0;//失败返回0
- };
- };
- CS = 1;
- SPI_W(0xff);//关片选后写8个空脉冲,SD卡复位完毕
- //设置SPI
- i = 0;
- CS = 0;
- while(Response != 0x00){//循环等待成功回应,若成功,回应信号为0x00
- SD_Cmd(0x01, 0, 0xff);//CMD1,将SD卡设置为SPI模式,无需CRC校验,填入0xff
- Response = SD_Response();
- if(i > 100){
- return 0;//尝试100次,失败返回0
- };
- };
- CS = 1;
- SPI_W(0xff);//给8个空脉冲
- return 1;
- }
六.SD卡读写数据
因为sd卡的读写都是以扇区为单位的,所以这里定义一个全局变量,512表示一个扇区
- unsigned int const len = 512;//扇区大小
1.写入数据:
adress为开始写的地址,一定要为512的整数倍,block为一个512字节的数组
- //***************SD卡写入数据块
- unsigned char SD_Block_W(unsigned char* block, unsigned long address){
- unsigned int i;
- unsigned char Response_Write;
- CS =0;
- SD_Cmd(0x18, address, 0xff);//CMD18,块写入命令
- while(SD_Response() != 0x00);//循环等待命令回应0x00
- for(i = 0; i<10; i++){
- SPI_W(0xff);//写入一定量空脉冲
- };
- SPI_W(0xfe);//0xfe为块头部,后面跟512b字节,+2bCRC(0xff,0xff)
- for(i=0; i<len; i++){
- SPI_W(block[i]);//写入512b字节
- };
- SPI_W(0xff);
- SPI_W(0xff);
- Response_Write = SPI_R()&0x0f;//写入CRC码后SD卡会回应一个xxx0,1001
- while(SPI_R() == 0);//等待SD卡回应
- CS = 1;
- SPI_W(0xff);//写入8个空脉冲
- if(Response_Write == 0x05){
- return 1;
- }else{
- return 0;
- };
- }
2.读数据:
adress为开始读的地址,一定要为512的整数倍,block为一个512字节的数组
- //****************从sd卡读数据块
- void SD_Block_R(unsigned char* block, unsigned long address){
- unsigned int i;
- CS = 0;
- SD_Cmd(0x11, address, 0xff);//CMD11,数据块读写命令,
- while(SD_Response()!=0x00);//循环等待命令回应0x00
- while(SPI_R() != 0xfe); //0xfe为块读出的头, 后面紧跟512字节的数据块+2字节的CRC
- for(i=0; i<len ; i++){
- block[i] = SPI_R();//读数据
- };
- SPI_R();
- SPI_R();//两个字节的CRC。舍弃
- CS =1;
- SPI_R();//8个空脉冲
- }
附:测试程序(程序从SD卡512000处写入512字节的数据,并读出)
- /*********************************************************************************************/
- #include <STC12C5A60S2.H> //单片机头文件
- sbit CLK = P3^4;//同步时钟
- sbit DI = P3^5;//Cmd/DataIn
- sbit DO = P3^6;//DataOut
- sbit CS = P3^7;//片选
- unsigned int const len = 512;//扇区大小
- void DELAY_MS (unsigned int a){
- unsigned int i;
- while( --a != 0){
- for(i = 0; i < 600; i++);
- }
- }
- //***********模拟spi写函数
- void SPI_W(unsigned char Data){
- unsigned char i;
- for(i = 0; i<8; i++){
- Data <<= 1;
- CLK = 0;
- DI = CY;
- CLK = 1;
- };
- DI = 1;
- }
- //***********模拟spi读函数
- unsigned char SPI_R(){
- unsigned char Data,i;
- DO = 1;//设置DO接口为输入状态
- for(i = 0; i<8; i++){
- Data <<= 1;
- CLK = 0;
- CLK = 1;
- Data |= DO;
- };
- return Data;
- }
- //**************读sd卡回应
- unsigned char SD_Response(){
- unsigned char i,Response;
- for(i = 0; i<10; i++){
- Response = SPI_R();
- if(Response == 0x00)
- break;
- if(Response == 0x01)
- break;
- };
- return Response;
- }
- //***************向SD发命令
- void SD_Cmd(unsigned char Cmd, unsigned long Argument, unsigned char CRC){
- unsigned char arg[4];
- arg[0] = (unsigned char)Argument;
- arg[1] = (unsigned char)(Argument >> 8);
- arg[2] = (unsigned char)(Argument >> 16);
- arg[3] = (unsigned char)(Argument >> 24);
- SPI_W(Cmd | 0x40);
- SPI_W(arg[3]);
- SPI_W(arg[2]);
- SPI_W(arg[1]);
- SPI_W(arg[0]);
- SPI_W(CRC);
- }
- //*************SD卡初始化
- unsigned char SD_Init(){
- unsigned int delay = 0;
- unsigned char i;
- unsigned char Response = 0xff;
- CS = 1;
- for(i = 0; i<10; i++){
- SPI_W(0xff);//上电后给74个以上的时间脉冲
- };
- CS = 0;
- SD_Cmd(0x00, 0, 0x95);//命令CMD0,复位SD卡
- //等待复位成功
- i = 0;
- while(SD_Response() != 0x01){//等待SD卡回应信号
- i++;
- if(i > 100){
- return 0;//失败返回0
- };
- };
- CS = 1;
- SPI_W(0xff);//关片选后写8个空脉冲,SD卡复位完毕
- //设置SPI
- i = 0;
- CS = 0;
- while(Response != 0x00){//循环等待成功回应,若成功,回应信号为0x00
- SD_Cmd(0x01, 0, 0xff);//CMD1,将SD卡设置为SPI模式,无需CRC校验,填入0xff
- Response = SD_Response();
- if(i > 100){
- return 0;//尝试100次,失败返回0
- };
- };
- CS = 1;
- SPI_W(0xff);//给8个空脉冲
- return 1;
- }
- //***************SD卡写入数据块
- unsigned char SD_Block_W(unsigned char* block, unsigned long address){
- unsigned int i;
- unsigned char Response_Write;
- CS =0;
- SD_Cmd(0x18, address, 0xff);//CMD18,块写入命令
- while(SD_Response() != 0x00);//循环等待命令回应0x00
- for(i = 0; i<10; i++){
- SPI_W(0xff);//写入一定量空脉冲
- };
- SPI_W(0xfe);//0xfe为块头部,后面跟512b字节,+2bCRC(0xff,0xff)
- for(i=0; i<len; i++){
- SPI_W(block[i]);//写入512b字节
- };
- SPI_W(0xff);
- SPI_W(0xff);
- Response_Write = SPI_R()&0x0f;//写入CRC码后SD卡会回应一个xxx0,1001
- while(SPI_R() == 0);//等待SD卡回应
- CS = 1;
- SPI_W(0xff);//写入8个空脉冲
- if(Response_Write == 0x05){
- return 1;
- }else{
- return 0;
- };
- }
- //****************从sd卡读数据块
- void SD_Block_R(unsigned char* block, unsigned long address){
- unsigned int i;
- CS = 0;
- SD_Cmd(0x11, address, 0xff);//CMD11,数据块读写命令,
- while(SD_Response()!=0x00);//循环等待命令回应0x00
- while(SPI_R() != 0xfe); //0xfe为块读出的头, 后面紧跟512字节的数据块+2字节的CRC
- for(i=0; i<len ; i++){
- block[i] = SPI_R();//读数据
- };
- SPI_R();
- SPI_R();//两个字节的CRC。舍弃
- CS =1;
- SPI_R();//8个空脉冲
- }
- void main (void){
- unsigned char xdata block[len];
- int i;
- //初始化SD卡
- SD_Init();
- for(i=0;i<512;i++){
- block[i]=0xaa;
- };
- //从512000处写sd卡
- SD_Block_W(block, len*1000);
- //从512000处读sd卡
- SD_Block_R(block, len*1000);
- }