你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32使用HAL库IO模拟SPI驱动SD卡

[复制链接]
STMCU小助手 发布时间:2022-4-11 10:16
  1. #include "mmc_sd.h"
  2. #include "delay.h"

  3. uint8_t SD_Type = 0; //SD卡的类型

  4. /// 移植修改区函数 //

  5. /**
  6. * SD卡SPI接口读写一个字节
  7. * @param  TxData 待写入的字节
  8. * @return        来自SPI的接收
  9. */
  10. uint8_t SD_SPI_ReadWriteByte(uint8_t TxData)
  11. {
  12.     int i = 0;
  13.     uint8_t RxData = 0;
  14.     HAL_GPIO_WritePin(SPI_CLK_GPIO_Port, SPI_CLK_Pin, GPIO_PIN_RESET);
  15.     for(i = 7; i >= 0; i--)
  16.     {
  17.         HAL_GPIO_WritePin(SPI_CLK_GPIO_Port, SPI_CLK_Pin, GPIO_PIN_RESET);
  18.         if(TxData & (1 << i))
  19.         {
  20.             HAL_GPIO_WritePin(SPI_MOSI_GPIO_Port, SPI_MOSI_Pin, GPIO_PIN_SET);
  21.         }
  22.         else
  23.         {
  24.             HAL_GPIO_WritePin(SPI_MOSI_GPIO_Port, SPI_MOSI_Pin, GPIO_PIN_RESET);
  25.         }
  26.         delay_us(1);
  27.         HAL_GPIO_WritePin(SPI_CLK_GPIO_Port, SPI_CLK_Pin, GPIO_PIN_SET);
  28.         RxData <<= 1;
  29.         RxData |= HAL_GPIO_ReadPin(SPI_MISO_GPIO_Port, SPI_MISO_Pin);
  30.         delay_us(1);
  31.     }
  32.     HAL_GPIO_WritePin(SPI_CLK_GPIO_Port, SPI_CLK_Pin, GPIO_PIN_RESET);
  33.     return RxData;
  34. }

  35. /// SPI硬件层初始化
  36. void SD_SPI_Init(void)
  37. {
  38.     HAL_GPIO_WritePin(SPI_CLK_GPIO_Port, SPI_CLK_Pin, GPIO_PIN_RESET);    /* 时钟空闲为低电平 */
  39.     SD_SPI_ReadWriteByte(0xFF);
  40.     SD_CS_H();
  41. }

  42.   SD SPI 驱动代码  /

  43. //取消选择,释放SPI总线
  44. void SD_DisSelect(void)
  45. {
  46.     SD_CS_H();
  47.     SD_SPI_ReadWriteByte(0xff);//提供额外的8个时钟
  48. }

  49. /**
  50. * 选中SD卡并等待卡准备好
  51. * @return  0:成功  1:失败
  52. */
  53. uint8_t SD_Select(void)
  54. {
  55.     SD_CS_L();
  56.     if (SD_WaitReady() == 0)return 0; //等待成功
  57.     SD_DisSelect();
  58.     return 1;//等待失败
  59. }

  60. /**
  61. * 等待SD卡准备好
  62. * @return  0:成功  other:失败
  63. */
  64. uint8_t SD_WaitReady(void)
  65. {
  66.     uint32_t t = 0;
  67.     do
  68.     {
  69.         if (SD_SPI_ReadWriteByte(0XFF) == 0XFF)return 0; //OK
  70.         t++;
  71.     }
  72.     while (t < 0XFFFFFF); //等待
  73.     return 1;
  74. }

  75. /**
  76. * 等待SD卡回应
  77. * @param  Response 要得到的回应值
  78. * @return          0:成功  other:失败
  79. */
  80. uint8_t SD_GetResponse(uint8_t Response)
  81. {
  82.     uint16_t Count = 0xFFF; //等待次数
  83.     while ((SD_SPI_ReadWriteByte(0XFF) != Response) && Count)Count--; //等待得到准确的回应
  84.     if (Count == 0)return MSD_RESPONSE_FAILURE; //得到回应失败
  85.     else return MSD_RESPONSE_NO_ERROR;//正确回应
  86. }

  87. /**
  88. * 接收来自SD卡的一包数据
  89. * @param  buf 存放接收的数据
  90. * @param  len 接收的数据长度
  91. * @return     0:成功  other:失败
  92. */
  93. uint8_t SD_RecvData(uint8_t*buf, uint16_t len)
  94. {
  95.     if (SD_GetResponse(0xFE))return 1; //等待SD卡发回数据起始令牌0xFE
  96.     while (len--) //开始接收数据
  97.     {
  98.         *buf = SD_SPI_ReadWriteByte(0xFF);
  99.         buf++;
  100.     }
  101.     //下面是2个伪CRC(dummy CRC)
  102.     SD_SPI_ReadWriteByte(0xFF);
  103.     SD_SPI_ReadWriteByte(0xFF);
  104.     return 0;//读取成功
  105. }
  106. //向sd卡写入一个数据包的内容 512字节
  107. //buf:数据缓存区
  108. //cmd:指令
  109. //返回值:0,成功;其他,失败;

  110. /**
  111. * 向SD卡写入一扇区数据
  112. * @param  buf 待写入的数据,size=512
  113. * @param  cmd 指令
  114. * @return     0:成功  other:失败
  115. */
  116. uint8_t SD_SendBlock(uint8_t*buf, uint8_t cmd)
  117. {
  118.     uint16_t t;
  119.     if (SD_WaitReady())return 1; //等待准备失效
  120.     SD_SPI_ReadWriteByte(cmd);
  121.     if (cmd != 0XFD) //不是结束指令
  122.     {
  123.         for (t = 0; t < 512; t++)SD_SPI_ReadWriteByte(buf[t]); //提高速度,减少函数传参时间
  124.         SD_SPI_ReadWriteByte(0xFF);//忽略crc
  125.         SD_SPI_ReadWriteByte(0xFF);
  126.         t = SD_SPI_ReadWriteByte(0xFF); //接收响应
  127.         if ((t & 0x1F) != 0x05)return 2; //响应错误
  128.     }
  129.     return 0;//写入成功
  130. }

  131. /**
  132. * 向SD卡发送命令
  133. * @param  cmd 待发送的命令
  134. * @param  arg 参数
  135. * @param  crc crc校验值
  136. * @return     SD卡返回的响应值
  137. */
  138. uint8_t SD_SendCmd(uint8_t cmd, uint32_t arg, uint8_t crc)
  139. {
  140.     uint8_t r1;
  141.     uint8_t Retry = 0;
  142.     SD_DisSelect();//取消上次片选
  143.     if (SD_Select())return 0XFF; //片选失效
  144.     //发送
  145.     SD_SPI_ReadWriteByte(cmd | 0x40);//分别写入命令
  146.     SD_SPI_ReadWriteByte(arg >> 24);
  147.     SD_SPI_ReadWriteByte(arg >> 16);
  148.     SD_SPI_ReadWriteByte(arg >> 8);
  149.     SD_SPI_ReadWriteByte(arg);
  150.     SD_SPI_ReadWriteByte(crc);
  151.     if (cmd == CMD12)SD_SPI_ReadWriteByte(0xff); //Skip a stuff byte when stop reading
  152.     //等待响应,或超时退出
  153.     Retry = 0X1F;
  154.     do
  155.     {
  156.         r1 = SD_SPI_ReadWriteByte(0xFF);
  157.     }
  158.     while ((r1 & 0X80) && Retry--);
  159.     //返回状态值
  160.     return r1;
  161. }

  162. /**
  163. * 查询SD卡的CID信息,包括制造商信息
  164. * @param  cid_data 存放CID数据,至少16字节
  165. * @return          0:成功  1:失败
  166. */
  167. uint8_t SD_GetCID(uint8_t *cid_data)
  168. {
  169.     uint8_t r1;
  170.     //发CMD10命令,读CID
  171.     r1 = SD_SendCmd(CMD10, 0, 0x01);
  172.     if (r1 == 0x00)
  173.     {
  174.         r1 = SD_RecvData(cid_data, 16); //接收16个字节的数据
  175.     }
  176.     SD_DisSelect();//取消片选
  177.     if (r1)return 1;
  178.     else return 0;
  179. }

  180. /**
  181. * 查询SD卡CID信息,包括制造商信息
  182. * @param  csd_data 存放CID信息,至少16字节
  183. * @return          0:成功  1:失败
  184. */
  185. uint8_t SD_GetCSD(uint8_t *csd_data)
  186. {
  187.     uint8_t r1;
  188.     r1 = SD_SendCmd(CMD9, 0, 0x01); //发CMD9命令,读CSD
  189.     if (r1 == 0)
  190.     {
  191.         r1 = SD_RecvData(csd_data, 16); //接收16个字节的数据
  192.     }
  193.     SD_DisSelect();//取消片选
  194.     if (r1)return 1;
  195.     else return 0;
  196. }
  197. /**
  198. * 获取SD卡扇区数
  199. * @return  0:获取出错  other:SD卡扇区数
  200. */
  201. uint32_t SD_GetSectorCount(void)
  202. {
  203.     uint8_t csd[16];
  204.     uint32_t Capacity;
  205.     uint8_t n;
  206.     uint16_t csize;
  207.     //取CSD信息,如果期间出错,返回0
  208.     if (SD_GetCSD(csd) != 0) return 0;
  209.     //如果为SDHC卡,按照下面方式计算
  210.     if ((csd[0] & 0xC0) == 0x40)         //V2.00的卡
  211.     {
  212.         csize = csd[9] + ((uint16_t)csd[8] << 8) + 1;
  213.         Capacity = (uint32_t)csize << 10;//得到扇区数
  214.     }
  215.     else //V1.XX的卡
  216.     {
  217.         n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
  218.         csize = (csd[8] >> 6) + ((uint16_t)csd[7] << 2) + ((uint16_t)(csd[6] & 3) << 10) + 1;
  219.         Capacity = (uint32_t)csize << (n - 9); //得到扇区数
  220.     }
  221.     return Capacity;
  222. }

  223. /// SD卡进入闲置状态
  224. uint8_t SD_Idle_Sta(void)
  225. {
  226.     uint16_t i;
  227.     uint8_t retry;
  228.     for (i = 0; i < 0xf00; i++); //纯延时,等待SD卡上电完成
  229.     //先产生>74个脉冲,让SD卡自己初始化完成
  230.     for (i = 0; i < 10; i++)SD_SPI_ReadWriteByte(0xFF);
  231.     //-----------------SD卡复位到idle开始-----------------
  232.     //循环连续发送CMD0,直到SD卡返回0x01,进入IDLE状态
  233.     //超时则直接退出
  234.     retry = 0;
  235.     do
  236.     {
  237.         //发送CMD0,让SD卡进入IDLE状态
  238.         i = SD_SendCmd(CMD0, 0, 0x95);
  239.         retry++;
  240.     }
  241.     while ((i != 0x01) && (retry < 200));
  242.     //跳出循环后,检查原因:初始化成功?or 重试超时?
  243.     if (retry == 200)return 1; //失败
  244.     return 0;//成功
  245. }
  246. /// 初始化SD卡
  247. uint8_t SD_Initialize(void)
  248. {
  249.     uint8_t r1;      // 存放SD卡的返回值
  250.     uint16_t retry;  // 用来进行超时计数
  251.     uint8_t buf[4];
  252.     uint16_t i;

  253.     SD_SPI_Init();                //初始化IO
  254.     for (i = 0; i < 10; i++)SD_SPI_ReadWriteByte(0XFF); //发送最少74个脉冲
  255.     retry = 20;
  256.     do
  257.     {
  258.         r1 = SD_SendCmd(CMD0, 0, 0x95); //进入IDLE状态
  259.     }
  260.     while ((r1 != 0X01) && retry--);
  261.     SD_Type = 0; //默认无卡
  262.     if (r1 == 0X01)
  263.     {
  264.         if (SD_SendCmd(CMD8, 0x1AA, 0x87) == 1) //SD V2.0
  265.         {
  266.             for (i = 0; i < 4; i++)buf = SD_SPI_ReadWriteByte(0XFF);        //Get trailing return value of R7 resp
  267.             if (buf[2] == 0X01 && buf[3] == 0XAA) //卡是否支持2.7~3.6V
  268.             {
  269.                 retry = 0XFFFE;
  270.                 do
  271.                 {
  272.                     SD_SendCmd(CMD55, 0, 0X01);        //发送CMD55
  273.                     r1 = SD_SendCmd(CMD41, 0x40000000, 0X01); //发送CMD41
  274.                 }
  275.                 while (r1 && retry--);
  276.                 if (retry && SD_SendCmd(CMD58, 0, 0X01) == 0) //鉴别SD2.0卡版本开始
  277.                 {
  278.                     for (i = 0; i < 4; i++)buf = SD_SPI_ReadWriteByte(0XFF); //得到OCR值
  279.                     if (buf[0] & 0x40)SD_Type = SD_TYPE_V2HC; //检查CCS
  280.                     else SD_Type = SD_TYPE_V2;
  281.                 }
  282.             }
  283.         }
  284.         else //SD V1.x/ MMC        V3
  285.         {
  286.             SD_SendCmd(CMD55, 0, 0X01);                //发送CMD55
  287.             r1 = SD_SendCmd(CMD41, 0, 0X01);        //发送CMD41
  288.             if (r1 <= 1)
  289.             {
  290.                 SD_Type = SD_TYPE_V1;
  291.                 retry = 0XFFFE;
  292.                 do //等待退出IDLE模式
  293.                 {
  294.                     SD_SendCmd(CMD55, 0, 0X01);        //发送CMD55
  295.                     r1 = SD_SendCmd(CMD41, 0, 0X01); //发送CMD41
  296.                 }
  297.                 while (r1 && retry--);
  298.             }
  299.             else
  300.             {
  301.                 SD_Type = SD_TYPE_MMC; //MMC V3
  302.                 retry = 0XFFFE;
  303.                 do //等待退出IDLE模式
  304.                 {
  305.                     r1 = SD_SendCmd(CMD1, 0, 0X01); //发送CMD1
  306.                 }
  307.                 while (r1 && retry--);
  308.             }
  309.             if (retry == 0 || SD_SendCmd(CMD16, 512, 0X01) != 0)SD_Type = SD_TYPE_ERR; //错误的卡
  310.         }
  311.     }
  312.     SD_DisSelect();//取消片选
  313.     if (SD_Type)return 0;
  314.     else if (r1)return r1;
  315.     return 0xaa;//其他错误
  316. }

  317. /**
  318. * 读取SD卡一个扇区
  319. * @param  buf    存放读取的数据
  320. * @param  sector 扇区编号
  321. * @param  cnt    要读取的扇区个数
  322. * @return        0:成功  other:失败
  323. */
  324. uint8_t SD_ReadDisk(uint8_t*buf, uint32_t sector, uint8_t cnt)
  325. {
  326.     uint8_t r1;
  327.     if (SD_Type != SD_TYPE_V2HC)sector <<= 9; //转换为字节地址
  328.     if (cnt == 1)
  329.     {
  330.         r1 = SD_SendCmd(CMD17, sector, 0X01); //读命令
  331.         if (r1 == 0) //指令发送成功
  332.         {
  333.             r1 = SD_RecvData(buf, 512); //接收512个字节
  334.         }
  335.     }
  336.     else
  337.     {
  338.         r1 = SD_SendCmd(CMD18, sector, 0X01); //连续读命令
  339.         do
  340.         {
  341.             r1 = SD_RecvData(buf, 512); //接收512个字节
  342.             buf += 512;
  343.         }
  344.         while (--cnt && r1 == 0);
  345.         SD_SendCmd(CMD12, 0, 0X01);        //发送停止命令
  346.     }
  347.     SD_DisSelect();//取消片选
  348.     return r1;//
  349. }

  350. /**
  351. * SD卡写一个扇区的数据
  352. * @param  buf    存放待写入的数据
  353. * @param  sector 扇区编号
  354. * @param  cnt    要写入的扇区个数
  355. * @return        0:成功  other:失败
  356. */
  357. uint8_t SD_WriteDisk(uint8_t*buf, uint32_t sector, uint8_t cnt)
  358. {
  359.     uint8_t r1;
  360.     if (SD_Type != SD_TYPE_V2HC)sector *= 512; //转换为字节地址
  361.     if (cnt == 1)
  362.     {
  363.         r1 = SD_SendCmd(CMD24, sector, 0X01); //读命令
  364.         if (r1 == 0) //指令发送成功
  365.         {
  366.             r1 = SD_SendBlock(buf, 0xFE); //写512个字节
  367.         }
  368.     }
  369.     else
  370.     {
  371.         if (SD_Type != SD_TYPE_MMC)
  372.         {
  373.             SD_SendCmd(CMD55, 0, 0X01);
  374.             SD_SendCmd(CMD23, cnt, 0X01); //发送指令
  375.         }
  376.         r1 = SD_SendCmd(CMD25, sector, 0X01); //连续读命令
  377.         if (r1 == 0)
  378.         {
  379.             do
  380.             {
  381.                 r1 = SD_SendBlock(buf, 0xFC); //接收512个字节
  382.                 buf += 512;
  383.             }
  384.             while (--cnt && r1 == 0);
  385.             r1 = SD_SendBlock(0, 0xFD); //接收512个字节
  386.         }
  387.     }
  388.     SD_DisSelect();//取消片选
  389.     return r1;//
  390. }
