文章目录

  • 前言
  • 一、背景
  • 二、制作虚拟磁盘VHD
  • 三、生成磁盘数据
  • 四、生成.h头文件
  • 五、改造read10函数
  • 六、测试
  • 七、工具
  • 八、参考



前言

前面的文章《使用ESP32S2开发板制作U盘,支持无线访问》讲述了如何将ESP32S2开发板制作成一个U盘。本文继续延伸,讲述如何把开发板制作成一个看起来容量远远大于flash实际大小的U盘。为方便起见,就叫它“虚拟U盘”吧。
本文用到的工具,附在了第七章。


一、背景

ESP32S2开发板flash只有4M,但出于某些目的,把它插到主机(PC、Android 手机、平板、电视、车机等)上时,需要识别成一个2G的U盘(也可以是4G、8G,大小可指定),并且里面存放大于4M的文件,比如一个长4小时的MP3文件。
——这听起来很玄妙!但实现起来非常简单!

二、制作虚拟磁盘VHD

  1. PC打开“创建并格式化硬盘分区”工具(磁盘管理也可),菜单栏点击“操作”-“创建VHD”,位置自选比如放在桌面,命名为“fat32_2g.vhd”,点击“确定”。
  2. 看到创建好的磁盘,在1区域右击鼠标-“初始化磁盘”-点选“MBR”-“确定”。
  3. 在2区域右击鼠标-“新建简单卷”,继续下一步,在格式化分区时,选择“FAT32”。然后“下一步”-“完成”。

三、生成磁盘数据

  1. 使用“HxD”工具,打开前面创建的硬盘:“工具”-“读取硬盘”-“硬盘3”。
  2. esp32单片机入门 esp32 diy_esp32单片机入门

  3. 这里可能会有疑问:逻辑硬盘中的“新加卷E”和物理硬盘中的“硬盘3”,是不是一回事。其实不是,逻辑硬盘是物理硬盘的一部分,简单说下区别:
    逻辑扇区0,对应的是DBR扇区。DBR分为两部分:DOS引导程序和BPB(BIOS参数块)。DBR的开头:EB 3C 90(FAT16),EB 58 90(FAT32)。
    物理扇区0,对应的是MBR扇区。开头为:33 C0 8E。
  4. 打开之后,点击“文件”-“另存为”,命名为“fat32_2g.data”。
  5. esp32单片机入门 esp32 diy_vfat_02

四、生成.h头文件

打开工具“FileToh.exe”-“Open”,选择上一步生成的“fat32_2g.data”,等计算完成后点击“Save”,会提示“fat32_2g.h”已经生成。

五、改造read10函数

  1. 将头文件“fat32_2g.h”加入到自己的工程中;
  2. 在tusb_msc.c中,引用头文件。
  3. 改造tud_msc_read10_cb()函数.
/* file: tusb_msc.c */
#include "fat32_2g.h"

#define	SKY_DISK_BLOCK_NUM	(2*1024*1024*1024/512)	// 扇区总数=2G/512
#define SKY_DISK_BLOCK_SIZE	(512)					// 一个扇区大小

/* 判断扇区是不是非空扇区 */
int32_t IsInRomSector(uint32_t lba) {
    for (int32_t i = 0; i < sizeof(Index)/sizeof(int); i++) {
        if (lba == Index[i]) {
          return i;
        } 
    }
    return 0xFFFFFFFF;
}
// Callback invoked when received READ10 command.
// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.

/* 返回第lba扇区 offset处 长度为bufsize 的内容。
   实际上 offset 一直为0,是因为一般读都是读512B的整数倍,即一次读几个扇区
   bufsize是我们需要填充的 Buffer 大小,一般也是一个扇区的整数倍。
*/ 
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize)
{
    ESP_LOGD(__func__, "lun=%d, lba=%d, offset=%d, bufsize=%d, s_disk_block_size=%d", lun, lba, offset, bufsize, s_disk_block_size);

    if (lun >= LOGICAL_DISK_NUM) {
        ESP_LOGE(__func__, "invalid lun number %u", lun);
        return 0;
    }
	
#ifdef 0
    const uint32_t block_count = bufsize / s_disk_block_size;
    disk_read(s_pdrv, buffer, lba, block_count);
	return block_count * s_disk_block_size;
#else
	/* 扇区超出最大扇区数 */
	if(lba >= SKY_DISK_BLOCK_NUM)
	{
		ESP_LOGE(__func__, "invalid lba number %u", lba);
        return 0;
	}
	
	/* 换算成一次读几个扇区 */
	const uint32_t secCnt = bufsize / SKY_DISK_BLOCK_SIZE;	
	int32_t secIdx;
	int32_t sector;
	static int32_t t=0;

	for(int32_t i= 0; i <secCnt; i++)
	{
		sector = lba+i;
		secIdx = IsInRomSector(sector);
		
		/* 非空扇区,使用头文件中的数据填充 */
		if(secIdx != 0xFFFFFFFF)
		{
			memcpy(buffer + (SKY_DISK_BLOCK_SIZE*i), &Sector[secIdx][0], SKY_DISK_BLOCK_SIZE);
			ESP_LOGI(__func__, "sector = %d hit!! secIdx = %d", sector, secIdx);
		}
		else	/* 空扇区,全部填充0 */
		{
			memset(buffer + (SKY_DISK_BLOCK_SIZE*i), 0, SKY_DISK_BLOCK_SIZE);
        	ESP_LOGD(__func__, "sector = %d miss! reply all 0", sector);
		}
	}
	return bufsize;
#endif
}

六、测试

编译、烧写后,将开发板插到PC上,可以看到有一个2G大小的U盘。

esp32单片机入门 esp32 diy_esp32单片机入门_03

在U盘中存入超长的音频文件并能播放,放在下一篇中介绍。

七、工具

FileTohHxD

八、参考

[ESP8266/ESP32]ESP32 S2 做一个假U盘