USB2.0挂载FatFs文件系统

一、前期准备
1、协议栈
USB协议栈
FatFs文件系统协议栈
2、物理芯片
W25Q64的8M大小的flash芯片
二、描述
文件系统fatfs、USB协议栈、物理层flash存储芯片关系。

1、单独使用flash芯片,开发flash芯片的响应的驱动即可进行按照 绝对地址 的读写方式进行数据的存储和读取。一般的单片机只做存储的可以这么做。
void W25q64_Spi_Init(void)
初始化flash芯片的驱动
void SPI_FLASH_BufferRead(uint32_t ReadAddr,uint8_t* pBuffer, uint16_t NumByteToRead)
形参1:flash的绝对地址
形参2:读取到数据要存储的位置
形参3:要读取数据的大小
void SPI_FLASH_BufferWrite(uint32_t WriteAddr,uint8_t* pBuffer, uint16_t NumByteToWrite)
形参1:flash的绝对地址
形参2:读写到flash中的源数据存储位置
形参3:要写入数据的大小
2、flash驱动+文件系统
这个一般要对文件进行管理的时候使用,这时候文件只是存储在flash的物理层中,只进行了文件系统的逻辑抽象,所有的操作只能在代码中执行,外部接口不可见。
想要可见要进行接口虚拟例如SD卡。部分是集成在SD中的,不过这种情况不是很多。
DSTATUS disk_initialize (BYTE pdrv)
这个函数中调用上面的W25q64_Spi_Init()函数进行初始化并进行唤醒芯片。
DRESULT disk_read (BYTE pdrv,BYTE *buff,DWORD sector,UINT count)
调用上面的SPI_FLASH_BufferRead()函数进行数据的读取,但是除此之外要进行你的flash的起始地址偏移和扇区大小的逻辑运算在里面。
/*形参
1、pdrv:设备物理编号
2、buff:读取出来数据缓存区
3、sector:要读取得扇区首地址 一个扇区大小是***字节 这个非常重要 每个型号的flash芯片可能不一样,例如这个就是4096byte大小扇区
m256pe16这款芯片的扇区大小就是512byte,所以在进行逻辑地址运算的时候 这个要进行不同的逻辑地址计算。
4、count:扇区个数(1/2/3。。。128。。。)
操作的最小单位是扇区 不是极小页 所以这个与M25PE的flash驱动有别
*/
eg:
#define Msd_Addr 0x50 0000 //这个定义是说明这个U盘的虚拟是从flash的地址0x50 0000 开始的直到结束
DRESULT disk_read (BYTE pdrv,BYTE *buff,DWORD sector,UINT count)
{
uint32_t address = 0;
DRESULT status = RES_PARERR;
switch(pdrv)
{
case ATA: //SD卡
break;
case MMC:
break;
case MSD: //这个是内部flash的虚拟
address = sector*4096;
if(count==1)
{
ReadFlash(address+Msd_Addr,buff,count*4096); //address+Msd_Addr 这个是进行地址偏移
}
else
{
while(count-->0)
{
ReadFlash(address+Msd_Addr,buff,4096); //address+Msd_Addr 这个是进行地址偏移
address = address + 4096;
buff = buff + 4096;
}
}
break;
default:
status = RES_PARERR;
}
return status;
}
DRESULT disk_write (BYTE pdrv,const BYTE *buff,DWORD sector,UINT count)
这个函数调用的是SPI_FLASH_BufferWrite()
写的时候一定要注意的一点是在写的响应的地址上面要进行一次擦除操作,不然在windows平台下 虚拟出U盘之后会出现无法格式化的现象。
/*形参
1、pdrv:设备物理编号(0,)
2、buff:预写数据的缓冲区
3、sector:扇区首地址
4、count:扇区个数(1.2.。。128)
*/
DRESULT disk_write (BYTE pdrv,const BYTE *buff,DWORD sector,UINT count)
{
uint32_t address = 0;
DRESULT status = RES_PARERR;
if (!count)
{
return RES_PARERR; /* Check parameter */
}
switch(pdrv)
{
case ATA:
break;
case MMC:
break;
case MSD:
address = sector*4096;
if(count==1)
{
SPI_FLASH_SectorErase(address+Msd_Addr); //把要写入的数据的地址 要进行先擦除 重要
WriteFlash(address+Msd_Addr,(uint8_t*)buff,4096);
}
else
{
while(count-->0)
{
SPI_FLASH_SectorErase(address+Msd_Addr); //把要写入的数据的地址 要进行先擦除 重要
WriteFlash(address+Msd_Addr,(uint8_t*)buff,4096); //address+Msd_Addr地址偏移
address = address + 4096;
buff = buff + 4096;
}
}
break;
default:
status = RES_PARERR;
break;
}
return status;
}
DRESULT disk_ioctl (BYTE pdrv,BYTE cmd,void *buff)
这个函数很重要奥
里面这些东西在你的U盘被windows识别之前会进行里面的参数进行提取
例如:
DRESULT disk_ioctl (BYTE pdrv,BYTE cmd,void *buff) //Miscellaneous Functions
{
DRESULT status = RES_PARERR;
switch(pdrv)
{
case ATA:
break;
case MMC:
break;
case MSD:
switch(cmd)
{
case CTRL_SYNC:
break;
case GET_BLOCK_SIZE: //同时擦除的扇区个数
*(DWORD*)buff = 1; //这个是 一个块区 包含多少个 扇区的个数 一般擦除时候用 针对支持块擦除的介质
break;
case GET_SECTOR_COUNT: //GET_SECTOR_COUNT * GET_SECTOR_SIZE + 基地址 = U盘空间结束地址
*(DWORD*)buff = (ALL_FLASH_ADDR-Msd_Addr)/4096; //这个是U盘有多少扇区数量 这个是给到window读取的
break;
case GET_SECTOR_SIZE: //扇区大小
*(WORD*)buff = 4096;
break;
}
status = RES_OK;
break;
default:
status = RES_PARERR;
break;
}
return status; // Process of the command the USB drive
}
GET_BLOCK_SIZE:这个命令获取的时候,你返回的内容会影响到擦除和写入的速度。
=1:说明 一次只能擦除一个扇区 这个开大 会写入的速率升高,但是这个会占用ram空间,所以我们最好是根据自己的实景情况进行设置。
这个是进行块擦除的 大小,意思是一个块区 包含多少个 扇区
GET_SECTOR_COUNT:
这个获取的就是 你的U盘的大小,大小的值一定要与你实际的flash起始和结束地址对应,不然那的话 会挂载失败。
公式可以这样写或者其他办法也可以:
*(DWORD*)buff = (ALL_FLASH_ADDR-Msd_Addr)/4096;
ALL_FLASH_ADDR:这个是flash的结束地址 可以是flash的最大的末尾地址 也可以是比 起始地址大的任意位置。
Msd_Addr:这个是U盘的虚拟地址的起始地址。
4096这个是一个sector的大小,不同的芯片可能会不一样,这个为了可移植一性高的话,可以做个宏定义。
GET_SECTOR_SIZE:
这个就是进行获取一个扇区的大小

