文章目录

  • 1. 准备工作
    • 1.1. 简单扫盲
      • 1.1.1. SD卡
      • 1.1.2. TF卡
      • 1.1.3. SDIO接口
    • 1.2. 所用硬件及原理图
  • 2. 创建工程
    • 2.1. 选择主控
    • 2.2. 系统配置
    • 2.3. 配置工程目录
  • 3. SD卡读写实验
    • 3.1. 原理图
    • 3.2. 代码实现(轮询模式)
    • 3.4. 程序中设置参数
    • 3.3. 代码实现(DMA模式)【还有问题没解决】
  • 4. SD卡移植FATFS文件系统
    • 4.1. FATFS配置
    • 4.2. 修改SD卡插入检测代码
    • 4.3. 代码实现
    • 4.4. 注意事项

====>>> 文章汇总(有代码汇总) <<<====

1. 准备工作

1.1. 简单扫盲

准备看看这方面的知识,一时间还没不清有什么区别,先补补课,不需要的跳过。
参考文章(内容来源):http://www.360doc.com/content/21/1125/22/59057945_1005908465.shtml

主要写这两个:SD卡、TF卡

共同点:SD、TF、MMC都是在MMC基础上演化发展不同的规范,比如物理尺寸,封装,电压,管脚,位宽,时钟信号等不同,但都使用相同的总线规范。

1.1.1. SD卡

SD卡(Secure Digital Card,安全数字卡)图片如下:

SD卡是(secure digital memory card)安全数码卡,是在MMC基础上发展起来的。
增加特色:

  1. 可以设置所存储的使用权限,防止数据被他人复制;
  2. 传输速度比2.11版mmc卡快。

特性:

  1. 可选通信协议:SD模式 和 SPI模式
  2. 可变时钟频率:0~25Mhz
  3. 通信电压范围:2.0~3.6V
  4. 数据寿命:10万次编程/擦除
  5. 正向兼容MMC卡
  6. 运行在25M的频率上,数据带宽是4位,因此最大传输速率是12.5MHz(12.5兆字节每秒)。

引脚定义:

1.1.2. TF卡

Micro SD Card,原名Trans-flash Card(TF卡),2004年正式更名为Micro SD Card。

特点:

  1. SD卡 比 TF卡 的尺寸要大。
  2. 应用于不同产品,SD卡一般都用在大一些的电子设备:如电脑,相机,AV等器材,而TF一般用在手机上。TF卡我们见的比较多,图片如下:
  3. SD 卡上有一个(lock)开关(上图中右边的图,卡左边有个波动的开关),即写保护开关,TF卡没有。

除此之外,其实没什么区别。另外:TF卡插入适配器(adapter)可以转换成SD卡,但SD卡一般无法转换成TF卡。

引脚定义:

1.1.3. SDIO接口

SDIO接口是在SD内存卡接口的基础上发展起来的接口,SDIO接口兼容以前的SD内存卡,并且可以连接SDIO接口的设备。

SDIO接口的信号传输模式有SPI、1-bit、4-bit三种。

  • 在SPI模式中,第8脚位被当成中断信号。其它脚位的功能和通信协定与SD记忆卡的标准规范一样。
  • 在SDIO总线定义中,DAT1信号线复用为中断线。
  • 在SDIO的 1BIT 模式下 DAT0 用来传输数据,DAT1用作中断线。
  • 在SDIO的 4BIT 模式下 DAT0-DAT3 用来传输数据,其中DAT1复用作中断线。

1.2. 所用硬件及原理图

此处硬件上不同的开发板可能略有差异,但是实际开发的过程中都是一样的。

STM32F103 普中-准端-Z100,主控 STM32F103ZET6。这个是直接用SD卡插上去就可以了。这里我们用这个开发板进行实验

正点原子Mini板,主控 STM32F103RCT6。这个是用了一个比较大的SD卡的卡槽,如果有SD卡可以直接插上去,如果是用的Micro SD Card卡,需要一个适配器才能连。

2. 创建工程

2.1. 选择主控

2.2. 系统配置

配置时钟源

配置debug模式(如果需要ST-Link下载及调试可以勾选)

配置时钟树(可以直接在HCLK那里输入72,然后敲回车会自动配置)

2.3. 配置工程目录


3. SD卡读写实验

3.1. 原理图

3.2. 代码实现(轮询模式)

