收藏网址

收藏官网,优惠快人一步

您可以尝试通过快捷键 CTRL + D 加入收藏夹

|
|
0755-83865666
|
|
手机立创
|
面板定制
|
消息(0)
|

温馨提示

您上传的BOM清单格式不准确,当前支持上传xls、xlsx、csv、JPG、PNG、JPEG格式,请检查后重新上传

BOM正在分析中...
首页 > 应用与分享 > 立创电赛展:《双机USB文件互传工具》,参赛赢2万现金!
  • 立创电赛展:《双机USB文件互传工具》,参赛赢2万现金!

  • 2024-10-10 16:35:45 阅读量:121

“盛思锐传感器”第九届立创电子设计开源大赛火热报名中,现在点击报名立创电赛赢2万现金大奖,报名后全阶段可触发福利!

  大赛网址DIY.SZLCSC.COM

为了给大家些创作灵感,今天带来第八届立创电赛优胜奖项目-《双机USB文件互传工具》案例分享!本文作者:瑞萨MCU杯第八届立创电子设计开源大赛选手@Arduino爱好者,禁止商用,未经许可禁止转载。

* 1、项目功能介绍


 

在工作中,我经常需要在 UEFI Shell 环境下测试编写的 UEFI Application 。通常情况下都是先编译,然后将生成的文件拷贝到U盘,然后再将这个U盘插入到测试机上。显而易见,这样非常繁琐,影响工作效率。因此使用 CH567 这一款来自 WCH 的双USB接口芯片制作一个双机互联工具。在无需插拔的情况下即可进行文件传输。

 

这个工具的使用说明如下:

 

主要部件如图所示  ①     供电指示灯 ② 工作指示灯,当有数据传输时会闪烁 ③ ⑦供电跳线,默认情况下通过USB1 供电,换句话说:默认情况下只插入USB0端口设备不工作 ④PSRAM 需要传输的数据会放在这里,这个芯片的容量是8M Bytes ,目前的限制是一次最大只能传输8MB 的文件 ⑤ SPINOR芯片,用于存放工具  ⑥   主控芯片,CH567 , 这是南京 WCH 出品的ARM主控,支持高速双USB接口。

 

双机互联工具。

 

双机互联工具

 

设备上电后(推荐USB1端口,就是带有两个LED的那端),在 Windows下可以看到下面的目录:

 

电脑目录

 

 

这里有传输所需的软件以及一些其他方便调试的工具。内置软件介绍:

 

1. EFI 目录下:

 

   a. Boot\BootX64.efi: 一个 Shell;

   b. crd32.efi: 根据DiskImage.bin 创建一个虚拟磁盘;

      运行之后再使用 map -r 命令可以看到当前系统会多出一个RAMDISK;

   c. GetFile.efi: 从设备收取文件的工具。直接运行即可;

   d. SendFile.efi:对设备发送文件的工具,后面接文件名即可发送指定文件;

   e. memtest86\bootx64.efi: Memtest86 工具;

   f. CrScreenshotDxe.efi: 截屏工具。 Load CrScreenshotDxe.efi 加载,之后ctrl+alt+F12 触发截屏;

  

2. Win 目录下:

 

   a.7z2300-x64.exe: 7Z 安装包;

   b.ntf.8.4.6.Installer.x64.exe: notepadfree安装包;

   c.Getfile.exe: 从设备收取文件的命令行工具。需要在管理员权限下运行;

   d.Sendfile.exe:对设备发送文件的命令行工具。需要在管理员权限下运行;

   e.HE_v1.22.10.19_Portable.zip: 硬件访问工具。

 

针对传输工具说明如下:

 

1.使用sendfile.exe 发送文件(需要管理员权限),后面跟着要发送的文件名称。

 

 

2.使用 getfile.exe 接收文件。会使用当前设备中的文件名为保存的文件名,如果当前目录下有同名文件,那么会在后面增加”_1”再保存。

 

 

 

3.UEFI 下面工具使用方法相同

 

*2、项目属性


首次公开

注:请说明项目是否首次公开;项目是否为原创;项目是否曾经在其他比赛中获奖,若有获奖则叙述获奖详情;项目是否在学校参加过答辩。

* 3、开源协议


Public Domain

