USB固件开发心得

      今年年初的时候,在驱动开发上发贴《在ARM7上使用D12开发U盘的详尽技术报告》(用书名号中的字符查找,可以找到)。其中详尽地介绍了我在 ARM7 上用D12开发U盘的过程,以及所涉及到的方方面面的知识。得到了一些朋友的肯定和鼓励。从中我也体会到了帮助别人的快乐。

      后来忙于别的,不经常上驱动了,但偶尔看一下仍然有许多人遇到U盘开发中的各种各样的问题,心里觉得十分不快。因为看到还有许多人仍然在为已经不存在什么问题的内容所折磨,看到我们的技术开发者仍然处于一种单干的状态,技术交流的模式和通道仍然不畅......别人做过的工作,我们完全可以用一种合理的方式拿过来为已所用,而把精力放在更多未知的问题上的。

      后来再做U盘文件系统(即在固件中创建文件,在PC上可以读取)、USB Host(可以读写U盘)的过程中,也碰到过一些问题,在向别人征询时,有一些朋友很爽快,愿意交换彼此的源码,有一些网友则是有所保留,可能每个人都忙,别人也没有时间解答所有问题。感觉就是得到的帮助往往是模糊的,往往还隔层纸

      在驱动上发贴,总会被淹没,因此,我有一个想法,成立一个邮件组,正在做U盘碰到问题,或是准备做U盘的朋友,请给我发邮件。 frank_wang@263.net,主题注明“U盘开发”字样,我会将这些邮件收集到一个邮件组中,以后大家有什么问题,我可以抄送到所有人的邮件中,大家回信的时候,也可以回复全部,让大家共享信息。并且,我们一段时间将问题整理一次,放到驱动开发网上,作为大家的参考。我也可以考虑把 Bulk Only和SCSI命令集响应部分的源码以合理的方式提供给大家,并乐意解答其中的问题。

      总之,我的愿望是,所有中国人,想做U盘的,让这个都不再是问题,让我们把精力放在其他更多更重要的事情上,让我们的整体水平更上层楼。我愿意为我这个愿望做些什么。同时,我也愿意跟大家交流我曾经做过的一些项目内容,比如文件系统读写,Usb-Host控制U盘等等。

      我真切地感受到初学一个东西时的感受。其实这些东西并不难,只要有一些时间,都可以搞出来,但我们有必要每个人都去花相当可观的时间么!没有必要。

    我们有时需要挑战自己,挑战我们自己的观念和心态。我希望自己可以做一个尝试。

USB固件编程之一:固件编程的工作内容
USB固件编程可以用以下语句来精练地进行描述:

Device的固件编程,要搞定的是那几个端点。端点多少和配置情况受所用的Device芯片决定,具体可以看芯片资料。芯片一般提供一个中断信号,与单片机接口时,只要端点接受到数据,或发送数据成功后,便后产生中断,在固件里面,只要对些中断进行响应即可。
当Device接收到数据时,对这些数据进行分析处理(端点0遵守标准的数据格式,其他端点受端点类型的不同,有不同的数据格式),一般来说,这些数据是主机对设备发出的请求,设备只要响应主机的这些请求即可。Device芯片发送完数据后也会产生中断,这个中断信号告诉与之接口的单片机,可以继续发送后续的数据。
USB固件,好比一个有“妻管炎”的男人,而主机,则好是一个女管家。一般来说,主机让干啥就干啥,所以,USB固件程序的结构一般是基于中断处理的。平时,主程序做完必要的初始化工作后,就什么事也不做了,等待USB中断的产生,中断产生后,根据中断状态对相应的端点读取数据,或是向相应的端点发送数据。这一点是USB通讯协议的主-从模式先天决定的。但让人不可思议的是,这还有点象是母系氏族时期,因为,一个USB总线上,只能有一个主机,可以有多个设备,整个USB总线上通讯的协议和处理,发起与中止,基本上是主机控制的。因此,固件编程中,只要满足了主机的要求,就万事大吉了,可以确保自己的氏族中的应有地位。
U盘固件编程之二:固件编程的几个主要部分