先把串口重定向配置一下,方便观察–>串口重定向配置<–

SDIO配置

SDIO分为两个部分:AHB interface和SDIO adapter。

  • AHB interface采用的时钟是HCLK/2=36MHz, 是用来访问STM32 SDIO本身的寄存器的。
  • SDIO adapter采用的时钟是SDIOCLK=HCLK=72MHz,SDIO_CK时钟线(PC12脚,单片机给SD卡提供的时钟)输出的时钟就是从这个上面分频得到的,分频公式为SDIOCLK/(CLKDIV+2)。CLKDIV就是hsd.Init.ClockDiv的值。(就是我们上面设定的值)
    • 当CLKDIV=70时,SDIO_CK输出的频率为72MHz/(70+2)=1MHz。在这个频率下可以不使用DMA收发数据。
    • 当CLKDIV=1时,SDIO_CK输出的频率为72MHz/(1+2)=24MHz。在这个频率下必须使用DMA收发数据。

前面第一章说了 SDIO的时钟频率为0~25Mhz,此处设置分频系数为34,而我们开发板的时钟频率为72Mhz,这里分频系数为34,也就是72/(34+2) = 2Mhz。(其他数也行,反正分频完不要超过25,否则读写都会失败)
不过,实际上经过测试,设置为4,也就是分频完为 72 / 6 = 12,也仍然会写失败,又是踩坑的一天。

然后生成工程。

main.c中,while(1)之前。

   /* USER CODE BEGIN 2 */printf("Micro SD Card Test... \r\n");uint8_t read_buf[512];      // 读数据缓存uint8_t write_buf[512];     // 写数据缓存/* SD卡状态 */int sdcard_status = 0;HAL_SD_CardCIDTypeDef sdcard_cid;/* 获取SD卡状态 */sdcard_status = HAL_SD_GetCardState(&hsd);// 处于数据传输模式的传输状态if(sdcard_status == HAL_SD_CARD_TRANSFER){printf("SD card init ok!\r\n\r\n");// 打印SD卡基本信息printf("SD card information! \r\n");// 容量信息printf("CardCapacity(Byte): %llu \r\n",((unsigned long long)hsd.SdCard.BlockSize * hsd.SdCard.BlockNbr));// 块大小 默认都是512个字节printf("CardBlockSize(Byte): %d \r\n", hsd.SdCard.BlockSize);// 有多少个块printf("CardBlockNumber: %d \r\n", hsd.SdCard.BlockNbr);printf("RCA: %d \r\n", hsd.SdCard.RelCardAdd);printf("CardType: %d \r\n", hsd.SdCard.CardType);// 读取并打印SD卡的CID信息HAL_SD_GetCardCID(&hsd, &sdcard_cid);// 制造商printf("ManufacturerID: %d \r\n",sdcard_cid.ManufacturerID);}else{printf("SD card init fail! \r\n" );return 0;}/* 读取未操作之前的数据 */printf("------------------- Read SD card block data Test ------------------\r\n");/*读一个扇区的数据:0: 从第0个扇区开始。1:读一个扇区的数据。0xffff:等待时间。note:也就是只读了第0个扇区。*/sdcard_status = HAL_SD_ReadBlocks(&hsd, (uint8_t *)read_buf, 0, 1, 0xffff);if(sdcard_status == HAL_OK){ printf("Read block data ok! \r\n");for(int i = 0; i < 512; i++){printf("0x%02x ", read_buf[i]);if((i+1)%16 == 0){printf("\r\n");}}}else{printf("Read block data fail! status = %d \r\n", sdcard_status);}/* 向SD卡块写入数据 */printf("------------------- Write SD card block data Test ------------------\r\n");/* 填充缓冲区数据 */for(int i = 0; i < 512; i++){write_buf[i] = i % 256;}// 开始写入数据/*写一个扇区的数据:0: 从第0个扇区开始。1:写一个扇区的数据。0xffff:等待时间。note:也就是只写了第0个扇区。*/sdcard_status = HAL_SD_WriteBlocks(&hsd, (uint8_t *)write_buf, 0, 1, 0xffff);if(sdcard_status == HAL_OK){ /* 传输完成不代表写入完成,因此要等待SD卡状态变为可传输状态。擦除操作也是一样。 */printf("Writing block data. state = %d \r\n", HAL_SD_GetCardState(&hsd));while (HAL_SD_GetCardState(&hsd) == HAL_SD_CARD_PROGRAMMING);printf("Write block data ok,state = %d \r\n", HAL_SD_GetCardState(&hsd));}else{printf("Write block data fail! status = %d \r\n", sdcard_status);}/* 读取写入之后的数据 */printf("------------------- Read SD card block data after Write ------------------\r\n");sdcard_status = HAL_SD_ReadBlocks(&hsd, (uint8_t *)read_buf, 0, 1, 0xffff);if(sdcard_status == HAL_OK){ printf("Read block data ok! \r\n");for(int i = 0; i < 512; i++){printf("0x%02x ", read_buf[i]);if((i+1)%16 == 0){printf("\r\n");}}}else{printf("Read block data fail! status = %d \r\n", sdcard_status);}/* 擦除SD卡块 */printf("------------------- Block Erase -------------------------------\r\n");/*擦除512个扇区的数据:0: 从第0个扇区开始。1:一直擦除到512扇区。note:擦除第0到第512个扇区数据,也包括0和512,也就是一共512个。*/sdcard_status = HAL_SD_Erase(&hsd, 0, 512);// 等待擦除完毕if (sdcard_status == HAL_OK){ printf("Erasing block. state = %d \r\n", HAL_SD_GetCardState(&hsd));while (HAL_SD_GetCardState(&hsd) == HAL_SD_CARD_PROGRAMMING);printf("Erase block ok state = %d \r\n", HAL_SD_GetCardState(&hsd));}else{printf("Erase block fail! status = %d \r\n", sdcard_status);}/* 读取擦除之后的数据 */printf("------------------- Read SD card block data after Erase ------------------\r\n");sdcard_status = HAL_SD_ReadBlocks(&hsd, (uint8_t *)read_buf, 0, 1, 0xffff);if(sdcard_status == HAL_OK){ printf("Read block data ok \r\n" );for(int i = 0; i < 512; i++){printf("0x%02x ", read_buf[i]);if((i+1)%16 == 0){printf("\r\n");}}}else{printf("Read block data fail! status = %d \r\n", sdcard_status);}printf("------------------- Over ------------------\r\n");/* USER CODE END 2 */