注:利他即利己,请认真阅读下述内容。

  1. 拥抱开源,赋予项目无限价值。建议项目核心功能开源80%以上;
  2. 若某一部分功能不可替代且删掉之后项目无法解决对应的问题,则这一部分实现的功能就是项目的核心功能;比如设计了一台电子负载且设计了一款上位机软件监控功率变化,则电子负载为核心功能,上位机软件为辅助功能;比如电子负载中使用了一款隔离485模块与上位机通信,则此485模块实现的通讯功能为辅助功能;
  3. 项目应选择适合自己的开源协议,若项目引用其他开源项目,应注明来源并遵循原作者的开源协议规定;原创项目推荐使用GPL3.0开源协议;
  4. 直接引用开源项目的原电路或原代码实现的功能不可作为自己项目的核心功能、使用市场上通用模块直接实现的功能不可作为自己项目的核心功能。

 

*4、硬件部分


本项目基于 WCH 的CH567 实现。CH567是一款高性能32位精简指令集微控制器,系统主频可达120MHZ。芯片带有丰富的外设资源。这次用到的是其中的两组独立的高速USB2.0 设备控制器。

 

CH567控制器

CH567控制器

可以看到,设备内部有三个主要芯片,分别是CH567 / PSRAM 和 SPINOR。

 

CH567控制器芯片内部

 

其中的 CH567 是这次设计的核心,这次的作品就是通过它模拟出了2个U盘,能够分别在两台电脑上同时工作。

然后为了实现数据交换,板子上放置了一个PSRAM,这种芯片可以理解为 SPI 接口的 SRAM,用户可以方便的使用 SPI 接口实现数据的存储,同时不用担心FLASH磨损这种问题。设备收到的文件会存放在这个芯片中,等待有人访问。这个芯片的大小为 8MB ,就是说每次不能传输大于8MB 的文件。当然,如果有这样的需求,也可以通过改写上位机软件的方式克服这个限制。

最后一个芯片是 GigaDevice 的32MB SPI NOR,我们将UEFI Shell 和文件传输用到的资料都放在其中,这样用户插上即可使用这个工具无需再使用U盘之类的存放工具。

 

*5、软件部分


 

软件设计包括:CH567 的固件代码、传输工具的应用程序(具体又分为 Windows 工具和 UEFI Shell 工具)

 

1)固件设计

这部分介绍CH567芯片代码的设计。

 

I.  描述符部分。具体定义在 USBShare.c 文件中,其中的 MyManuInfo 定义了厂家信息为 “LAB-Z.COM”,MyProdInfo定义了产品信息为 “DUMT”,此外还有MySerial定义了序列号“012345”,如果有需要可以自行修改;特别的,我们在 BIOS Setup 界面中看到的设备名称之类的信息,是定义在InquiryReply 中的,在启动过程中X86会USB接口通过发送 USB MSD Inquiry Command获取这个字符串,并且显示为启动设备名称;

II.   分布结构。芯片代码部分最主要的是一个 USB Mass Storage(U盘)的框架,所有的功能都是在访问内容部分进行扩展的。

 

CH567芯片代码设计

 

 

对于上述区域进一步解说。从图中可以看到设备中存在着2个区域:SPINOR 的区域,以及 PSRAM 区域。对于处于这两个区域之外的位置(中间部分),设备会返回空操作。对于读操作,直接返回全0 ;对于写操作,直接返回写入完成。

 

1.      SPI NOR 区域操作。对于这个区域的处理,分成三个 Topic: 0x1800 位置的读取写入、SPINOR 的读取和写入、PSRAM 的读取和写入。

a.      这个区域有一个 0x1800 的位置需要特别处理。这个存放着TranHeader结构体,它是一个用来存储传输状态的结构体,它的大小是512Bytes(刚好一个扇区),具体定义如下:

                struct _ TranHeader {

               // Offset 0,状态标志,目前用到的有 Busy标志。比如,一个 USB 端口在写入的时候

                   会将这个位置设置起来,另外一端在访问的时候看到了就知道对面正在使用;  另外

                    一个是只读标志,默认情况下模拟出来的U盘是只读无法写入的。这样做是为了节省

                    SPI NOR 的寿命,另外,放置意外篡改内容。

               UINT32 Flag; //Bit0: 0 - Busy

                                       //      1 – Not Busy

                                       //Bit1: 0 - ReadOnly Enable

                                      //      1 – ReadOnly Disable

               // Offset 4 ,这是一个单调递增的标志,目前没有使用

               UINT32 Monot;//单调递增

               // Offset 8,传输数据时,写入文件的长度(目前一次传输最高支持8MB 的文件)

               UINT32 Size; //本次传输的全部长度(头文件和内容) 1Bytes ---- 8Mbytes

               // Offset 12,预留的长文件传输,目前没有使用

               UINT32 Counter; //本次传输为第 Counter 段。例如,要传输一个64MB文件,

                                               //需要分割成8段,第一次传输的 Counter 为7,最后一个为0;

               // Offset 16

               UINT16 FilenameLength; //文件名长度

               // Offset 18

               UINT8  Filename[494]; // ASCII 的文件名称(不包括路径)

};

 