DWORD get_fattime(void)
这个函数是记录对文件操作的时间,有改动的话会进行时间戳的记录。这个最好进行移植一下,不过不进行移植的话,对数据存储功能貌似没有多大的影响。

3、flash驱动+fatfs文件系统+USB协议栈
这种方式也是用的最为广泛的了,在一些小型嵌入式系统中,只要你的flash芯片大小足够大,都可以虚拟出来U盘,在windows平台上进行傻瓜操作。
USB中的功能包含很多:
HID,CDC,MSC,HUB。。。我们这里只介绍在MSC中进行U盘挂载这一部分。
新定义一下U盘的代码文件,假设是MSD.c

我们对u盘进行初始化:
Msd_Init()
这里面调用:
if ( disk_initialize( 2 ) != 0 ) // 进行初始化flash芯片 调用disk_initialize() 2参数 是逻辑驱动号 这个驱动号 是在diskio.c中定义的
return false;
/* Get numSectors from media. */
if ( disk_ioctl( 2, GET_SECTOR_COUNT, &numSectors ) != RES_OK )
return false; //这个是进行 U盘属性的获取 调用disk_ioctl()函数
#define ATA 0 //u盘逻辑驱动号是 0
#define MMC 1 //u盘逻辑驱动号是 1
#define MSD 2 //u盘逻辑驱动号是 2

