打印
[STM32F1]

通过SPI方式读写SD卡

[复制链接]
376|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
sesefadou|  楼主 | 2023-12-31 10:00 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

sd.h
#ifndef __SD_H
#define __SD_H  
#include "sys.h"

#define SD_CS PAout(15)

// SD卡类型定义  
#define SD_TYPE_ERR     0X00
#define SD_TYPE_MMC     0X01
#define SD_TYPE_V1      0X02
#define SD_TYPE_V2      0X04
#define SD_TYPE_V2HC    0X06

// SD卡指令表            
#define CMD0    0       //卡复位
#define CMD1    1
#define CMD8    8       //命令8 ,SEND_IF_COND
#define CMD9    9       //命令9 ,读CSD数据
#define CMD10   10      //命令10,读CID数据
#define CMD12   12      //命令12,停止数据传输
#define CMD16   16      //命令16,设置SectorSize 应返回0x00
#define CMD17   17      //命令17,读sector
#define CMD18   18      //命令18,读Multi sector
#define CMD23   23      //命令23,设置多sector写入前预先擦除N个block
#define CMD24   24      //命令24,写sector
#define CMD25   25      //命令25,写Multi sector
#define CMD41   41      //命令41,应返回0x00
#define CMD55   55      //命令55,应返回0x01
#define CMD58   58      //命令58,读OCR信息
#define CMD59   59      //命令59,使能/禁止CRC,应返回0x00

//数据写入回应字意义
#define MSD_DATA_OK                0x05
#define MSD_DATA_CRC_ERROR         0x0B
#define MSD_DATA_WRITE_ERROR       0x0D
#define MSD_DATA_OTHER_ERROR       0xFF

//SD卡回应标记字
#define MSD_RESPONSE_NO_ERROR      0x00
#define MSD_IN_IDLE_STATE          0x01
#define MSD_ERASE_RESET            0x02
#define MSD_ILLEGAL_COMMAND        0x04
#define MSD_COM_CRC_ERROR          0x08
#define MSD_ERASE_SEQUENCE_ERROR   0x10
#define MSD_ADDRESS_ERROR          0x20
#define MSD_PARAMETER_ERROR        0x40
#define MSD_RESPONSE_FAILURE       0xFF

void SD_Read_Sectorx(u32 sec);
u8 SD_Init(void);
u8 SD_WaitReady(void);
u8 SD_GetResponse(u8 Response);
u32 SD_GetSectorCount(void);
u8 SD_GetCID(u8 *cid_data);
u8 SD_GetCSD(u8 *csd_data);
u8 SD_ReadDisk(u8*buf,u32 sector,u8 cnt);
u8 SD_WriteDisk(u8*buf,u32 sector,u8 cnt);

#endif




sd.c
#include "SD.h"
#include "Basic_cfg.h"

u8 SD_Type = 0;
/***************************/
//SD读写函数,便于替换SPI接口
/***************************/
u8 SD_ReadWriteByte(u8 TxData)
{
    SPI3_ReadWriteByte(TxData);
}

