本帖最后由 小小毛 于 2020-12-22 22:29 编辑

一. 硬件平台
     硬件以ch32V103 开发板为平台,记录了tf卡驱动的实验过程。
硬件连接关系
SD采用SPI模式时的引脚连接关系
硬件系统设计图如下所示:
SPI模式的TF卡引脚连接关系图如下。

  
序号
  
TF卡引脚
CPU引脚
1
CS
PA3
2
SCK
PA5
3
MISO
PA6
4
MOSI
PA7
5
SD_IN
PA8

这里需要特别提醒,CS引脚默认是不焊接的,如果需要测试必须先短接后PA3引脚才能生效。在这里绕了好几天。
软件驱动
1)     IO口配置
这里默认配置SPI的CS引脚为软件控制。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE );
  •     GPIO_InitStructure.GPIO_Pin = GPIO_SCK;
  •     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  •     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  •     GPIO_Init( GPIOA, &GPIO_InitStructure);
  •     GPIO_InitStructure.GPIO_Pin = GPIO_MISO;
  •     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  •     GPIO_Init( GPIOA, &GPIO_InitStructure);
  •     GPIO_InitStructure.GPIO_Pin = GPIO_MOSI;
  •     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  •     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  •     GPIO_Init( GPIOA, &GPIO_InitStructure);
  •     GPIO_InitStructure.GPIO_Pin = GPIO_NSS;
  •     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  •     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  •     GPIO_Init( GPIOA, &GPIO_InitStructure);
  •     GPIO_InitStructure.GPIO_Pin = GPIO_SDCMD;
  •     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
  •     GPIO_Init( GPIOA, &GPIO_InitStructure);
  • 复制代码

    2)     SPI设备配置
    SPI设备采用SPI1,全双工2线方式,软件控制CS信号。
        SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
  •     SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
  •     SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
  •     SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //SPI_CPOL_High
  •     SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
  •     SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
  •     SPI_InitStructure.SPI_BaudRatePrescaler =SPI_BaudRatePrescaler_256;
  •     SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
  •     SPI_InitStructure.SPI_CRCPolynomial = 7;
  •     SPI_Init( SPI1, &SPI_InitStructure );
  •     SPI_Cmd( SPI1, ENABLE );
  • 复制代码

    3)     TF卡驱动
    向SD卡写入一个CMD或者ACMD指令的过程是这样的: 首先使CS为低电平,SD卡使能;其次在SD卡的Din写入指令;写入指令后还要附加8个填充时钟,是SD卡完成内部操作;之后在SD卡的Dout上接受回应;回应接受完毕使CS为低电平,再附加8个填充时钟。
    注意:在SD卡的Din没有数据写入时,应使Din保持高电平。
    l  SD卡复位:
    1.拉高CS,发送至少74个clk周期来使SD卡达到正常工作电压和进行同步
    2.选低CS,发送CMD0,需要收到回应0x01表示成功进入idle状态
    3.拉高CS,发送8个时钟
           实现代码:
      for(i=0;i<10;i++)
  •       SPI_ReadWrite_Byte(0XFF);//发送至少74个脉冲
  •     retry=20;
  •     do
  •     {
  •         r1=SD_SendCmd(CMD0,0,0x95);//进入IDLE状态
  •     }while((r1!=0X01) && retry--);
  • 复制代码
    l  SD卡初始化:
    复位成功后,SD卡就进入了SPI模式,接着应该进行初始化。发送CMD55+ACMD41完成初始化。
    时序图如下:
    程序代码实现:
         SD_SendCmd(CMD55,0,0X01);     //发送CMD55
         r1=SD_SendCmd(CMD41,0,0X01);  //发送CMD41

    l  SDK卡读操作
    操作流程:
    1.发送CMD18读,收到0x00表示成功
    2.连续读直到读到开始字节0xFE
    3.读512字节
    4.读两个CRC字节
    5.如果还想读下一扇区,重复2-4
    6.发送CMD12来停止读多块操作
    时序图如下:
    程序实现:
    r1=SD_SendCmd(CMD18,sector,0X01);
  •         do
  •         {
  •           r1=SD_RecvData(buf,512);
  •           buf+=512;
  •         }while(--cnt && r1==0);
  • SD_SendCmd(CMD12,0,0X01)
  • 复制代码
    l  SDK卡写操作
    操作走流程:
        1.发送CMD24(写单块),CMD25(写多块)收到0x00表示成功
        2.发送若干时钟
        3.发送写单块开始字节0xFE,写多块开始字节0xFC
        4.发送512个字节数据
        5.发送2字节CRC(可以均为0xff)
        6.连续读直到读到XXX00101表示数据写入成功
    7.继续读进行忙检测(读到0x00表示SD卡正忙),当读到0xff表示写操作完成
        8.如果想写下一扇区重复2-7步骤
        9.发送写多块停止字节0xFD来停止写操作
        10.进行忙检测直到读到0xFF
    时序图:
    程序代码实现:
      r1=SD_SendCmd(CMD25,sector,0X01);
  •        if(r1==0)
  •        {
  •            do
  •            {
  •                r1=SD_SendBlock(buf,0xFC);
  •                buf+=512;
  •            }while(--cnt && r1==0);
  •            r1=SD_SendBlock(0,0xFD);
  •        }
  • 复制代码
    测试效果
    成功的完成SD卡初始化,并且读到了SD卡容量。