实验效果
编译、烧录、串口下载。

Micro SD Card Test...
SD card init ok!SD card information!
CardCapacity(Byte): 3965190144
CardBlockSize(Byte): 512
CardBlockNumber: 7744512
RCA: 58916
CardType: 1
ManufacturerID: 3
------------------- Read SD card block data Test ------------------
Read block data ok!
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
------------------- Write SD card block data Test ------------------
Writing block data. state = 7
Write block data ok,state = 4
------------------- Read SD card block data after Write ------------------
Read block data ok!
0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f
0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1a 0x1b 0x1c 0x1d 0x1e 0x1f
0x20 0x21 0x22 0x23 0x24 0x25 0x26 0x27 0x28 0x29 0x2a 0x2b 0x2c 0x2d 0x2e 0x2f
0x30 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39 0x3a 0x3b 0x3c 0x3d 0x3e 0x3f
0x40 0x41 0x42 0x43 0x44 0x45 0x46 0x47 0x48 0x49 0x4a 0x4b 0x4c 0x4d 0x4e 0x4f
0x50 0x51 0x52 0x53 0x54 0x55 0x56 0x57 0x58 0x59 0x5a 0x5b 0x5c 0x5d 0x5e 0x5f
0x60 0x61 0x62 0x63 0x64 0x65 0x66 0x67 0x68 0x69 0x6a 0x6b 0x6c 0x6d 0x6e 0x6f
0x70 0x71 0x72 0x73 0x74 0x75 0x76 0x77 0x78 0x79 0x7a 0x7b 0x7c 0x7d 0x7e 0x7f
0x80 0x81 0x82 0x83 0x84 0x85 0x86 0x87 0x88 0x89 0x8a 0x8b 0x8c 0x8d 0x8e 0x8f
0x90 0x91 0x92 0x93 0x94 0x95 0x96 0x97 0x98 0x99 0x9a 0x9b 0x9c 0x9d 0x9e 0x9f
0xa0 0xa1 0xa2 0xa3 0xa4 0xa5 0xa6 0xa7 0xa8 0xa9 0xaa 0xab 0xac 0xad 0xae 0xaf
0xb0 0xb1 0xb2 0xb3 0xb4 0xb5 0xb6 0xb7 0xb8 0xb9 0xba 0xbb 0xbc 0xbd 0xbe 0xbf
0xc0 0xc1 0xc2 0xc3 0xc4 0xc5 0xc6 0xc7 0xc8 0xc9 0xca 0xcb 0xcc 0xcd 0xce 0xcf
0xd0 0xd1 0xd2 0xd3 0xd4 0xd5 0xd6 0xd7 0xd8 0xd9 0xda 0xdb 0xdc 0xdd 0xde 0xdf
0xe0 0xe1 0xe2 0xe3 0xe4 0xe5 0xe6 0xe7 0xe8 0xe9 0xea 0xeb 0xec 0xed 0xee 0xef
0xf0 0xf1 0xf2 0xf3 0xf4 0xf5 0xf6 0xf7 0xf8 0xf9 0xfa 0xfb 0xfc 0xfd 0xfe 0xff
0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f
0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1a 0x1b 0x1c 0x1d 0x1e 0x1f
0x20 0x21 0x22 0x23 0x24 0x25 0x26 0x27 0x28 0x29 0x2a 0x2b 0x2c 0x2d 0x2e 0x2f
0x30 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39 0x3a 0x3b 0x3c 0x3d 0x3e 0x3f
0x40 0x41 0x42 0x43 0x44 0x45 0x46 0x47 0x48 0x49 0x4a 0x4b 0x4c 0x4d 0x4e 0x4f
0x50 0x51 0x52 0x53 0x54 0x55 0x56 0x57 0x58 0x59 0x5a 0x5b 0x5c 0x5d 0x5e 0x5f
0x60 0x61 0x62 0x63 0x64 0x65 0x66 0x67 0x68 0x69 0x6a 0x6b 0x6c 0x6d 0x6e 0x6f
0x70 0x71 0x72 0x73 0x74 0x75 0x76 0x77 0x78 0x79 0x7a 0x7b 0x7c 0x7d 0x7e 0x7f
0x80 0x81 0x82 0x83 0x84 0x85 0x86 0x87 0x88 0x89 0x8a 0x8b 0x8c 0x8d 0x8e 0x8f
0x90 0x91 0x92 0x93 0x94 0x95 0x96 0x97 0x98 0x99 0x9a 0x9b 0x9c 0x9d 0x9e 0x9f
0xa0 0xa1 0xa2 0xa3 0xa4 0xa5 0xa6 0xa7 0xa8 0xa9 0xaa 0xab 0xac 0xad 0xae 0xaf
0xb0 0xb1 0xb2 0xb3 0xb4 0xb5 0xb6 0xb7 0xb8 0xb9 0xba 0xbb 0xbc 0xbd 0xbe 0xbf
0xc0 0xc1 0xc2 0xc3 0xc4 0xc5 0xc6 0xc7 0xc8 0xc9 0xca 0xcb 0xcc 0xcd 0xce 0xcf
0xd0 0xd1 0xd2 0xd3 0xd4 0xd5 0xd6 0xd7 0xd8 0xd9 0xda 0xdb 0xdc 0xdd 0xde 0xdf
0xe0 0xe1 0xe2 0xe3 0xe4 0xe5 0xe6 0xe7 0xe8 0xe9 0xea 0xeb 0xec 0xed 0xee 0xef
0xf0 0xf1 0xf2 0xf3 0xf4 0xf5 0xf6 0xf7 0xf8 0xf9 0xfa 0xfb 0xfc 0xfd 0xfe 0xff
------------------- Block Erase -------------------------------
Erasing block. state = 7
Erase block ok state = 4
------------------- Read SD card block data after Erase ------------------
Read block data ok
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
------------------- Over ------------------

