文章目录[隐藏]
本文采用的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)短路,外壳是接地的。所以高电平是没插卡,低电平是插了卡。
示例代码
#include <stdio.h>
#include <stm32f1xx.h>
#include "common.h"
#define WRITE_ENABLE 0
DMA_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~15
gpio.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~15
gpio.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~15
gpio.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~PG5
gpio.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: PC12
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);
// SDIO_CMD: PD2
gpio.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_ENABLE
for (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扇区是否为全0
status = 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");
else
printf("Sector 628 is not empty. pos=%d\n", i - 512);
}
else
{
printf("Failed to check block 627~628!\n");
return -1;
}
// 将0x60000000内存里面的数据写到SD卡的扇区501~627
status = 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;
}
#endif
return 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 0
DMA_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卡
else
sd_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 == 0
DRESULT 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);
}
版权声明:本文为CSDN博主「巨大八爪鱼」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ZLK1214/article/details/121388735
暂无评论