- 使用FatFs文件系统
添加FatFs文件系统,使用DMA读写方式驱动。
1、打开配置文件,在FatFs勾选SD Card
2、在configuration选项卡中打开“FATFS”选项,按下图配置,以支持中文和长目录。生成代码并打开工程。
打开菜单栏project -> settings,如图红框部分,调大堆栈,heap = 0x400, Stack = 0x1000.
3、修改代码
添加FATFS测试代码。
打开main.c文件,在变量定义区( 79行附近 )加入变量:
FATFS fs; // Work area (file system object) for logical drive
FIL fil; // file objects
uint32_t byteswritten; /* File write counts */
uint32_t bytesread; /* File read counts */
uint8_t wtext[] = "This is STM32 working with FatFs"; /* File write buffer */
uint8_t rtext[100]; /* File read buffers */
char filename[] = "STM32cube.txt";
在void SD_Write_Read_Test(void)函数下面添加文件系统测试函数:
void Fatfs_RW_test(void)
{
printf("\r\n ****** FatFs Example ******\r\n\r\n");
/*##-1- Register the file system object to the FatFs module ##############*/
retSD = f_mount(&fs, "", 1);
if(retSD)
{
printf(" mount error : %d \r\n",retSD);
Error_Handler();
}
else
printf(" mount sucess!!! \r\n");
/*##-2- Create and Open new text file objects with write access ######*/
retSD = f_open(&fil, filename, FA_CREATE_ALWAYS | FA_WRITE);
if(retSD)
printf(" open file error : %d\r\n",retSD);
else
printf(" open file sucess!!! \r\n");
/*##-3- Write data to the text files ###############################*/
retSD = f_write(&fil, wtext, sizeof(wtext), (void *)&byteswritten);
if(retSD)
printf(" write file error : %d\r\n",retSD);
else
{
printf(" write file sucess!!! \r\n");
printf(" write Data : %s\r\n",wtext);
}
/*##-4- Close the open text files ################################*/
retSD = f_close(&fil);
if(retSD)
printf(" close error : %d\r\n",retSD);
else
printf(" close sucess!!! \r\n");
/*##-5- Open the text files object with read access ##############*/
retSD = f_open(&fil, filename, FA_READ);
if(retSD)
printf(" open file error : %d\r\n",retSD);
else
printf(" open file sucess!!! \r\n");
/*##-6- Read data from the text files ##########################*/
retSD = f_read(&fil, rtext, sizeof(rtext), (UINT*)&bytesread);
if(retSD)
printf(" read error!!! %d\r\n",retSD);
else
{
printf(" read sucess!!! \r\n");
printf(" read Data : %s\r\n",rtext);
}
/*##-7- Close the open text files ############################*/
retSD = f_close(&fil);
if(retSD)
printf(" close error!!! %d\r\n",retSD);
else
printf(" close sucess!!! \r\n");
/*##-8- Compare read data with the expected data ############*/
if(bytesread == byteswritten)
{
printf(" FatFs is working well!!!\r\n");
}
}
添加完成后,到main函数中修改:
注释掉以下语句:
// SD_EraseTest();
// SD_Write_Read_Test();
接着添加:
Fatfs_RW_test();
4、完成后编译下载工程。
发现凉凉了。。。
这就是ST挖的一个坑。。。
这个坑怎么填呢?
打开下载的库,找到下面的文件位置:
C:\Users\XXXXXX(你的电脑用户名)\STM32Cube\Repository\STM32Cube_FW_L4_V1.13.0\Projects\STM32L476G-EVAL\Applications\FatFs\FatFs_uSD_Standalone\MDK-ARM
打开ST的官方例程。
在“stm32l476g_eval_sd.c”文件下的第302行,发现了好东西。
而打开我们的工程中的bsp_driver_sd.c,第214行,却发现少了点什么。
ST官方的例子,在每次调用HAL_SD_WriteBlocks_DMA和HAL_SD_ReadBlocks_DMA之前都进行了DMA通道的初始化配置,这就是解决问题的关键。而CubeMX生成的源码缺少了这个配置,甚至连一个注释都没有。。。
5、找到问题的所在就开始解决问题。
添加以下源码到main.c的void Fatfs_RW_test(void)函数的后面:
/**
* @brief Configure the DMA to receive data from the SD card
* @retval
* HAL_ERROR or HAL_OK
*/
HAL_StatusTypeDef SD_DMAConfigRx(SD_HandleTypeDef *hsd)
{
HAL_StatusTypeDef status = HAL_ERROR;
/* Configure DMA Rx parameters */
hdma_sdmmc1_rx.Instance = DMA2_Channel4;
hdma_sdmmc1_rx.Init.Request = DMA_REQUEST_7;
hdma_sdmmc1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_sdmmc1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_sdmmc1_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_sdmmc1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_sdmmc1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_sdmmc1_rx.Init.Mode = DMA_NORMAL;
hdma_sdmmc1_rx.Init.Priority = DMA_PRIORITY_LOW;
/* Associate the DMA handle */
__HAL_LINKDMA(hsd,hdmarx,hdma_sdmmc1_rx);
/* Stop any ongoing transfer and reset the state*/
HAL_DMA_Abort(&hdma_sdmmc1_rx);
/* Deinitialize the Channel for new transfer */
HAL_DMA_DeInit(&hdma_sdmmc1_tx);//注意这里!!!DeInit的是另一个通道!!!
/* Configure the DMA Channel */
status = HAL_DMA_Init(&hdma_sdmmc1_rx);
return (status);
}
/**
* @brief Configure the DMA to transmit data to the SD card
* @retval
* HAL_ERROR or HAL_OK
*/
HAL_StatusTypeDef SD_DMAConfigTx(SD_HandleTypeDef *hsd)
{
HAL_StatusTypeDef status;
/* SDMMC1_TX Init */
hdma_sdmmc1_tx.Instance = DMA2_Channel5;
hdma_sdmmc1_tx.Init.Request = DMA_REQUEST_7;
hdma_sdmmc1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_sdmmc1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_sdmmc1_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_sdmmc1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_sdmmc1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_sdmmc1_tx.Init.Mode = DMA_NORMAL;//这里NORMAL或其他都可以,无所谓
hdma_sdmmc1_tx.Init.Priority = DMA_PRIORITY_LOW;
/* Associate the DMA handle */
__HAL_LINKDMA(hsd, hdmatx, hdma_sdmmc1_tx);
/* Stop any ongoing transfer and reset the state*/
HAL_DMA_Abort(&hdma_sdmmc1_tx);
/* Deinitialize the Channel for new transfer */
HAL_DMA_DeInit(&hdma_sdmmc1_rx); //注意这里!!!DeInit的是另一个通道!!!
/* Configure the DMA Channel */
status = HAL_DMA_Init(&hdma_sdmmc1_tx);
return (status);
}
注意:经测试实验,SD_DMAConfigRx和SD_DMAConfigTx函数可做适当精简,可以只保留
HAL_DMA_DeInit和HAL_DMA_Init,如果是收发复用DMA,仅格外配置变化项(hdma_sdmmc1_tx.Init.Direction)和__HAL_LINKDMA即可。
然后,打开bsp_driver_sd.c文件,到BSP_SD_ReadBlocks_DMA函数中(186行附近),在
if (HAL_SD_ReadBlocks_DMA(&hsd1, (uint8_t *)pData, ReadAddr, NumOfBlocks) != HAL_OK)
{
sd_state = MSD_ERROR;
}
上方添加以下代码:
if(SD_DMAConfigRx(&hsd1) != HAL_OK)
{
return MSD_ERROR;
}
在BSP_SD_WriteBlocks_DMA(214行附近),在
if (HAL_SD_WriteBlocks_DMA(&hsd1, (uint8_t *)pData, WriteAddr, NumOfBlocks) != HAL_OK)
{
sd_state = MSD_ERROR;
}
上方添加以下代码:
if(SD_DMAConfigTx(&hsd1) != HAL_OK)
{
return MSD_ERROR;
}
添加完成后效果:(头文件声明自己改改)
编译工程,下载测试。将SD卡取出插到电脑上,可以看到生成的文件和文件内容。