3.4. 程序中设置参数

因为用了CubeMX之后,很多东西虽然会用了,但是到最后都没明白很多东西到底怎么设置的,因此这里还是要说明一下的。

  1. main.c的头文件中打开sdio.h
  2. sdio.h中有函数void MX_SDIO_SD_Init(void);
  3. 查看这个函数的定义,可以看到所有设置的参数 都在 SD_HandleTypeDef hsd;中设置
SD_HandleTypeDef hsd;void MX_SDIO_SD_Init(void)
{hsd.Instance = SDIO;hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;hsd.Init.BusWide = SDIO_BUS_WIDE_1B;hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;hsd.Init.ClockDiv = 34;if (HAL_SD_Init(&hsd) != HAL_OK){Error_Handler();}if (HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK){Error_Handler();}
}

问题:上面的函数中有一个 hsd.Init.BusWide = SDIO_BUS_WIDE_1B;,但是我们配置的不是四线的吗。

答案:(其实也不太明白为什么,但是先这样吧):
SD卡可以采用1位数据线模式,也可以采用4位数据线模式。但是必须确保STM32单片机的SDIO设置的数据线位宽,和SD卡上设置的数据线位宽是一致的。
如果将 hsd.Init.BusWide 设为 SDIO_BUS_WIDE_4B,然后执行HAL_SD_Init 函数,只能把STM32单片机的SDIO设置为4位位宽,SD卡上还是用的1位位宽。
所以通常的做法是 hsd.Init.BusWide 设为 SDIO_BUS_WIDE_1B,HAL_SD_Init 执行完成后,再调用 HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B),这个函数可以将STM32和SD卡同时设为4位模式。

