发新帖我要提问
12
返回列表
打印
[其他ST产品]

STM32利用SPI读写SD卡的程序详解

[复制链接]
楼主: gaonaiweng
手机看帖
扫描二维码
随时随地手机跟帖
21
gaonaiweng|  楼主 | 2023-10-19 13:18 | 只看该作者 回帖奖励 |倒序浏览
读单块:
u8 SD_ReadSingleBlock(u32 sector, u8 *buffer)
{
  u8 r1;
  //高速模式
  SPI_SetSpeed(SPI_SPEED_HIGH);
  if(SD_Type!=SD_TYPE_V2HC)        //如果不是SDHC卡
  {
    sector = sector<<9;        //512*sector即物理扇区的边界对其地址
  }
   r1 = SD_SendCommand(CMD17, sector, 1);        //发送CMD17 读命令
   if(r1 != 0x00)        return r1;                                                                              
   r1 = SD_ReceiveData(buffer, 512, RELEASE);        //一个扇区为512字节
   if(r1 != 0)
     return r1;   //读数据出错
   else
     return 0;                 //读取正确,返回0
}

使用特权

评论回复
22
gaonaiweng|  楼主 | 2023-10-19 13:19 | 只看该作者
读多块:
u8 SD_ReadMultiBlock(u32 sector, u8 *buffer, u8 count)
{
  u8 r1;                                  
  SPI_SetSpeed(SPI_SPEED_HIGH);
  if(SD_Type != SD_TYPE_V2HC)
  {
            sector = sector<<9;
  }
  r1 = SD_SendCommand(CMD18, sector, 1);                //读多块命令
  if(r1 != 0x00)        return r1;         
  do        //开始接收数据
  {
            if(SD_ReceiveData(buffer, 512, NO_RELEASE) != 0x00)
            {
                       break;
            }
            buffer += 512;
                }         while(--count);                 
  
  SD_SendCommand(CMD12, 0, 1);        //全部传输完成,发送停止命令
  SD_CS_DISABLE();        //释放总线
  SPI_ReadWriteByte(0xFF);   
  if(count != 0)
    return count;   //如果没有传完,返回剩余个数
  else
    return 0;       
}

使用特权

评论回复
23
gaonaiweng|  楼主 | 2023-10-19 13:20 | 只看该作者
写单块和写多块
SD卡用CMD24和CMD25来写单块和多块,参数的定义和读操作是一样的。

使用特权

评论回复
24
gaonaiweng|  楼主 | 2023-10-19 13:21 | 只看该作者
忙检测:
        SD卡写入数据并自编程时,数据线上读到0x00表示SD卡正忙,当读到0xff表示写操作完成。

使用特权

评论回复
25
gaonaiweng|  楼主 | 2023-10-19 13:21 | 只看该作者
u8 SD_WaitReady(void)
{
  u8 r1;
  u16 retry=0;
  do
  {
    r1 = SPI_ReadWriteByte(0xFF);
    retry++;
    if(retry==0xfffe)
            return 1;
  }while(r1!=0xFF);
            return 0;
}

使用特权

评论回复
26
gaonaiweng|  楼主 | 2023-10-19 13:22 | 只看该作者
写单块流程:
1.发送CMD24,收到0x00表示成功

2.发送若干时钟

3.发送写单块开始字节0xFE

4.发送512个字节数据

5.发送2字节CRC(可以均为0xff)

6.连续读直到读到XXX00101表示数据写入成功

7.继续读进行忙检测(读到0x00表示SD卡正忙),当读到0xff表示写操作完成

使用特权

