找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 596|回复: 0
收起左侧

分享程序:STM32通过SPI方式读写SD卡

[复制链接]
ID:250869 发表于 2023-12-7 17:23 | 显示全部楼层 |阅读模式
分享一下通过SPI方式读写SD卡的驱动源码,只需要修改SPI读写函数以及片选口就可以投入使用,可自行搭配FATFS文件系统。
SD.c
  1. #include "SD.h"
  2. #include "Basic_cfg.h"

  3. u8 SD_Type = 0;
  4. /***************************/
  5. //SD读写函数,便于替换SPI接口
  6. /***************************/
  7. u8 SD_ReadWriteByte(u8 TxData)
  8. {
  9.     SPI3_ReadWriteByte(TxData);
  10. }

  11. /***************************/
  12. //忙检测,等待SD卡
  13. /***************************/
  14. u8 SD_WaitReady(void)
  15. {
  16.     u32 t=0;
  17.     u8 reg;
  18.     for(t=0; t<0xffff; t++)
  19.     {
  20.         reg=SD_ReadWriteByte(0XFF);//获取返回值
  21.         if(reg==0XFF)
  22.             break;
  23.     }
  24.     if(t<0xffffff)
  25.         return 0;
  26.     else
  27.         return 1;
  28. }
  29. /***************************/
  30. //取消卡选择
  31. /***************************/
  32. void SD_DisSelect(void)
  33. {
  34.     SD_CS=1;
  35.     SD_ReadWriteByte(0xff);//提供额外的8个时钟
  36. }
  37. /***************************/
  38. //卡选择,选择SD卡
  39. /***************************/
  40. u8 SD_Select(void)
  41. {
  42.     SD_CS=0;
  43.     if(SD_WaitReady()==0)    //等待成功
  44.         return 0;
  45.     SD_DisSelect();    //等待失败
  46.     return 1;
  47. }
  48. /***************************/
  49. //向SD卡发送一个命令
  50. //CMD:命令
  51. //arg:命令参数
  52. //crc:校验
  53. /***************************/
  54. u8 SD_SendCmd(u8 cmd, u32 arg, u8 crc)
  55. {
  56.     u8 r1=0;
  57.     u8 Retry=0;
  58.     SD_DisSelect();    //取消上次片选

  59.     if(SD_Select())    //片选失效
  60.     {
  61.         return 0XFF;
  62.     }
  63.     //发送
  64.     //分别写入命令
  65.     SD_ReadWriteByte(cmd | 0x40);
  66.     SD_ReadWriteByte(arg >> 24);
  67.     SD_ReadWriteByte(arg >> 16);
  68.     SD_ReadWriteByte(arg >> 8);
  69.     SD_ReadWriteByte(arg);
  70.     SD_ReadWriteByte(crc);
  71.     if(cmd==CMD12)SD_ReadWriteByte(0xff);
  72.     //等待响应,或超时退出
  73.     Retry=0X1F;
  74.     do
  75.     {
  76.         r1=SD_ReadWriteByte(0xFF);
  77.     }
  78.     while((r1&0X80) && Retry--);
  79.     //返回状态值
  80.     return r1;
  81. }
  82. /***************************/
  83. //等待SD卡回应
  84. //Response:要得到的回应值
  85. //返回值:0,成功得到了该回应值
  86. //        其他,得到回应值失败
  87. /***************************/
  88. u8 SD_GetResponse(u8 Response)
  89. {
  90.     //等待次数
  91.     u16 Count=0xFFFF;
  92.     //等待得到准确的回应
  93.     while ((SD_ReadWriteByte(0XFF)!=Response)&&Count)
  94.     {
  95.         Count--;
  96.     }
  97.     if (Count==0)
  98.     {
  99.         //得到回应失败
  100.         return MSD_RESPONSE_FAILURE;
  101.     }
  102.     else
  103.     {
  104.         //正确回应
  105.         return MSD_RESPONSE_NO_ERROR;
  106.     }
  107. }
  108. //初始化SD卡
  109. u8 SD_Init(void)
  110. {
  111.     u8 r1=0;          // 存放SD卡的返回值
  112.     u16 retry;    // 用来进行超时计数
  113.     u8 buf[4];
  114.     u16 i;


  115.     SPI3_Init();    //初始化SPI3

  116.     SPI3_SetSpeed(SPI_BAUDRATEPRESCALER_256);    //配置为低速度模式
  117.     for(i=0; i<10; i++)    //发送至少74个脉冲
  118.     {
  119.         SD_ReadWriteByte(0xff);
  120.     }
  121.     retry=20;
  122.     do
  123.     {
  124.         //进入IDLE状态
  125.         r1=SD_SendCmd(CMD0,0,0x95);
  126.     }
  127.     while((r1!=0X01) && (retry--));
  128.     //默认无卡
  129.     SD_Type=0;
  130.     //识别卡类型
  131.     if(r1==0X01)
  132.     {
  133.         //SD V2.0
  134.         if(SD_SendCmd(CMD8,0x1AA,0x87)==1)
  135.         {
  136.             //Get trailing return value of R7 resp
  137.             for(i=0; i<4; i++)
  138.                 buf[i]=SD_ReadWriteByte(0XFF);
  139.             //卡是否支持2.7~3.6V
  140.             if(buf[2]==0X01&&buf[3]==0XAA)
  141.             {
  142.                 retry=0XFFFE;
  143.                 do
  144.                 {
  145.                     //发送CMD55
  146.                     SD_SendCmd(CMD55,0,0X01);
  147.                     //发送CMD41
  148.                     r1=SD_SendCmd(CMD41,0x40000000,0X01);
  149.                 }
  150.                 while(r1&&retry--);
  151.                 //鉴别SD2.0卡版本开始
  152.                 if(retry&&SD_SendCmd(CMD58,0,0X01)==0)
  153.                 {
  154.                     //得到OCR值
  155.                     for(i=0; i<4; i++)
  156.                         buf[i]=SD_ReadWriteByte(0XFF);
  157.                     //检查CCS
  158.                     if(buf[0]&0x40)
  159.                     {
  160.                         SD_Type=SD_TYPE_V2HC;
  161.                     }
  162.                     else
  163.                     {
  164.                         SD_Type=SD_TYPE_V2;
  165.                     }
  166.                 }
  167.             }
  168.         }
  169.     }
  170.     //SD V1.x/ MMC    V3
  171.     else
  172.     {
  173.         //发送CMD55
  174.         SD_SendCmd(CMD55,0,0X01);
  175.         //发送CMD41
  176.         r1=SD_SendCmd(CMD41,0,0X01);
  177.         if(r1<=1)
  178.         {
  179.             SD_Type=SD_TYPE_V1;
  180.             retry=0XFFFE;
  181.             //等待退出IDLE模式
  182.             do
  183.             {
  184.                 //发送CMD55
  185.                 SD_SendCmd(CMD55,0,0X01);
  186.                 //发送CMD41
  187.                 r1=SD_SendCmd(CMD41,0,0X01);
  188.             } while(r1&&retry--);
  189.         }
  190.         //MMC卡不支持CMD55+CMD41识别
  191.         else
  192.         {
  193.             //MMC V3
  194.             SD_Type=SD_TYPE_MMC;
  195.             retry=0XFFFE;
  196.             //等待退出IDLE模式
  197.             do
  198.             {
  199.                 //发送CMD1
  200.                 r1=SD_SendCmd(CMD1,0,0X01);
  201.             } while(r1&&retry--);
  202.         }
  203.         //错误的卡
  204.         if(retry==0||SD_SendCmd(CMD16,512,0X01)!=0)
  205.         {
  206.             SD_Type=SD_TYPE_ERR;
  207.         }
  208.     }
  209.     //取消片选
  210.     SD_DisSelect();
  211.     //配置为高速度模式
  212.     SPI3_SetSpeed(SPI_BAUDRATEPRESCALER_4);
  213.     if(SD_Type)
  214.     {
  215.         return 0;
  216.     }
  217.     else if(r1)
  218.     {
  219.         return r1;
  220.     }
  221.     //其他错误
  222.     return 0xaa;
  223. }
  224. /***************************/
  225. //从sd卡读取一个数据包的内容
  226. //buf:数据缓存区
  227. //len:要读取的数据长度.
  228. //返回值:0,成功;其他,失败;   
  229. /***************************/
  230. u8 SD_RecvData(u8*buf,u16 len)
  231. {                    
  232.     //等待SD卡发回数据起始令牌0xFE
  233.     if(SD_GetResponse(0xFE))
  234.     {
  235.         return 1;
  236.     }
  237.     //开始接收数据
  238.   while(len--)
  239.   {
  240.     *buf=SD_ReadWriteByte(0xFF);
  241.     buf++;
  242.   }
  243.   //下面是2个伪CRC(dummy CRC)
  244.   SD_ReadWriteByte(0xFF);
  245.   SD_ReadWriteByte(0xFF);        
  246.   //读取成功                                                      
  247.   return 0;
  248. }
  249. /***************************/
  250. //获取SD卡的CSD信息,包括容量和速度信息
  251. //输入:u8 *cid_data(存放CID的内存,至少16Byte)        
  252. //返回值:0:NO_ERR
  253. //         1:错误   
  254. /***************************/
  255. u8 SD_GetCSD(u8 *csd_data)
  256. {
  257.   u8 r1;     
  258.     //发CMD9命令,读CSD
  259.   r1=SD_SendCmd(CMD9,0,0x01);
  260.   if(r1==0)
  261.     {
  262.         //接收16个字节的数据
  263.     r1=SD_RecvData(csd_data, 16);
  264.   }
  265.     //取消片选
  266.     SD_DisSelect();
  267.     if(r1)
  268.     {
  269.         return 1;
  270.     }
  271.     else
  272.     {
  273.         return 0;
  274.     }
  275. }  
  276. /***************************/
  277. //获取SD卡的总扇区数(扇区数)   
  278. //返回值:0: 取容量出错
  279. //       其他:SD卡的容量(扇区数/512字节)
  280. //每扇区的字节数必为512,因为如果不是512,则初始化不能通过.
  281. /***************************/
  282. u32 SD_GetSectorCount(void)
  283. {
  284.     u8 csd[16];
  285.     u32 Capacity;  
  286.     u8 n;
  287.         u16 csize;                          
  288.         //取CSD信息,如果期间出错,返回0
  289.     if(SD_GetCSD(csd)!=0)
  290.         {
  291.             return 0;        
  292.         }
  293.     //如果为SDHC卡,按照下面方式计算
  294.         //V2.00的卡
  295.     if((csd[0]&0xC0)==0x40)     
  296.     {   
  297.             csize = csd[9] + ((u16)csd[8] << 8) + 1;
  298.             //得到扇区数   
  299.             Capacity = (u32)csize << 10;            
  300.     }
  301.         //V1.XX的卡
  302.         else
  303.     {   
  304.             n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
  305.             csize = (csd[8] >> 6) + ((u16)csd[7] << 2) + ((u16)(csd[6] & 3) << 10) + 1;
  306.             //得到扇区数
  307.             Capacity= (u32)csize << (n - 9);  
  308.     }
  309.     return Capacity;
  310. }

  311. u8 SD_ReadDisk(u8*buf,u32 sector,u8 cnt)
  312. {
  313.     u8 r1;
  314.     //转换为字节地址
  315.     if(SD_Type!=SD_TYPE_V2HC)
  316.     {
  317.         sector <<= 9;
  318.     }
  319.     if(cnt==1)
  320.     {
  321.         //读命令
  322.         r1=SD_SendCmd(CMD17,sector,0X01);
  323.         //指令发送成功
  324.         if(r1==0)
  325.         {
  326.             //接收512个字节     
  327.             r1=SD_RecvData(buf,512);  
  328.         }
  329.     }
  330.     else
  331.     {
  332.         //连续读命令
  333.         r1=SD_SendCmd(CMD18,sector,0X01);
  334.         do
  335.         {
  336.             //接收512个字节     
  337.             r1=SD_RecvData(buf,512);
  338.             buf+=512;  
  339.         }
  340.         while(--cnt && r1==0);     
  341.         //发送停止命令
  342.         SD_SendCmd(CMD12,0,0X01);   
  343.     }   
  344.     //取消片选
  345.     SD_DisSelect();
  346.     return r1;
  347. }
  348. /***************************/
  349. //读取SD卡的指定扇区的内容,并通过串口1输出
  350. //sec:扇区物理地址编号
  351. /***************************/
  352. void SD_Read_Sectorx(u32 sec)
  353. {
  354.     //存储扇区数据
  355.     u8 buf[512];
  356.     u16 i;
  357.    
  358.     //读取0扇区的内容
  359.     if(SD_ReadDisk(buf,sec,1)==0)   
  360.     {   
  361.         printf("SECTOR 0 DATA:\r\n");
  362.         //打印sec扇区数据  
  363.         for(i=0;i<512;i++)
  364.             printf("%X ",buf[i]);         
  365.         printf("\r\nDATA ENDED\r\n");
  366.     }
  367. }
  368. /***************************/
  369. //获取SD卡的CID信息,包括制造商信息
  370. //输入: u8 *cid_data(存放CID的内存,至少16Byte)      
  371. //返回值:0:NO_ERR
  372. //         1:错误   
  373. /***************************/
  374. u8 SD_GetCID(u8 *cid_data)
  375. {
  376.   u8 r1;      
  377.   //发CMD10命令,读CID
  378.   r1=SD_SendCmd(CMD10,0,0x01);
  379.   if(r1==0x00)
  380.     {
  381.         //接收16个字节的数据     
  382.         r1=SD_RecvData(cid_data,16);
  383.   }
  384.     //取消片选
  385.     SD_DisSelect();
  386.     if(r1)
  387.         return 1;
  388.     else
  389.         return 0;
  390. }
  391. /***************************/
  392. //向sd卡写入一个数据包的内容 512字节
  393. //buf:数据缓存区
  394. //cmd:指令
  395. //返回值:0,成功;其他,失败;   
  396. /***************************/
  397. u8 SD_SendBlock(u8*buf,u8 cmd)
  398. {   
  399.     u16 t;               
  400.     //等待准备失效
  401.     if(SD_WaitReady())
  402.     {
  403.         return 1;
  404.     }
  405.     SD_ReadWriteByte(cmd);
  406.     //不是结束指令
  407.     if(cmd!=0XFD)
  408.     {
  409.         //提高速度,减少函数传参时间
  410.         for(t=0;t<512;t++)
  411.         {
  412.             SD_ReadWriteByte(buf[t]);
  413.         }
  414.         //忽略crc
  415.       SD_ReadWriteByte(0xFF);
  416.       SD_ReadWriteByte(0xFF);
  417.         //接收响应
  418.         t=SD_ReadWriteByte(0xFF);
  419.         if((t&0x1F)!=0x05)
  420.         {
  421.             //响应错误        
  422.             return 2;        
  423.         }            
  424.     }                                 
  425.     //写入成功                                                      
  426.   return 0;
  427. }
  428. /***************************/
  429. //写SD卡
  430. //buf:数据缓存区
  431. //sector:起始扇区
  432. //cnt:扇区数
  433. //返回值:0,ok;其他,失败.
  434. /***************************/
  435. u8 SD_WriteDisk(u8*buf,u32 sector,u8 cnt)
  436. {
  437.     u8 r1;
  438.     //转换为字节地址
  439.     if(SD_Type!=SD_TYPE_V2HC)
  440.     {
  441.         sector *= 512;
  442.     }
  443.     if(cnt==1)
  444.     {
  445.         //读命令
  446.         r1=SD_SendCmd(CMD24,sector,0X01);
  447.         //指令发送成功
  448.         if(r1==0)
  449.         {
  450.             //写512个字节      
  451.             r1=SD_SendBlock(buf,0xFE);
  452.         }
  453.     }
  454.     else
  455.     {
  456.         if(SD_Type!=SD_TYPE_MMC)
  457.         {
  458.             SD_SendCmd(CMD55,0,0X01);   
  459.             //发送指令   
  460.             SD_SendCmd(CMD23,cnt,0X01);
  461.         }
  462.         //连续读命令
  463.          r1=SD_SendCmd(CMD25,sector,0X01);
  464.         if(r1==0)
  465.         {
  466.             do
  467.             {
  468.                 //接收512个字节     
  469.                 r1=SD_SendBlock(buf,0xFC);
  470.                 buf+=512;  
  471.             }
  472.             while(--cnt && r1==0);
  473.             //接收512个字节
  474.             r1=SD_SendBlock(0,0xFD);
  475.         }
  476.     }   
  477.     //取消片选
  478.     SD_DisSelect();
  479.     return r1;
  480. }   