3.3. 代码实现(DMA模式)【还有问题没解决】

STM32F1的主频较低,在SDIO时钟频率很高的情况下(如24MHz)必须要用DMA收发数据。
应用中一般都使用DMA 传输模式

因此我们把原有的配置的频率提高一点(非必须,只是频率高一点就只能用)

然后需要添DMA的配置

可以看到,作为一个传输数据的东西,SDIO是有两个方向的。但是只能添加一个DMA Handle,要么选择收,要么选择发。实际上SDIO收发数据是可以用同一个DMA Handle的。

在生成的代码中:
sdio.c文件中有个void HAL_SD_MspInit(SD_HandleTypeDef* sdHandle)函数。

设置完 hdma.Init 的其他成员后,调用了HAL_DMA_Init(&hdma_sdio)初始化DMA2_Channel4,之后,调用了两次__HAL_LINKDMA();函数,宏将hdma同时绑定到 hsd 的 hdmarx 和 hdmatx上。
然后在实际使用时,调用HAL_SD_ReadBlocks_DMA或者HAL_SD_WriteBlocks_DMA就会自动切换DMA传输方向。但是为了确保DMA传输方向能切换成功,必须在每次传输完成后关闭DMA,也就是在 HAL_SD_RxCpltCallback 和 HAL_SD_TxCpltCallback 传输完成回调函数里面调用__HAL_DMA_DISABLE(hsd->hdma)。
内容来源:https://blog.csdn.net/ZLK1214/article/details/121388735

中断优先级配置,一般用到DMA的地方,都需要把DMA的优先级比其他的优先级要低

main.c中 main函数 之前添加如下代码:

static volatile uint8_t sd_rx_complete = 0, sd_tx_complete = 0;void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)
{__HAL_DMA_DISABLE(hsd->hdmarx);sd_rx_complete = 1;
}void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
{__HAL_DMA_DISABLE(hsd->hdmatx);sd_tx_complete = 1;
}

