一、前期准备
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中的读写函数移植正确 ,就可以正常的数据读写和挂载。

USB2.0挂载FatFs文件系统相关推荐

  1. RT-Thread--外部 flash 挂载 fatfs 文件系统

    外部 flash 挂载 fatfs 文件系统 /** Copyright (c) 2006-2018, RT-Thread Development Team** SPDX-License-Identi ...

  2. STM32F407移植Little vGL系统,freeRTOS系统,FATFS文件系统

    准备freeRTOS源码和一个简单的工程 链接:https://pan.baidu.com/s/1hgyQqoDqDuETEHr_I80M8Q  提取码:e890 另外还需要准备一个简单的工程,这里使 ...

  3. Fatfs文件系统挂载

    目录 函数参数: 源码分析: 1)解析逻辑驱动号 2)初始化FatFs结构 3)挂载驱动 主引导记录MBR 未完待续... 先来看一张在fatfs截取的一张图,文件系统就是中间这个带颜色一层,它贯通连 ...

  4. 关于FATFS文件系统挂载多个磁盘

    关于FATFS文件系统挂载多个磁盘以及SPI FLASH的应用问题!

  5. STM32挂载SD卡基于Fatfs文件系统读取文件信息

    STM32挂载SD卡基于Fatfs文件系统读取文件信息

  6. 2021-08-11 TM32F103 Buffer FatFs 文件系统移植

    FatFs 本文展示了STM32 FatFs文件系统移植 内容涉及 : FatFs 文件系统移植 SPI函数移植过程 SPI字节数据模拟输出独写 缓存读写 USART串口的识别 IO口输入输出 按键的 ...

  7. 模拟SPI进行TF卡操作+Fatfs文件系统移植

    FATFS版本:FATFS R0.13b SD卡容量:16G 概述 本文的重点是进行Fatfs文件系统的移植和初步的使用.TF卡的操作实际上是指令操作,即你想它发送固定的CMD指令,它接收到指令给你返 ...

  8. 【FatFs】基于STM32 SD卡移植FatFs文件系统

    相关文章 <[SDIO]SDIO.SD卡.FatFs文件系统相关文章索引> 1.前言 FatFs是一个通用的FAT/exFAT文件系统模块,用于小型嵌入式系统.它完全是由 ANSI C 语 ...

  9. ARM单片机FATFS文件系统的移植

    ARM单片机FATFS文件系统的移植 测试效果 前提条件 下载所需源码 FATFS 文件系统 SFUD万能驱动 加入工程 接口驱动 测试代码 FreeRTOS10.0.1 FATFS FF14A SF ...

最新文章

  1. 微信突然更新,新增了这些功能...
  2. java接口 泛型_java 泛型接口示例
  3. Java ArrayList和Vector、LinkedList与ArrayList、数组(Array)和列表集合(ArrayList)的区别...
  4. shutil常用语法
  5. bzoj 2763 [JLOI2011]飞行路线——分层图
  6. Mac OS X终端的常用操作命令(UNIX指令)
  7. 前端开发框架整理(一些记录给自己看的)
  8. http 请求中的 referer
  9. 推荐几个无版权的免费图片网站
  10. python输入三个整数_Python求解输入三个整数排序
  11. 分布式系统的发展历程
  12. awgn matlab,Matlab实现加性高斯白噪声信道(AWGN)下的digital调制格式识别分类
  13. bugku——宽带信息泄露(RouterPassView 查看路由器配置信息)
  14. 软件测试就是找茬的?那些年和开发小哥的“爱恨情仇”
  15. 计算机考研abc区划分,研究生地区分类-考研ABC区域的划分考研ABC区域的划分, – 手机爱问...
  16. 52单片机led灯闪烁c语言程序,单片机LED灯闪烁程序
  17. linux中清除git记住密码的方法
  18. HENGSHI SENSE 4.0 预置明道云连接器,实现更灵活的数据自动传输
  19. ReactNative学习笔记(七)——ref属性的用法和可点击组件
  20. stateflow(状态流)简单应用

热门文章

  1. 格式化数据恢复|u盘格式化恢复
  2. Chaos Emulator核心功能开发历程
  3. python调用usb设备_用Python与USB设备通信
  4. python 整合_python的资源整合
  5. css3有哪些新增属性,CSS3新增属性
  6. Live Messenger ,Gmail ,Orkut ,Wallop
  7. 1279 验证角谷猜想
  8. 服务器任务栏不显示程序,win10系统下任务栏不显示程序窗口预览图怎么处理
  9. 网易我的世界 java错误_我的世界:网易有点懒,明明是同一个版本,待遇却大不一样!...
  10. CF924C Riverside Curio