
一、sd和fatfs的配置流程界面1、选择sd4bits 根据自己的sd卡的硬件插槽进行选择。2、fatfs配置由于使用的是sd卡所以直接选择sd选项3、程序中对sd卡的初始化需要进行改动直接使用默认的参数sd卡是挂载不上的。4、在sd卡挂载好后就可以使用文件系统的函数进行操作了。写入读取注意f_open后写和读取要分开操作即写完后f_close以保持写入的内容当需要读取时重新打开文件进行读取。调试结果显示二、sd和fatfs的配置打开dma进行处理的流程界面在使用f_write和f_read时会调用disk_write和disk_read。下图就是其调用关系f_read同理。默认的disk_write和disk_read没用使用到dma需要自己编辑对应的dma调用函数。1、重新编写disk_write和disk_read函数DRESULTdisk_write(BYTE pdrv,/* Physical drive nmuber to identify the drive */constBYTE*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;//}{uint8_tres0;if(!count)returnRES_PARERR;//count不能等于0否则返回参数错误switch(pdrv){case0://SD卡resSD_WriteDisk((uint8_t*)buff,sector,count);while(res)//写出错{MX_SDIO_SD_Init();//重新初始化SD卡resSD_WriteDisk((uint8_t*)buff,sector,count);//printf(sd wr error:%d\r\n,res);}res0;break;default:res1;}if(res0x00)returnRES_OK;elsereturnRES_ERROR;}DRESULTdisk_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;//}{uint8_tres0;if(!count)returnRES_PARERR;//count不能等于0否则返回参数错误switch(pdrv){case0://SD卡resSD_ReadDisk(buff,sector,count);while(res)//读出错{MX_SDIO_SD_Init();//重新初始化SD卡resSD_ReadDisk(buff,sector,count);//printf(sd rd error:%d\r\n,res);}res0;break;default:res1;}if(res0x00)returnRES_OK;elsereturnRES_ERROR;}2、编写写sd卡的写入操作函数//获取SD运行状态uint8_tSD_GetCardState(void){return((HAL_SD_GetCardState(hsd)HAL_SD_CARD_TRANSFER)?SD_TRANSFER_OK:SD_TRANSFER_BUSY);}//写SD卡//buf:写数据缓存区//sector:扇区地址//blocksize:扇区大小(一般都是512字节)//cnt:扇区个数//返回值:错误状态;0,正常;其他,错误代码;uint8_tSD_WriteBlocks_DMA(uint32_t*buf,uint64_tsector,uint32_tblocksize,uint32_tcnt){uint8_terr0;uint32_ttimeout100000000;errHAL_SD_WriteBlocks_DMA(hsd,(uint8_t*)buf,sector,cnt);//通过DMA写SD卡n个扇区if(err0){//等待SD卡写完while(SD_GetCardState()!SD_TRANSFER_OK){if(timeout--0){errSD_TRANSFER_BUSY;}}}returnerr;}//写SD卡操作//buf:写数据缓存区//sector:扇区地址//cnt:扇区个数//返回值:错误状态;0,正常;其他,错误代码;uint8_tSD_WriteDisk(uint8_t*buf,uint32_tsector,uint32_tcnt){uint8_tstaHAL_OK;longlonglsectorsector;staSD_WriteBlocks_DMA((uint32_t*)buf,lsector,512,cnt);//多个sector的写操作returnsta;}3、编写写sd卡的读取操作函数//通过DMA读取SD卡一个扇区//buf:读数据缓存区//sector:扇区地址//blocksize:扇区大小(一般都是512字节)//cnt:扇区个数//返回值:错误状态;0,正常;其他,错误代码;uint8_tSD_ReadBlocks_DMA(uint32_t*buf,uint64_tsector,uint32_tblocksize,uint32_tcnt){uint8_terr0;uint32_ttimeout100000000;errHAL_SD_ReadBlocks_DMA(hsd,(uint8_t*)buf,sector,cnt);//通过DMA读取SD卡n个扇区if(err0){//等待SD卡读完while(SD_GetCardState()!SD_TRANSFER_OK){if(timeout--0){errSD_TRANSFER_BUSY;}}}returnerr;}//读SD卡//buf:读数据缓存区//sector:扇区地址//cnt:扇区个数//返回值:错误状态;0,正常;其他,错误代码;uint8_tSD_ReadDisk(uint8_t*buf,uint32_tsector,uint32_tcnt){uint8_tstaHAL_OK;longlonglsectorsector;staSD_ReadBlocks_DMA((uint32_t*)buf,lsector,512,cnt);returnsta;}三、对SDMM配置注意事项__align(4)charreadbuffer[512];//4字节对齐调用时打印路径信息charfpath[100];strcpy(fpath,SDPath);scan_files(fpath);/** * 函数功能: 递归扫描FatFs内的文件 * 输入参数: 初始扫描路径 * 返 回 值: 文件系统的返回值 * 说 明: 无 */staticFRESULTscan_files(char*path){FRESULT res;//部分在递归过程被修改的变量不用全局变量//打开目录resf_opendir(dir,path);if(resFR_OK){while(1){resf_readdir(dir,fno);//读取目录下的一个文件if(res!FR_OK||fno.fname[0]0)break;//错误了/到末尾了,退出//if (fileinfo.fname[0] .) continue; //忽略上级目录// printf(FatFs Internal files:);printf(%s\n,path);//打印路径printf(%s\n,fno.fname);//打印文件名}f_closedir(dir);}returnres;}staticFRESULTfile_check(char*path){staticFILINFO finfo;/* 获取文件信息 */f_resf_stat(path,finfo);if(f_resFR_OK){printf(file size: %ld btye\n,finfo.fsize);printf(time stamp:%u/%02u/%02u, %02u:%02u\n,(finfo.fdate9)1980,finfo.fdate515,finfo.fdate31,finfo.ftime11,finfo.ftime563);printf(attribute: %c%c%c%c%c\n\n,(finfo.fattribAM_DIR)?D:-,// 是一个目录(finfo.fattribAM_RDO)?R:-,// 只读文件(finfo.fattribAM_HID)?H:-,// 隐藏文件(finfo.fattribAM_SYS)?S:-,// 系统文件(finfo.fattribAM_ARC)?A:-);// 档案文件}returnf_res;}对文件夹的操作 f_resf_opendir(dir,TestDir);if(f_res!FR_OK){/* 打开目录失败就创建目录 */f_resf_mkdir(TestDir);}else{/* 如果目录已经存在关闭它 */f_resf_closedir(dir);/* 删除文件 */f_unlink(TestDir/testdir.txt);}if(f_resFR_OK){/* 重命名并移动文件 */f_resf_rename(test.txt,TestDir/testdir.txt);}注意当sd卡读取正常但写入报错时检查的方向基本可以断定当计算不合理时如 SDMMC_CK 只有 ~91kHz。而 HAL_SD_ConfigWideBusOperation 里那段自动提速(hal_sd.c:)是这么判断的if (hsd-Init.ClockDiv (sdmmc_clk / (2U * SD_NORMAL_SPEED_FREQ)))Init.ClockDiv hsd-Init.ClockDiv; // 250 0 恒成立即写入时超时了。所以写不进去。另一种就是配置时sd对应的时钟源太高如180mhz~ 300mhz的时钟源却给极小的分频系数导致通信太快sd反应不过来。配置合适的时钟源和分配系数很重要。如下就是补救措施识别阶段保持 250SD 规范要求识别 400kHz识别完成后把ClockDiv 归零、重走一次 ConfigWideBusOperation让 HAL 按卡的能力重算分频普通卡 25MHz / 高速卡 50MHz。当前配置sd对应时钟源 45.68MHz。