当前位置:   article > 正文

stm32读写SD卡(SPI模式)_spi sd卡

spi sd卡

目录

一、SD卡简介

二、源码下载

三、移植条件

1、芯片参数

2、硬件连接

四、驱动代码

1、依赖宏如下

2、驱动代码实现

3、测试代码

4、运行截图


一、SD卡简介

SD卡有SD驱动模式和SPI驱动模式,本例中使用SPI模式驱动SD卡。

二、源码下载

https://download.csdn.net/download/qq_30095023/88014550

三、移植条件

1、芯片参数

芯片类型:STM32F103VET6。

flash大小为512KB,RAM大小 64KB。

2、硬件连接

本例使用TFT屏幕上的SD卡插口,测试所用SD卡容量大小为4GB。

 SD_CS、SPI_MOSI、SPI_MISO、SPI_CLK 与单片机连接。note:请确保屏幕电源连接正常。

如上图,红色框框所标记区域为TFT屏幕SD卡的SPI接口,将此接口与STM32单片机的SPI所对应的IO连接。

四、驱动代码

note: u8 、u16、u32等为自定义数据类型,编译报错请按需修改。LOG_XX为日志打印,请按需替换。

1、依赖宏如下

定义一些操作SD卡的命令和SD卡的类型。

  1. //CMD定义
  2. #define CMD0 0//卡复位
  3. #define CMD1 1
  4. #define CMD8 8//命令8 ,SEND_IF_COND
  5. #define CMD9 9//命令9 ,读CSD数据
  6. #define CMD10 10//命令10,读CID数据
  7. #define CMD12 12//命令12,停止数据传输
  8. #define CMD16 16//命令16,设置SectorSize 应返回0x00
  9. #define CMD17 17//命令17,读sector
  10. #define CMD18 18//命令18,读Multi sector
  11. #define CMD23 23//命令23,设置多sector写入前预先擦除N个block
  12. #define CMD24 24//命令24,写sector
  13. #define CMD25 25//命令25,写Multi sector
  14. #define CMD41 41//命令41,应返回0x00
  15. #define CMD55 55//命令55,应返回0x01
  16. #define CMD58 58//命令58,读OCR信息
  17. #define CMD59 59//命令59,使能/禁止CRC,应返回0x00
  18. //SD卡类型
  19. #define ERR 0x00
  20. #define MMC 0x01
  21. #define V1 0x02
  22. #define V2 0x04
  23. #define V2HC 0x06

2、驱动代码实现

1)、ENTER_SD_BLOCK_SIZE定义SD卡的物理扇区大小为512Byte。

2)、函数SPI_Enable_T用来使能SPI接口,在SDinit的时候需要调用。

3)、函数inline u8 SPI_WriteByte_T用来单片机通过SPI读写SD卡,这里使用内联方式,减少函数调用过程中时间代价,有效提高SD卡的读写速率。关于inline关键字的详细用法请参考:

C语言基础知识--inline(内联)关键字_BIN-XYB的博客-CSDN博客

4)、 函数b_sd_read_write_byte用来读写数据。

5)、函数b_sd_send_cmd用来发送指令给SD卡。

6)、函数b_sd_read_data用来从SD卡读取指定长度的数据。

7)、函数b_sd_send_block_data用来向SD卡写入数据。

8)、函数b_sd_set_speed用来配置SPI通信速度。

9)、函数b_sd_init用来初始化SD卡,需要在使用SD卡前调用一次。

10)、函数b_sd_get_cid用来查询SD卡的CID。

11)、函数b_sd_get_csd用来查询SD卡的CSD。

12)、函数b_sd_read_sector用来读扇区数据,该接口一般用于FatFs文件系统。

13)、函数b_sd_write_sector用来写扇区数据,该接口一般用于FatFs文件系统。

FatFs文件系统移植请参考:FatFs移植到STM32(SD卡)_BIN-XYB的博客-CSDN博客

14)、函数b_sd_get_sector_number用来查询SD卡的扇区数量。

15)、函数b_sd_sync_data用来保存SD卡的数据。