评论回复
27
gaonaiweng|  楼主 | 2023-10-19 13:22 | 只看该作者
u8 SD_WriteSingleBlock(u32 sector, const u8 *data)
{
          u8 r1;
          u16 i;
          16 retry;
        //高速模式
          SPI_SetSpeed(SPI_SPEED_HIGH);
        //如果不是SDHC卡,将sector地址转为byte地址
        if(SD_Type!=SD_TYPE_V2HC)               
          {
                     sector = sector<<9;
          }
        //写扇区指令
        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个伪CRC校验
          SPI_ReadWriteByte(0xff);
          SPI_ReadWriteByte(0xff);
          //等待SD卡应答
          r1 = SPI_ReadWriteByte(0xff);
        //如果为0x05说明数据写入成功
          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;
}

使用特权

评论回复
28
gaonaiweng|  楼主 | 2023-10-19 13:22 | 只看该作者
写多块流程:
1.发送CMD25,收到0x00表示成功

2.发送若干时钟

3.发送写多块开始字节0xFC

4.发送512字节数据

5.发送两个CRC(可以均为0xff)

6.连续读直到读到XXX00101表示数据写入成功

7.继续读进行忙检测,直到读到0xFF表示写操作完成

8.如果想读下一扇区重复2-7步骤

9.发送写多块停止字节0xFD来停止写操作

10.进行忙检测直到读到0xFF

使用特权

评论回复
29
gaonaiweng|  楼主 | 2023-10-19 13:22 | 只看该作者
u8 SD_WriteMultiBlock(u32 sector, const u8 *data, u8 count)
{
          u8 r1;
          u16 i;                          
          SPI_SetSpeed(SPI_SPEED_HIGH);
          if(SD_Type != SD_TYPE_V2HC)
        {
                sector = sector<<9;
        }
          if(SD_Type != SD_TYPE_MMC)
        {
              //启用ACMD23指令使能预擦除
                 r1 = SD_SendCommand(ACMD23, count, 0x01);                 
        }  
        //写多块指令CMD25
        r1 = SD_SendCommand(CMD25, sector, 0x01);       
        //应答不正确,直接返回
          if(r1 != 0x00)        return r1;       
        //开始准备数据传输
          SD_CS_ENABLE();
        //放3个空数据让SD卡准备好               
        SPI_ReadWriteByte(0xff);       
          SPI_ReadWriteByte(0xff);
        SPI_ReadWriteByte(0xff);   
          //下面是N个sector循环写入的部分
          do
          {
                //放起始令牌0xFC,表明是多块写入
                 SPI_ReadWriteByte(0xFC);          
                 //发1个sector的数据
                 for(i=0;i<512;i++)
                 {
                SPI_ReadWriteByte(*data++);
                 }
                 //发2个伪CRC
            SPI_ReadWriteByte(0xff);
                SPI_ReadWriteByte(0xff);
            //等待SD卡回应
                 r1 = SPI_ReadWriteByte(0xff);
            //0x05表示数据写入成功
                 if((r1&0x1F)!=0x05)                       
                 {
                SD_CS_DISABLE();   
                return r1;
                 }
            //检测SD卡忙信号
                 if(SD_WaitReady()==1)
                {
                SD_CS_DISABLE();    //长时间写入未完成,退出
                return 1;
                }          
       }
       while(--count);       
       //发送传输结束令牌0xFD
       SPI_ReadWriteByte(0xFD);       
       //等待准备好   
       if(SD_WaitReady())                
       {
           SD_CS_DISABLE();
           return 1;  
        }
       //写入完成,片选置1
       SD_CS_DISABLE();
       SPI_ReadWriteByte(0xff);
       //返回count值,如果写完,则count=0,否则count=未写完的sector数  
       return count;   
}

使用特权

评论回复
30
gaonaiweng|  楼主 | 2023-10-19 13:22 | 只看该作者
SD卡的基本读写程序就是这些,编写的思路就是由最底层的SPI 读写一字节数据的程序作为基本程序,然后根据SD卡不同时序进行相应的组合。

想细致研究STM32的SPI在这个例程中的配置还是需要下载源码仔细阅读的。掌握了这个例程的读写SD卡的函数原理,下一步就可以着手运用到FATFS文件系统了。

使用特权

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

本版积分规则