模拟SPI驱动SD卡

使用新唐的SDIO例子修改为SDH1始终驱动不了SD卡,所以使用模拟的方式驱动SD卡

头文件:

#ifndef __SD_H
#define __SD_H

#include "NuMicro.h"

#define SD_CLK PG14

#define SD_CMD PA5
#define SD_D0 PG12
#define SD_D1 PA9
#define SD_D2 PG10
#define SD_D3 PG9
#define SD_NCD PE14

#define SD_CS SD_D3
#define SD_DI SD_CMD
#define SD_DO SD_D0


// 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
 					
#define GPIO_NSS    GPIO_Pin_12
#define GPIO_SCK    GPIO_Pin_13
#define GPIO_MISO   GPIO_Pin_14 
#define GPIO_MOSI   GPIO_Pin_15
#define CS_H        GPIO_SetBits(GPIOB,GPIO_NSS);
#define CS_L        GPIO_ResetBits(GPIOB,GPIO_NSS);
 
uint8_t SD_Initialize(void);
//uint8_t SPI_ReadWrite_Byte(u8 TxData);
//uint8_t SPI_Read_Byte(u8 txdata);
//uint8_t SD_SPI_ReadWriteByte(u8 data);
//void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler);
uint8_t SD_WaitReady(void);							
//uint8_t SD_GetResponse(u8 Response);					
//uint8_t SD_Init_Config(void);							
//uint8_t SD_ReadDisk2(u8*buf,u32 sector,u8 cnt);		
//uint8_t SD_WriteDisk2(u8*buf,u32 sector,u8 cnt);		
//uint32_t SD_GetSectorCount(void);   					
//uint8_t SD_GetCID(u8 *cid_data);                    
//uint8_t SD_GetCSD(u8 *csd_data);                   
// 
 
void SD_GPIO_init(void);

#endif

 .c文件

#include "sd.h"

static void delay_us(uint32_t us)
{
  uint32_t i=0xFF*us;
  while(i>0)i--;
}

void SD_GPIO_init(void)
{
  GPIO_SetMode(PG, BIT14, GPIO_MODE_OUTPUT);
  GPIO_SetMode(PE, BIT14, GPIO_MODE_INPUT);
  GPIO_SetMode(PA, BIT5, GPIO_MODE_QUASI);
  GPIO_SetMode(PA, BIT9, GPIO_MODE_QUASI);
  GPIO_SetMode(PG, BIT12, GPIO_MODE_QUASI);
  GPIO_SetMode(PG, BIT10, GPIO_MODE_QUASI);
  GPIO_SetMode(PG, BIT9, GPIO_MODE_QUASI);
  PA9=PA5=PE14=PG14=PG9=PG10=PG12 = 1;
//  SD_CLK = SD_CMD = SD_D0 = SD_D1 =SD_D2=SD_D3=SD_NCD =1;
}


/// 移植修改区函数 //

/**
 * SD卡SPI接口读写一个字节
 * @param  TxData 待写入的字节
 * @return        来自SPI的接收
 */
uint8_t SD_SPI_ReadWriteByte(uint8_t TxData)
{
    int i = 0;
    uint8_t RxData = 0;
    SD_CS =0;
    for(i = 7; i >= 0; i--)
    {
        SD_CLK = 0;
        if(TxData & (1 << i))
        {
           SD_DI = 1;
        }
        else
        {
            SD_DI = 0;
        }
        delay_us(1);
        SD_CLK = 1;
        RxData <<= 1;
        if(SD_DO){
          RxData |= 1;
        }
        delay_us(1);
    }
    SD_CS =1;
    return RxData;
}

/// SPI硬件层初始化
void SD_SPI_Init(void)
{
    SD_CLK = 0;    /* 时钟空闲为低电平 */
    SD_SPI_ReadWriteByte(0xFF);
    SD_CS =1;
}



//取消选择,释放SPI总线
void SD_DisSelect(void)
{
    SD_CS =1;
    SD_SPI_ReadWriteByte(0xff);//提供额外的8个时钟
}

/**
 * 选中SD卡并等待卡准备好
 * @return  0:成功  1:失败
 */
