本文采用的HAL库版本为STM32Cube_FW_F1_V1.8.0(带Patch-CubeF1 1.8.4)。

知识点一:SD卡数据线位宽的配置

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位模式。

知识点二:SDIO_CK时钟线频率的配置

如图所示,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收发数据。

知识点三:获取SD卡容量

SD卡读写是以扇区为单位的,每个扇区的大小都是512字节。总容量=扇区数×扇区大小。
要获取SD卡的总容量可使用HAL_SD_GetCardInfo(&hsd, &info)函数。函数调用后,info.BlockNbr就是扇区数,info.BlockSize就是扇区大小。两者相乘就是SD卡的总容量,单位为字节。
读写扇区时,扇区号必须是0~info.BlockNbr-1之间的号码。
那info.LogBlockNbr和info.LogBlockSize又是什么呢?假如插入的卡是SDSC卡,有可能info.BlockSize不等于512。而在HAL库里面info.LogBlockSize始终等于512,那么info.LogBlockNbr就等于info.BlockNbr*info.BlockSize/512,也就是说此时info.BlockNbr为实际的扇区数,而info.LogBlockNbr为按512字节扇区大小来算的扇区数

知识点四:SDIO DMA的初始化

STM32F1的主频较低,在SDIO时钟频率很高的情况下(如24MHz)必须要用DMA收发数据。但STM32CubeMX里面,SDIO却只能添加一个DMA Handle,要么选择收,要么选择发,是不是很奇怪呢?实际上SDIO收发数据是可以用同一个DMA Handle的。
C语言全局变量的值默认为全0,所以hdma24.Init.Direction=0,不用管。
设置完hdma24.Init的其他成员后,调用HAL_DMA_Init初始化DMA2_Channel4,然后两次调用__HAL_LINKDMA宏将hdma24同时绑定到hsd的hdmarx和hdmatx上:
__HAL_LINKDMA(&hsd, hdmarx, hdma24);
__HAL_LINKDMA(&hsd, hdmatx, hdma24);
之后调用HAL_SD_ReadBlocks_DMA或者HAL_SD_WriteBlocks_DMA就会自动切换DMA传输方向。但是为了确保DMA传输方向能切换成功,必须在每次传输完成后关闭DMA,也就是在HAL_SD_RxCpltCallback和HAL_SD_TxCpltCallback传输完成回调函数里面调用__HAL_DMA_DISABLE(hsd->hdmaXX)。这一点非常重要。

知识点五:HAL_SD_Erase的参数和HAL_SD_ReadBlocks、HAL_SD_WriteBlocks的区别

HAL_SD_Erase的第二、第三个参数名称为BlockStartAdd和BlockEndAdd,两个参数都是扇区号。
HAL_SD_Erase(&hsd, 2, 7)意思是擦除2、3、4、5、6、7这6个扇区,包括扇区2和扇区7。
HAL_SD_ReadBlocks、HAL_SD_WriteBlocks的第三、第四个参数名称是BlockAdd和NumberOfBlocks。是起始扇区号和扇区个数的意思。
所以HAL_SD_ReadBlocks(&hsd, (uint8_t *)0x60000000, 4096, 2048, HAL_MAX_DELAY)意思是从第4096扇区开始,读2048个扇区,读出来的数据放到0x60000000内存地址处。也就是说读的是扇区4096~6143。

知识点六:等待擦除、写入数据完毕

读数据时,DMA传输完毕就是读操作完毕了。
但擦除和写入数据,DMA传输完毕并不代表擦除和写操作完毕。必须要等到HAL_SD_GetCardState(&hsd)的返回值从HAL_SD_CARD_PROGRAMMING(=7)变为HAL_SD_CARD_TRANSFER(=4)后,才算完毕了,才能执行新的操作(比如读操作)。
提示:读操作未完毕时,返回值是HAL_SD_CARD_SENDING(=5),完毕后是HAL_SD_CARD_TRANSFER(=4)。

电路图

SD卡部分有两个卡检测引脚(CD)。
第2脚CD/DAT3是SD卡上的引脚,低电平是没有插卡,高电平是插了卡。由于SD卡内部是一个几十kΩ的上拉电阻,所以卡外的下拉电阻必须要很大,这里选择的是470kΩ。
第9脚CD是SD卡槽上的引脚,不是SD卡上的引脚,插卡后9脚和外壳(PAD)短路,外壳是接地的。所以高电平是没插卡,低电平是插了卡。