对U盘进行读操作
void Msd_Read( MSDD_CmdStatus_TypeDef *pCmd, uint8_t *data, uint32_t sectors )
调用disk_read( 2, data, pCmd->lba, sectors )函数
/**************************************************************************//**
* @brief
* Read from indirectly accessed media.
*
* @param[in] pCmd
* Points to a MSDD_CmdStatus_TypeDef structure which holds info about the
* current transfer.
*
* @param[in] data
* Pointer to data buffer.
*
* @param[in] sectors
* Number of 512 byte sectors to read from media.
*****************************************************************************/
void Msd_Read( MSDD_CmdStatus_TypeDef *pCmd, uint8_t *data, uint32_t sectors )
{
#if ( MSD_MEDIA == MSD_SRAM_MEDIA ) || ( MSD_MEDIA == MSD_PSRAM_MEDIA )
(void)pCmd;
(void)data;
(void)sectors;
#endif

#if ( MSD_MEDIA == MSD_SDCARD_MEDIA )
disk_read( 2, data, pCmd->lba, sectors );
#endif

#if ( MSD_MEDIA == MSD_FLASH_MEDIA ) || ( MSD_MEDIA == MSD_NORFLASH_MEDIA )
/* Write pending data to flash before starting the read operation. */
Msd_Flush();
//memcpy( data, pCmd->pData, sectors * 512 );
//pCmd->pData += sectors * 512;
memcpy( data, pCmd->pData, sectors * 4096 );
pCmd->pData += sectors * 4096;
#endif
}
对U盘进行写操作
void Msd_Write( MSDD_CmdStatus_TypeDef *pCmd, uint8_t *data, uint32_t sectors )
调用 disk_write( 2, data, pCmd->lba, sectors )函数
所有的操作都是以扇区sector进行操作的。
/**************************************************************************//**
* @brief
* Write to indirectly accessed media.
*
* @param[in] pCmd
* Points to a MSDD_CmdStatus_TypeDef structure which holds info about the
* current transfer.
*
* @param[in] data
* Pointer to data buffer.
*
* @param[in] sectors
* Number of 512 byte sectors to write to media.
*****************************************************************************/
void Msd_Write( MSDD_CmdStatus_TypeDef *pCmd, uint8_t *data, uint32_t sectors )
{
#if ( MSD_MEDIA == MSD_SRAM_MEDIA ) || ( MSD_MEDIA == MSD_PSRAM_MEDIA )
(void)pCmd;
(void)data;
(void)sectors;
#endif

#if ( MSD_MEDIA == MSD_SDCARD_MEDIA )
disk_write( 2, data, pCmd->lba, sectors );
#endif

#if ( MSD_MEDIA == MSD_FLASH_MEDIA ) || ( MSD_MEDIA == MSD_NORFLASH_MEDIA )
unsigned int i;
uint32_t offset;

i = 0;
while ( i < sectors )
{
if ( !flashStatus.pendingWrite )
{
/* Copy an entire flash page to the page buffer */
flashStatus.pendingWrite = true;
flashStatus.pPageBase = (uint8_t*)((uint32_t)pCmd->pData & ~( flashPageSize - 1 ));
offset = pCmd->pData - flashStatus.pPageBase;
memcpy( flashPageBuf, flashStatus.pPageBase, flashPageSize );

/* Write the received data in the page buffer */
//memcpy( flashPageBuf + offset, data, 512 );
//data += 512;
//pCmd->pData += 512;
memcpy( flashPageBuf + offset, data, 4096 );
data += 4096;
pCmd->pData += 4096;

USBTIMER_Start( FLUSH_TIMER_ID, FLUSH_TIMER_TIMEOUT, FlushTimerTimeout );
}
else
{
/* Check if current sector is located in the page buffer. */
offset = pCmd->pData - flashStatus.pPageBase;
if ( offset >= flashPageSize )
{
/*
* Current sector not located in page buffer, flush pending data
* before continuing.
*/
Msd_Flush();
i--;
}
else
{
/* Write the received data in the page buffer */
//memcpy( flashPageBuf + offset, data, 512 );
//data += 512;
//pCmd->pData += 512;
memcpy( flashPageBuf + offset, data, 4096 );
data += 4096;
pCmd->pData += 4096;

USBTIMER_Start( FLUSH_TIMER_ID, FLUSH_TIMER_TIMEOUT, FlushTimerTimeout );
}
}
i++;
}
#endif
}

以上的所有函数又会被msdd.c中的MSDD_Handler()函数进行调用
调用是根据USB协议栈传下来的值去确定的。
bool MSDD_Handler(void)
{
static uint32_t len; /* Note: len is static ! */
switch (msdState)
{
case MSDD_ACCESS_INDIRECT:
if (pCmdStatus->xferLen)
{
len = EFM32_MIN(pCmdStatus->xferLen, pCmdStatus->maxBurst);
msdState = MSDD_IDLE;
if (pCmdStatus->direction)
{
Msd_Read(pCmdStatus, mediaBuffer, len / 4096);
//Msd_Read(pCmdStatus, mediaBuffer, len / 512);
}
UsbXferBotData(mediaBuffer, len, XferBotDataIndirectCallback);
}
else
{
/* We are done ! */
msdState = savedState;
if (msdState == MSDD_SEND_CSW)
{
SendCsw();
EnableNextCbw();
msdState = MSDD_WAITFOR_CBW;
}
else if (msdState == MSDD_STALL_IN)
{
USBD_StallEp(MSD_BULK_IN);
msdState = MSDD_WAIT_FOR_INUNSTALLED;
}
}
break;
case MSDD_WRITE_INDIRECT:
Msd_Write(pCmdStatus, mediaBuffer, len / 4096);
pCmdStatus->lba += len / 4096;
msdState = MSDD_ACCESS_INDIRECT;
break;

case MSDD_DO_CMD_TASK:
if (pCbw->CBWCB[ 0 ] == SCSI_STARTSTOP_UNIT)
{
Msd_Flush();
}
/* else if ( .... ) Add more when needed. */
SendCsw();
EnableNextCbw();
msdState = MSDD_WAITFOR_CBW;
break;
default:
break;
}
return (msdState == MSDD_WAITFOR_CBW) || (msdState == MSDD_IDLE);
}

只要USB中MSDD和MSD和MSC中的读写函数移植正确 ,就可以正常的数据读写和挂载。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值