在整个U盘固件中,程序从功能模块上分成两个部分:USB协议的处理和对Flash的读写.

USB协议的处理又分成两个方面.
一是端点0的配置过程.所有USB设备在插入USB端口时,主机都通过地址0与设备的端口0进行通讯。在这个过程中,主机发出一系列得到描述符的请求,通过这些请求,主机得到所有感兴趣的设备的描述符,从而知道设备的情况,然后通过Set Address为设备设置一个唯一的地址,配置过程完成以后,主机就通过为设备所设定的地址与设备通讯,而不再是使用默认地址0.配置地址后,可能还要获取一次描述符,然后设置配置(Set Configuration),之后便完成了对新插入USB总线的设备的配置过程。

二是其他端点的数据通讯过程。在配置阶段中,主机已经知道了设备的端点的使用情况,以后,便可以通过这些端点来进行特定传输方式的通讯了。对于U盘来说,有两种传输方式,BULK ONLY和CBI方式,一般使用Bulk Only较多。这种传输方式要使用特定的 Bulk 端点,然后还要为其选择一种命令集。比如UFI或SCSI,因为Bulk端点的数据没有特定的数据格式,因此,需要使用某种命令集,来约定所传数据的格式。对于U盘固件编程来讲,就是要处理BULK端点的各种数据通讯。

除了对各个相关的端点的USB协议的处理,剩下的就是FLASH的读写问题。这里存在两个层面的问题。
一是解决Flash读写的问题,就是说你使用的Flash,先要实现成功的读写和擦除,这部分内容,是比较成熟的,一般都使用三星公司出的K9FXX08 系列的,有16M(2808),32M(5608),64M(1208),138M(1G08)。它们的封装一致,只需要软件编程中稍做修改,便可以进行适应于另一种容量的存储器。

第二个层面的问题,就是在U盘通讯过程中的问题了。NAND型的Flash有个特点,不可随机存取,擦除操作一次对16K的内容进行。所以,在U盘响应过程中,不可避免要对数据进行缓存。如果你的U盘方案中有较宽裕的RAM(超过16K),这个问题变得简单,只需要开一个16K的数组,把数据存到这 16K 中,最后再写入Flash即可。否则,在缓冲上面是要花一些功夫的。最基本的思路是用Flash的另外一个Block做缓冲空间。

但这种方式会引发下列问题:1、速度;2、Flash的那个用来做缓存的块将比别的块使用频度大幅上升,磨损最严重,最先坏,这影响整个Flash 的寿命。在实际处理中,引入了一系列的折中方案,比如,对Write命令所写的Block号进行判断,如果是整个的数据Block,则直接擦除原有内容,将数据写入。再如,对于非整个Block的数据进行缓冲,而对于整个Block的读写,不缓冲直接写入。再就是对于前面文件分配表、目录项所在的 Block进行缓冲,等等。经过这些处理,可以尽可能地提高Write的速度。

U盘固件编程之三:合理的USB通讯调试方法和思路是成功的关键

在介绍更多细节内容之前,我不得不谈谈我对USB调试方法的理解。

USB通讯过程是一个动态的过程,是不太好使用硬件仿真器来设断点调试的,因为每一次USB的传输过程,都有时效要求,等待时间过长,通讯过程也就中止了。但也不排除可以巧妙地使用断点仿真的方法进行调试。但个人认为,使用串口辅助编程过程,却是一种经济有效的方法。

所谓用串口辅助调试过程,也就是在固件代码中加入类似于Printf的语句,向串口输出一些信息。这些信息可以是几个字符(如A、B、C),或是某个变量或寄存器的值。程序运行到此处时,便会输出这些信息,借此,便可以知道:1、程序是否运行到此处;2、运行到此处时相应变量或寄存器值。这不就是硬件仿真调试的功能么?