示例代码

在Keil MDK 5里面直接创建使用HAL库的STM32工程的方法(不使用STM32CubeMX)_ZLK1214的专栏-CSDN博客https://blog.csdn.net/ZLK1214/article/details/103224868

#include <stdio.h>
#include <stm32f1xx.h>
#include "common.h"#define WRITE_ENABLE 0DMA_HandleTypeDef hdma24;
SD_HandleTypeDef hsd;
SRAM_HandleTypeDef hsram;
static volatile uint8_t sd_rx_complete, sd_tx_complete;/* 初始化外部SRAM内存 */
// 型号:IS62WV51216BLL
// 容量:1MB
// 地址:0x60000000~0x600fffff
static void sram_init(void)
{FSMC_NORSRAM_TimingTypeDef timing = {0};GPIO_InitTypeDef gpio;__HAL_RCC_FSMC_CLK_ENABLE();__HAL_RCC_GPIOD_CLK_ENABLE();__HAL_RCC_GPIOE_CLK_ENABLE();__HAL_RCC_GPIOF_CLK_ENABLE();__HAL_RCC_GPIOG_CLK_ENABLE();// FSMC_D2~3: PD0~1, NOE: PD4, NWE: PD5, FSMC_NE1: PD7, FSMC_D13~15: PD8~10, FSMC_A16~18: PD11~13, FSMC_D0~1: PD14~15gpio.Mode = GPIO_MODE_AF_PP;gpio.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;gpio.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOD, &gpio);// FSMC_NBL0~1: PE0~1, FSMC_D4~12: PE7~15gpio.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;HAL_GPIO_Init(GPIOE, &gpio);// FSMC_A0~5: PF0~5, FSMC_A6~9: PF12~15gpio.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;HAL_GPIO_Init(GPIOF, &gpio);// FSMC_A10~15: PG0~PG5gpio.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5;HAL_GPIO_Init(GPIOG, &gpio);hsram.Instance = FSMC_NORSRAM_DEVICE;hsram.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;hsram.Init.MemoryDataWidth = FSMC_NORSRAM_MEM_BUS_WIDTH_16;hsram.Init.MemoryType = FSMC_MEMORY_TYPE_SRAM;hsram.Init.NSBank = FSMC_NORSRAM_BANK1;hsram.Init.WriteOperation = FSMC_WRITE_OPERATION_ENABLE;timing.AddressHoldTime = 1;timing.AddressSetupTime = 1;timing.BusTurnAroundDuration = 0;timing.DataSetupTime = 1;// 以下参数与SRAM无关, 仅仅为了避免assert_param报错timing.CLKDivision = 2;timing.DataLatency = 2;HAL_SRAM_Init(&hsram, &timing, NULL);
}/* 初始化SD卡 */
static int sd_init(void)
{GPIO_InitTypeDef gpio;HAL_SD_CardInfoTypeDef info;HAL_StatusTypeDef status;__HAL_RCC_DMA2_CLK_ENABLE();__HAL_RCC_GPIOC_CLK_ENABLE();__HAL_RCC_GPIOD_CLK_ENABLE();__HAL_RCC_SDIO_CLK_ENABLE();// SDIO_D0~3: PC8~11, SDIO_CK: PC12gpio.Mode = GPIO_MODE_AF_PP;gpio.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12;gpio.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOC, &gpio);// SDIO_CMD: PD2gpio.Pin = GPIO_PIN_2;HAL_GPIO_Init(GPIOD, &gpio);hsd.Instance = SDIO;hsd.Init.BusWide = SDIO_BUS_WIDE_1B; // 这里必须设置为1位宽度, 不能是4位, 想要4位宽度必须使用HAL_SD_ConfigWideBusOperation函数hsd.Init.ClockDiv = 70; // 分频系数status = HAL_SD_Init(&hsd);if (status == HAL_OK)printf("SD init OK! frequency=%lgMHz\n", (double)SystemCoreClock / (2 + hsd.Init.ClockDiv) / 1000000);else{// 没有插卡printf("SD init failed! status=%d\n", status);return -1;}// 发送和接收共用1个DMA Handle, 不用填写Init.Direction成员// alignment必须为word(每次传输4字节数据)hdma24.Instance = DMA2_Channel4;hdma24.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;hdma24.Init.MemInc = DMA_MINC_ENABLE;hdma24.Init.Mode = DMA_NORMAL;hdma24.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;hdma24.Init.PeriphInc = DMA_PINC_DISABLE;hdma24.Init.Priority = DMA_PRIORITY_VERY_HIGH;HAL_DMA_Init(&hdma24);__HAL_LINKDMA(&hsd, hdmarx, hdma24);__HAL_LINKDMA(&hsd, hdmatx, hdma24);HAL_NVIC_EnableIRQ(DMA2_Channel4_5_IRQn);HAL_NVIC_EnableIRQ(SDIO_IRQn);// 现在可以设置为4位宽度status = HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B);if (status != HAL_OK){printf("Failed to configure wide bus! status=%d\n", status);return -1;}// 查看卡的类型、版本和容量status = HAL_SD_GetCardInfo(&hsd, &info);if (status == HAL_OK){if (info.CardType == CARD_SDSC)printf("SDCard type: SDSC\n");else if (info.CardType == CARD_SDHC_SDXC)printf("SDCard type: SDHC/SDXC\n");if (info.CardVersion == CARD_V1_X)printf("SDCard version: V1\n");else if (info.CardVersion == CARD_V2_X)printf("SDCard version: V2\n");printf("SDCard block number: %u\n", info.BlockNbr);printf("SDCard block size: %u\n", info.BlockSize);printf("SDCard capacity: %lgMB\n", (double)info.BlockNbr * info.BlockSize / 1048576);}return 0;
}/* 测试读写SD卡 */
static int sd_test(void)
{int i, n;uint32_t addr;HAL_StatusTypeDef status;/* 普通模式读SD卡 */// 从SD卡扇区0开始读, 读1个扇区到0x60000000内存地址处status = HAL_SD_ReadBlocks(&hsd, (uint8_t *)0x60000000, 0, 1, HAL_MAX_DELAY);if (status == HAL_OK){printf("[SDCard Block 0]\n");dump_data((void *)0x60000000, 512); // 显示数据内容}else{printf("Failed to read sdcard block 0! status=%d\n", status);return -1;}// 从SD卡扇区4096开始读, 读2048个扇区到0x60000000内存地址处, 填满整个1MB内存status = HAL_SD_ReadBlocks(&hsd, (uint8_t *)0x60000000, 4096, 2048, HAL_MAX_DELAY);if (status == HAL_OK)printf("Read 2048 sdcard blocks!\n");else{printf("Failed to read 2048 sdcard blocks! status=%d\n", status);return -1;}/* 普通模式写SD卡 */
#if WRITE_ENABLEfor (i = 0; i < 500 * 512; i += 4)*(uint32_t *)(0x60000000 + i) = 0x60000000 + i;// 写之前先擦除, 擦除后扇区的内容为全0// 请注意HAL_SD_Erase(&hsd, x, y)的意思是擦除扇区x到扇区y, 共计y-x+1个扇区, 也就是说要包括扇区x和扇区y// 擦除扇区1~500, 共计500-1+1=500个扇区status = HAL_SD_Erase(&hsd, 1, 500);if (status == HAL_OK){printf("Erasing 500 sdcard blocks. state=%d\n", HAL_SD_GetCardState(&hsd));while (HAL_SD_GetCardState(&hsd) == HAL_SD_CARD_PROGRAMMING);printf("Erase complete! state=%d\n", HAL_SD_GetCardState(&hsd));}else{printf("Failed to erase 500 sdcard blocks! status=%d\n", status);return -1;}// 将0x60000000内存里面的数据写到SD卡的扇区1~500(共500个扇区)status = HAL_SD_WriteBlocks(&hsd, (uint8_t *)0x60000000, 1, 500, HAL_MAX_DELAY);if (status == HAL_OK){printf("Writing 500 sdcard blocks. state=%d\n", HAL_SD_GetCardState(&hsd));while (HAL_SD_GetCardState(&hsd) == HAL_SD_CARD_PROGRAMMING);printf("Write complete! state=%d\n", HAL_SD_GetCardState(&hsd));}else{printf("Failed to write 500 sdcard blocks! status=%d\n", status);return -1;}
#endif/* DMA模式读SD卡 */// 提速hsd.Init.ClockDiv = 1;status = HAL_SD_Init(&hsd);if (status == HAL_OK)printf("Frequency is changed to %lgMHz.\n", (double)SystemCoreClock / (2 + hsd.Init.ClockDiv) / 1000000);else{printf("Failed to change frequency! status=%d\n", status);return -1;}// 从SD卡扇区1开始读, 读1个扇区到0x60000000内存地址处status = HAL_SD_ReadBlocks_DMA(&hsd, (uint8_t *)0x60000000, 1, 1);if (status == HAL_OK){while (!sd_rx_complete);sd_rx_complete = 0;printf("[SDCard Block 1 with DMA]\n");dump_data((void *)0x60000000, 512);}else{printf("Failed to read sdcard block 4096 with DMA! status=%d\n", status);return -1;}// 从SD卡扇区4096开始读, 读2048个扇区到0x60000000内存地址处, 填满整个1MB内存// 注意DMA模式下每次最多只能读127个扇区for (i = 0; i < 2048; i += n){n = 127;if (i + n > 2048)n = 2048 - i;addr = 0x60000000 + i * 512;status = HAL_SD_ReadBlocks_DMA(&hsd, (uint8_t *)addr, 4096 + i, n);if (status == HAL_OK){while (!sd_rx_complete);sd_rx_complete = 0;printf("Read %d sdcard blocks with DMA! memaddr=0x%08x\n", n, addr);}else{printf("Failed to read %d sdcard blocks with DMA! status=%d\n", n, status);return -1;}}/* DMA模式写SD卡 */
#if WRITE_ENABLE// 擦除扇区501~627, 共计627-501+1=127个扇区status = HAL_SD_Erase(&hsd, 501, 627);if (status == HAL_OK){printf("Erasing 127 sdcard blocks. state=%d\n", HAL_SD_GetCardState(&hsd));while (HAL_SD_GetCardState(&hsd) == HAL_SD_CARD_PROGRAMMING);printf("Erase complete! state=%d\n", HAL_SD_GetCardState(&hsd));}else{printf("Failed to erase 127 sdcard blocks! status=%d\n", status);return -1;}// 检查第627、628扇区是否为全0status = HAL_SD_ReadBlocks_DMA(&hsd, (uint8_t *)0x600e0000, 627, 2);if (status == HAL_OK){while (!sd_rx_complete);sd_rx_complete = 0;for (i = 0; i < 512; i++){if (*(uint8_t *)(0x600e0000 + i) != 0)break;}if (i == 512)printf("Sector 627 is empty.\n");else{printf("Sector 627 is NOT empty!!!! pos=%d\n", i);return -1;}for (i = 512; i < 1024; i++){if (*(uint8_t *)(0x600e0000 + i) != 0)break;}if (i == 1024)printf("Sector 628 is empty.\n");elseprintf("Sector 628 is not empty. pos=%d\n", i - 512);}else{printf("Failed to check block 627~628!\n");return -1;}// 将0x60000000内存里面的数据写到SD卡的扇区501~627status = HAL_SD_WriteBlocks_DMA(&hsd, (uint8_t *)0x60000000, 501, 127);if (status == HAL_OK){while (!sd_tx_complete);sd_tx_complete = 0;printf("Writing 127 sdcard blocks with DMA. state=%d\n", HAL_SD_GetCardState(&hsd));while (HAL_SD_GetCardState(&hsd) == HAL_SD_CARD_PROGRAMMING);printf("Write complete! state=%d\n", HAL_SD_GetCardState(&hsd));}else{printf("Failed to write 127 sdcard blocks with DMA! status=%d\n", status);return -1;}
#endifreturn 0;
}int main(void)
{HAL_Init();clock_init();usart_init(115200);printf("STM32F103ZE SDCard\n");printf("SystemCoreClock=%u\n", SystemCoreClock);sram_init();if (sd_init() == 0)sd_test();while (1){}
}void DMA2_Channel4_5_IRQHandler(void)
{HAL_DMA_IRQHandler(&hdma24);
}void SDIO_IRQHandler(void)
{HAL_SD_IRQHandler(&hsd);
}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;
}