2)应用程序的编写

 

目前使用到的相关应用程序有三个:发送文件,接收文件和写入镜像。

 I.  共有代码

上面三个应用程序有部分内容有一些公头的代码。在使用的时候,为了确定系PhysicalDiskX 为我们的设备,需要检查下面2个标志。

第一处标志在注册表中,例如,下图中编号为“1”的设备就是我们的设备,因此,这里对应该 PhysicalDeisk1 是我们的设备。

 

共有代码

 

对应的代码示例如下:

           // 当前硬盘信息在

            // Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\disk\Enum

 

            RegistryKey myreg = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Services\disk\Enum");

            // 如果当前系统只有1个硬盘,那么直接退出

            if (myreg.GetValue("Count").ToString() == "1")

            {

                myreg.Close();

                Console.WriteLine("No transfer disk, pressing anykey to exit");

                Console.ReadKey();

                Environment.Exit(0);

            }

 

            string[] subkeyNames = myreg.GetValueNames();

            int DiskNo = 0;

            foreach (string keyName in subkeyNames)

            {

                if (myreg.GetValue(keyName).ToString().StartsWith(@"USBSTOR\Disk&Ven_DUMT"))

                {

                    Console.WriteLine(keyName);

                    Console.WriteLine(myreg.GetValue(keyName).ToString());

                    DiskNo = int.Parse(keyName);

                }

            }

            myreg.Close();

 

第二处在磁盘镜像中(写入SPINOR中的内容),如下,可以看到存在一个Signature:

 

Signature

 

对应的代码如下:

           // 检查第5个扇区,判断是否为设备

            disk1.Seek(0x800, SeekOrigin.Begin);

            disk1.Read(Sector, 0, ONESECTORSIZE);

            Console.WriteLine("Checking PHYSICALDRIVE" + DiskNo.ToString()+" Signature:");

            string arr = "DATTRANSFTERDISKWWW.LAB-Z.COM";

            byte[] bytearr = System.Text.Encoding.Default.GetBytes(arr);

 

            // 如果签名不符合,说明不是我们需要的设备

            if (Program.EqualsBytes(ref Sector, ref bytearr) != true)

            {

                //不是UDT设备,输出错误然后退出

                Console.WriteLine("PHYSICALDRIVE"+DiskNo.ToString() + " is NOT a DATATRANFER! ");

                disk1.Close();

                Console.ReadLine();

                Environment.Exit(0);

            }

            else

            {

                Console.WriteLine("PHYSICALDRIVE" + DiskNo.ToString() + " signature tests pass! ");

            }

 

除了上面的检查确认设备的代码,还有Busy位的检测,无论是读取还是写入都要进行Busy位的检测,这样避免一端正在写入时另外一端在读取,这样会导致读取出来的数据可能不完整。检查的方式就是读取 0x1800处的TranHeader结构体,确认另外一端没有 Busy才会进行其他操作。

 

           // 检查 Busy 位

            disk1.Seek(0x1800, SeekOrigin.Begin);

            disk1.Read(Sector, 0, 4096);

            if (DEBUGMODE)

            {

                Console.WriteLine("Busy check:");

                for (int i = 0; i < 32; i++)

                {

                    Console.Write("{0:x} ", Sector[i]);

                }

                Console.WriteLine(" ");

            }

 

            //检查 Busy位

            if ((Sector[0] & 1) == 0)

            {

                Console.WriteLine("Device busy, please try again later!");

                Console.ReadLine();

                Environment.Exit(0);

            }

 

II.  发送文件的代码