如果想使用这种方式来调试,在硬件上必须增加一个RS232串口电平转换芯片,而且所使用的MCU得要有串口,并且,一般要自己编写Printf函数的实现方式。这个翻翻串口控制方面的书籍,很容易就可以搞定。

串口调试的方法,还可以推广到其他的单片机应用中,在简单系统中,它基本可以替代掉硬件仿真器,降低开发者的门槛和投资。

在USB通讯过程中,有两个阶段,一是通过端点0完成对设备的配置,在此阶段,把从USB端点得到的数据输出到串口,就对通讯所处的阶段一目了然了。一旦完成配置阶段,Bus Hound便可以粉墨登场了,因为此时,Bus Hound中已经可以看到设备了,看到设备后,便可以选择设备,对主机与此设备间的通讯数据进行分析和*。

串口调试和Bus Hound这两种手段配合使用,可以使USB通讯过程的调试更加容易。比如,刚开始时,端点0的数据量本来就少,因此,使用串口调试比较方便。而对于Bulk端点的数据传送过程,再使用串口就不太方便了,因为数据量大,串口输出的数据太多,延时会比较严重,影响 USB 通讯过程,所以改用BUS HOUND来*USB总线上的数据。这个时候很有趣的一件事情是,Bus Hound在 PC机上,而串口实际上在单片机端。所以,利用这两种手段,里应外合,有助于我们确定一方发时另一方收的数据是否正确。比如,单片机上发出的一组数,将其输出到串口,然后看看Bus Hound上是否收到的是这些数,如果正确,则说明硬件通讯过程没有问题,如果不正确,则说明通讯的某一方有问题,进一步可以定位此问题,排除之。

正确的调试思路,将使调试过程事半功倍。

比如,在调试端点0 的配置过程时,可以先用Switch...Case语句建立对于端点0的数据的分支响应,对照标准请求的数据格式,可以得到什么情况下是Get DeviceDescriptor,什么时候是Get ConfigurationDescriptor,每个分支处理时对应一个函数,在这个函数里向串口标志信息。这个工作完成以后,便一个一个地来处理请求,处理完一个后,主机会自动进入下一个阶段,这时,通过串口可以看到相应的状态,按步就班地一个一个处理余下的请求,即可完成端点0的设备配置过程的固件程序的编写。

对于Bulk端点也是一样,先建立程序框架,然后再一个一个地处理请求。这种自上而下,逐步求精的思路并不稀奇,在USB调试过程中十分受用。最忌一上来就处理请求,不讲求结构,不讲求代码的条理性,最后可能弄得自己都是一头雾水。

补充建议:在能够使用BusHound之前,使用USBCheck工具,可以逐条、逐项、随意反复地调试端点0的各项数据和设置。感觉很方便。

U盘固件编程之四:玩转你的端点

接上面hewx,我来谈谈端点的问题。

前面提到过,端点是由USB设备端的接口芯片决定的。你选择了什么样的芯片,那么端点的配置情况属性就已经决定了,你只能使用将就这些特定的情况。这些端点的配置,具体要参考你所使用的接口芯片的芯片资料,比如说,端点0当然都为控制端点,其MaxPacketSize可能为8,16,32,64;端点1 可能是Bulk-In端点,2是Bulk-Out端点,其字长也有可能是8,16,32,64,但一般是64。其他端点可能有同步端点,或者同一端点既可被配置成同步传输方式,也可以工作在Bulk传输方式下,等等,不一而足。

USB协议精妙之处就在于枚举过程。主机最初发过来的包,一定是8个字符长的。所以,你的端点的MaxPacketSize至少必须是8,能满足与主机之间最基本的通讯过程。对于主机的第一个请求GetDeviceDescriptor,你也只用回复8个字符就 OK 了,因为主机在第一次只对这8个字符感兴趣,在后面逐渐的获取描述符的过程中,主机逐渐得到设备使用那些端点,每个端点的最大字长(这些内容在 EndpointDescriptor中,通过ConfigurationDescriptor提供)是多少,等等,总之,通过枚举,主机便知道你的端点的情况了,以后就会用这些端点来与设备进行通讯。