uint8_t SD_Select(void)
{
    SD_CS =0;
    if (SD_WaitReady() == 0)return 0; //等待成功
    SD_DisSelect();
    return 1;//等待失败
}

/**
 * 等待SD卡准备好
 * @return  0:成功  other:失败
 */
uint8_t SD_WaitReady(void)
{
    uint32_t t = 0;
    do
    {
        if (SD_SPI_ReadWriteByte(0XFF) == 0XFF)return 0; //OK
        t++;
    }
    while (t < 0XFFFFFF); //等待
    return 1;
}

/**
 * 等待SD卡回应
 * @param  Response 要得到的回应值
 * @return          0:成功  other:失败
 */
uint8_t SD_GetResponse(uint8_t Response)
{
    uint16_t Count = 0xFFF; //等待次数
    while ((SD_SPI_ReadWriteByte(0XFF) != Response) && Count)Count--; //等待得到准确的回应
    if (Count == 0)return MSD_RESPONSE_FAILURE; //得到回应失败
    else return MSD_RESPONSE_NO_ERROR;//正确回应
}

/**
 * 接收来自SD卡的一包数据
 * @param  buf 存放接收的数据
 * @param  len 接收的数据长度
 * @return     0:成功  other:失败
 */
uint8_t SD_RecvData(uint8_t*buf, uint16_t len)
{
    if (SD_GetResponse(0xFE))return 1; //等待SD卡发回数据起始令牌0xFE
    while (len--) //开始接收数据
    {
        *buf = SD_SPI_ReadWriteByte(0xFF);
        buf++;
    }
    //下面是2个伪CRC(dummy CRC)
    SD_SPI_ReadWriteByte(0xFF);
    SD_SPI_ReadWriteByte(0xFF);
    return 0;//读取成功
}
//向sd卡写入一个数据包的内容 512字节
//buf:数据缓存区
//cmd:指令
//返回值:0,成功;其他,失败;

/**
 * 向SD卡写入一扇区数据
 * @param  buf 待写入的数据,size=512
 * @param  cmd 指令
 * @return     0:成功  other:失败
 */
uint8_t SD_SendBlock(uint8_t*buf, uint8_t cmd)
{
    uint16_t t;
    if (SD_WaitReady())return 1; //等待准备失效
    SD_SPI_ReadWriteByte(cmd);
    if (cmd != 0XFD) //不是结束指令
    {
        for (t = 0; t < 512; t++)SD_SPI_ReadWriteByte(buf[t]); //提高速度,减少函数传参时间
        SD_SPI_ReadWriteByte(0xFF);//忽略crc
        SD_SPI_ReadWriteByte(0xFF);
        t = SD_SPI_ReadWriteByte(0xFF); //接收响应
        if ((t & 0x1F) != 0x05)return 2; //响应错误
    }
    return 0;//写入成功
}

/**
 * 向SD卡发送命令
 * @param  cmd 待发送的命令
 * @param  arg 参数
 * @param  crc crc校验值
 * @return     SD卡返回的响应值
 */
uint8_t SD_SendCmd(uint8_t cmd, uint32_t arg, uint8_t crc)
{
    uint8_t r1;
    uint8_t Retry = 0;
    SD_DisSelect();//取消上次片选
    if (SD_Select())return 0XFF; //片选失效
    //发送
    SD_SPI_ReadWriteByte(cmd | 0x40);//分别写入命令
    SD_SPI_ReadWriteByte(arg >> 24);
    SD_SPI_ReadWriteByte(arg >> 16);
    SD_SPI_ReadWriteByte(arg >> 8);
    SD_SPI_ReadWriteByte(arg);
    SD_SPI_ReadWriteByte(crc);
    if (cmd == CMD12)SD_SPI_ReadWriteByte(0xff); //Skip a stuff byte when stop reading
    //等待响应,或超时退出
    Retry = 0X1F;
    do
    {
        r1 = SD_SPI_ReadWriteByte(0xFF);
    }
    while ((r1 & 0X80) && Retry--);
    //返回状态值
    return r1;
}

/**
 * 查询SD卡的CID信息,包括制造商信息
 * @param  cid_data 存放CID数据,至少16字节
 * @return          0:成功  1:失败
 */