/***************************/
//忙检测,等待SD卡
/***************************/
u8 SD_WaitReady(void)
{
    u32 t=0;
    u8 reg;
    for(t=0; t<0xffff; t++)
    {
        reg=SD_ReadWriteByte(0XFF);//获取返回值
        if(reg==0XFF)
            break;
    }
    if(t<0xffffff)
        return 0;
    else
        return 1;
}
/***************************/
//取消卡选择
/***************************/
void SD_DisSelect(void)
{
    SD_CS=1;
    SD_ReadWriteByte(0xff);//提供额外的8个时钟
}
/***************************/
//卡选择,选择SD卡
/***************************/
u8 SD_Select(void)
{
    SD_CS=0;
    if(SD_WaitReady()==0)    //等待成功
        return 0;
    SD_DisSelect();    //等待失败
    return 1;
}
/***************************/
//向SD卡发送一个命令
//CMD:命令
//arg:命令参数
//crc:校验
/***************************/
u8 SD_SendCmd(u8 cmd, u32 arg, u8 crc)
{
    u8 r1=0;
    u8 Retry=0;
    SD_DisSelect();    //取消上次片选

    if(SD_Select())    //片选失效
    {
        return 0XFF;
    }
    //发送
    //分别写入命令
    SD_ReadWriteByte(cmd | 0x40);
    SD_ReadWriteByte(arg >> 24);
    SD_ReadWriteByte(arg >> 16);
    SD_ReadWriteByte(arg >> 8);
    SD_ReadWriteByte(arg);
    SD_ReadWriteByte(crc);
    if(cmd==CMD12)SD_ReadWriteByte(0xff);
    //等待响应,或超时退出
    Retry=0X1F;
    do
    {
        r1=SD_ReadWriteByte(0xFF);
    }
    while((r1&0X80) && Retry--);
    //返回状态值
    return r1;
}
/***************************/
//等待SD卡回应
//Response:要得到的回应值
//返回值:0,成功得到了该回应值
//        其他,得到回应值失败
/***************************/
u8 SD_GetResponse(u8 Response)
{
    //等待次数
    u16 Count=0xFFFF;
    //等待得到准确的回应
    while ((SD_ReadWriteByte(0XFF)!=Response)&&Count)
    {
        Count--;
    }
    if (Count==0)
    {
        //得到回应失败
        return MSD_RESPONSE_FAILURE;
    }
    else
    {
        //正确回应
        return MSD_RESPONSE_NO_ERROR;
    }
}
//初始化SD卡
u8 SD_Init(void)
{
    u8 r1=0;          // 存放SD卡的返回值
    u16 retry;    // 用来进行超时计数
    u8 buf[4];
    u16 i;


    SPI3_Init();    //初始化SPI3

    SPI3_SetSpeed(SPI_BAUDRATEPRESCALER_256);    //配置为低速度模式
    for(i=0; i<10; i++)    //发送至少74个脉冲
    {
        SD_ReadWriteByte(0xff);
    }
    retry=20;
    do
    {
        //进入IDLE状态
        r1=SD_SendCmd(CMD0,0,0x95);
    }
    while((r1!=0X01) && (retry--));
    //默认无卡
    SD_Type=0;
    //识别卡类型
    if(r1==0X01)
    {
        //SD V2.0
        if(SD_SendCmd(CMD8,0x1AA,0x87)==1)
        {
            //Get trailing return value of R7 resp
            for(i=0; i<4; i++)
                buf[i]=SD_ReadWriteByte(0XFF);
            //卡是否支持2.7~3.6V
            if(buf[2]==0X01&&buf[3]==0XAA)
            {
                retry=0XFFFE;
                do
                {
                    //发送CMD55
                    SD_SendCmd(CMD55,0,0X01);
                    //发送CMD41
                    r1=SD_SendCmd(CMD41,0x40000000,0X01);
                }
                while(r1&&retry--);
                //鉴别SD2.0卡版本开始
                if(retry&&SD_SendCmd(CMD58,0,0X01)==0)
                {
                    //得到OCR值
                    for(i=0; i<4; i++)
                        buf[i]=SD_ReadWriteByte(0XFF);
                    //检查CCS
                    if(buf[0]&0x40)
                    {
                        SD_Type=SD_TYPE_V2HC;
                    }
                    else
                    {
                        SD_Type=SD_TYPE_V2;
                    }
                }
            }
        }
    }
    //SD V1.x/ MMC    V3
    else
    {
        //发送CMD55
        SD_SendCmd(CMD55,0,0X01);
        //发送CMD41
        r1=SD_SendCmd(CMD41,0,0X01);
        if(r1<=1)
        {
            SD_Type=SD_TYPE_V1;
            retry=0XFFFE;
            //等待退出IDLE模式
            do
            {
                //发送CMD55
                SD_SendCmd(CMD55,0,0X01);
                //发送CMD41
                r1=SD_SendCmd(CMD41,0,0X01);
            } while(r1&&retry--);
        }
        //MMC卡不支持CMD55+CMD41识别
        else
        {
            //MMC V3
            SD_Type=SD_TYPE_MMC;
            retry=0XFFFE;
            //等待退出IDLE模式
            do
            {
                //发送CMD1
                r1=SD_SendCmd(CMD1,0,0X01);
            } while(r1&&retry--);
        }
        //错误的卡
        if(retry==0||SD_SendCmd(CMD16,512,0X01)!=0)
        {
            SD_Type=SD_TYPE_ERR;
        }
    }
    //取消片选
    SD_DisSelect();
    //配置为高速度模式
    SPI3_SetSpeed(SPI_BAUDRATEPRESCALER_4);
    if(SD_Type)
    {
        return 0;
    }
    else if(r1)
    {
        return r1;
    }
    //其他错误
    return 0xaa;
}
/***************************/
//从sd卡读取一个数据包的内容
//buf:数据缓存区
//len:要读取的数据长度.
//返回值:0,成功;其他,失败;   
/***************************/
u8 SD_RecvData(u8*buf,u16 len)
{                    
    //等待SD卡发回数据起始令牌0xFE
    if(SD_GetResponse(0xFE))
    {
        return 1;
    }
    //开始接收数据
  while(len--)
  {
    *buf=SD_ReadWriteByte(0xFF);
    buf++;
  }
  //下面是2个伪CRC(dummy CRC)
  SD_ReadWriteByte(0xFF);
  SD_ReadWriteByte(0xFF);        
  //读取成功                                                      
  return 0;
}
/***************************/
//获取SD卡的CSD信息,包括容量和速度信息
//输入:u8 *cid_data(存放CID的内存,至少16Byte)        
//返回值:0:NO_ERR
//         1:错误   
/***************************/
u8 SD_GetCSD(u8 *csd_data)
{
  u8 r1;     
    //发CMD9命令,读CSD
  r1=SD_SendCmd(CMD9,0,0x01);
  if(r1==0)
    {
        //接收16个字节的数据
    r1=SD_RecvData(csd_data, 16);
  }
    //取消片选
    SD_DisSelect();
    if(r1)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}  