对于Hewx的问题,我想是你在EndpointDescriptor中没有正确进行端点的设置,因为,如果进行了正确的端点配置,主机是会自动通过Bulk端点来发Inquiry命令的,而不会从你说的Endpoint1(16B)来发送这一信息。而且,主机会自动对要发送的信息进行分割,每次以不高于相应端点的MaxPacketSize长度来发送。

除了描述符中要给出正确的端点描述符的描述,有些时候在芯片中也需要设备相应的控制位,在决定你要使用哪些及如何使用这些端点,这个也得根据具体的芯片资料来设置。

U盘固件编程之四:玩转你的端点(增补)

对于某种设备来说,需要使用到的端点是固定的。比如说,Mass Storage设备吧,就只需要用到一个Bulk-In端点和一个 Bulk-Out端点。而不需要几个此类端点。至于到底需要几个端点,完全需要根据有关协议中的说明进行,描述符也据此进行提供,而不是没有根据地在描述符中提供许多端点。

至于哪些端点可以做何种方式来使用,这也要看接口芯片的资料,比如,很可能,有的端点只能用作同步方式,那你就不要勉强将其用作批量方式,控制端点就只能用作控制传输方式,就不能为同步方式......搞清楚这个概念,在固件编程中才好正确地提供描述符。

整个U盘所涉及的内容有如下一些:
控制传输和批量传输的传输过程
Read和Write之前的命令响应
文件系统
Read命令和WRite命令的响应
容量与格式化
速度问题
其他问题

先不忙说文件系统吧。我的思路是,接下来讲讲在做U盘的过程中常见的两种传输方式:控制传输和批量传输的事务处理过程。因为我们经常在处理控制端点的传输或Bulk端点的传输时,有些朋友常碰到“主机没有反应了”这样的问题,知道这两种传输方式的几个阶段,可以对传输过程更加了解,知道主机为什么会没有反应,是因为固件中没有正确响应,还是因为别的因素。


本文来自: DZ3W.COM 原文网址:http://www.dz3w.com/articlescn/usb/0075483.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
AN039 - USB Firmware Programmer's Guide v1.1 Last updated 16 JUL 2003 To exercise the example firmware: 1. Extract the contents of the AN039.zip file (if using WinZip, use the "Extract" function to retain sub-directory information). 2. Open the Cygnal IDE (v1.72 or later). 3. From IDE menu Options->Debug Interface, select Cygnal 2-Wire. 4. Connect to the target C8051F32x device (Alt+C, or IDE menu Debug->Connect). 5. From the Project menu, select "Open Project" (a warning dialogue may appear; this is expected). 6. Browse to directory \AN039\Firmware and select file FirmwareProgramGuide.wsp. 7. Through IDE menu Project->Tool Chain Integration, select the path to your compiler and linker. The default is C:\Cygnal\IDEFiles\C51\Bin\. 8. Build the project (F7, or through IDE menu Project->Build/Make Project). 9. Download the firmware (Alt+D). 10. Connect a USB cable between the target device and the host PC. 11. Run (F5, or IDE menu Debug->Go). 12. When Windows detects the device and the driver installation wizard opens: a. Choose "Search for a suitable driver for my device", and click Next. b. Check the box "Specify a location", and click Next. c. Browse to directory \AN039\Application and select the file CygnalInt.inf, and click OK. d. Follow the dialogue to finish driver installation. 13. After the driver has been installed, run the USBTest.exe application from directory \AN039\Application. Notes: - Firmware works with the Cygnal IDE v1.71 or later and the Keil C51 tool chain. Project and code modifications will be necessary for use with different tool chains. - Compiler optimization emphasis is selected as "favor small code". This selection is necessary for the project to be compiled with the trial version of the Keil C51 Compiler (under 4k code space). - If using the C8051F320TB target board, jumper J2 should be installed if the board is wall-powered; jumper J11 should be installed if bus-powered. - Windows application and driver supports Win2K and XP only.

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值