main.c中while(1)之前添加如下代码:

 /* DMA模式读SD卡 */HAL_StatusTypeDef status;    // 状态uint8_t read_buf[512];     // 读数据缓存uint8_t write_buf[512];     // 写数据缓存printf("\r\n --------------------- 修改频率 -------------------- \r\n");/*设置SDIO时钟频率 = 72 /(ClockDiv + 2) = 18 Mhz1. 修改结构体 hsd 参数2. 重新初始化3. 判断初始化是否成功*/hsd.Init.ClockDiv = 2;status = HAL_SD_Init(&hsd);if (status == HAL_OK)printf("Frequency is changed to %d Hz.\r\n", SystemCoreClock / (2 + hsd.Init.ClockDiv));else{printf("Failed to change frequency! status = %d \r\n", status);}printf("\r\n --------------------- SD擦除 -------------------- \r\n");// 注意查看参数status = HAL_SD_Erase(&hsd, 0, 0);if (status == HAL_OK){printf("Erasing 1 sdcard blocks. state = %d \r\n", HAL_SD_GetCardState(&hsd));while (HAL_SD_GetCardState(&hsd) == HAL_SD_CARD_PROGRAMMING);printf("Erase complete! state = %d \r\n", HAL_SD_GetCardState(&hsd));}else{printf("Failed to erase 1 sdcard blocks! status = %d \r\n", status);}printf("\r\n --------------------- DMA模式读SD卡 -------------------- \r\n");// 从SD卡扇区0开始读, 读1个扇区到 read_buf 中// note:注意DMA模式下每次最多只能读127个扇区,也就是最后一个参数最多只能是127status = HAL_SD_ReadBlocks_DMA(&hsd, (uint8_t *)read_buf, 0, 1);if (status == HAL_OK){while (!sd_rx_complete);    // 等待传输完成(传输完成会有回调函数)sd_rx_complete = 0;         // 清除标志位printf("SDCard Block 1 with DMA \r\n");for(int i = 0; i < 512; i++){printf("0x%02x ", read_buf[i]);if((i+1)%16 == 0){printf("\r\n");}}}else{printf("Failed to read with DMA! status = %d \r\n", status);}printf("\r\n --------------------- DMA模式写SD卡 -------------------- \r\n");for(int i = 0; i < 512; i++){write_buf[i] = i;}status = HAL_SD_WriteBlocks_DMA(&hsd, (uint8_t *)write_buf, 0, 1);if (status == HAL_OK){while (!sd_tx_complete);sd_tx_complete = 0;printf("Writing 1 sdcard blocks with DMA. state = %d \r\n", HAL_SD_GetCardState(&hsd));// 注意DMA传输完成,不代表SD卡就写完成了。while (HAL_SD_GetCardState(&hsd) == HAL_SD_CARD_PROGRAMMING);printf("Write complete! state = %d \r\n", HAL_SD_GetCardState(&hsd));}else{printf("Failed to write 1 sdcard blocks with DMA! status=%d \r\n", status);}printf("\r\n --------------------- 写完之后再读一次 -------------------- \r\n");status = HAL_SD_ReadBlocks_DMA(&hsd, (uint8_t *)read_buf, 0, 1);if (status == HAL_OK){while (!sd_rx_complete);    // 等待传输完成(传输完成会有回调函数)sd_rx_complete = 0;         // 清除标志位printf("SDCard Block 1 with DMA \r\n");for(int i = 0; i < 512; i++){printf("0x%02x ", read_buf[i]);if((i+1)%16 == 0){printf("\r\n");}}}else{printf("Failed to read with DMA! status = %d \r\n", status);}

4. SD卡移植FATFS文件系统

4.1. FATFS配置

CubeMX配置还是很方便的。

保留上一节轮询模式的配置不变,添加FATFS系统的配置。

其他参数保持默认即可。

中文文件名,其实也可以选,但是没必要,因为占用的内存比较大。
文件名最大长度256(默认),这里设置64,如果使用的超过64,f_open可能会失败。

还需要配置一个SD卡插入引脚,如果不配置 生成文件时会报错,所以这里即使没有硬件连接,也可以任意设置一引脚,生成工程后不需要的话直接注释掉。

把栈空间大小设置大一点

生成工程后,目录如下。可以发现和以往的工程相比多了两个文件夹。

4.2. 修改SD卡插入检测代码

生成工程后,首先修改SD卡插入检测代码。

bsp_driver.sd.c文件中,可以看到__weak uint8_t BSP_SD_Init(void),该部分代码是检测SD卡是否正常工作。其中红框中的函数BSP_SD_IsDetected()是用于检测SD卡是否插入(或者写保护开关是否拨动,在前文SD卡介绍中有写)。

函数BSP_SD_IsDetected()定义如下,可以看到,如果监测到SD卡未插入(写保护)就无法正常初始化。

其中,这部分也就是检测我们配置时任意配置的那个引脚的高低电平。

明白原理后,我们可以直接不管这部分的,因此需要进行修改。

  1. 可以选择直接把这个引脚在硬件上拉低
  2. 软件上在这行之前把引脚电平拉低(设置为下拉)
  3. 修改代码跳过这行。

这里我采用第三种,直接把BSP_SD_IsDetected()函数中的这部分屏蔽掉。

这样要小心,你重新生成工程的话,这部分还要改。

4.3. 代码实现

