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
}

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;       
}

gaonaiweng 发表于 2023-10-19 13:20

写单块和写多块
SD卡用CMD24和CMD25来写单块和多块,参数的定义和读操作是一样的。

gaonaiweng 发表于 2023-10-19 13:21

忙检测:
      SD卡写入数据并自编程时,数据线上读到0x00表示SD卡正忙,当读到0xff表示写操作完成。

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;
}

gaonaiweng 发表于 2023-10-19 13:22

写单块流程:
1.发送CMD24,收到0x00表示成功

2.发送若干时钟

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

4.发送512个字节数据

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

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

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

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;
}

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

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;   
}

gaonaiweng 发表于 2023-10-19 13:22

SD卡的基本读写程序就是这些,编写的思路就是由最底层的SPI 读写一字节数据的程序作为基本程序,然后根据SD卡不同时序进行相应的组合。

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

偶就挖到 发表于 2025-6-23 10:58

请问在哪里可以下载呢?
页: 1 [2]
查看完整版本: STM32利用SPI读写SD卡的程序详解