完整源码如下:

  1. #define ENTER_SD_BLOCK_SIZE (512)
  2. #define ENTER_SD_SECTOR_SIZE (ENTER_SD_BLOCK_SIZE)
  3. #define GPIO_TYPE GPIOD //GPIO组类型
  4. #define SD_CS SD_CS_Pin//片选引脚PD8
  5. #define SPI hspi2 //spi设备
  6. #define SD_CS_DISABLE() GPIO_TYPE->BSRR=SD_CS//GPIO置位(拉高)
  7. #define SD_CS_ENABLE() GPIO_TYPE->BRR=SD_CS//GPIO复位(拉低)
  8. static u8 SPI_Enable_T(SPI_HandleTypeDef *hspi)
  9. {
  10. if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE)
  11. {
  12. __HAL_SPI_ENABLE(hspi);
  13. }
  14. return 0;
  15. }
  16. static inline u8 SPI_WriteByte_T(SPI_HandleTypeDef* hspi,u8 Byte)
  17. {
  18. while((hspi->Instance->SR&SPI_FLAG_TXE)==RESET);//等待发送区空
  19. hspi->Instance->DR=Byte;//发送一个byte
  20. while((hspi->Instance->SR&SPI_FLAG_RXNE)==RESET);//等待接收完一个byte
  21. return hspi->Instance->DR;//返回收到的数据
  22. }
  23. #define SPI_WriteByte(spi_x, data) SPI_WriteByte_T(&spi_x, data)
  24. #define SPI_Enable(spi_x) SPI_Enable_T(&spi_x)
  25. u8 b_sd_read_write_byte(u8 tx_data)
  26. {
  27. u8 rx_data = SPI_WriteByte(SPI, tx_data);
  28. return rx_data;
  29. }
  30. static int b_sd_send_cmd(u8 cmd,u32 arg, u8 crc)
  31. {
  32. u8 r1;
  33. u8 retry;
  34. SD_CS_DISABLE();
  35. b_sd_read_write_byte(0XFF);
  36. SD_CS_ENABLE();
  37. do
  38. {
  39. retry = b_sd_read_write_byte(0XFF);
  40. }while(retry!=0xFF);
  41. b_sd_read_write_byte(cmd | 0x40);
  42. b_sd_read_write_byte(arg >> 24);
  43. b_sd_read_write_byte(arg >> 16);
  44. b_sd_read_write_byte(arg >> 8);
  45. b_sd_read_write_byte(arg);
  46. b_sd_read_write_byte(crc);
  47. if(cmd == CMD12)
  48. {
  49. b_sd_read_write_byte(0XFF);
  50. }
  51. do
  52. {
  53. r1 = b_sd_read_write_byte(0xFF);
  54. }while(r1 & 0X80);
  55. return r1;
  56. }
  57. static u8 sd_type = 0;
  58. static u8 b_sd_read_data(u8 *data, u16 len)
  59. {
  60. u8 r1 = 0;
  61. SD_CS_ENABLE();
  62. do
  63. {
  64. r1 = b_sd_read_write_byte(0xFF);
  65. }while(r1 != 0xFE);
  66. while(len--)
  67. {
  68. *data = b_sd_read_write_byte(0xFF);
  69. data++;
  70. }
  71. b_sd_read_write_byte(0xFF);
  72. b_sd_read_write_byte(0xFF);
  73. return 0;
  74. }
  75. static u8 b_sd_send_block_data(u8* data, u8 cmd)
  76. {
  77. u16 t;
  78. u8 r1;
  79. do{
  80. r1 = b_sd_read_write_byte(0xFF);
  81. }while(r1 != 0xFF);
  82. b_sd_read_write_byte(cmd);
  83. if(cmd != 0XFD)//不是结束指令
  84. {
  85. for(t = 0; t < ENTER_SD_BLOCK_SIZE; t++)
  86. {
  87. b_sd_read_write_byte(data[t]);//提高速度,减少函数传参时间
  88. }
  89. b_sd_read_write_byte(0xFF);//忽略crc
  90. b_sd_read_write_byte(0xFF);
  91. t = b_sd_read_write_byte(0xFF);//接收响应
  92. if((t & 0x1F) != 0x05)
  93. {
  94. return 2;//响应错误
  95. }
  96. }
  97. return 0;//写入成功
  98. }
  99. inline void b_sd_set_speed(u32 speed)
  100. {
  101. hspi2.Init.BaudRatePrescaler = speed;
  102. }
  103. u8 b_sd_init(void)
  104. {
  105. u8 r1;
  106. u8 buff[6] = {0};
  107. u16 retry;
  108. u8 i;
  109. SPI_Enable(SPI);
  110. b_sd_set_speed(SPI_BAUDRATEPRESCALER_256);
  111. SD_CS_DISABLE();
  112. for(retry = 0; retry < 10; retry++)
  113. {
  114. b_sd_read_write_byte(0XFF);
  115. }
  116. do//SD卡进入IDLE状态
  117. {
  118. r1 = b_sd_send_cmd(CMD0 ,0, 0x95);
  119. }while(r1 != 0x01);
  120. //查看SD卡的类型
  121. sd_type = 0;
  122. r1 = b_sd_send_cmd(CMD8, 0x1AA, 0x87);
  123. if(r1 == 0x01)
  124. {
  125. for(i = 0; i < 4; i++)
  126. {
  127. buff[i] = b_sd_read_write_byte(0XFF);//Get trailing return value of R7 resp
  128. }
  129. if(buff[2] == 0X01 && buff[3] == 0XAA)//卡是否支持2.7~3.6V
  130. {
  131. retry = 0XFFFE;
  132. do
  133. {
  134. b_sd_send_cmd(CMD55, 0, 0X01);//发送CMD55
  135. r1 = b_sd_send_cmd(CMD41, 0x40000000, 0X01);//发送CMD41
  136. }while(r1 && retry--);
  137. if(retry && b_sd_send_cmd(CMD58, 0, 0X01) == 0)//鉴别SD2.0卡版本开始
  138. {
  139. for(i = 0; i < 4; i++)
  140. {
  141. buff[i] = b_sd_read_write_byte(0XFF);//得到OCR值
  142. }
  143. if(buff[0] & 0x40)
  144. {
  145. sd_type = V2HC;
  146. }else {
  147. sd_type = V2;
  148. }
  149. }
  150. }
  151. else
  152. {
  153. b_sd_send_cmd(CMD55, 0, 0X01);//发送CMD55
  154. r1 = b_sd_send_cmd(CMD41, 0, 0X01);//发送CMD41
  155. if(r1 <= 1)
  156. {
  157. sd_type = V1;
  158. retry = 0XFFFE;
  159. do //等待退出IDLE模式
  160. {
  161. b_sd_send_cmd(CMD55, 0, 0X01);//发送CMD55
  162. r1 = b_sd_send_cmd(CMD41,0,0X01);//发送CMD41
  163. }while(r1 && retry--);
  164. }else//MMC卡不支持CMD55+CMD41识别
  165. {
  166. sd_type = MMC;//MMC V3
  167. retry = 0XFFFE;
  168. do //等待退出IDLE模式
  169. {
  170. r1 = b_sd_send_cmd(CMD1, 0, 0X01);//发送CMD1
  171. }while(r1 && retry--);
  172. }
  173. if(retry == 0 || b_sd_send_cmd(CMD16, 512, 0X01) != 0)
  174. {
  175. sd_type = 0;//错误的卡
  176. }
  177. }
  178. }
  179. SD_CS_DISABLE();
  180. b_sd_set_speed(SPI_BAUDRATEPRESCALER_2);
  181. if(sd_type)
  182. {
  183. return HAL_OK;
  184. }
  185. else
  186. {
  187. return HAL_ERROR;
  188. }
  189. }
  190. u8 b_sd_get_cid(u8* cid_data)
  191. {
  192. u8 r1 = b_sd_send_cmd(CMD10, 0, 0x01);//读取CID寄存器
  193. if(r1 == 0x00)
  194. {
  195. r1 = b_sd_read_data(cid_data,16);
  196. }
  197. SD_CS_DISABLE();
  198. if(r1 != 0)
  199. {
  200. return HAL_ERROR;
  201. }
  202. else
  203. {
  204. return HAL_OK;
  205. }
  206. }
  207. u8 b_sd_get_csd(u8 *csd_data)
  208. {
  209. u8 r1 = b_sd_send_cmd(CMD9, 0, 0x01);//发CMD9命令,读CSD寄存器
  210. if(r1 == 0)
  211. {
  212. r1 = b_sd_read_data(csd_data, 16);//接收16个字节的数据
  213. }
  214. SD_CS_DISABLE();//取消片选
  215. if(r1)
  216. {
  217. return 1;
  218. }
  219. else
  220. {
  221. return 0;
  222. }
  223. }
  224. s32 b_sd_read_sector(u32 sector, u8 number, u8*data, u32 timeout)
  225. {
  226. u8 r1;
  227. if(sd_type != V2HC)
  228. {
  229. sector <<= 9;//转换为字节地址
  230. }
  231. if(number == 1)
  232. {
  233. r1 = b_sd_send_cmd(CMD17, sector, 0X01);//读命令
  234. if(r1 == 0)//指令发送成功
  235. {
  236. r1 = b_sd_read_data(data, ENTER_SD_SECTOR_SIZE);//接收512个字节
  237. }
  238. }
  239. else
  240. {
  241. r1 = b_sd_send_cmd(CMD18, sector, 0X01);//连续读命令
  242. do
  243. {
  244. r1 = b_sd_read_data(data, ENTER_SD_SECTOR_SIZE);//接收512个字节
  245. data += ENTER_SD_SECTOR_SIZE;
  246. }while(--number && r1 == 0);
  247. b_sd_send_cmd(CMD12, 0, 0X01);//发送停止命令
  248. }
  249. SD_CS_DISABLE();//取消片选
  250. return HAL_OK;
  251. }
  252. s32 b_sd_write_sector(u32 sector, u8 number, u8 *data, u32 timeout)
  253. {
  254. u8 r1;
  255. if(sd_type != V2HC)
  256. {
  257. sector *= ENTER_SD_SECTOR_SIZE;//转换为字节地址
  258. }
  259. if(number == 1)
  260. {
  261. r1 = b_sd_send_cmd(CMD24, sector, 0X01);//读命令
  262. if(r1 == 0)//指令发送成功
  263. {
  264. r1 = b_sd_send_block_data(data, 0xFE);//写512个字节
  265. }
  266. }else
  267. {
  268. if(sd_type != MMC)
  269. {
  270. b_sd_send_cmd(CMD55, 0, 0X01);
  271. b_sd_send_cmd(CMD23, number, 0X01);//发送指令
  272. }
  273. r1 = b_sd_send_cmd(CMD25, sector, 0X01);//连续读命令
  274. if(r1 == 0)
  275. {
  276. do
  277. {
  278. r1 = b_sd_send_block_data(data, 0xFC);//接收512个字节
  279. data += ENTER_SD_SECTOR_SIZE;
  280. }while(--number && r1 == 0);
  281. r1 = b_sd_send_block_data(0, 0xFD);//接收512个字节
  282. }
  283. }
  284. SD_CS_DISABLE();//取消片选
  285. return HAL_OK;
  286. }
  287. u32 b_sd_get_sector_number(void)
  288. {
  289. u8 csd[16];
  290. u32 capacity;
  291. u16 csize;
  292. //取CSD信息,如果期间出错,返回0
  293. if(b_sd_get_csd(csd)!=0)
  294. {
  295. return 0;
  296. }
  297. //如果为SDHC卡,按照下面方式计算
  298. if((csd[0] & 0xC0) == 0x40)//V2.00的卡
  299. {
  300. csize = csd[9] + ((u16)csd[8] << 8) + 1;
  301. capacity = (u32)csize << 10;//得到扇区数
  302. }
  303. else//V1.XX的卡
  304. {
  305. u8 n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
  306. csize = (csd[8] >> 6) + ((u16)csd[7] << 2) + ((u16)(csd[6] & 3) << 10) + 1;
  307. capacity= (u32)csize << (n - 9);//得到扇区数
  308. }
  309. return capacity;
  310. }
  311. u32 b_sd_get_sector_size(void)
  312. {
  313. return ENTER_SD_SECTOR_SIZE;
  314. }
  315. u32 b_sd_get_block_size(void)
  316. {
  317. return ENTER_SD_BLOCK_SIZE;
  318. }
  319. u32 b_sd_sync_data(void)
  320. {
  321. SD_CS_ENABLE();//片选
  322. do{
  323. HAL_Delay(5);
  324. }while(b_sd_read_write_byte(0xFF)!=0xFF);
  325. SD_CS_DISABLE();//取消片选
  326. return 0;
  327. }

