打印
[应用相关]

基于SPI模式的SD卡驱动程序

[复制链接]
1726|3
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
huangcunxiake|  楼主 | 2017-1-14 00:14 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
u8 SD_SendCommand(u8 cmd,u32 arg, u8 crc)
{
           u8 r1;       
           unsigned int Retry = 0;
       
           SD_CS_DISABLE();
           SPI_ReadWriteByte(0xff);//提高兼容性,如果没有这里,有些SD卡可能不支持
           SD_CS_ENABLE();//片选端置低,选中SD卡
       
           /*发送命令序列*/
           SPI_ReadWriteByte(cmd | 0x40);
           SPI_ReadWriteByte((u8)(arg>>24));//参数[31..24]
           SPI_ReadWriteByte((u8)(arg>>16));//参数[23..16]
           SPI_ReadWriteByte((u8)(arg>>8)); //参数[15..8]
           SPI_ReadWriteByte((u8)arg);      //参数[7..0]
           SPI_ReadWriteByte(crc);

       //等待响应,或超时退出
       while((r1 = SPI_ReadWriteByte(0xff)==0xff))
        {
               Retry++;
               if(Retry>800)break;//根据实验测得,最好重试次数多点
         }
        //关闭片选
        SD_CS_DISABLE();
        //在总线上额外增加8个时钟,让SD卡完成剩下的工作
        SPI_ReadWriteByte(0xff);

       //返回状态值
       return r1;

  }
/************************************************************************
*SD卡初始化函数
*延时等待SD卡上电完成
*给至少74个脉冲让SD卡自己初始化完成
*持续发送发送CMD0接收0x01(可以不接受)SD回应进入Idle(空闲)状态
*
************************************************************************/

u8 SD_Init(void)
{
          u16 i;         //用来循环计数
          u8 r1;              //存放SD卡的返回值
          u16 retry;         //用来进行超时计数
          u8 buff[6];
       
          SPI_ControlLine();  //SPI的配置初始化
          SPI_SetSpeed(SPI_SPEED_LOW);
          SD_CS_ENABLE();
       
          //纯延时,等待SD卡上电完成
          for(i=0;i<0xf00;i++);
       
          //先产生至少74个脉冲,让SD卡自己初始化完成
          for(i=0;i<10;i++)
          {
            SPI_ReadWriteByte(0xFF); //80clks
          }
       
          //-------------------SD卡复位到idle开始-------------------
          //循环连续发送CMD0,直到SD卡返回0x01,进入IDLE状态
          //超时则直接退出
          retry = 0;
      do
      {
          //发送CMD0,让SD卡进入IDLE状态
              ri = SD_SendCommand(CMD0,0,0x95);
              retry++;
           }while((r1 != 0x01)&& (retry<200));
           //跳出循环后,检查原因: 初始化成功?or重试超时?
           if(retry==200) return 1;//超时返回1
         

           //--------------SD卡复位到idle结束----------
           //获取卡片的SD版本信息
           r1 = SD_SendCommand_NoDeassert(CMD8,0x1aa,0x87);
           //如果卡片版本信息是V1.0版本的,即r1=0x05,则进行以下初始化
           if(r1==0x05)
            {
               //设置卡类型为SDV1.0,如果后面检测为MMC卡,再修改为MMC
               SD_Type = SD_TYPE_V1;
               //如果是V1.0卡,CMD8指令后没有后续数据
               //片选置高,结束本次命令
               SD_CS_DISABLE();
               //多发8个clk,让SD结束后续操作       
               SPI_ReadWriteByte(0xff);
               //----------------SD卡、MMC卡初始化开始------------------
               //发卡初始化指令CMD55+ACMD41
               //如果有应答,说明是SD卡,且初始化完成
               //没有回应,说明是MMC卡,额外进行相应初始化
               retry = 0;
               do
                {
                   //先发CMD55,应返回0x01,否则出错
                       r1 = SD_SendCommand(CMD55,0,0);
                       if(r1 !=0x01)
                       return r1;
                       //得到正确响应后,发ACMD41,应得到返回值0x00,佛则重试400次
                       r1 = SD_SendCommand(ACMD41,0,0);
                       retry++;
                      }while((r1!=0x00)&&(retry<400));
                      //判断是超时还是得到正确回应
                      // 若有回应:是SD卡:没有回应:是MMC卡

                      //---------------MMC卡额外初始化操作开始-------------
                     if(retry==400)
                      {
                         retry =0;
                             //发送MMC卡初始化命令(没有测试)
                             do
                              {
                                 r1=SD_SendCommand(CMD1,0,0);
                                 retry++;
                               }while(r1!=0x00)&&(retry<400);
                               if(retry==400)return 1;//MMC卡初始化超时
                               //写入卡类型
                               SD_Type=SD_TYPE_MMC;
                               }
                               //----------MMC卡额外初始化操作结束---------------
                               //设置SPI为高速模式
                               SPI_SetSpeed(SPI_SPEED_HIGH);
                               SPI_ReaadWriteByte(0xff);

                               //禁止CRC校验
                               r1=SD_SendCommand(CMD59,0,0x95);
                               if(r1!=0x00)return r1;//命令错误,返回r1
                               //-------------SD卡、MMC卡初始化结束-------------

                           }//SD卡为V1.0版本的初始化结束
                           //下面是V2.0卡的初始化
                               //其中需要读取OCR数据,判断是SD2.0还是SD2.0HC
                         else if(r1==0x01)
                          {
                              //v2.0的卡,CMD8命令后会传回4字节的数据,要跳过在结束本命令
                              buff[0]=SPI_ReadWriteByte(0xff);//shoule be 0x00
                              buff[1]=SPI_ReadWriteByte(0xff);//shoule be 0x00
                              buff[2]=SPI_ReadWriteByte(0xff);//shoule be 0x11
                              SD_CS_DISABLE();
                              SPI_ReadWriteByte(0xff);//the next 8 clocks
                              //判断该卡是否支持2.7-3.6的电压范围
                              //if(buff[2]==0x01&&buff[3]==0xaa)//如不判断,让其支持的卡更多
                              //{
                              retry = 0;
                                  //发卡初始化指令CMD55+ACMD41
                                  do
                                   {
                                      r1=SD_SendCommand(CMD55,0,0);
                                          if(r1!=0x01)return r1;
                                          r1=SD_SendCommand(ACMD41,0x40000000,1);
                                          if(retry>200)return r1;//超时则返回r1状态
                                          }while(r1!=0);
                                          //初始化指令发送完成,接下来获取OCR信息
                                          //----------鉴别SD2.0卡版本开始------------
                                          r1=SD_SendCommand_NoDeassert(CMD58,0,0);
                                          if(r1!=0x00)return r1;//如果命令没有返回正确应答,直接退出返回应答
                                          //读OCR指令发出后,紧接着是4字节的OCR信息
                                          buff[0]=SPI_ReadWriteByte(0xff);
                                          buff[1]=SPI_ReadWriteByte(0xff);
                                          buff[2]=SPI_ReadWriteByte(0xff);
                                          buff[3]=SPI_ReadWriteByte(0xff);

                                        //OCR接收完成,片选置高
                                          SD_CS_DISABLE();
                                          SPI_ReadWriteByte(0xff);

                                          //检查接收到的OCR中的bit30位(CCS),确定其为SD2还是SDHC
                                          //如果CCS=1:SDHC   CCS=0:   SD2.0
                                          if(buff[0]&0x40)SD_Type = SD_TYPE_V2HC;//检查CCS
                                          else SD_Type=SD_TYPE_V2;
                                          //------------------鉴别SD2.0卡版本结束------------------
                                          //设置SPI为高速模式
                                          SPI_SetSpeed(1);
                                         }
                                         return r1

}