/***************************/
//获取SD卡的总扇区数(扇区数)   
//返回值:0: 取容量出错
//       其他:SD卡的容量(扇区数/512字节)
//每扇区的字节数必为512,因为如果不是512,则初始化不能通过.
/***************************/
u32 SD_GetSectorCount(void)
{
    u8 csd[16];
    u32 Capacity;  
    u8 n;
        u16 csize;                          
        //取CSD信息,如果期间出错,返回0
    if(SD_GetCSD(csd)!=0)
        {
            return 0;        
        }
    //如果为SDHC卡,按照下面方式计算
        //V2.00的卡
    if((csd[0]&0xC0)==0x40)     
    {   
            csize = csd[9] + ((u16)csd[8] << 8) + 1;
            //得到扇区数   
            Capacity = (u32)csize << 10;            
    }
        //V1.XX的卡
        else
    {   
            n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
            csize = (csd[8] >> 6) + ((u16)csd[7] << 2) + ((u16)(csd[6] & 3) << 10) + 1;
            //得到扇区数
            Capacity= (u32)csize << (n - 9);  
    }
    return Capacity;
}

u8 SD_ReadDisk(u8*buf,u32 sector,u8 cnt)
{
    u8 r1;
    //转换为字节地址
    if(SD_Type!=SD_TYPE_V2HC)
    {
        sector <<= 9;
    }
    if(cnt==1)
    {
        //读命令
        r1=SD_SendCmd(CMD17,sector,0X01);
        //指令发送成功
        if(r1==0)
        {
            //接收512个字节     
            r1=SD_RecvData(buf,512);  
        }
    }
    else
    {
        //连续读命令
        r1=SD_SendCmd(CMD18,sector,0X01);
        do
        {
            //接收512个字节     
            r1=SD_RecvData(buf,512);
            buf+=512;  
        }
        while(--cnt && r1==0);     
        //发送停止命令
        SD_SendCmd(CMD12,0,0X01);   
    }   
    //取消片选
    SD_DisSelect();
    return r1;
}
/***************************/
//读取SD卡的指定扇区的内容,并通过串口1输出
//sec:扇区物理地址编号
/***************************/
void SD_Read_Sectorx(u32 sec)
{
    //存储扇区数据
    u8 buf[512];
    u16 i;
   
    //读取0扇区的内容
    if(SD_ReadDisk(buf,sec,1)==0)   
    {   
        printf("SECTOR 0 DATA:\r\n");
        //打印sec扇区数据  
        for(i=0;i<512;i++)
            printf("%X ",buf[i]);         
        printf("\r\nDATA ENDED\r\n");
    }
}
/***************************/
//获取SD卡的CID信息,包括制造商信息
//输入: u8 *cid_data(存放CID的内存,至少16Byte)      
//返回值:0:NO_ERR
//         1:错误   
/***************************/
u8 SD_GetCID(u8 *cid_data)
{
  u8 r1;      
  //发CMD10命令,读CID
  r1=SD_SendCmd(CMD10,0,0x01);
  if(r1==0x00)
    {
        //接收16个字节的数据     
        r1=SD_RecvData(cid_data,16);
  }
    //取消片选
    SD_DisSelect();
    if(r1)
        return 1;
    else
        return 0;
}
/***************************/
//向sd卡写入一个数据包的内容 512字节
//buf:数据缓存区
//cmd:指令
//返回值:0,成功;其他,失败;   
/***************************/
u8 SD_SendBlock(u8*buf,u8 cmd)
{   
    u16 t;               
    //等待准备失效
    if(SD_WaitReady())
    {
        return 1;
    }
    SD_ReadWriteByte(cmd);
    //不是结束指令
    if(cmd!=0XFD)
    {
        //提高速度,减少函数传参时间
        for(t=0;t<512;t++)
        {
            SD_ReadWriteByte(buf[t]);
        }
        //忽略crc
      SD_ReadWriteByte(0xFF);
      SD_ReadWriteByte(0xFF);
        //接收响应
        t=SD_ReadWriteByte(0xFF);
        if((t&0x1F)!=0x05)
        {
            //响应错误        
            return 2;        
        }            
    }                                 
    //写入成功                                                      
  return 0;
}
/***************************/
//写SD卡
//buf:数据缓存区
//sector:起始扇区
//cnt:扇区数
//返回值:0,ok;其他,失败.
/***************************/
u8 SD_WriteDisk(u8*buf,u32 sector,u8 cnt)
{
    u8 r1;
    //转换为字节地址
    if(SD_Type!=SD_TYPE_V2HC)
    {
        sector *= 512;
    }
    if(cnt==1)
    {
        //读命令
        r1=SD_SendCmd(CMD24,sector,0X01);
        //指令发送成功
        if(r1==0)
        {
            //写512个字节      
            r1=SD_SendBlock(buf,0xFE);
        }
    }
    else
    {
        if(SD_Type!=SD_TYPE_MMC)
        {
            SD_SendCmd(CMD55,0,0X01);   
            //发送指令   
            SD_SendCmd(CMD23,cnt,0X01);
        }
        //连续读命令
         r1=SD_SendCmd(CMD25,sector,0X01);
        if(r1==0)
        {
            do
            {
                //接收512个字节     
                r1=SD_SendBlock(buf,0xFC);
                buf+=512;  
            }
            while(--cnt && r1==0);
            //接收512个字节
            r1=SD_SendBlock(0,0xFD);
        }
    }   
    //取消片选
    SD_DisSelect();
    return r1;
}   


使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

6

主题

1564

帖子

0

粉丝