程序运行结果(WRITE_ENABLE=1):

STM32F103ZE SDCard
SystemCoreClock=72000000
SD init OK! frequency=1MHz
SDCard type: SDHC/SDXC
SDCard version: V2
SDCard block number: 60506112
SDCard block size: 512
SDCard capacity: 29544MB
[SDCard Block 0]
FAB800108ED0BC00B0B800008ED88EC0FBBE007CBF0006B90002F3A4EA21060000BEBE073804750B83C61081FEFE0775F3EB16B402B001BB007CB2808A74018B4C02CD13EA007C0000EBFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B35E8A8400000004010483FEC2FF0008000000B82C0000FEC2FF83FEC2FF00C02C000040A10100FEC2FF0CFEC2FF0000CE010040CD010000000000000000000000000000000055AA
Read 2048 sdcard blocks!
Erasing 500 sdcard blocks. state=7
Erase complete! state=4
Writing 500 sdcard blocks. state=7
Write complete! state=4
Frequency is changed to 24MHz.
[SDCard Block 1 with DMA]
0000006004000060080000600C0000601000006014000060180000601C0000602000006024000060280000602C0000603000006034000060380000603C0000604000006044000060480000604C0000605000006054000060580000605C0000606000006064000060680000606C0000607000006074000060780000607C0000608000006084000060880000608C0000609000006094000060980000609C000060A0000060A4000060A8000060AC000060B0000060B4000060B8000060BC000060C0000060C4000060C8000060CC000060D0000060D4000060D8000060DC000060E0000060E4000060E8000060EC000060F0000060F4000060F8000060FC0000600001006004010060080100600C0100601001006014010060180100601C0100602001006024010060280100602C0100603001006034010060380100603C0100604001006044010060480100604C0100605001006054010060580100605C0100606001006064010060680100606C0100607001006074010060780100607C0100608001006084010060880100608C0100609001006094010060980100609C010060A0010060A4010060A8010060AC010060B0010060B4010060B8010060BC010060C0010060C4010060C8010060CC010060D0010060D4010060D8010060DC010060E0010060E4010060E8010060EC010060F0010060F4010060F8010060FC010060
Read 127 sdcard blocks with DMA! memaddr=0x60000000
Read 127 sdcard blocks with DMA! memaddr=0x6000fe00
Read 127 sdcard blocks with DMA! memaddr=0x6001fc00
Read 127 sdcard blocks with DMA! memaddr=0x6002fa00
Read 127 sdcard blocks with DMA! memaddr=0x6003f800
Read 127 sdcard blocks with DMA! memaddr=0x6004f600
Read 127 sdcard blocks with DMA! memaddr=0x6005f400
Read 127 sdcard blocks with DMA! memaddr=0x6006f200
Read 127 sdcard blocks with DMA! memaddr=0x6007f000
Read 127 sdcard blocks with DMA! memaddr=0x6008ee00
Read 127 sdcard blocks with DMA! memaddr=0x6009ec00
Read 127 sdcard blocks with DMA! memaddr=0x600aea00
Read 127 sdcard blocks with DMA! memaddr=0x600be800
Read 127 sdcard blocks with DMA! memaddr=0x600ce600
Read 127 sdcard blocks with DMA! memaddr=0x600de400
Read 127 sdcard blocks with DMA! memaddr=0x600ee200
Read 16 sdcard blocks with DMA! memaddr=0x600fe000
Erasing 127 sdcard blocks. state=7
Erase complete! state=4
Sector 627 is empty.
Sector 628 is not empty. pos=0
Writing 127 sdcard blocks with DMA. state=7
Write complete! state=4

