88.7.4 第4步,配置GPIO和时钟
根据大家使用SDMMC1或者SDMMC2配置相应时钟和GPIO,当前V7板子是用的SDMMC1:
- __weak void BSP_SD_MspInit(SD_HandleTypeDef *hsd, void *Params)
- {
- GPIO_InitTypeDef gpio_init_structure;
- /* Enable SDIO clock */
- __HAL_RCC_SDMMC1_CLK_ENABLE();
- /* Enable GPIOs clock */
- __HAL_RCC_GPIOB_CLK_ENABLE();
- __HAL_RCC_GPIOC_CLK_ENABLE();
- __HAL_RCC_GPIOD_CLK_ENABLE();
- gpio_init_structure.Mode = GPIO_MODE_AF_PP;
- gpio_init_structure.Pull = GPIO_NOPULL;
- gpio_init_structure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
- /* D0(PC8), D1(PC9), D2(PC10), D3(PC11), CK(PC12), CMD(PD2) */
- /* Common GPIO configuration */
- gpio_init_structure.Alternate = GPIO_AF12_SDIO1;
- /* GPIOC configuration */
- gpio_init_structure.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12;
- HAL_GPIO_Init(GPIOC, &gpio_init_structure);
- /* GPIOD configuration */
- gpio_init_structure.Pin = GPIO_PIN_2;
- HAL_GPIO_Init(GPIOD, &gpio_init_structure);
- __HAL_RCC_SDMMC1_FORCE_RESET();
- __HAL_RCC_SDMMC1_RELEASE_RESET();
- /* NVIC configuration for SDIO interrupts */
- HAL_NVIC_SetPriority(SDMMC1_IRQn, 5, 0);
- HAL_NVIC_EnableIRQ(SDMMC1_IRQn);
- }
复制代码
88.7.5 第5步,MPU配置
为了方便大家移植测试,我们这里直接关闭AXI SRAM的读Cache和写Cache(这样就跟使用STM32F1或者STM32F4系列里面的SRAM一样)。此配置是在bsp.c文件的MPU_Config函数里面实现:
- /*
- *********************************************************************************************************
- * 函 数 名: MPU_Config
- * 功能说明: 配置MPU
- * 形 参: 无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- static void MPU_Config( void )
- {
- MPU_Region_InitTypeDef MPU_InitStruct;
- /* 禁止 MPU */
- HAL_MPU_Disable();
- #if 0
- /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
- MPU_InitStruct.Enable = MPU_REGION_ENABLE;
- MPU_InitStruct.BaseAddress = 0x24000000;
- MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;
- MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
- MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
- MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
- MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
- MPU_InitStruct.Number = MPU_REGION_NUMBER0;
- MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
- MPU_InitStruct.SubRegionDisable = 0x00;
- MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
- HAL_MPU_ConfigRegion(&MPU_InitStruct);
- #else
- /* 当前是采用下面的配置 */
- /* 配置AXI SRAM的MPU属性为NORMAL, NO Read allocate,NO Write allocate */
- MPU_InitStruct.Enable = MPU_REGION_ENABLE;
- MPU_InitStruct.BaseAddress = 0x24000000;
- MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;
- MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
- MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
- MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
- MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
- MPU_InitStruct.Number = MPU_REGION_NUMBER0;
- MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
- MPU_InitStruct.SubRegionDisable = 0x00;
- MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
- HAL_MPU_ConfigRegion(&MPU_InitStruct);
- #endif
- /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
- MPU_InitStruct.Enable = MPU_REGION_ENABLE;
- MPU_InitStruct.BaseAddress = 0x60000000;
- MPU_InitStruct.Size = ARM_MPU_REGION_SIZE_64KB;
- MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
- MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
- MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
- MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
- MPU_InitStruct.Number = MPU_REGION_NUMBER1;
- MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
- MPU_InitStruct.SubRegionDisable = 0x00;
- MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
- HAL_MPU_ConfigRegion(&MPU_InitStruct);
- /*使能 MPU */
- HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
- }
复制代码
88.7.6 第6步,FatFs的配置文件ffconf.h设置
移植阶段,这个里面的配置可以不用动,无需任何修改。需要注意的是我们这里开启了长文件名支持,对应的宏定义:
复制代码
88.7.7 第7步,添加应用代码
这里将FatFs大部分操作函数都做了应用,专门整理到了文件demo_sd_fatfs.c。通过串口命令进行操作,大家可以直接将这个文件添加到自己的工程里面。
另外注意,如果自己的工程里面没有移植我们其它的驱动,可以直接调用FatFs的测试函数,比如浏览SD根目录文件,可以直接调用函数ViewRootDir。
88.8 FatFs应用代码测试
这里将FatFs大部分函数都做了测试。注意,所有用到的函数在FatFs官网都有详细说明。
88.8.1 注册SD卡驱动
注册SD卡功能是ST简单封装的一个函数,方便用户实现FatFs驱动多个磁盘。
代码如下:
- char DiskPath[4]; /* SD卡逻辑驱动路径,比盘符0,就是"0:/" */
- /* 注册SD卡驱动 */
- FATFS_LinkDriver(&SD_Driver, DiskPath);
复制代码
88.8.2 SD卡文件浏览
SD卡根目录的文件浏览代码实现如下:
- /*
- *********************************************************************************************************
- * 函 数 名: ViewRootDir
- * 功能说明: 显示SD卡根目录下的文件名
- * 形 参:无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- extern SD_HandleTypeDef uSdHandle;
- static void ViewRootDir(void)
- {
- FRESULT result;
- uint32_t cnt = 0;
- FILINFO fno;
- /* 挂载文件系统 */
- result = f_mount(&fs, DiskPath, 0); /* Mount a logical drive */
- if (result != FR_OK)
- {
- printf("挂载文件系统失败 (%s)\r\n", FR_Table[result]);
- }
- /* 打开根文件夹 */
- result = f_opendir(&DirInf, DiskPath); /* 如果不带参数,则从当前目录开始 */
- if (result != FR_OK)
- {
- printf("打开根目录失败 (%s)\r\n", FR_Table[result]);
- return;
- }
- printf("属性 | 文件大小 | 短文件名 | 长文件名\r\n");
- for (cnt = 0; ;cnt++)
- {
- result = f_readdir(&DirInf, &FileInf); /* 读取目录项,索引会自动下移 */
- if (result != FR_OK || FileInf.fname[0] == 0)
- {
- break;
- }
- if (FileInf.fname[0] == '.')
- {
- continue;
- }
- /* 判断是文件还是子目录 */
- if (FileInf.fattrib & AM_DIR)
- {
- printf("(0x%02d)目录 ", FileInf.fattrib);
- }
- else
- {
- printf("(0x%02d)文件 ", FileInf.fattrib);
- }
- f_stat(FileInf.fname, &fno);
- /* 打印文件大小, 最大4G */
- printf(" %10d", (int)fno.fsize);
- printf(" %s\r\n", (char *)FileInf.fname); /* 长文件名 */
- }
- /* 打印卡速度信息 */
- if(uSdHandle.SdCard.CardSpeed == CARD_NORMAL_SPEED)
- {
- printf("Normal Speed Card <12.5MB/S, MAX Clock < 25MHz, Spec Version 1.01\r\n");
- }
- else if (uSdHandle.SdCard.CardSpeed == CARD_HIGH_SPEED)
- {
- printf("High Speed Card <25MB/s, MAX Clock < 50MHz, Spec Version 2.00\r\n");
- }
- else if (uSdHandle.SdCard.CardSpeed == CARD_ULTRA_HIGH_SPEED)
- {
- printf("UHS-I SD Card <50MB/S for SDR50, DDR50 Cards, MAX Clock < 50MHz OR 100MHz\r\n");
- printf("UHS-I SD Card <104MB/S for SDR104, MAX Clock < 108MHz, Spec version 3.01\r\n");
- }
- /* 卸载文件系统 */
- f_mount(NULL, DiskPath, 0);
- }
复制代码
f_mount可以上电后仅调用一次,我们这里是为了测试方式,使用前挂载,使用完毕后卸载。
代码里面加入了SD卡速度信息打印功能,方便大家了解自己的卡类型。通过查询全局结构体变量uSdHandle来实现。
文件浏览通过函数f_readdir实现。
88.8.3 SD卡创建txt文件并写入数据
代码实现如下:
- /*
- *********************************************************************************************************
- * 函 数 名: CreateNewFile
- * 功能说明: 在SD卡创建一个新文件,文件内容填写“<a target="_blank">www.armfly.com</a>”
- * 形 参:无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- static void CreateNewFile(void)
- {
- FRESULT result;
- uint32_t bw;
- char path[32];
- /* 挂载文件系统 */
- result = f_mount(&fs, DiskPath, 0); /* Mount a logical drive */
- if (result != FR_OK)
- {
- printf("挂载文件系统失败 (%s)\r\n", FR_Table[result]);
- }
- /* 打开文件 */
- sprintf(path, "%sarmfly.txt", DiskPath);
- result = f_open(&file, path, FA_CREATE_ALWAYS | FA_WRITE);
- if (result == FR_OK)
- {
- printf("armfly.txt 文件打开成功\r\n");
- }
- else
- {
- printf("armfly.txt 文件打开失败 (%s)\r\n", FR_Table[result]);
- }
- /* 写一串数据 */
- result = f_write(&file, FsWriteBuf, strlen(FsWriteBuf), &bw);
- if (result == FR_OK)
- {
- printf("armfly.txt 文件写入成功\r\n");
- }
- else
- {
- printf("armfly.txt 文件写入失败 (%s)\r\n", FR_Table[result]);
- }
- /* 关闭文件*/
- f_close(&file);
- /* 卸载文件系统 */
- f_mount(NULL, DiskPath, 0);
- }
复制代码
f_mount可以上电后仅调用一次,我们这里是为了测试方式,使用前挂载,使用完毕后卸载。
函数f_open用来创建并打开文件。
函数f_write用来写入数据。
函数f_close用来关闭文件,注意调用完函数f_write后,内容还没有实际写入到SD卡中,调用了f_close后,数据才真正的写入到SD卡。当然也可以调用函数f_sync,内容也会实际的写入。
88.8.4 SD卡文件读取
代码实现如下:
- /*
- *********************************************************************************************************
- * 函 数 名: ReadFileData
- * 功能说明: 读取文件armfly.txt前128个字符,并打印到串口
- * 形 参:无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- static void ReadFileData(void)
- {
- FRESULT result;
- uint32_t bw;
- char path[64];
- /* 挂载文件系统 */
- result = f_mount(&fs, DiskPath, 0); /* Mount a logical drive */
- if (result != FR_OK)
- {
- printf("挂载文件系统失败 (%s)\r\n", FR_Table[result]);
- }
- /* 打开文件 */
- sprintf(path, "%sarmfly.txt", DiskPath);
- result = f_open(&file, path, FA_OPEN_EXISTING | FA_READ);
- if (result != FR_OK)
- {
- printf("Don't Find File : armfly.txt\r\n");
- return;
- }
- /* 读取文件 */
- result = f_read(&file, FsReadBuf, sizeof(FsReadBuf), &bw);
- if (bw > 0)
- {
- FsReadBuf[bw] = 0;
- printf("\r\narmfly.txt 文件内容 : \r\n%s\r\n", FsReadBuf);
- }
- else
- {
- printf("\r\narmfly.txt 文件内容 : \r\n");
- }
- /* 关闭文件*/
- f_close(&file);
- /* 卸载文件系统 */
- f_mount(NULL, DiskPath, 0);
- }
复制代码
f_mount可以上电后仅调用一次,我们这里是为了测试方式,使用前挂载,使用完毕后卸载。
函数f_open用来打开文件。
函数f_read用来读取文件中的内容。
函数f_close用来关闭打开的文件。
88.8.5 SD卡创建文件夹
代码实现如下:
- /*
- *********************************************************************************************************
- * 函 数 名: CreateDir
- * 功能说明: 在SD卡根目录创建Dir1和Dir2目录,在Dir1目录下创建子目录Dir1_1
- * 形 参:无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- static void CreateDir(void)
- {
- FRESULT result;
- char path[64];
- /* 挂载文件系统 */
- result = f_mount(&fs, DiskPath, 0); /* Mount a logical drive */
- if (result != FR_OK)
- {
- printf("挂载文件系统失败 (%s)\r\n", FR_Table[result]);
- }
- /* 创建目录/Dir1 */
- sprintf(path, "%sDir1", DiskPath);
- result = f_mkdir(path);
- if (result == FR_OK)
- {
- printf("f_mkdir Dir1 Ok\r\n");
- }
- else if (result == FR_EXIST)
- {
- printf("Dir1 目录已经存在(%d)\r\n", result);
- }
- else
- {
- printf("f_mkdir Dir1 失败 (%s)\r\n", FR_Table[result]);
- return;
- }
- /* 创建目录/Dir2 */
- sprintf(path, "%sDir2", DiskPath);
- result = f_mkdir(path);
- if (result == FR_OK)
- {
- printf("f_mkdir Dir2 Ok\r\n");
- }
- else if (result == FR_EXIST)
- {
- printf("Dir2 目录已经存在(%d)\r\n", result);
- }
- else
- {
- printf("f_mkdir Dir2 失败 (%s)\r\n", FR_Table[result]);
- return;
- }
- /* 创建子目录 /Dir1/Dir1_1 注意:创建子目录Dir1_1时,必须先创建好Dir1 */
- sprintf(path, "%sDir1/Dir1_1", DiskPath);
- result = f_mkdir(path); /* */
- if (result == FR_OK)
- {
- printf("f_mkdir Dir1_1 成功\r\n");
- }
- else if (result == FR_EXIST)
- {
- printf("Dir1_1 目录已经存在 (%d)\r\n", result);
- }
- else
- {
- printf("f_mkdir Dir1_1 失败 (%s)\r\n", FR_Table[result]);
- return;
- }
- /* 卸载文件系统 */
- f_mount(NULL, DiskPath, 0);
- }
复制代码
f_mount可以上电后仅调用一次,我们这里是为了测试方式,使用前挂载,使用完毕后卸载。
创建目录通过函数f_mkdir。
88.8.6 SD卡文件和文件夹删除
代码实现如下:
- /*
- *********************************************************************************************************
- * 函 数 名: DeleteDirFile
- * 功能说明: 删除SD卡根目录下的 armfly.txt 文件和 Dir1,Dir2 目录
- * 形 参:无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- static void DeleteDirFile(void)
- {
- FRESULT result;
- uint8_t i;
- char path[64];
- /* 挂载文件系统 */
- result = f_mount(&fs, DiskPath, 0); /* Mount a logical drive */
- if (result != FR_OK)
- {
- printf("挂载文件系统失败 (%s)\r\n", FR_Table[result]);
- }
- /* 删除目录/Dir1 【因为还存在目录非空(存在子目录),所以这次删除会失败】*/
- sprintf(path, "%sDir1", DiskPath);
- result = f_unlink(path);
- if (result == FR_OK)
- {
- printf("删除目录Dir1成功\r\n");
- }
- else if (result == FR_NO_FILE)
- {
- printf("没有发现文件或目录 :%s\r\n", "/Dir1");
- }
- else
- {
- printf("删除Dir1失败(错误代码 = %d) 文件只读或目录非空\r\n", result);
- }
- /* 先删除目录/Dir1/Dir1_1 */
- sprintf(path, "%sDir1/Dir1_1", DiskPath);
- result = f_unlink(path);
- if (result == FR_OK)
- {
- printf("删除子目录/Dir1/Dir1_1成功\r\n");
- }
- else if ((result == FR_NO_FILE) || (result == FR_NO_PATH))
- {
- printf("没有发现文件或目录 :%s\r\n", "/Dir1/Dir1_1");
- }
- else
- {
- printf("删除子目录/Dir1/Dir1_1失败(错误代码 = %d) 文件只读或目录非空\r\n", result);
- }
- /* 先删除目录/Dir1 */
- sprintf(path, "%sDir1", DiskPath);
- result = f_unlink(path);
- if (result == FR_OK)
- {
- printf("删除目录Dir1成功\r\n");
- }
- else if (result == FR_NO_FILE)
- {
- printf("没有发现文件或目录 :%s\r\n", "/Dir1");
- }
- else
- {
- printf("删除Dir1失败(错误代码 = %d) 文件只读或目录非空\r\n", result);
- }
- /* 删除目录/Dir2 */
- sprintf(path, "%sDir2", DiskPath);
- result = f_unlink(path);
- if (result == FR_OK)
- {
- printf("删除目录 Dir2 成功\r\n");
- }
- else if (result == FR_NO_FILE)
- {
- printf("没有发现文件或目录 :%s\r\n", "/Dir2");
- }
- else
- {
- printf("删除Dir2 失败(错误代码 = %d) 文件只读或目录非空\r\n", result);
- }
- /* 删除文件 armfly.txt */
- sprintf(path, "%sarmfly.txt", DiskPath);
- result = f_unlink(path);
- if (result == FR_OK)
- {
- printf("删除文件 armfly.txt 成功\r\n");
- }
- else if (result == FR_NO_FILE)
- {
- printf("没有发现文件或目录 :%s\r\n", "armfly.txt");
- }
- else
- {
- printf("删除armfly.txt失败(错误代码 = %d) 文件只读或目录非空\r\n", result);
- }
- /* 删除文件 speed1.txt */
- for (i = 0; i < 20; i++)
- {
- sprintf(path, "%sSpeed%02d.txt", DiskPath, i);/* 每写1次,序号递增 */
- result = f_unlink(path);
- if (result == FR_OK)
- {
- printf("删除文件%s成功\r\n", path);
- }
- else if (result == FR_NO_FILE)
- {
- printf("没有发现文件:%s\r\n", path);
- }
- else
- {
- printf("删除%s文件失败(错误代码 = %d) 文件只读或目录非空\r\n", path, result);
- }
- }
- /* 卸载文件系统 */
- f_mount(NULL, DiskPath, 0);
- }
复制代码
f_mount可以上电后仅调用一次,我们这里是为了测试方式,使用前挂载,使用完毕后卸载。
文件夹和文件的删除都是通过函数f_unlink实现,这里注意一点,删除文件夹时,只有文件夹中的内容为空时,才可以删除文件夹。
88.8.7 SD卡读写速度测试
代码实现如下,主要是方便大家测试SD卡的读写性能。
- /*
- *********************************************************************************************************
- * 函 数 名: WriteFileTest
- * 功能说明: 测试文件读写速度
- * 形 参:无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- static void WriteFileTest(void)
- {
- FRESULT result;
- char path[64];
- uint32_t bw;
- uint32_t i,k;
- uint32_t runtime1,runtime2,timelen;
- uint8_t err = 0;
- static uint8_t s_ucTestSn = 0;
- for (i = 0; i < sizeof(g_TestBuf); i++)
- {
- g_TestBuf<i> = (i / 512) + '0';
- </i>}
- /* 挂载文件系统 */
- result = f_mount(&fs, DiskPath, 0); /* Mount a logical drive */
- if (result != FR_OK)
- {
- printf("挂载文件系统失败 (%s)\r\n", FR_Table[result]);
- }
- /* 打开文件 */
- sprintf(path, "%sSpeed%02d.txt", DiskPath, s_ucTestSn++); /* 每写1次,序号递增 */
- result = f_open(&file, path, FA_CREATE_ALWAYS | FA_WRITE);
- /* 写一串数据 */
- printf("开始写文件%s %dKB ...\r\n", path, TEST_FILE_LEN / 1024);
- runtime1 = bsp_GetRunTime(); /* 读取系统运行时间 */
- for (i = 0; i < TEST_FILE_LEN / BUF_SIZE; i++)
- {
- result = f_write(&file, g_TestBuf, sizeof(g_TestBuf), &bw);
- if (result == FR_OK)
- {
- if (((i + 1) % 8) == 0)
- {
- printf(".");
- }
- }
- else
- {
- err = 1;
- printf("%s文件写失败\r\n", path);
- break;
- }
- }
- runtime2 = bsp_GetRunTime(); /* 读取系统运行时间 */
- if (err == 0)
- {
- timelen = (runtime2 - runtime1);
- printf("\r\n 写耗时 : %dms 平均写速度 : %dB/S (%dKB/S)\r\n",
- timelen,
- (TEST_FILE_LEN * 1000) / timelen,
- ((TEST_FILE_LEN / 1024) * 1000) / timelen);
- }
- f_close(&file); /* 关闭文件*/
- /* 开始读文件测试 */
- result = f_open(&file, path, FA_OPEN_EXISTING | FA_READ);
- if (result != FR_OK)
- {
- printf("没有找到文件: %s\r\n", path);
- return;
- }
- printf("开始读文件 %dKB ...\r\n", TEST_FILE_LEN / 1024);
- runtime1 = bsp_GetRunTime(); /* 读取系统运行时间 */
- for (i = 0; i < TEST_FILE_LEN / BUF_SIZE; i++)
- {
- result = f_read(&file, g_TestBuf, sizeof(g_TestBuf), &bw);
- if (result == FR_OK)
- {
- if (((i + 1) % 8) == 0)
- {
- printf(".");
- }
- /* 比较写入的数据是否正确,此语句会导致读卡速度结果降低到 3.5MBytes/S */
- for (k = 0; k < sizeof(g_TestBuf); k++)
- {
- if (g_TestBuf[k] != (k / 512) + '0')
- {
- err = 1;
- printf("Speed1.txt 文件读成功,但是数据出错\r\n");
- break;
- }
- }
- if (err == 1)
- {
- break;
- }
- }
- else
- {
- err = 1;
- printf("Speed1.txt 文件读失败\r\n");
- break;
- }
- }
- runtime2 = bsp_GetRunTime(); /* 读取系统运行时间 */
- if (err == 0)
- {
- timelen = (runtime2 - runtime1);
- printf("\r\n 读耗时 : %dms 平均读速度 : %dB/S (%dKB/S)\r\n", timelen,
- (TEST_FILE_LEN * 1000) / timelen, ((TEST_FILE_LEN / 1024) * 1000) / timelen);
- }
- /* 关闭文件*/
- f_close(&file);
- /* 卸载文件系统 */
- f_mount(NULL, DiskPath, 0);
- }
复制代码
f_mount可以上电后仅调用一次,我们这里是为了测试方式,使用前挂载,使用完毕后卸载。
为了实现更高性能的测试,大家可以加大宏定义
#define BUF_SIZE (4*1024) /* 每次读写SD卡的最大数据长度 */
设置的缓冲大小,比如设置为64KB进行测试。
88.9 FatFs移植接口文件diskio.c说明
这里将FatFs的底层接口文件diskio.c的实现为大家简单做个说明。
88.9.1 磁盘状态函数disk_status
代码如下:
- /**
- * @brief Gets Disk Status
- * @param pdrv: Physical drive number (0..)
- * @retval DSTATUS: Operation status
- */
- DSTATUS disk_status (
- BYTE pdrv /* Physical drive number to identify the drive */
- )
- {
- DSTATUS stat;
- stat = disk.drv[pdrv]->disk_status(disk.lun[pdrv]);
- return stat;
- }
- 实际对应的函数在文件sd_diskio_dma.c
- /**
- * @brief Gets Disk Status
- * @param lun : not used
- * @retval DSTATUS: Operation status
- */
- DSTATUS SD_status(BYTE lun)
- {
- return SD_CheckStatus(lun);
- }
- static DSTATUS SD_CheckStatus(BYTE lun)
- {
- Stat = STA_NOINIT;
- if(BSP_SD_GetCardState() == MSD_OK)
- {
- Stat &= ~STA_NOINIT;
- }
- return Stat;
- }
复制代码
88.9.2 磁盘初始化函数disk_initialize
代码如下:
- /**
- * @brief Initializes a Drive
- * @param pdrv: Physical drive number (0..)
- * @retval DSTATUS: Operation status
- */
- DSTATUS disk_initialize (
- BYTE pdrv /* Physical drive nmuber to identify the drive */
- )
- {
- DSTATUS stat = RES_OK;
- if(disk.is_initialized[pdrv] == 0)
- {
- disk.is_initialized[pdrv] = 1;
- stat = disk.drv[pdrv]->disk_initialize(disk.lun[pdrv]);
- }
- return stat;
- }
- 实际对应的函数在文件sd_diskio_dma.c:
- /**
- * @brief Initializes a Drive
- * @param lun : not used
- * @retval DSTATUS: Operation status
- */
- DSTATUS SD_initialize(BYTE lun)
- {
- #if !defined(DISABLE_SD_INIT)
- if(BSP_SD_Init() == MSD_OK)
- {
- Stat = SD_CheckStatus(lun);
- }
- #else
- Stat = SD_CheckStatus(lun);
- #endif
- return Stat;
- }
复制代码
88.9.3 磁盘读函数disk_read
代码如下:
- /**
- * @brief Reads Sector(s)
- * @param pdrv: Physical drive number (0..)
- * @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 disk_read (
- BYTE pdrv, /* Physical drive nmuber to identify the drive */
- BYTE *buff, /* Data buffer to store read data */
- DWORD sector, /* Sector address in LBA */
- UINT count /* Number of sectors to read */
- )
- {
- DRESULT res;
- res = disk.drv[pdrv]->disk_read(disk.lun[pdrv], buff, sector, count);
- return res;
- }
复制代码
实际对应的函数在文件sd_diskio_dma.c:
下面代码中最关键的处理是形参buff的4字节对齐问题(SDMMC自带的IDMA需要4字节对齐),如果buff地址是4字节对齐的,不做处理,如果不是对齐,通过复制到一个4字节对齐的缓冲里面做DMA传递。这个是理解下面代码的关键。
- /**
- * @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;
- uint32_t timeout;
- ReadStatus = 0;
- if (!((uint32_t)buff & 0x3))
- {
- if(BSP_SD_ReadBlocks_DMA((uint32_t*)buff,
- (uint32_t) (sector),
- count) == MSD_OK)
- {
- /* Wait that the reading process is completed or a timeout occurs */
- timeout = HAL_GetTick();
- while((ReadStatus == 0) && ((HAL_GetTick() - timeout) < SD_TIMEOUT))
- {
- }
- /* incase of a timeout return error */
- if (ReadStatus == 0)
- {
- res = RES_ERROR;
- }
- else
- {
- ReadStatus = 0;
- timeout = HAL_GetTick();
- while((HAL_GetTick() - timeout) < SD_TIMEOUT)
- {
- if (BSP_SD_GetCardState() == SD_TRANSFER_OK)
- {
- res = RES_OK;
- #if (ENABLE_SD_DMA_CACHE_MAINTENANCE_READ == 1)
- SCB_CleanInvalidateDCache();
- #endif
- break;
- }
- }
- }
- }
- }
- else
- {
- uint8_t ret;
- int i;
- for (i = 0; i < count; i++)
- {
- ret = BSP_SD_ReadBlocks_DMA((uint32_t*)scratch, (uint32_t)sector++, 1);
- if(ret == MSD_OK)
- {
- /* Wait that the reading process is completed or a timeout occurs */
- timeout = HAL_GetTick();
- while((ReadStatus == 0) && ((HAL_GetTick() - timeout) < SD_TIMEOUT))
- {
- }
- /* incase of a timeout return error */
- if (ReadStatus == 0)
- {
- break;
- }
- else
- {
- ReadStatus = 0;
- timeout = HAL_GetTick();
- while((HAL_GetTick() - timeout) < SD_TIMEOUT)
- {
- if (BSP_SD_GetCardState() == SD_TRANSFER_OK)
- {
- #if (ENABLE_SD_DMA_CACHE_MAINTENANCE_READ == 1)
- SCB_CleanInvalidateDCache();
- #endif
- memcpy(buff, scratch, BLOCKSIZE);
- buff += BLOCKSIZE;
- break;
- }
- }
- }
- }
- else
- {
- break;
- }
- }
- if ((i == count) && (ret == MSD_OK))
- {
- res = RES_OK;
- }
- }
- return res;
- }
复制代码
88.9.4 磁盘写函数disk_write
代码如下:
- /**
- * @brief Writes Sector(s)
- * @param pdrv: Physical drive number (0..)
- * @param *buff: Data to be written
- * @param sector: Sector address (LBA)
- * @param count: Number of sectors to write (1..128)
- * @retval DRESULT: Operation result
- */
- DRESULT disk_write (
- BYTE pdrv, /* Physical drive nmuber to identify the drive */
- const BYTE *buff, /* Data to be written */
- DWORD sector, /* Sector address in LBA */
- UINT count /* Number of sectors to write */
- )
- {
- DRESULT res;
- res = disk.drv[pdrv]->disk_write(disk.lun[pdrv], buff, sector, count);
- return res;
- }
复制代码
实际对应的函数在文件sd_diskio_dma.c:
下面代码中最关键的处理是形参buff的4字节对齐问题(SDMMC自带的IDMA需要4字节对齐),如果buff地址是4字节对齐的,不做处理,如果不是对齐,通过复制到一个4字节对齐的缓冲里面做DMA传递。这个是理解下面代码的关键。
- /**
- * @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;
- uint32_t timeout;
- WriteStatus = 0;
- #if (ENABLE_SD_DMA_CACHE_MAINTENANCE_WRITE == 1)
- SCB_CleanInvalidateDCache();
- #endif
- if (!((uint32_t)buff & 0x3))
- {
- if(BSP_SD_WriteBlocks_DMA((uint32_t*)buff,
- (uint32_t)(sector),
- count) == MSD_OK)
- {
- /* Wait that writing process is completed or a timeout occurs */
- timeout = HAL_GetTick();
- while((WriteStatus == 0) && ((HAL_GetTick() - timeout) < SD_TIMEOUT))
- {
- }
- /* incase of a timeout return error */
- if (WriteStatus == 0)
- {
- res = RES_ERROR;
- }
- else
- {
- WriteStatus = 0;
- timeout = HAL_GetTick();
- while((HAL_GetTick() - timeout) < SD_TIMEOUT)
- {
- if (BSP_SD_GetCardState() == SD_TRANSFER_OK)
- {
- res = RES_OK;
- break;
- }
- }
- }
- }
- }
- else
- {
- int i;
- uint8_t ret;
- for (i = 0; i < count; i++)
- {
- WriteStatus = 0;
- memcpy((void *)scratch, (void *)buff, BLOCKSIZE);
- buff += BLOCKSIZE;
- ret = BSP_SD_WriteBlocks_DMA((uint32_t*)scratch, (uint32_t)sector++, 1);
- if(ret == MSD_OK)
- {
- /* Wait that writing process is completed or a timeout occurs */
- timeout = HAL_GetTick();
- while((WriteStatus == 0) && ((HAL_GetTick() - timeout) < SD_TIMEOUT))
- {
- }
- /* incase of a timeout return error */
- if (WriteStatus == 0)
- {
- break;
- }
- else
- {
- WriteStatus = 0;
- timeout = HAL_GetTick();
- while((HAL_GetTick() - timeout) < SD_TIMEOUT)
- {
- if (BSP_SD_GetCardState() == SD_TRANSFER_OK)
- {
- break;
- }
- }
- }
- }
- else
- {
- break;
- }
- }
- if ((i == count) && (ret == MSD_OK))
- {
- res = RES_OK;
- }
- }
- return res;
- }
复制代码
88.9.5 磁盘I/O控制函数disk_ioctl
代码如下:
- /**
- * @brief I/O control operation
- * @param pdrv: Physical drive number (0..)
- * @param cmd: Control code
- * @param *buff: Buffer to send/receive control data
- * @retval DRESULT: Operation result
- */
- #if _USE_IOCTL == 1
- DRESULT disk_ioctl (
- BYTE pdrv, /* Physical drive nmuber (0..) */
- BYTE cmd, /* Control code */
- void *buff /* Buffer to send/receive control data */
- )
- {
- DRESULT res;
- res = disk.drv[pdrv]->disk_ioctl(disk.lun[pdrv], cmd, buff);
- return res;
- }
- #endif /* _USE_IOCTL == 1 */
复制代码
实际对应的函数在文件sd_diskio_dma.c
特别注意,如果大家要调用FatFs的API格式化SD卡,此函数比较重要。下面几个cmd一定实现:
- /**
- * @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;
- BSP_SD_CardInfo 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 :
- BSP_SD_GetCardInfo(&CardInfo);
- *(DWORD*)buff = CardInfo.LogBlockNbr;
- res = RES_OK;
- break;
- /* Get R/W sector size (WORD) */
- case GET_SECTOR_SIZE :
- BSP_SD_GetCardInfo(&CardInfo);
- *(WORD*)buff = CardInfo.LogBlockSize;
- res = RES_OK;
- break;
- /* Get erase block size in unit of sector (DWORD) */
- case GET_BLOCK_SIZE :
- BSP_SD_GetCardInfo(&CardInfo);
- *(DWORD*)buff = CardInfo.LogBlockSize / SD_DEFAULT_BLOCK_SIZE;
- res = RES_OK;
- break;
- default:
- res = RES_PARERR;
- }
- return res;
- }
- #endif /* _USE_IOCTL == 1 */
复制代码
88.9.6 RTC时间获取函数get_fattime
我们这里未使用这个函数,此函数的作用是用户创建文件时,可以将创建文件时间设置为此函数的获取值
- /**
- * @brief Gets Time from RTC
- * @param None
- * @retval Time in DWORD
- */
- __weak DWORD get_fattime (void)
- {
- return 0;
- }
复制代码
|