1.准备文件
GD官网下载区下载开发板Demo包《GD32F4xx_Demo_Suites_V2.2.0》和《GD32F4xx_Firmware_Library_VX.X.X》。
解压《GD32F4xx_Demo_Suites_V2.2.0》找到文件里的文件fat_fs和SDIO_SDCardTest工程。这些是我们所需的文件系统和SDIO的初始化程序。
2.移植
复制SDIO的接口文件到工程文件下
工程下建立Fatfs文件夹并添加fat_fs的文件到工程
头文件目录添加
3.配置是否支持OS
本文是使用的FreeRTOS
然后修改ffsystem.c文件,打开对应的操作系统即可。
/*------------------------------------------------------------------------*/
/* Sample Code of OS Dependent Functions for FatFs */
/* (C)ChaN, 2018 */
/*------------------------------------------------------------------------*/
#include "ff.h"
#if FF_USE_LFN == 3 /* Dynamic memory allocation */
/*------------------------------------------------------------------------*/
/* Allocate a memory block */
/*------------------------------------------------------------------------*/
void* ff_memalloc ( /* Returns pointer to the allocated memory block (null if not enough core) */
UINT msize /* Number of bytes to allocate */
)
{
return malloc(msize); /* Allocate a new memory block with POSIX API */
}
/*------------------------------------------------------------------------*/
/* Free a memory block */
/*------------------------------------------------------------------------*/
void ff_memfree (
void* mblock /* Pointer to the memory block to free (nothing to do if null) */
)
{
free(mblock); /* Free the memory block with POSIX API */
}
#endif
#if FF_FS_REENTRANT /* Mutal exclusion */
/*------------------------------------------------------------------------*/
/* Create a Synchronization Object */
/*------------------------------------------------------------------------*/
/* This function is called in f_mount() function to create a new
/ synchronization object for the volume, such as semaphore and mutex.
/ When a 0 is returned, the f_mount() function fails with FR_INT_ERR.
*/
//const osMutexDef_t Mutex[FF_VOLUMES]; /* Table of CMSIS-RTOS mutex */
int ff_cre_syncobj ( /* 1:Function succeeded, 0:Could not create the sync object */
BYTE vol, /* Corresponding volume (logical drive number) */
FF_SYNC_t* sobj /* Pointer to return the created sync object */
)
{
// /* Win32 */
// *sobj = CreateMutex(NULL, FALSE, NULL);
// return (int)(*sobj != INVALID_HANDLE_VALUE);
/* uITRON */
// T_CSEM csem = {TA_TPRI,1,1};
// *sobj = acre_sem(&csem);
// return (int)(*sobj > 0);
/* uC/OS-II */
// OS_ERR err;
// *sobj = OSMutexCreate(0, &err);
// return (int)(err == OS_NO_ERR);
/* FreeRTOS */
*sobj = xSemaphoreCreateMutex();
return (int)(*sobj != NULL);
/* CMSIS-RTOS */
// *sobj = osMutexCreate(&Mutex[vol]);
// return (int)(*sobj != NULL);
}
/*------------------------------------------------------------------------*/
/* Delete a Synchronization Object */
/*------------------------------------------------------------------------*/
/* This function is called in f_mount() function to delete a synchronization
/ object that created with ff_cre_syncobj() function. When a 0 is returned,
/ the f_mount() function fails with FR_INT_ERR.
*/
int ff_del_syncobj ( /* 1:Function succeeded, 0:Could not delete due to an error */
FF_SYNC_t sobj /* Sync object tied to the logical drive to be deleted */
)
{
// /* Win32 */
// return (int)CloseHandle(sobj);
/* uITRON */
// return (int)(del_sem(sobj) == E_OK);
/* uC/OS-II */
// OS_ERR err;
// OSMutexDel(sobj, OS_DEL_ALWAYS, &err);
// return (int)(err == OS_NO_ERR);
/* FreeRTOS */
vSemaphoreDelete(sobj);
return 1;
/* CMSIS-RTOS */
// return (int)(osMutexDelete(sobj) == osOK);
}
/*------------------------------------------------------------------------*/
/* Request Grant to Access the Volume */
/*------------------------------------------------------------------------*/
/* This function is called on entering file functions to lock the volume.
/ When a 0 is returned, the file function fails with FR_TIMEOUT.
*/
int ff_req_grant ( /* 1:Got a grant to access the volume, 0:Could not get a grant */
FF_SYNC_t sobj /* Sync object to wait */
)
{
// /* Win32 */
// return (int)(WaitForSingleObject(sobj, FF_FS_TIMEOUT) == WAIT_OBJECT_0);
/* uITRON */
// return (int)(wai_sem(sobj) == E_OK);
/* uC/OS-II */
// OS_ERR err;
// OSMutexPend(sobj, FF_FS_TIMEOUT, &err));
// return (int)(err == OS_NO_ERR);
/* FreeRTOS */
return (int)(xSemaphoreTake(sobj, FF_FS_TIMEOUT) == pdTRUE);
/* CMSIS-RTOS */
// return (int)(osMutexWait(sobj, FF_FS_TIMEOUT) == osOK);
}
/*------------------------------------------------------------------------*/
/* Release Grant to Access the Volume */
/*------------------------------------------------------------------------*/
/* This function is called on leaving file functions to unlock the volume.
*/
void ff_rel_grant (
FF_SYNC_t sobj /* Sync object to be signaled */
)
{
// /* Win32 */
// ReleaseMutex(sobj);
/* uITRON */
// sig_sem(sobj);
/* uC/OS-II */
// OSMutexPost(sobj);
/* FreeRTOS */
xSemaphoreGive(sobj);
/* CMSIS-RTOS */
// osMutexRelease(sobj);
}
#endif
4.修改添加文件
-
添加初始化和中断程序
#include "fatfs.h" #include "sdcard.h" sd_card_info_struct sd_cardinfo; /* information of SD card */ /*! \brief this function handles SDIO interrupt request \param[in] none \param[out] none \retval none */ void SDIO_IRQHandler(void) { sd_interrupts_process(); } /*! \brief initialize the card, get the card information, set the bus mode and transfer mode \param[in] none \param[out] none \retval sd_error_enum */ sd_error_enum sd_config(void) { sd_error_enum status = SD_OK; uint32_t cardstate = 0; nvic_irq_enable(SDIO_IRQn, 5, 0); /* initialize the card */ status = sd_init(); if(SD_OK == status){ status = sd_card_information_get(&sd_cardinfo); } if(SD_OK == status){ status = sd_card_select_deselect(sd_cardinfo.card_rca); } status = sd_cardstatus_get(&cardstate); if(cardstate & 0x02000000){ trace("\r\n The card is locked!"); #if 0 /* unlock the card if necessary */ status = sd_lock_unlock(SD_UNLOCK); if(SD_OK != status){ trace("\r\n Unlock failed!"); while (1){ } }else{ trace("\r\n The card is unlocked! Please reset MCU!"); } #endif while (1){ } } if ((SD_OK == status) && (!(cardstate & 0x02000000))) { /* set bus mode */ status = sd_bus_mode_config(SDIO_BUSMODE_4BIT); // status = sd_bus_mode_config( SDIO_BUSMODE_1BIT ); } if (SD_OK == status) { /* set data transfer mode */ // status = sd_transfer_mode_config( SD_DMA_MODE ); status = sd_transfer_mode_config( SD_POLLING_MODE ); } return status; }
-
添加文件系统初始化程序
#include "fatfs.h" uint8_t retSD; /* Return value for SD */ char SDPath[4]; /* SD logical drive path */ void MX_FATFS_Init(void) { /*## FatFS: Link the SD driver ###########################*/ retSD = FATFS_LinkDriver(&SD_Driver, SDPath); }
-
文件系统的SDIO接口,注意GD32和STM32的区别,STM32通过CubeMX生成的程序是以sector为单位写的,写的buf为byte。GD32是以地址写的,buf为WORD。
static volatile DSTATUS Stat = STA_NOINIT; //static QueueHandle_t SDQueueID = NULL; /* Private function prototypes -----------------------------------------------*/ static DSTATUS SD_CheckStatus(BYTE lun); DSTATUS SD_initialize (BYTE); DSTATUS SD_status (BYTE); DRESULT SD_read (BYTE, BYTE*, DWORD, UINT); #if _USE_WRITE == 1 DRESULT SD_write (BYTE, const BYTE*, DWORD, UINT); #endif /* _USE_WRITE == 1 */ #if _USE_IOCTL == 1 DRESULT SD_ioctl (BYTE, BYTE, void*); #endif /* _USE_IOCTL == 1 */ const Diskio_drvTypeDef SD_Driver = { SD_initialize, SD_status, SD_read, #if _USE_WRITE == 1 SD_write, #endif /* _USE_WRITE == 1 */ #if _USE_IOCTL == 1 SD_ioctl, #endif /* _USE_IOCTL == 1 */ }; static int SD_CheckStatusWithTimeout(uint32_t timeout) { uint32_t timer; uint32_t cardstate = 0; /* block until SDIO peripheral is ready again or a timeout occur */ timer = xTaskGetTickCount(); while( xTaskGetTickCount() - timer < timeout) { if(sd_cardstatus_get(&cardstate) == SD_OK) { return RES_OK; } } return -1; } static DSTATUS SD_CheckStatus(BYTE lun) { Stat = STA_NOINIT; uint32_t cardstate = 0; if(sd_cardstatus_get(&cardstate) == SD_OK) { Stat = RES_OK; } return RES_OK; } extern sd_error_enum sd_config(void); /** * @brief Initializes a Drive * @param lun : not used * @retval DSTATUS: Operation status */ DSTATUS SD_initialize(BYTE lun) { Stat = STA_NOINIT; uint8_t sd_state = MSD_OK; /* initialize the card */ sd_state = sd_config(); Stat = (sd_state == SD_OK) ? MSD_OK : MSD_ERROR; return Stat; } /** * @brief Gets Disk Status * @param lun : not used * @retval DSTATUS: Operation status */ DSTATUS SD_status(BYTE lun) { return SD_CheckStatus(lun); } /* USER CODE BEGIN beforeReadSection */ /* can be used to modify previous code / undefine following code / add new code */ /* USER CODE END beforeReadSection */ /** * @brief Reads Sector(s) * @param lun : not used * @param *buff: Data buffer to store read data * @param sector: Sector address (LBA) * @param count: Number of sectors to read (1..128) * @retval DRESULT: Operation result */ DRESULT SD_read(BYTE lun, BYTE *buff, DWORD sector, UINT count) { DRESULT res = RES_ERROR; uint8_t sd_state = MSD_OK; /* * ensure the SDCard is ready for a new operation */ if (SD_CheckStatusWithTimeout(SD_TIMEOUT) < 0) { return res; } /* Fast path cause destination buffer is correctly aligned */ sd_transfer_mode_config( SD_POLLING_MODE ); /* Read block(s) in DMA transfer mode */ sd_state = sd_block_read((uint32_t*)buff, (sector*512), (count*512)); if(sd_state != SD_OK) { res = RES_ERROR; } else { res = RES_OK; } return res; } /* USER CODE BEGIN beforeWriteSection */ /* can be used to modify previous code / undefine following code / add new code */ /* USER CODE END beforeWriteSection */ /** * @brief Writes Sector(s) * @param lun : not used * @param *buff: Data to be written * @param sector: Sector address (LBA) * @param count: Number of sectors to write (1..128) * @retval DRESULT: Operation result */ #if _USE_WRITE == 1 DRESULT SD_write(BYTE lun, const BYTE *buff, DWORD sector, UINT count) { DRESULT res = RES_ERROR; uint8_t sd_state = MSD_OK; /* * ensure the SDCard is ready for a new operation */ if (SD_CheckStatusWithTimeout(SD_TIMEOUT) < 0) { return res; } sd_transfer_mode_config( SD_POLLING_MODE ); /* Write block(s) in DMA transfer mode */ sd_state = sd_block_write((uint32_t*)buff, (sector*512), (count*512)); if(sd_state != SD_OK) { res = RES_ERROR; } else { res = RES_OK; } return res; } #endif /* _USE_WRITE == 1 */ /** * @brief I/O control operation * @param lun : not used * @param cmd: Control code * @param *buff: Buffer to send/receive control data * @retval DRESULT: Operation result */ #if _USE_IOCTL == 1 DRESULT SD_ioctl(BYTE lun, BYTE cmd, void *buff) { DRESULT res = RES_ERROR; sd_card_info_struct CardInfo; if (Stat & STA_NOINIT) return RES_NOTRDY; switch (cmd) { /* Make sure that no pending write process */ case CTRL_SYNC : res = RES_OK; break; /* Get number of sectors on the disk (DWORD) */ case GET_SECTOR_COUNT : sd_card_information_get(&CardInfo); *(DWORD*)buff = CardInfo.card_blocksize; res = RES_OK; break; /* Get R/W sector size (WORD) */ case GET_SECTOR_SIZE : sd_card_information_get(&CardInfo); *(WORD*)buff = CardInfo.card_blocksize; res = RES_OK; break; /* Get erase block size in unit of sector (DWORD) */ case GET_BLOCK_SIZE : sd_card_information_get(&CardInfo); *(DWORD*)buff = CardInfo.card_blocksize / SD_DEFAULT_BLOCK_SIZE; res = RES_OK; break; default: res = RES_PARERR; } return res; } #endif /* _USE_IOCTL == 1 */
5.测试
测试前,确保SD卡的格式为FAT32和512字节。
void Fat32_SD_test(void)
{
FATFS fs; // Work area (file system object) for logical drive
FIL fil; // file objects
uint8_t rtext[100]; /* File read buffers */
char filename[] = "GD32_SD_FATFS.txt";
uint8_t wtext[] = "This is GD32 working with FatFs"; /* File write buffer */
uint32_t byteswritten; /* File write counts */
uint32_t bytesread; /* File read counts */
trace("\r\n ****** FatFs Example ******\r\n\r\n");
/*##-1- Register the file system object to the FatFs module ##############*/
retSD = f_mount(&fs, "", 0);
if(retSD)
{
trace(" mount error : %d \r\n",retSD);
}
else
{
trace(" 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);
card_info_get();
if(retSD)
trace(" open file error : %d\r\n",retSD);
else
trace(" open file sucess!!! \r\n");
/*##-3- Write data to the text files ###############################*/
retSD = f_write(&fil, wtext, sizeof(wtext), (void *)&byteswritten);
if(retSD)
trace(" write file error : %d\r\n",retSD);
else
{
trace(" write file sucess!!! \r\n");
trace(" write Data : %s\r\n",wtext);
}
/*##-4- Close the open text files ################################*/
retSD = f_close(&fil);
if(retSD)
trace(" close error : %d\r\n",retSD);
else
trace(" close sucess!!! \r\n");
/*##-5- Open the text files object with read access ##############*/
retSD = f_open(&fil, filename, FA_READ);
if(retSD)
trace(" open file error : %d\r\n",retSD);
else
trace(" open file sucess!!! \r\n");
/*##-6- Read data from the text files ##########################*/
retSD = f_read(&fil, rtext, sizeof(rtext), (UINT*)&bytesread);
if(retSD)
trace(" read error!!! %d\r\n",retSD);
else
{
trace(" read sucess!!! \r\n");
trace(" read Data : %s\r\n",rtext);
}
/*##-7- Close the open text files ############################*/
retSD = f_close(&fil);
if(retSD)
trace(" close error!!! %d\r\n",retSD);
else
trace(" close sucess!!! \r\n");
/*##-8- Compare read data with the expected data ############*/
if(bytesread == byteswritten)
{
trace(" FatFs is working well!!!\r\n");
}
}
6.追加写/读
FRESULT f_open_append_addr (
FIL* fp, /* [OUT] File object to create */
const char* path, /* [IN] File name to be opened */
unsigned long addr,
BYTE mode
)
{
FRESULT fr;
/* Opens an existing file. If not exist, creates a new file. */
fr = f_open(fp, path, mode);
if (fr == FR_OK) {
/* Seek to file addr */
// fr = f_lseek(fp, f_size(fp));
fr = f_lseek(fp, addr);
if (fr != FR_OK)
f_close(fp);
}
return fr;
}
unsigned char f_sd_r(unsigned long addr,void *pDat,unsigned long len)
{
uint32_t bytesread; /* File write counts */
// f_mount(&fs, "", 0);
retSD = f_open_append_addr(&fil, filename, addr, FA_READ | FA_OPEN_ALWAYS);
if(!retSD)
{
retSD = f_read(&fil, pDat, len, (void *)&bytesread);
f_close(&fil);
}
// f_mount(NULL, "", 0);
return retSD;
}
unsigned char f_sd_w(unsigned long addr,void *pDat,unsigned long len)
{
uint32_t byteswritten; /* File write counts */
// f_mount(&fs, "", 0);
retSD = f_open_append_addr(&fil, filename, addr, FA_WRITE | FA_OPEN_ALWAYS);
if(!retSD)
{
retSD = f_write(&fil, pDat, len, (void *)&byteswritten);
f_close(&fil);
}
// f_mount(NULL, "", 0);
return retSD;
}