发送文件的操作就是从本地读取文件,然后将文件的大小和名称整理后写入TranHeader结构体中。特别注意,有时候收到的命令行参数的文件名可能带有路径信息,而发送的文件信息中只应该包含文件名信息。例如:sendfile c:\test\foo.bin ,我们读取c:\test\foo.bin文件作为发送内容,但是只应该把 foo.bin 写入TranHeader;

III.  接收文件的代码

接收文件的操作就是首先获得文件基本信息,然后再读取设备的PSRAM。将内容保存为文件。需要特别注意的是可能遇到存在同名文件的问题。对于 Windows下会对收到的文件重命名,UEFI App 的处理方法是直接删除之前的文件,这样设计的原因是为了避免后续的误操作。

IV.  写入镜像

首先,对0x1800处设置ReadOnly标志为 False;这样 CH567的代码会将对 0-32MB范围内的写入操作真正写入 SPINOR中。

 

            byte[] bt = new byte[ONEDATA];

            bt[0] = 0x02; // Readonly== False

            // 发送命令,Write Enable

            disk.Seek(0x1800, SeekOrigin.Begin);

            disk.Write(bt, 0, 512);

            disk.Flush();

接下来要对前面的扇区是否为空进行检测。Windows会阻止我们对一个带有分区表的硬盘写入,因此我们需要先擦除分区表。如果为 0xFF,后面开始写入操作;否则,先发送擦除然后再进行下一步:

            // 检查最前面的扇区是否为全 0xFF

            disk.Seek(0, SeekOrigin.Begin);

 

最后就可以进行硬盘的写入操作了,写入是从硬盘末尾先前进行的。因为实验发现,如果从前开始写入,那么写入分区之后,Windows 会“发现新增加的硬盘”,然后后面内容的写入

            for (int i = 0; i < MAXSPIADDRESS / ONEDATA; i++)

 

*6、BOM清单


 

项目使用立创 EDA 设计,BOM 如下:

注:项目涉及的BOM清单。这个位置请上传BOM的截图。清单详情请以PDF的格式上传到附件中。建议包括型号、品牌、名称、封装、采购渠道、用途等内容。具体内容和形式应以表达清楚项目构成为准。

 

*7、大赛LOGO验证


 

USB双击互传工具

USB双击互传工具

 

* 8、演示您的项目并录制成视频上传


 

视频要求:请横屏拍摄,分辨率不低于1280×720,格式Mp4/Mov,单个视频大小限100M内;

视频标题:立创电赛:{项目名称}-{视频模块名称};如立创电赛:《自动驾驶》-团队介绍。

更多详情:https://diy.szlcsc.com/posts/15a52db9fd7d40c492eb505280278e45

热门物料
型号
价格
TPS5430DDAR/DC-DC电源芯片 1.17
DS18B20+/温度传感器 4.54
AD623ARZ-R7/仪表放大器 13.54
STM32F030F4P6TR/单片机(MCU/MPU/SOC) 2.21
ULN2003ADR/达林顿晶体管阵列 0.3762
STM32F103C8T6/单片机(MCU/MPU/SOC) 4.24
STM32F103VCT6/单片机(MCU/MPU/SOC) 7.8
STM32F103CBT6/单片机(MCU/MPU/SOC) 6.27
OP07CDR/精密运放 0.4075
STM8S003F3P6TR/单片机(MCU/MPU/SOC) 1.0396
您的浏览器版本过低(IE8及IE8以下的浏览器或者其他浏览器的兼容模式),存在严重安全漏洞,请切换浏览器为极速模式或者将IE浏览器升级到更高版本。 【查看详情】
推荐您下载并使用 立创商城APP 或者最新版 谷歌浏览器火狐浏览器360浏览器搜狗浏览器QQ浏览器 的极(高)速模式进行访问。
© 2022 深圳市立创电子商务有限公司 版权所有

提示

您确定要删除此收货地址的吗?

请填写订单取消原因

提示

您确定删除此收货地址吗?

成功提示

content

失败提示

content

微信咨询

关注公众号咨询客服

咨询客服
  • 在线客服热线

    0755-83865666

  • 服务时间

    工作日  8:30~20:30

    节假日  8:30~18:00

  • 服务投诉

QQ咨询
投诉意见

紧急问题投诉电话:

18826549599

更快的受理通道

对常规通道处理结果不满意

请在此扫码

此意见箱直通立创管理层

优惠券 建议反馈
填问卷 立创用户体验问卷调查 立即参与
活动规则
活动规则
展开客服