Simulate SPI driver SD card

Using Nuvoton's SDIO example and modifying it to SDH1 can never drive the SD card, so we use simulation to drive the SD card.

head File:

#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 file

#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 card SPI driver timing explanation:

STM32 development_Using SPI protocol to read and write SD cards, introducing SD card SPI timing_DS Xiaolongge's blog-CSDN blog

Some SD card information collected:

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

Guess you like

Origin blog.csdn.net/klp1358484518/article/details/128160094