uint8_t SD_GetCID(uint8_t *cid_data)
{
    uint8_t r1;
    //发CMD10命令,读CID
    r1 = SD_SendCmd(CMD10, 0, 0x01);
    if (r1 == 0x00)
    {
        r1 = SD_RecvData(cid_data, 16); //接收16个字节的数据
    }
    SD_DisSelect();//取消片选
    if (r1)return 1;
    else return 0;
}

/**
 * 查询SD卡CID信息,包括制造商信息
 * @param  csd_data 存放CID信息,至少16字节
 * @return          0:成功  1:失败
 */
uint8_t SD_GetCSD(uint8_t *csd_data)
{
    uint8_t r1;
    r1 = SD_SendCmd(CMD9, 0, 0x01); //发CMD9命令,读CSD
    if (r1 == 0)
    {
        r1 = SD_RecvData(csd_data, 16); //接收16个字节的数据
    }
    SD_DisSelect();//取消片选
    if (r1)return 1;
    else return 0;
}
/**
 * 获取SD卡扇区数
 * @return  0:获取出错  other:SD卡扇区数
 */
uint32_t SD_GetSectorCount(void)
{
    uint8_t csd[16];
    uint32_t Capacity;
    uint8_t n;
    uint16_t csize;
    //取CSD信息,如果期间出错,返回0
    if (SD_GetCSD(csd) != 0) return 0;
    //如果为SDHC卡,按照下面方式计算
    if ((csd[0] & 0xC0) == 0x40)	 //V2.00的卡
    {
        csize = csd[9] + ((uint16_t)csd[8] << 8) + 1;
        Capacity = (uint32_t)csize << 10;//得到扇区数
    }
    else //V1.XX的卡
    {
        n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
        csize = (csd[8] >> 6) + ((uint16_t)csd[7] << 2) + ((uint16_t)(csd[6] & 3) << 10) + 1;
        Capacity = (uint32_t)csize << (n - 9); //得到扇区数
    }
    return Capacity;
}

/// SD卡进入闲置状态
uint8_t SD_Idle_Sta(void)
{
    uint16_t i;
    uint8_t retry;
    for (i = 0; i < 0xf00; i++); //纯延时,等待SD卡上电完成
    //先产生>74个脉冲,让SD卡自己初始化完成
    for (i = 0; i < 10; i++)SD_SPI_ReadWriteByte(0xFF);
    //-----------------SD卡复位到idle开始-----------------
    //循环连续发送CMD0,直到SD卡返回0x01,进入IDLE状态
    //超时则直接退出
    retry = 0;
    do
    {
        //发送CMD0,让SD卡进入IDLE状态
        i = SD_SendCmd(CMD0, 0, 0x95);
        retry++;
    }
    while ((i != 0x01) && (retry < 200));
    //跳出循环后,检查原因:初始化成功?or 重试超时?
    if (retry == 200)return 1; //失败
    return 0;//成功
}
uint8_t SD_Type;
/// 初始化SD卡
uint8_t SD_Initialize(void)
{
    uint8_t r1;      // 存放SD卡的返回值
    uint16_t retry;  // 用来进行超时计数
    uint8_t buf[4];
    uint16_t i;

    SD_SPI_Init();		//初始化IO
    for (i = 0; i < 10; i++)SD_SPI_ReadWriteByte(0XFF); //发送最少74个脉冲
    retry = 20;
    do
    {
        r1 = SD_SendCmd(CMD0, 0, 0x95); //进入IDLE状态
    }
    while ((r1 != 0X01) && retry--);
    SD_Type = 0; //默认无卡
    if (r1 == 0X01)
    {
        if (SD_SendCmd(CMD8, 0x1AA, 0x87) == 1) //SD V2.0
        {
            for (i = 0; i < 4; i++)buf[i] = SD_SPI_ReadWriteByte(0XFF);	//Get trailing return value of R7 resp
            if (buf[2] == 0X01 && buf[3] == 0XAA) //卡是否支持2.7~3.6V
            {
                retry = 0XFFFE;
                do
                {
                    SD_SendCmd(CMD55, 0, 0X01);	//发送CMD55
                    r1 = SD_SendCmd(CMD41, 0x40000000, 0X01); //发送CMD41
                }
                while (r1 && retry--);
                if (retry && SD_SendCmd(CMD58, 0, 0X01) == 0) //鉴别SD2.0卡版本开始
                {
                    for (i = 0; i < 4; i++)buf[i] = SD_SPI_ReadWriteByte(0XFF); //得到OCR值
                    if (buf[0] & 0x40)SD_Type = SD_TYPE_V2HC; //检查CCS
                    else SD_Type = SD_TYPE_V2;
                }
            }
        }
        else //SD V1.x/ MMC	V3
        {
            SD_SendCmd(CMD55, 0, 0X01);		//发送CMD55
            r1 = SD_SendCmd(CMD41, 0, 0X01);	//发送CMD41
            if (r1 <= 1)
            {
                SD_Type = SD_TYPE_V1;
                retry = 0XFFFE;
                do //等待退出IDLE模式
                {
                    SD_SendCmd(CMD55, 0, 0X01);	//发送CMD55
                    r1 = SD_SendCmd(CMD41, 0, 0X01); //发送CMD41
                }
                while (r1 && retry--);
            }
            else
            {
                SD_Type = SD_TYPE_MMC; //MMC V3
                retry = 0XFFFE;
                do //等待退出IDLE模式
                {
                    r1 = SD_SendCmd(CMD1, 0, 0X01); //发送CMD1
                }
                while (r1 && retry--);
            }
            if (retry == 0 || SD_SendCmd(CMD16, 512, 0X01) != 0)SD_Type = SD_TYPE_ERR; //错误的卡
        }
    }
    SD_DisSelect();//取消片选
    if (SD_Type)return 0;
    else if (r1)return r1;
    return 0xaa;//其他错误
}