实现FATFS读写接口

很遗憾,STM32CubeMX没有给出DMA模式下fatfs的diskio.c的实现,SDIO_CK频率很高时又必须用DMA模式,我们只能自己来实现了。
SDIO的DMA只支持32位传输模式,所以内存地址必须按4字节对齐,而disk_read和disk_write函数传入的buff地址是有可能没有对齐的。
为了解决这个问题,我们必须创建一个uint32型的512字节全局数组:static uint32_t sd_buffer[128];
在Keil中,uint32_t数组变量的首地址一定是四字节对齐的,所以sd_buffer的地址一定能被4整除。
读数据和写数据都要一块一块地写。
读数据时,先通过DMA读到sd_buffer中,然后再memcpy到buff中。
写数据时,先把buff中的数据memcpy到sd_buffer中,再通过DMA写入SD卡。
请看代码:(fatfs版本为ff13c)

/*-----------------------------------------------------------------------*/
/* Low level disk I/O module skeleton for FatFs     (C)ChaN, 2016        */
/*-----------------------------------------------------------------------*/
/* If a working storage control module is available, it should be        */
/* attached to the FatFs via a glue function rather than modifying it.   */
/* This is an example of glue functions to attach various exsisting      */
/* storage control modules to the FatFs module with a defined API.       */
/*-----------------------------------------------------------------------*/#include <stdio.h>
#include <stm32f1xx.h>
#include <string.h>
#include "ff.h"           /* Obtains integer types */
#include "diskio.h"       /* Declarations of disk functions *//* Definitions of physical drive number for each drive */
#define DEV_SDCARD 0DMA_HandleTypeDef hdma24;
SD_HandleTypeDef hsd;
static uint32_t sd_buffer[128];
static DSTATUS sd_status = STA_NOINIT;
static HAL_SD_CardInfoTypeDef sd_info;static int sd_init(void)
{GPIO_InitTypeDef gpio;HAL_StatusTypeDef status;__HAL_RCC_DMA2_CLK_ENABLE();__HAL_RCC_GPIOC_CLK_ENABLE();__HAL_RCC_GPIOD_CLK_ENABLE();__HAL_RCC_SDIO_CLK_ENABLE();gpio.Mode = GPIO_MODE_AF_PP;gpio.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12;gpio.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOC, &gpio);gpio.Pin = GPIO_PIN_2;HAL_GPIO_Init(GPIOD, &gpio);hsd.Instance = SDIO;hsd.Init.BusWide = SDIO_BUS_WIDE_1B;hsd.Init.ClockDiv = 1;status = HAL_SD_Init(&hsd);hdma24.Instance = DMA2_Channel4;hdma24.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;hdma24.Init.MemInc = DMA_MINC_ENABLE;hdma24.Init.Mode = DMA_NORMAL;hdma24.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;hdma24.Init.PeriphInc = DMA_PINC_DISABLE;hdma24.Init.Priority = DMA_PRIORITY_VERY_HIGH;HAL_DMA_Init(&hdma24);__HAL_LINKDMA(&hsd, hdmarx, hdma24);__HAL_LINKDMA(&hsd, hdmatx, hdma24);HAL_NVIC_EnableIRQ(DMA2_Channel4_5_IRQn);HAL_NVIC_EnableIRQ(SDIO_IRQn);if (status == HAL_OK)printf("SD init OK! frequency=%lgMHz\n", (double)SystemCoreClock / (2 + hsd.Init.ClockDiv) / 1000000);else{printf("SD init failed! status=%d\n", status);return -1;}status = HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B);if (status != HAL_OK){printf("Failed to configure wide bus! status=%d\n", status);return -1;}status = HAL_SD_GetCardInfo(&hsd, &sd_info);if (status == HAL_OK){if (sd_info.CardType == CARD_SDSC)printf("SDCard type: SDSC\n");else if (sd_info.CardType == CARD_SDHC_SDXC)printf("SDCard type: SDHC/SDXC\n");if (sd_info.CardVersion == CARD_V1_X)printf("SDCard version: V1\n");else if (sd_info.CardVersion == CARD_V2_X)printf("SDCard version: V2\n");printf("SDCard block number: %u\n", sd_info.BlockNbr);printf("SDCard block size: %u\n", sd_info.BlockSize);printf("SDCard capacity: %lgMB\n", (double)sd_info.BlockNbr * sd_info.BlockSize / 1048576);}return 0;
}/*-----------------------------------------------------------------------*/
/* Get Drive Status                                                      */
/*-----------------------------------------------------------------------*/DSTATUS disk_status (BYTE pdrv       /* Physical drive nmuber to identify the drive */
)
{switch (pdrv){case DEV_SDCARD:return sd_status;}return STA_NOINIT;
}/*-----------------------------------------------------------------------*/
/* Inidialize a Drive                                                    */
/*-----------------------------------------------------------------------*/DSTATUS disk_initialize (BYTE pdrv               /* Physical drive nmuber to identify the drive */
)
{int ret;switch (pdrv){case DEV_SDCARD:// 初始化SD卡if (sd_status != 0){ret = sd_init();if (ret == 0)sd_status = 0; // 插了SD卡elsesd_status = STA_NODISK; // 没有插SD卡}return 0;}return STA_NOINIT;
}/*-----------------------------------------------------------------------*/
/* Read Sector(s)                                                        */
/*-----------------------------------------------------------------------*/DRESULT disk_read (BYTE pdrv,        /* Physical drive nmuber to identify the drive */BYTE *buff,        /* Data buffer to store read data */DWORD sector,   /* Start sector in LBA */UINT count     /* Number of sectors to read */
)
{HAL_StatusTypeDef status;switch (pdrv){case DEV_SDCARD:while (count != 0) // 一个扇区一个扇区地读{status = HAL_SD_ReadBlocks_DMA(&hsd, (uint8_t *)sd_buffer, sector, 1); // 通过DMA方式读取SD卡, 暂存入4字节对齐的数组中if (status != HAL_OK){printf("Failed to read sector %d! status=%u\n", sector, status);return RES_ERROR;}while (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER); // 等待读取完毕memcpy(buff, sd_buffer, sd_info.LogBlockSize); // 将读出的数据复制到最终的buff数组中// 跳转到下一个扇区buff += sd_info.LogBlockSize;sector++;count--;}return RES_OK;}return RES_PARERR;
}/*-----------------------------------------------------------------------*/
/* Write Sector(s)                                                       */
/*-----------------------------------------------------------------------*/#if FF_FS_READONLY == 0DRESULT disk_write (BYTE pdrv,          /* Physical drive nmuber to identify the drive */const BYTE *buff,  /* Data to be written */DWORD sector,       /* Start sector in LBA */UINT count         /* Number of sectors to write */
)
{HAL_StatusTypeDef status;printf("W%d,%d\n", sector, count);switch (pdrv){case DEV_SDCARD:// 可以不用擦除, 直接写数据while (count != 0) // 一个扇区一个扇区地写{memcpy(sd_buffer, buff, sd_info.LogBlockSize); // 先将要写入的数据复制到4字节对齐的数组中status = HAL_SD_WriteBlocks_DMA(&hsd, (uint8_t *)sd_buffer, sector, 1); // 通过DMA方式写入SD卡if (status != HAL_OK){printf("Failed to write sector %d! status=%u\n", sector, status);return RES_ERROR;}while (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER); // 等待写入完毕// 跳转到下一个扇区buff += sd_info.LogBlockSize;sector++;count--;}return RES_OK;}return RES_PARERR;
}#endif/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions                                               */
/*-----------------------------------------------------------------------*/DRESULT disk_ioctl (BYTE pdrv,       /* Physical drive nmuber (0..) */BYTE cmd,      /* Control code */void *buff        /* Buffer to send/receive control data */
)
{DWORD *p = buff;switch (pdrv){case DEV_SDCARD:if (sd_status != 0)return RES_ERROR;switch (cmd){case CTRL_SYNC:return RES_OK;case GET_SECTOR_COUNT:*p = sd_info.LogBlockNbr; // 扇区数 (假定每个扇区都是512字节)return RES_OK;case GET_BLOCK_SIZE:case GET_SECTOR_SIZE:*p = sd_info.LogBlockSize; // 扇区大小return RES_OK;}break;}return RES_PARERR;
}void DMA2_Channel4_5_IRQHandler(void)
{HAL_DMA_IRQHandler(&hdma24);
}void SDIO_IRQHandler(void)
{HAL_SD_IRQHandler(&hsd);
}void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)
{__HAL_DMA_DISABLE(hsd->hdmarx);
}void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
{__HAL_DMA_DISABLE(hsd->hdmatx);
}

STM32F1 HAL库读写SD卡的操作要点相关推荐

  1. 使用STM32CUBEMX HAL库读写SD卡

    SD 卡系统(包括主机和 SD 卡)定义了两种操作模式: 卡识别模式 数据传输模式 在系统复位后,主机处于卡识别模式,寻找总线上可用的 SD卡设备:同时,SD 卡也处于卡 识别模式,直到被主机识别到. ...

  2. HAL库USB+SD卡,读卡器设置

    HAL库USB+SD卡,读卡器设置 主控:STM32L476VE STM32CUBE设置如下 开启SDMMC的DMA,RX和TX都设置为轮询模式 开启FATFS修改红框中内容 开启USB,设置devi ...

  3. SPI读写SD卡速度有多快?

    SD卡是一个嵌入式中非常常用的外设,可以用于存储一些大容量的数据.但用单片机读写SD卡速度一般都有限(对于高速SD卡,主要是受限于单片机本身的接口速度),在高速.实时数据存储时可能会有影响.但具体速度 ...

  4. SPI方式读写SD卡速度有多快?

    很久没有写公众号了,一方面忙,另一方面也不知道写些什么内容,大家如果有想了解的(前提是我也懂),可以后台发送给我. 今天主要来测试一下SPI读写SD卡的速度.SD卡是一个嵌入式中非常常用的外设,可以用 ...

  5. SDIO读写SD卡速度有多快?

    前两天测试了SPI方式读写SD卡的速度<SPI方式读写SD卡速度测试>,今天来测试一下SDIO方式的读写速度. 测试条件: 单片机:STM32F407VET6 编译环境:MDK 5.30+ ...

  6. STM32CUBEMX_SDIO和FATFS_读写SD卡

    STM32CUBEMX_SDIO和FATFS_读写SD卡 简述 FATFS是一个完全免费开源,专为小型嵌入式系统设计的FAT(File Allocation Table)文件系统模块.FATFS的编写 ...

  7. SDIO——读写SD卡

    SDIO的设备分类: SD / IO卡:这不是一种卡,现在已经不常用了,用到时再查. SD存储卡:分局容量不同有三种名字① <= 2GB 的叫SDSC:② >2GB <= 32GB的 ...

  8. VS code+STM32CubeMX 使用 FreeRTOS+FatFS+USB_DEVICE 搭建 SD卡 读卡器 和 读写 SD卡 示例项目

    文章目录 1. 新建项目 2. 配置 CubeMX 项目 3. 配置 EIDE 项目 4. 编写代码 5. 编译下载 6. 效果展示 本例介绍如何使用 vscode 插件 EIDE 和 STM32Cu ...

  9. STM32开发板入门教程(十三) - SPI模式读写SD卡

    功能介绍 :使用SPI模式 读写SD卡block数据 可通过串口发送到PC机查看 SD卡是Secure Digital Card卡的简称,直译成汉语就是"安全数字卡",是由日本松下 ...

最新文章

  1. 微信支付的架构真的那么牛吗?
  2. 关于现在人工智能预测的一些冷水
  3. 多线程题目 2019.06.02 晚
  4. 25+AI技术主题演讲及项目展示!英特尔AI全球影响力嘉年华开启,全球AI人才线上群聚
  5. Lync 小技巧-42-动态-IP-统一沟通-环境-IP-变更后-操作
  6. STM32F4+Wi-Fi+EDP 向 OneNet 上传数据
  7. 关于FCN的数据集着色说明
  8. ABAP操作Excel(转)
  9. Linux-CentOS上一些快捷键的使用
  10. Guitar Pro如何更改五线谱的符杆方向
  11. 批量转换excel文件格式
  12. 广州目前有几家等保测评机构呢?
  13. 蚂蚁金服AntV开源地理可视化引擎 L7 2.0——聊聊AntV背后那些事
  14. openstack虚拟机配置vip
  15. hive两拼接字段对比,涉及到的拼接字段拼接顺序问题
  16. MySQL 中 delete where in 语句的子查询限制
  17. ethereum扫描区块,获取区块内的交易记录
  18. t3安装找不到主机服务器,T3标准版不能连接服务器的处理方法
  19. 这5个“计算机专业”就业很吃香,毕业生需求量大,还不会过时
  20. Android中注解处理器系列之-简单使用

热门文章

  1. python数据的容器
  2. day 06 非空约束、唯一约束、主键约束、外键约束
  3. cesium模拟计算点
  4. ElementUI表单校验rules封装
  5. 决策树算法实现:泰坦尼克号乘客生存预测 (python实现)
  6. 浪涌-雷击浪涌的防护
  7. 使用php-excel-reader读取excel文件
  8. Mysql 启动与关闭
  9. 14. 异步加载Js的方式有哪些?
  10. ggplot2:初次见面,请多多关照!