main.c中的while(1)之前添加如下代码:

 FATFS fs;                       /* FatFs 文件系统对象 */FIL file;                       /* 文件对象 */FRESULT f_res;                  /* 文件操作结果 */UINT fnum;                      /* 文件成功读写数量 */BYTE ReadBuffer[1024] = {0};    /* 读缓冲区 */BYTE WriteBuffer[] =            /* 写缓冲区 */"This is STM32 working with FatFs \r\n";printf("\r\n ****** FatFs Example ****** \r\n \r\n");// 在外部 SD 卡挂载文件系统,文件系统挂载时会对 SD 卡初始化// note:必须先要保证SD卡正常拥有FAT文件系统,如果没有会失败。f_res = f_mount(&fs, "0:", 1);/*----------------------- 格式化测试 ---------------------------*/printf("\r\n ****** Register the file system object to the FatFs module ****** \r\n");/* 如果没有文件系统就格式化创建创建文件系统 */if(f_res == FR_NO_FILESYSTEM){printf("The SD card does not yet have a file system and is about to be formatted... \r\n");/* 格式化 */f_res = f_mkfs("0:", 0, 0);if(f_res == FR_OK){printf("The SD card successfully formatted the file system\r\n");/* 格式化后,先取消挂载 */f_res = f_mount(NULL, "0:", 1);/* 重新挂载 */f_res = f_mount(&fs, "0:", 1);}else{printf("The format failed\r\n");while(1);}}else if(f_res != FR_OK){printf(" mount error : %d \r\n", f_res);while(1);}else{printf(" mount sucess!!! \r\n");}/*----------------------- 文件系统测试:写测试 -----------------------------*//* 打开文件,如果文件不存在则创建它 */printf("\r\n ****** Create and Open new text file objects with write access ****** \r\n");f_res = f_open(&file, "0:FatFs STM32cube.txt", FA_CREATE_ALWAYS | FA_WRITE);if(f_res == FR_OK){printf(" open file sucess!!! \r\n");/* 将指定存储区内容写入到文件内 */printf("\r\n****** Write data to the text files ******\r\n");f_res = f_write(&file, WriteBuffer, sizeof(WriteBuffer), &fnum);if(f_res == FR_OK){printf(" write file sucess!!! (%d)\n", fnum);printf(" write Data : %s\r\n", WriteBuffer);}else{printf(" write file error : %d\r\n", f_res);}/* 不再读写,关闭文件 */f_close(&file);}else{printf(" open file error : %d\r\n", f_res);}/*------------------- 文件系统测试:读测试 ------------------------------------*/printf("\r\n****** Read data from the text files ******\r\n");f_res = f_open(&file, "0:FatFs STM32cube.txt", FA_OPEN_EXISTING | FA_READ);if(f_res == FR_OK){printf(" open file sucess!!! \r\n");f_res = f_read(&file, ReadBuffer, sizeof(ReadBuffer), &fnum);if(f_res == FR_OK){printf("read sucess!!! (%d)\n", fnum);printf("read Data : %s\r\n", ReadBuffer);}else{printf(" read error!!! %d\r\n", f_res);}}else{printf(" open file error : %d\r\n", f_res);}/* 不再读写,关闭文件 */f_close(&file);/* 不再使用文件系统,取消挂载文件系统 */f_mount(NULL, "0:", 1);/* 操作完成,停机 */

效果测试
编译、烧录、用串口查看效果。

拔下SD卡,插入电脑,也可以看到这个文件。

4.4. 注意事项

f_open、f_write、f_read如果偶尔有问题;f_mkfs报错 FS_DISK_ERR,可以加上了SDIO硬件流使能试试。

别人的文章里看到的,实际上我测试没有出现这个问题,先放这吧。