复制代码

sd.h
  1. #ifndef __SD_H
  2. #define __SD_H  
  3. #include "sys.h"

  4. #define SD_CS PAout(15)

  5. // SD卡类型定义  
  6. #define SD_TYPE_ERR     0X00
  7. #define SD_TYPE_MMC     0X01
  8. #define SD_TYPE_V1      0X02
  9. #define SD_TYPE_V2      0X04
  10. #define SD_TYPE_V2HC    0X06

  11. // SD卡指令表            
  12. #define CMD0    0       //卡复位
  13. #define CMD1    1
  14. #define CMD8    8       //命令8 ,SEND_IF_COND
  15. #define CMD9    9       //命令9 ,读CSD数据
  16. #define CMD10   10      //命令10,读CID数据
  17. #define CMD12   12      //命令12,停止数据传输
  18. #define CMD16   16      //命令16,设置SectorSize 应返回0x00
  19. #define CMD17   17      //命令17,读sector
  20. #define CMD18   18      //命令18,读Multi sector
  21. #define CMD23   23      //命令23,设置多sector写入前预先擦除N个block
  22. #define CMD24   24      //命令24,写sector
  23. #define CMD25   25      //命令25,写Multi sector
  24. #define CMD41   41      //命令41,应返回0x00
  25. #define CMD55   55      //命令55,应返回0x01
  26. #define CMD58   58      //命令58,读OCR信息
  27. #define CMD59   59      //命令59,使能/禁止CRC,应返回0x00

  28. //数据写入回应字意义
  29. #define MSD_DATA_OK                0x05
  30. #define MSD_DATA_CRC_ERROR         0x0B
  31. #define MSD_DATA_WRITE_ERROR       0x0D
  32. #define MSD_DATA_OTHER_ERROR       0xFF

  33. //SD卡回应标记字
  34. #define MSD_RESPONSE_NO_ERROR      0x00
  35. #define MSD_IN_IDLE_STATE          0x01
  36. #define MSD_ERASE_RESET            0x02
  37. #define MSD_ILLEGAL_COMMAND        0x04
  38. #define MSD_COM_CRC_ERROR          0x08
  39. #define MSD_ERASE_SEQUENCE_ERROR   0x10
  40. #define MSD_ADDRESS_ERROR          0x20
  41. #define MSD_PARAMETER_ERROR        0x40
  42. #define MSD_RESPONSE_FAILURE       0xFF

  43. void SD_Read_Sectorx(u32 sec);
  44. u8 SD_Init(void);
  45. u8 SD_WaitReady(void);
  46. u8 SD_GetResponse(u8 Response);
  47. u32 SD_GetSectorCount(void);
  48. u8 SD_GetCID(u8 *cid_data);
  49. u8 SD_GetCSD(u8 *csd_data);
  50. u8 SD_ReadDisk(u8*buf,u32 sector,u8 cnt);
  51. u8 SD_WriteDisk(u8*buf,u32 sector,u8 cnt);

  52. #endif

复制代码


原理图: 无
仿真: 无



回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表