复制代码
  1. #ifndef _MMC_SD_H_
  2. #define _MMC_SD_H_

  3. #include "main.h"

  4. // SD卡扇区大小
  5. #define SD_SECTOR_SIZE  512U

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

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

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

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

  44. // SD卡片选引脚控制,使用STM32CUBEMX配置SD卡片选引脚为输出模式并设置为高电平
  45. #define SD_CS_H()                HAL_GPIO_WritePin(SD_CS_GPIO_Port, SD_CS_Pin, GPIO_PIN_SET)
  46. #define SD_CS_L()                HAL_GPIO_WritePin(SD_CS_GPIO_Port, SD_CS_Pin, GPIO_PIN_RESET)

  47. extern uint8_t  SD_Type;                        //SD卡的类型

  48. //函数申明区
  49. uint8_t SD_WaitReady(void);                                                        //等待SD卡准备
  50. uint8_t SD_GetResponse(uint8_t Response);                                        //获得相应
  51. uint8_t SD_Initialize(void);                                                        //初始化
  52. uint8_t SD_ReadDisk(uint8_t*buf,uint32_t sector,uint8_t cnt);                //读块
  53. uint8_t SD_WriteDisk(uint8_t*buf,uint32_t sector,uint8_t cnt);                //写块
  54. uint32_t SD_GetSectorCount(void);                                           //读扇区数
  55. uint8_t SD_GetCID(uint8_t *cid_data);                     //读SD卡CID
  56. uint8_t SD_GetCSD(uint8_t *csd_data);                     //读SD卡CSD

  57. #endif


复制代码

收藏 评论0 发布时间:2022-4-11 10:16

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版