/******************************************************************
*********************SPI模式GPIO端口设置***************************
**************PA5=SCK、PA6=MISO、PA7=MOSI、PA4=CS******************
******************************************************************/

void SPI_ControlLine(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC ,ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);

    /*configuration SPI1 pins:,SCK,MISO and MOSI*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
       
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //MISO应该要初始化为上拉输入
    GPIO_Init(GPIOA, &GPIO_InitStructure);
       
        /*configration PA4 Pin:  CS Pin*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
}

/**************************************************************************
***********************SPI通信模式初始化***********************************
***********************设置高速或低速模式**********************************
**************************************************************************/

void SPI_SetSpeed(u8 SpeedSet)
{
   /* Initialize the SPI1 according to the SPI_InitStructure members */
   SPI_InitTypeDef SPI_InitStructure;
   if(SpeedSet==SPI_SPEED_HIGH)//高速
   {
      SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
      SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
      SPI_InitStructure.SPI_DatSize = SPI_DatSize_8b;
      SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
      SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
      SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
      SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
      SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
      SPI_InitStructure.SPI_CRCPolynomial = 7;
   
   
   SPI_Init(SPI1, &SPI_InitStructure);
   /*SPI1 enable*/
   SPI_Cmd(SPI1,ENABLE);
   }
   else//低速
     {  
                SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
        SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
        SPI_InitStructure.SPI_DatSize = SPI_DatSize_8b;
        SPI_InitStructure.SPI_CPOL = 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);
                /*SPI1 enable*/
                SPI_Cmd(SPI1,ENABLE);
      }
}

/************************************************************
**************************发送命令***************************
************************************************************/

u8 SD_SendCommand_NoDeassert(u8 cmd, u32 arg,u8 crc)
{
    unsigned char r1;
    unsigned int Retry = 0;

    SD_CS_DISABLE();
    SPI_ReadWriteByte(0xff);//提高兼容性,如果没有这里,有些SD卡可能不支持
    SD_CS_ENABLE();//片选端置低,选中SD卡

    /*发送命令序列*/
    SPI_ReadWriteByte(cmd | 0x40);
    SPI_ReadWriteByte((u8)(arg>>24));//参数[31..24]
    SPI_ReadWriteByte((u8)(arg>>24));//参数[23..16]
    SPI_ReadWriteByte((u8)(arg>>24));//参数[7..0]
    SPI_ReadWriteByte(crc);

    //等待响应,或超时退出
    while((r1 = SPI_ReadWriteByte(0xff))==0xff)
    {
           Retry++;
           if(Retry>600)break;//根据实验测得,最好重试次数多点
     }
         //返回响应值
         return r1;
}

