[应用相关] 基于SPI模式的SD卡驱动程序

[复制链接]
1864|3
 楼主| huangcunxiake 发表于 2017-1-14 00:14 | 显示全部楼层 |阅读模式
  1. u8 SD_SendCommand(u8 cmd,u32 arg, u8 crc)
  2. {
  3.            u8 r1;       
  4.            unsigned int Retry = 0;
  5.        
  6.            SD_CS_DISABLE();
  7.            SPI_ReadWriteByte(0xff);//提高兼容性,如果没有这里,有些SD卡可能不支持
  8.            SD_CS_ENABLE();//片选端置低,选中SD卡
  9.        
  10.            /*发送命令序列*/
  11.            SPI_ReadWriteByte(cmd | 0x40);
  12.            SPI_ReadWriteByte((u8)(arg>>24));//参数[31..24]
  13.            SPI_ReadWriteByte((u8)(arg>>16));//参数[23..16]
  14.            SPI_ReadWriteByte((u8)(arg>>8)); //参数[15..8]
  15.            SPI_ReadWriteByte((u8)arg);      //参数[7..0]
  16.            SPI_ReadWriteByte(crc);

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

  27.        //返回状态值
  28.        return r1;

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

  37. u8 SD_Init(void)
  38. {
  39.           u16 i;         //用来循环计数
  40.           u8 r1;              //存放SD卡的返回值
  41.           u16 retry;         //用来进行超时计数
  42.           u8 buff[6];
  43.        
  44.           SPI_ControlLine();  //SPI的配置初始化
  45.           SPI_SetSpeed(SPI_SPEED_LOW);
  46.           SD_CS_ENABLE();
  47.        
  48.           //纯延时,等待SD卡上电完成
  49.           for(i=0;i<0xf00;i++);
  50.        
  51.           //先产生至少74个脉冲,让SD卡自己初始化完成
  52.           for(i=0;i<10;i++)
  53.           {
  54.             SPI_ReadWriteByte(0xFF); //80clks
  55.           }
  56.        
  57.           //-------------------SD卡复位到idle开始-------------------
  58.           //循环连续发送CMD0,直到SD卡返回0x01,进入IDLE状态
  59.           //超时则直接退出
  60.           retry = 0;
  61.       do
  62.       {
  63.           //发送CMD0,让SD卡进入IDLE状态
  64.               ri = SD_SendCommand(CMD0,0,0x95);
  65.               retry++;
  66.            }while((r1 != 0x01)&& (retry<200));
  67.            //跳出循环后,检查原因: 初始化成功?or重试超时?
  68.            if(retry==200) return 1;//超时返回1
  69.          

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

  100.                       //---------------MMC卡额外初始化操作开始-------------
  101.                      if(retry==400)
  102.                       {
  103.                          retry =0;
  104.                              //发送MMC卡初始化命令(没有测试)
  105.                              do
  106.                               {
  107.                                  r1=SD_SendCommand(CMD1,0,0);
  108.                                  retry++;
  109.                                }while(r1!=0x00)&&(retry<400);
  110.                                if(retry==400)return 1;//MMC卡初始化超时
  111.                                //写入卡类型
  112.                                SD_Type=SD_TYPE_MMC;
  113.                                }
  114.                                //----------MMC卡额外初始化操作结束---------------
  115.                                //设置SPI为高速模式
  116.                                SPI_SetSpeed(SPI_SPEED_HIGH);
  117.                                SPI_ReaadWriteByte(0xff);

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

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

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

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

  166. }

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

  171. void SPI_ControlLine(void)
  172. {
  173.     GPIO_InitTypeDef GPIO_InitStructure;
  174.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC ,ENABLE);
  175.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);

  176.     /*configuration SPI1 pins:,SCK,MISO and MOSI*/
  177.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_7;
  178.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  179.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  180.     GPIO_Init(GPIOA, &GPIO_InitStructure);
  181.        
  182.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
  183.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //MISO应该要初始化为上拉输入
  184.     GPIO_Init(GPIOA, &GPIO_InitStructure);
  185.        
  186.         /*configration PA4 Pin:  CS Pin*/
  187.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
  188.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  189.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  190.     GPIO_Init(GPIOA, &GPIO_InitStructure);
  191. }

  192. /**************************************************************************
  193. ***********************SPI通信模式初始化***********************************
  194. ***********************设置高速或低速模式**********************************
  195. **************************************************************************/

  196. void SPI_SetSpeed(u8 SpeedSet)
  197. {
  198.    /* Initialize the SPI1 according to the SPI_InitStructure members */
  199.    SPI_InitTypeDef SPI_InitStructure;
  200.    if(SpeedSet==SPI_SPEED_HIGH)//高速
  201.    {
  202.       SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
  203.       SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
  204.       SPI_InitStructure.SPI_DatSize = SPI_DatSize_8b;
  205.       SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
  206.       SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
  207.       SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
  208.       SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
  209.       SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
  210.       SPI_InitStructure.SPI_CRCPolynomial = 7;
  211.    
  212.    
  213.    SPI_Init(SPI1, &SPI_InitStructure);
  214.    /*SPI1 enable*/
  215.    SPI_Cmd(SPI1,ENABLE);
  216.    }
  217.    else//低速
  218.      {  
  219.                 SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
  220.         SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
  221.         SPI_InitStructure.SPI_DatSize = SPI_DatSize_8b;
  222.         SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
  223.         SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
  224.         SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
  225.         SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
  226.         SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
  227.         SPI_InitStructure.SPI_CRCPolynomial = 7;
  228.           
  229.         SPI_Init(SPI1,&SPI_InitStructure);
  230.                 /*SPI1 enable*/
  231.                 SPI_Cmd(SPI1,ENABLE);
  232.       }
  233. }

  234. /************************************************************
  235. **************************发送命令***************************
  236. ************************************************************/

  237. u8 SD_SendCommand_NoDeassert(u8 cmd, u32 arg,u8 crc)
  238. {
  239.     unsigned char r1;
  240.     unsigned int Retry = 0;

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

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

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

  259. /**************************************************************
  260. ************************向SD卡写一个块*************************
  261. **************************************************************/

  262. u8 SD_WriteSingleBlock(u32 sector,const u8 *data)
  263. {
  264.         u8 r1;
  265.         u16 i;
  266.         u16 retry;

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

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

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

  279.          //开始准备数据传输
  280.          SD_CS_ENABLE();
  281.          //先放3个空数据,等待SD卡准备好
  282.          SPI_ReadWriteByte(0xff);
  283.          SPI_ReadWriteByte(0xff);
  284.          SPI_ReadWriteByte(0xff);
  285.          //放起始令牌0xfe
  286.          SPI_ReadWriteByte(0xfe);

  287.          //发一个sector的数据
  288.          for(i=0;i<512;i++)
  289.          {
  290.              SPI_ReadWriteByte(*data++);
  291.          }
  292.          //发2个Byte的dummy CRC
  293.          SPI_ReadWriteByte(0xff);
  294.          SPI_ReadWriteByte(0xff);

  295.      //等待SD卡应答
  296.          r1 = SPI_ReadWriteByte(0xff);
  297.          if((r1&0x1f)!=0x05)
  298.          {
  299.              SD_CS_DISABLE();
  300.                  return r1;
  301.           }

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

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

  316.          return 0;
  317. }

  318. /********************************************************
  319. ******************从SD中读取一个块***********************
  320. ********************************************************/

  321. u8 SD_ReadSingleBlock(u32 sector, u8 *buffer)
  322. {
  323.    u8 r1;
  324.    //设置为高速模式
  325.    SPI_SetSpeed(SPI_SPEED_LOW);

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

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

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

  333.         if(r1 !=0x00)return r1;
  334.         r1 = SD_ReceiveData(buffer,512,EELEASE)        ;
  335.         if(r1 != 0)
  336.           return r1;   //读数据出错!
  337.         else
  338.           return 0;
  339. }
  340.          
  341. /************************************************************
  342. ************************接收数据*****************************
  343. ************************************************************/

  344. u8 SD_ReceiveData(u8 *data,u16 len,u8 release)
  345. {
  346.    u16 retry;
  347.    u8 r1;
  348.    //启动一次传输
  349.    SD_CSENABLE();
  350.    //等待SD卡发回数据起始令牌0xfe
  351.    retry = 0;
  352.    do
  353.    {
  354.           r1 = SPI_ReadWriteByte(0xff);
  355.           retry++;
  356.           if(retry>4000)  //4000次等待后没有应答,退出报错(根据实验测试,此处最好多试几次)
  357.            {
  358.              SD_CS_DISABLE();
  359.                  return 1;
  360.                  }
  361.                  }while(r1 != 0xfe);

  362.                  //开始接收数据
  363.                  while(len--)
  364.                  {
  365.                     *data = SPI_ReadWriteByte(0xff);
  366.                         data++;
  367.                  }
  368.                  //下面是2个伪CRC(dummy CRC)
  369.                  SPI_ReadWriteByte(0xff);
  370.                  SPI_ReadWriteByte(0xff);
  371.                  //按需释放总线,将CS置高
  372.                  if(release == RELEASE)
  373.                  {  
  374.                     //传输结束
  375.                         SD_CS_DISABLE();
  376.                         SPI_ReadWriteByte(0xff);
  377.                         }
  378.                   return 0;
  379. }

  380. void SPI_ReadWriteByte(u8 xxx)
  381. {
  382. }

  383. void USART_Configuration(void)
  384. {
  385. }


 楼主| 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 | 显示全部楼层
学习了
您需要登录后才可以回帖 登录 | 注册

本版积分规则

223

主题

3745

帖子

11

粉丝
快速回复 在线客服 返回列表 返回顶部