[其他ST产品] STM32利用SPI读写SD卡的程序详解

[复制链接]
3809|30
 楼主| gaonaiweng 发表于 2023-10-19 13:18 | 显示全部楼层
读单块:
  1. u8 SD_ReadSingleBlock(u32 sector, u8 *buffer)
  2. {
  3.   u8 r1;
  4.   //高速模式
  5.   SPI_SetSpeed(SPI_SPEED_HIGH);
  6.   if(SD_Type!=SD_TYPE_V2HC)        //如果不是SDHC卡
  7.   {
  8.     sector = sector<<9;        //512*sector即物理扇区的边界对其地址
  9.   }
  10.    r1 = SD_SendCommand(CMD17, sector, 1);        //发送CMD17 读命令
  11.    if(r1 != 0x00)        return r1;                                                                              
  12.    r1 = SD_ReceiveData(buffer, 512, RELEASE);        //一个扇区为512字节
  13.    if(r1 != 0)
  14.      return r1;   //读数据出错
  15.    else
  16.      return 0;                 //读取正确,返回0
  17. }
 楼主| gaonaiweng 发表于 2023-10-19 13:19 | 显示全部楼层
读多块:
  1. u8 SD_ReadMultiBlock(u32 sector, u8 *buffer, u8 count)
  2. {
  3.   u8 r1;                                  
  4.   SPI_SetSpeed(SPI_SPEED_HIGH);
  5.   if(SD_Type != SD_TYPE_V2HC)
  6.   {
  7.             sector = sector<<9;
  8.   }
  9.   r1 = SD_SendCommand(CMD18, sector, 1);                //读多块命令
  10.   if(r1 != 0x00)        return r1;         
  11.   do        //开始接收数据
  12.   {
  13.             if(SD_ReceiveData(buffer, 512, NO_RELEASE) != 0x00)
  14.             {
  15.                        break;
  16.             }
  17.             buffer += 512;
  18.                 }         while(--count);                 
  19.   
  20.   SD_SendCommand(CMD12, 0, 1);        //全部传输完成,发送停止命令
  21.   SD_CS_DISABLE();        //释放总线
  22.   SPI_ReadWriteByte(0xFF);   
  23.   if(count != 0)
  24.     return count;   //如果没有传完,返回剩余个数
  25.   else
  26.     return 0;       
  27. }
 楼主| 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 | 显示全部楼层
  1. u8 SD_WaitReady(void)
  2. {
  3.   u8 r1;
  4.   u16 retry=0;
  5.   do
  6.   {
  7.     r1 = SPI_ReadWriteByte(0xFF);
  8.     retry++;
  9.     if(retry==0xfffe)
  10.             return 1;
  11.   }while(r1!=0xFF);
  12.             return 0;
  13. }
 楼主| 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 | 显示全部楼层
  1. u8 SD_WriteSingleBlock(u32 sector, const u8 *data)
  2. {
  3.           u8 r1;
  4.           u16 i;
  5.           16 retry;
  6.         //高速模式
  7.           SPI_SetSpeed(SPI_SPEED_HIGH);
  8.         //如果不是SDHC卡,将sector地址转为byte地址
  9.         if(SD_Type!=SD_TYPE_V2HC)               
  10.           {
  11.                      sector = sector<<9;
  12.           }
  13.         //写扇区指令
  14.         r1 = SD_SendCommand(CMD24, sector, 0x00);       
  15.           if(r1 != 0x00)
  16.           {
  17.                 //应答错误,直接返回
  18.                     return r1;  
  19.           }
  20.         //开始准备数据传输
  21.         SD_CS_ENABLE();       
  22.           //先发3个空数据,等待SD卡准备好
  23.           SPI_ReadWriteByte(0xff);
  24.           SPI_ReadWriteByte(0xff);
  25.           SPI_ReadWriteByte(0xff);
  26.           //放起始令牌0xFE
  27.         SPI_ReadWriteByte(0xFE);
  28.         //发一个sector数据
  29.           for(i=0;i<512;i++)
  30.           {
  31.                      SPI_ReadWriteByte(*data++);
  32.           }
  33.           //发送2个伪CRC校验
  34.           SPI_ReadWriteByte(0xff);
  35.           SPI_ReadWriteByte(0xff);
  36.           //等待SD卡应答
  37.           r1 = SPI_ReadWriteByte(0xff);
  38.         //如果为0x05说明数据写入成功
  39.           if((r1&0x1F)!=0x05)               
  40.           {
  41.                      SD_CS_DISABLE();
  42.                      return r1;
  43.           }
  44.             //等待操作完成
  45.           retry = 0;
  46.         //卡自编程时,数据线被拉低
  47.           while(!SPI_ReadWriteByte(0xff))               
  48.           {
  49.                      retry++;
  50.                      if(retry>65534)        //如果长时间没有写入完成,退出报错
  51.                      {
  52.                         SD_CS_DISABLE();
  53.                         return 1;           //写入超时,返回1
  54.                      }
  55.           }
  56.         //写入完成,片选置1
  57.           SD_CS_DISABLE();
  58.           SPI_ReadWriteByte(0xff);
  59.         return 0;
  60. }
 楼主| 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 | 显示全部楼层
  1. u8 SD_WriteMultiBlock(u32 sector, const u8 *data, u8 count)
  2. {
  3.           u8 r1;
  4.           u16 i;                          
  5.           SPI_SetSpeed(SPI_SPEED_HIGH);
  6.           if(SD_Type != SD_TYPE_V2HC)
  7.         {
  8.                 sector = sector<<9;
  9.         }
  10.           if(SD_Type != SD_TYPE_MMC)
  11.         {
  12.               //启用ACMD23指令使能预擦除
  13.                  r1 = SD_SendCommand(ACMD23, count, 0x01);                 
  14.         }  
  15.         //写多块指令CMD25
  16.         r1 = SD_SendCommand(CMD25, sector, 0x01);       
  17.         //应答不正确,直接返回
  18.           if(r1 != 0x00)        return r1;       
  19.         //开始准备数据传输
  20.           SD_CS_ENABLE();
  21.         //放3个空数据让SD卡准备好               
  22.         SPI_ReadWriteByte(0xff);       
  23.           SPI_ReadWriteByte(0xff);
  24.         SPI_ReadWriteByte(0xff);   
  25.           //下面是N个sector循环写入的部分
  26.           do
  27.           {
  28.                 //放起始令牌0xFC,表明是多块写入
  29.                  SPI_ReadWriteByte(0xFC);          
  30.                  //发1个sector的数据
  31.                  for(i=0;i<512;i++)
  32.                  {
  33.                 SPI_ReadWriteByte(*data++);
  34.                  }
  35.                  //发2个伪CRC
  36.             SPI_ReadWriteByte(0xff);
  37.                 SPI_ReadWriteByte(0xff);
  38.             //等待SD卡回应
  39.                  r1 = SPI_ReadWriteByte(0xff);
  40.             //0x05表示数据写入成功
  41.                  if((r1&0x1F)!=0x05)                       
  42.                  {
  43.                 SD_CS_DISABLE();   
  44.                 return r1;
  45.                  }
  46.             //检测SD卡忙信号
  47.                  if(SD_WaitReady()==1)
  48.                 {
  49.                 SD_CS_DISABLE();    //长时间写入未完成,退出
  50.                 return 1;
  51.                 }          
  52.        }
  53.        while(--count);       
  54.        //发送传输结束令牌0xFD
  55.        SPI_ReadWriteByte(0xFD);       
  56.        //等待准备好   
  57.        if(SD_WaitReady())                
  58.        {
  59.            SD_CS_DISABLE();
  60.            return 1;  
  61.         }
  62.        //写入完成,片选置1
  63.        SD_CS_DISABLE();
  64.        SPI_ReadWriteByte(0xff);
  65.        //返回count值,如果写完,则count=0,否则count=未写完的sector数  
  66.        return count;   
  67. }
 楼主| gaonaiweng 发表于 2023-10-19 13:22 | 显示全部楼层
SD卡的基本读写程序就是这些,编写的思路就是由最底层的SPI 读写一字节数据的程序作为基本程序,然后根据SD卡不同时序进行相应的组合。

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

偶就挖到 发表于 2025-6-23 10:58 | 显示全部楼层
请问在哪里可以下载呢?
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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