3、测试代码

  1. void b_sd_test_demo(void)
  2. {
  3. static u8 test_buffer[ENTER_SD_SECTOR_SIZE] = {0};
  4. LOG_INFO("start b_sd_init\r\n");
  5. u8 ret = b_sd_init();
  6. u8 *p = test_buffer;
  7. LOG_INFO("b_sd_init:%d\r\n", sd_type);
  8. memset(test_buffer, 0, ENTER_SD_SECTOR_SIZE);
  9. ret = b_sd_get_cid(test_buffer);
  10. LOG_INFO("cid:%02X,%02X,%02X,%02X\r\n", p[0],p[1],p[2],p[3]);
  11. LOG_INFO("cid:%02X,%02X,%02X,%02X\r\n", p[4],p[5],p[6],p[7]);
  12. LOG_INFO("cid:%02X,%02X,%02X,%02X\r\n", p[8],p[9],p[10],p[11]);
  13. LOG_INFO("cid:%02X,%02X,%02X,%02X\r\n", p[12],p[13],p[14],p[15]);
  14. LOG_INFO("b_sd_get_cid:%d\r\n", ret);
  15. memset(test_buffer, 0, ENTER_SD_SECTOR_SIZE);
  16. ret = b_sd_get_csd(test_buffer);
  17. LOG_INFO("csd:%02X,%02X,%02X,%02X\r\n", p[0],p[1],p[2],p[3]);
  18. LOG_INFO("csd:%02X,%02X,%02X,%02X\r\n", p[4],p[5],p[6],p[7]);
  19. LOG_INFO("csd:%02X,%02X,%02X,%02X\r\n", p[8],p[9],p[10],p[11]);
  20. LOG_INFO("csd:%02X,%02X,%02X,%02X\r\n", p[12],p[13],p[14],p[15]);
  21. LOG_INFO("b_sd_get_csd:%d\r\n", ret);
  22. u32 num = b_sd_get_sector_number();
  23. LOG_INFO("b_sd_get_sector_number:%d\r\n", num);
  24. }

 测试代码先sd初始化,然后查询cid,csd,再查询SD容量。

4、运行截图

可以看到,成功查到SD数据。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/295086
推荐阅读
相关标签
  

闽ICP备14008679号