首先使用DiskGenius 建立一个.img格式的磁盘映像文件,我虚拟了一个256MB的SD卡,如下
并格式化为FAT32文件系统,可以使用DiskGenius打开并拷贝数据。
使用windows API实现底层虚拟SD卡的读写操作。
//代码如下
/*-----------------------------------------------------------------------*/
/* 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. */
/*-----------------------------------------------------------------------*/
/*************************************************************************************************************
* 文件名 : diskio.c
* 功能 : 文件系统底层接口
* 作者 : [email protected]
* 创建时间 : 2019-10-31
* 最后修改时间 : 2019-10-31
* 详细 : 修复了读写接口的1个扇区与多个扇区颠倒问题,并且限制多个扇区读写操作是,一次操作为100个扇区
*************************************************************************************************************/
#include "diskio.h" /* FatFs lower layer API */
#include "system.h"
#define SECTOR_SIZE_ 512 //扇区大小定义
static HFILE sg_fpImg = NULL; //文件指针
/* Definitions of physical drive number for each drive */
#define DEV_SD 0 //默认为SD卡
#define DEV_MMC 1 /* Example: Map MMC/SD card to physical drive 1 */
#define DEV_USB 2 /* Example: Map USB MSD to physical drive 2 */
/*-----------------------------------------------------------------------*/
/* Get Drive Status 读取状态寄存器 */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
switch (pdrv)
{
case DEV_SD : //SD卡
{
if(sg_fpImg != NULL) return RES_OK;
}
default:break;
}
return STA_NOINIT;
}
/*-----------------------------------------------------------------------*/
/* Inidialize a Drive 初始化 */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
static bool InitStatus[1]; //记录初始化状态,防止重复初始化
switch (pdrv)
{
case DEV_SD : //初始化SD卡
{
if (sg_fpImg == NULL) //防止重复打开
{
char pPath[MAX_PATH + 1];
char ImgFilePath[MAX_PATH + 1];
char ext[32];
GetModuleFileName(NULL, pPath, MAX_PATH); //获取当前可执行文件的路径
_splitpath(pPath, NULL, ImgFilePath, NULL, ext); //处理路径,去掉后面的程序名称
strcat(ImgFilePath, "system\\SDCARD.img");
//sg_fpImg = (HFILE)OpenFile(ImgFilePath, GENERIC_ALL, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL); //读写打开文件,允许其他软件只读方式打开
sg_fpImg = CreateFile(ImgFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL); //打开文件-如不不存在返回失败
if (sg_fpImg == NULL)
{
DEBUG("初始化SD卡错误,SD卡错误:%d\r\n", GetLastError());
return STA_NOINIT;
}
}
return RES_OK;
}
default:break;
}
return STA_NOINIT;
}
//#include "StopWatch.h"
//STOP_WATCH_HANDLE g_StopWatchHandle1;
/*-----------------------------------------------------------------------*/
/* 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 */
)
{
u32 cnt;
switch (pdrv)
{
case DEV_SD :
{
if (sg_fpImg == NULL) return RES_ERROR;
//设置文件偏移
if (SetFilePointer(sg_fpImg, SECTOR_SIZE_ * sector, NULL, FILE_BEGIN) == HFILE_ERROR)
{
DEBUG("移动写文件指针错误:%d\r\n", GetLastError());
break;
}
//Sleep(1); //模拟延时
Sleep(4 + count / 20);
if (ReadFile(sg_fpImg, buff, SECTOR_SIZE_*count, &cnt, NULL) == FALSE)//最后一个参数必须为NULL,否则不会自动移动文件读写指针,SetFilePointer也会无效
{
DEBUG("读文件错误:%d\r\n", GetLastError());
break;
}
return RES_OK;
}
default:break;
}
return RES_PARERR;
}
/*-----------------------------------------------------------------------*/
/* Write Sector(s) */
/*-----------------------------------------------------------------------*/
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 */
)
{
u32 cnt;
switch (pdrv)
{
case DEV_SD :
{
if (sg_fpImg == NULL) return RES_ERROR;
//设置文件偏移
if (SetFilePointer(sg_fpImg, SECTOR_SIZE_ * sector, NULL, FILE_BEGIN) == HFILE_ERROR)
{
DEBUG("移动写文件指针错误:%d\r\n", GetLastError());
break;
}
Sleep(1); //模拟延时
Sleep(1 + count / 20);
if (WriteFile(buff, buff, SECTOR_SIZE_ * count, &cnt, NULL) == FALSE) //最后一个参数必须为NULL,否则不会自动移动文件读写指针,SetFilePointer也会无效
{
DEBUG("串口发送失败,错误:%d\r\n", GetLastError());
}
return RES_OK;
}
default:break;
}
return RES_PARERR;
}
/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions 杂项功能 */
/*-----------------------------------------------------------------------*/
DRESULT disk_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
switch (pdrv)
{
case DEV_SD :
{
switch(cmd)
{
case GET_SECTOR_SIZE: //获取扇区大小
{
*((WORD *)buff) = SECTOR_SIZE_;
uart_printf("FATFS获取SD卡块大小:%dB\r\n", SECTOR_SIZE_);
}break;
default: return RES_PARERR;
}
return RES_OK;
}
default:break;
}
return RES_PARERR;
}
需要注意的是windows API读写文件时,最后一个参数必须为NULL,否则SetFilePointer()函数将无法设置偏移,应为最后一个参数也是用于设置偏移的。
if (WriteFile(buff, buff, SECTOR_SIZE_ * count, &cnt, NULL) == FALSE) //最后一个参数必须为NULL,否则不会自动移动文件读写指针,SetFilePointer也会无效
这样就可以在PC端虚拟STM32上面的文件系统,使用FATFS了。
读取的虚拟SD卡中的字库与编码文件,实现与开发板上同样的操作。