/**************************************************************
************************向SD卡写一个块*************************
**************************************************************/

u8 SD_WriteSingleBlock(u32 sector,const u8 *data)
{
        u8 r1;
        u16 i;
        u16 retry;

        //设置为高速模式
        SPI_SetSpeed(SPI_SPEED_LOW);

         //如果不是SDHC,给定的是sector地址,将其转换成byte地址
         if(SD_Type!=SD_TYPE_V2HC)
         {
             sector = sector<<9;//512*sector即物理扇区的边界对齐地址
         }

         r1 = SD_SendCommand(CMD24,sector,0x00);
         if(r1 !=0x00)
         {
            return r1;//应答不正确,直接返回
         }

         //开始准备数据传输
         SD_CS_ENABLE();
         //先放3个空数据,等待SD卡准备好
         SPI_ReadWriteByte(0xff);
         SPI_ReadWriteByte(0xff);
         SPI_ReadWriteByte(0xff);
         //放起始令牌0xfe
         SPI_ReadWriteByte(0xfe);

         //发一个sector的数据
         for(i=0;i<512;i++)
         {
             SPI_ReadWriteByte(*data++);
         }
         //发2个Byte的dummy CRC
         SPI_ReadWriteByte(0xff);
         SPI_ReadWriteByte(0xff);

     //等待SD卡应答
         r1 = SPI_ReadWriteByte(0xff);
         if((r1&0x1f)!=0x05)
         {
             SD_CS_DISABLE();
                 return r1;
          }

         //等待操作完成
         retry = 0;
         while(!SPI_ReadWriteByte(0xff))//卡自编程时,数据线被拉低
         {
             retry++;
                 if(retry>65534)       //如果长时间写入没有完成,报错退出
                 {
                         SD_CS_DISABLE();
                         return 1;         //写入超时返回1
                  }
           }

         //写入完成,片选置1
         SD_CS_DISABLE();
         SPI_ReadWriteByte(0xff);

         return 0;
}

/********************************************************
******************从SD中读取一个块***********************
********************************************************/

u8 SD_ReadSingleBlock(u32 sector, u8 *buffer)
{
   u8 r1;
   //设置为高速模式
   SPI_SetSpeed(SPI_SPEED_LOW);

   if(SD_Type!=SD_TYPE_V2HC)
    {
           sector = sector<<9;//512*sector即物理扇区的边界对齐地址
         }

        //如果不是SDHC,将sector地址转成byte地址
        //sector = sector<<9;

        r1 = SD_SendCommand(CMD17,sector,1);//读命令

        if(r1 !=0x00)return r1;
        r1 = SD_ReceiveData(buffer,512,EELEASE)        ;
        if(r1 != 0)
          return r1;   //读数据出错!
        else
          return 0;
}
         
/************************************************************
************************接收数据*****************************
************************************************************/

u8 SD_ReceiveData(u8 *data,u16 len,u8 release)
{
   u16 retry;
   u8 r1;
   //启动一次传输
   SD_CSENABLE();
   //等待SD卡发回数据起始令牌0xfe
   retry = 0;
   do
   {
          r1 = SPI_ReadWriteByte(0xff);
          retry++;
          if(retry>4000)  //4000次等待后没有应答,退出报错(根据实验测试,此处最好多试几次)
           {
             SD_CS_DISABLE();
                 return 1;
                 }
                 }while(r1 != 0xfe);

                 //开始接收数据
                 while(len--)
                 {
                    *data = SPI_ReadWriteByte(0xff);
                        data++;
                 }
                 //下面是2个伪CRC(dummy CRC)
                 SPI_ReadWriteByte(0xff);
                 SPI_ReadWriteByte(0xff);
                 //按需释放总线,将CS置高
                 if(release == RELEASE)
                 {  
                    //传输结束
                        SD_CS_DISABLE();
                        SPI_ReadWriteByte(0xff);
                        }
                  return 0;
}

void SPI_ReadWriteByte(u8 xxx)
{
}

void USART_Configuration(void)
{
}


沙发
huangcunxiake|  楼主 | 2017-1-14 00:16 | 只看该作者
第一步:实现SD卡的初始化利用usart与笔记本通信。
第二步:在SD卡初始化实现的基础上对SD卡进行读写操作。
第三步:移植FAT文件系统管理图像,声音以及视频文件(猜想或有技术上的错误)。
第四步:ucgui的使用以及TFT用户界面的设计。

使用特权

评论回复
板凳
mmuuss586| | 2017-1-14 12:10 | 只看该作者
感谢分享;

使用特权

评论回复
地板
feelhyq| | 2017-1-19 17:37 | 只看该作者
学习了

使用特权

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

本版积分规则

204

主题

3476

帖子

10

粉丝