/**
 * 读取SD卡一个扇区
 * @param  buf    存放读取的数据
 * @param  sector 扇区编号
 * @param  cnt    要读取的扇区个数
 * @return        0:成功  other:失败
 */
uint8_t SD_ReadDisk(uint8_t*buf, uint32_t sector, uint8_t cnt)
{
    uint8_t r1;
    if (SD_Type != SD_TYPE_V2HC)sector <<= 9; //转换为字节地址
    if (cnt == 1)
    {
        r1 = SD_SendCmd(CMD17, sector, 0X01); //读命令
        if (r1 == 0) //指令发送成功
        {
            r1 = SD_RecvData(buf, 512); //接收512个字节
        }
    }
    else
    {
        r1 = SD_SendCmd(CMD18, sector, 0X01); //连续读命令
        do
        {
            r1 = SD_RecvData(buf, 512); //接收512个字节
            buf += 512;
        }
        while (--cnt && r1 == 0);
        SD_SendCmd(CMD12, 0, 0X01);	//发送停止命令
    }
    SD_DisSelect();//取消片选
    return r1;//
}

/**
 * SD卡写一个扇区的数据
 * @param  buf    存放待写入的数据
 * @param  sector 扇区编号
 * @param  cnt    要写入的扇区个数
 * @return        0:成功  other:失败
 */
uint8_t SD_WriteDisk(uint8_t*buf, uint32_t sector, uint8_t cnt)
{
    uint8_t r1;
    if (SD_Type != SD_TYPE_V2HC)sector *= 512; //转换为字节地址
    if (cnt == 1)
    {
        r1 = SD_SendCmd(CMD24, sector, 0X01); //读命令
        if (r1 == 0) //指令发送成功
        {
            r1 = SD_SendBlock(buf, 0xFE); //写512个字节
        }
    }
    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
            {
                r1 = SD_SendBlock(buf, 0xFC); //接收512个字节
                buf += 512;
            }
            while (--cnt && r1 == 0);
            r1 = SD_SendBlock(0, 0xFD); //接收512个字节
        }
    }
    SD_DisSelect();//取消片选
    return r1;//
}

 SD卡SPI驱动时序讲解:

STM32开发_利用SPI协议读写SD卡、介绍SD卡SPI时序_DS小龙哥的博客-CSDN博客

收集的一些SD卡资料:

https://download.csdn.net/download/klp1358484518/87238010

猜你喜欢

转载自blog.csdn.net/klp1358484518/article/details/128160094
今日推荐