STM32CubeMX系列09——SDIO(SD卡读写、SD卡移植FATFS文件系统)相关推荐

  1. 使用vscode + gcc进行 STM32 单片机开发(三)DMA读写SD卡,移植FATFS文件系统

    背景 在本系列的前两篇文章( 使用vscode + gcc进行 STM32 单片机开发(一)编译及调试 使用vscode + gcc进行 STM32 单片机开发(二)gcc环境 移植rtthread) ...

  2. 【STM32Cube_20】在SD卡上移植FATFS文件系统

    本篇详细的记录了如何使用STM32CubeMX移植FATFS文件系统到SD卡上. 1. 准备工作 硬件准备 开发板 首先需要准备一个开发板,这里我准备的是STM32L4的开发板(BearPi): Mi ...

  3. SD卡读卡程序 C语言 IO模拟,请问大神些,有谁用过SD模式读写SD卡?时序是用单片机IO模拟。...

    大家读写SD卡怎么不用SD模式方式读取,是SPI方式读取简单? 下面贴一段代码,是在FPGA上程序,只能用SD模式进行读,没有文件系统,没有用AVR硬件实现, 电路图: image001.jpg (2 ...

  4. 【FatFs】基于STM32 SD卡移植FatFs文件系统

    相关文章 <[SDIO]SDIO.SD卡.FatFs文件系统相关文章索引> 1.前言 FatFs是一个通用的FAT/exFAT文件系统模块,用于小型嵌入式系统.它完全是由 ANSI C 语 ...

  5. 贴片SD卡移植FATFS文件系统

    一.前言 手头有一块深圳雷龙提供的贴片 CS 创世 SD NAND Flash,挂载个文件系统把玩一下.首先介绍一下使用的贴片 SD 卡,以前使用的都是拔插式的 SD 卡,下面这个样子: 深圳雷龙提供 ...

  6. stm32f769 寄存器配置SD卡---移植fatfs

    昨天开始在上周实现的SD卡读写基础上移植fatfs文件系统,开始不是很顺利,之前没有搞过,完全按照f767的例程移植的,但是在加载SD卡时一直是失败的,很郁闷,折腾了一天,结果还不理想,睡了个好觉,思 ...

  7. STM32利用FATFS文件系统给SD卡读写数据

    STM32利用FATFS文件系统给SD卡读写数据 注:FATFS文件系统官方网址(内含各种函数具体功能及参数配置): FATFS文件系统官方网址 1. 所需.c和.h文件 FATFS中的文件的作用如下 ...

  8. STM32CubeMX学习笔记(27)——FatFs文件系统使用(操作SD卡)

    一.FatFs简介 FatFs 是面向小型嵌入式系统的一种通用的 FAT 文件系统.它完全是由 ANSI C 语言编写并且完全独立于底层的 I/O 介质.因此它可以很容易地不加修改地移植到其他的处理器 ...

  9. STM32CubeMX配置SD卡+DMA+Fatfs文件系统

    STM32CubeMX配置SD卡+DMA+Fatfs文件系统 一.设备及软件 1.keil 2.STM32CubeMX 3.正点原子STM32F407探索者开发板 二.配置步骤 1.配置RCC外部晶振 ...

最新文章

  1. HDU——2874 Connections between cities
  2. PHP date, strtotime, mktime处理
  3. ITK:图像的区域最大值
  4. think php框架 跨域,ThinkPHP框架实现session跨域问题
  5. 数据库:如果MySQl磁盘满了,如何解决!
  6. 4001.基于双向链表的双向冒泡排序法
  7. 国内外ACM/ICPC的OJ,BBS列表
  8. 华为RH2285H V2设备管理口白屏的解决方法
  9. Paradigm此前投资Uniswap的资金价值或超30亿美元
  10. 爱思助手短信备份到安卓_爱思助手 Bug 修复,完美降级10.3.3
  11. 【Java web】Servlet面试题
  12. python最小二乘法线性回归_python 分别用梯度下降法和最小二乘法求线性回归 | 学步园...
  13. [Android]按阶段编译Android kernel中的代码
  14. c语言编程杭电1008,C语言程序设计_杭州电子科技大学cyy1_3
  15. msvcr120.dll WIN7 64位/32位丢失怎么办
  16. Android11.0(R) MTK平台添加新分区
  17. Allegro PCB Design GXL (legacy) 设置自动保存brd文件
  18. PHP ACCESS-CONTROL-ALLOW-ORIGIN ,设置跨域头
  19. Mac家谱制作软件MacFamilyTree 9好用吗?如何在MacFamilyTree 9中创建数据库的备份?
  20. 安得指针千万间,大庇天下地址具欢颜(中)

热门文章

  1. Gensim官方教程翻译(二)——语料库与向量空间(Corpora and Vector Spaces)
  2. linux添加windows字体文件夹里,如何把Windows下的字体添加到Linux中
  3. python 爬取淘宝网课
  4. 通用 OCR API 接口
  5. airpods版本号_airpods蓝牙版本是多少,苹果AirPods正式版使用评测
  6. 【STM32】SPI协议通信详解
  7. 【转】人脸识别图像库
  8. jquery 编码解码
  9. JS/JQ实现页面跳转
  10. XSS-labs靶场通关秘籍(level 17-20)