资源描述
技术小结目录摘要.IAbstract.II符号说明.III第1章硬件设计.11.1硬件概述.1第2章U盘的逻辑结构.22.1 U盘的逻辑结构.2第3章USB通信协议.33.1 USB设备开工的机理.33.2 USB描述符.33.3 USB设备的枚举过程(开工过程).43.4 USB1.1 协议.63.4.1 重新认识枚举过程.73.4.2 基于SL811的USB底层传输函数实现要点.93.4.3 usbXfer()函数.103.4.4 epOXfer()函数.203.5 块传输(Bulk).223.6 SCSI 命令.243.6.1 跟U盘初始化有关的SCSI命令.243.6.2 Read 和 Write U 盘的命令.273.7 U盘兼容性问题的探讨.29第4章微软的文件系统.344.1 FAT16文件系统简介.344.1.1 保留区.344.1.2 FAT 区.374.1.3 根文件夹.404.1.4 数据区.404.2 FAT32文件系统简介.404.3 FAT文件系统的局限性.41第5章 编码实例分析.425.1 需求简述.425.2 文件结构.425.3 Main.c.425.4 USB.c.435.5 timer.c.445.6 filesys.c.44-IV-5.6.1 变量说明.445.6.2 扇区读写函数.445.6.3 询问下一簇号函数.455.6.4 FAT初始化函数(BPB信息分析).455.6.5 Open Files.475.6.6 创建文件.505.6.7 写入文件.51第6章 使用CH375的解决方案.526.1只作简单介绍.52结论.53参考文献.54致谢.55-V-第1章硬件设计1.1硬件概述先详细介绍基于Cypress公司的SL811芯片的扩展方案,基于国内南京沁恒电子的 CH375芯片的方案最后介绍。本文尽量介绍关于USB,U盘和文件系统等平台无关的内 容,SL8n硬件平台方面的内容不可避免要涉及到一点,但尽量控制到最少。硬件很简单,详见protel文件(省略了无关的部分,比较粗糙,凑合看.)。主要是扩展 一片32K的外部RAM 62256作为数据缓冲,同时分配好SL8n的地址。通过51单片机的 A15地址线分别连接到外部RAM 62256和SL8n的片选CS端,来区分两者的地址,可见 RAM占据低32K地址空间,而SL811依“写地址”和“读写数据地址”分别占据8000H 和8001H两个字节的地址空间。-1-技术小结第2章U盘的逻辑结构2.1 U盘的逻辑结构U盘可以看成是以扇区(1扇区=512Bytes)为单位线性排列的实体,即0号扇区,1 号扇区,2号扇区,这样按顺序地排列下去。U盘是flash,对flash的操作总是以块为 单位的,因此单片机对U盘的操作是以扇区为单位,整个扇区地读取,或整个扇区地写 入。-2-第3章USB通信协议第3章USB通信协议3.1 USB设备开工的机理USB是即插即用的,涵盖海量存储器(如U盘、移动硬盘)、人机交互设备(如鼠标 键盘游戏杆)、扫描仪、打印机等等各种各样功能的设备,那么USB主机是如何判断目前 接入的设备到底是怎么样的呢?答案是USB描述符,以及USB的枚举。3.2 USB描述符这个概念很简单,就是对各种纷繁芜杂的USB外设按功能划分大类(class),大类下又 再细分小类(subclass),每个类别给予一串特定的符号(Descriptor)供主机辨识。每个USB设备只能有一个DEVICE描述符,它指明了该设备属于哪一大类,是海量存 储器类,还是人机交互设备类,还是打印机或者扫描仪类,等等。每个DEVICE下可以有1个或多个配置描述符(configuration),以说明该设备含有哪 些功能。如一个USB接口的CDROM可以同时具有读写光盘的功能和播放CD的功能。有 几个功能,就有几个配置描述符。每种配置对应若干个接口描述符(Interface),以描述该配置使用哪些接口与主机进行 通信。每个Interface又都是端点(EndPoint)的集合,端点就是设备与USB主机交换数据的 最原子单位了。每个Interface用到的端点可以是一个或多个。下图摘自USB MASS STORAGE CBI Transport Specification第6页,清楚说明各种描述符的组织情况。Figure 1-Sample Descriptor Organization-3-技术小结3.3 USB设备的枚举过程(开工过程)有了完善的分类后,USB设备上电即可通过枚举过程告诉USB主机自己的详细信息,这很类似一个一问一答的过程,如下:主机(下称H):你是甚么设备?设备(下称D):我是12 01 0100(这就是DEVICE描述符了)H:你有几种功能?D:我有09 02 09(配置描述符)H:每种功能有几个接口?D:09 04 00(接口描述符)H:每个接口用到哪些端点?D:07 05 81(端点描述符)H:好了,我知道你是谁了,开始传数据吧!D:OK.READY GO!具体而言,USB枚举过程有以下步骤:(1)集线器检测新设备主机集线器监视着每个端口的信号电压,当有新设备接入时便可觉察。(集线器端口的两根 信号线的每一根都有15kQ的下拉电阻,而每一个设备在D+都有一个1.5kQ的上拉电阻。当用USB线将PC和设备接通后,设备的上拉电阻使信号线的电位升高,因此被主机集线 器检测到。)(2)主机知道了新设备连接后每个集线器用中断传输来报告在集线器上的事件。当主机知道了这个事件,它给集线器发 送一个Get-Status请求来了解更多的消息。返回的消息告诉主机一个设备是什么时候连接 的。(3)集线器重新设置这个新设备当主机知道有一个新的设备时,主机给集线器发送一个Set_Feature请求,请求集线器来重 新设置端口。集线器使得设备的USB数据线处于重启(RESET)状态至少10ms。(4)集线器在设备和主机之间建立一个信号通路主机发送一个Get.Status请求来验证设备是否激起重启状态。返回的数据有一位表示设备 仍然处于重启状态。当集线器释放了重启状态,设备就处于默认状态了,即设备已经准备 好通过Endpoint 0的默认流程响应控制传输。即设备现在使用默认地址0 x0与主机通信。(5)集线器检测设备速度集线器通过测定那根信号线(D+或D-)在空闲时有更高的电压来检测设备是低速设备还是 全速设备。(全速和高速设备D+有上拉电阻,低速设备D-有上拉电阻)。以下,需要USB的firmware进行干预-4-第3章USB通信协议(6)获取最大数据包长度PC向address 0发送USB协议规定的Get_Device_Descriptor命令,以取得缺省控制管道所 支持的最大数据包长度,并在有限的时间内等待USB设备的响应,该长度包含在设备描述 符的bMaxPacketSizeO字段中,其地址偏移量为7,所以这时主机只需读取该描述符的前8 个字节。注意,主机一次只能列举一个USB设备,所以同一时刻只能有一个USB设备使 用缺省地址Oo(7)主机分配一个新的地址给设备主机通过发送一个Set.Address请求来分配一个唯一的地址给设备。设备读取这个请求,返回一个确认,并保存新的地址。从此开始所有通信都使用这个新地址。(8)主机向新地址重新发送Get_Device_Descriptor命令,此次读取其设备描述符的全 部字段,以了解该设备的总体信息,如VID,PIDo(9)主机向设备循环发送Get_Device_Configuration命令,要求USB设备回答,以读取 全部配置信息。(10)主机发送Get_Device_String命令,获得字符集描述(unicode),比如产商、产品 描述、型号等等。(11)如果主机是PC电脑,此时主机将会弹出窗口,展示发现新设备的信息,产商、产品描述、型号等。(12)根据 Device_Descriptor 和 Device_Configuration 应答,PC 判断是否能够提供 USB 的Driver,一般win2k能提供几大类的设备,如游戏操作杆、存储、打印机、扫描仪等,操作就在后台运行。(13)加载了 USB设备驱动以后,主机发送Set_Configuration(x)命令请求为该设备 选择一个合适的配置(x代表非0的配置值)。如果配置成功,USB设备进入“配置”状 态,并可以和客户软件进行数据传输。此时,常规的USB完成了其必须进行的配置和连接 工作。查看注册表,能够发现相应的项目已经添加完毕,至此设备应当可以开始使用。以上是PC电脑为主机的枚举过程,对于单片机作为主机的情形,过程要简单一些,以枚举U盘为例:(1)芯片SL8n监视USB总线电平,当发现有U盘插入后,给单片机一个中断信号。(2)单片机给SL8n发出端口复位命令,持续100毫秒以上。(3)单片机发出Get_Device_descriptor命令,从默认的端口 0和地址0发出。该命令先 假设了包传送的大小是64字节,在获得命令返回时修正MaxPacketSize。此步同PC。(4)单片机发送Set_Address请求来分配一个唯一的地址给U盘,我们实际应用中固定 分配了地址2o此步同PCo(5)单片机向新地址2重新发送Get_Device_Descriptor命令,此次读取U盘设备描述 符的全部字段,以了解该设备的总体信息,如VID,PIDo此步同PC。(6)单片机发送Get_Configuration_Descriptor命令获取配置描述符。-5-技术小结(7)根据获取的配置信息,单片机发送SetConfig和Setinterface命令对U盘进行配 置。(8)对获取的Interface描述符和Endpoint描述符进行分析,判断是否大容量存储设 备、是否支持SCSI命令集、是否BULKJDNLY传输、端口的最大包长等内容。(9)发送Get_Max_LUN命令获取U盘的进一步信息(根据协议看此步非必须,有些 U盘此步会返回STALL,即不支持,也没有关系)。但是建议在枚举过程中不省略此步,因为不同品牌U盘其固件可能不一样,有些固件可能不允许省略此步。(10)完成上述步骤后,U盘的枚举过程完成,接着需要发送几条SCSI命令来对U盘 进行初始化,这几条命令依次是Inquiry、ReadFormatCapacity ReadCapacityo完成后,U 盘已经准备好接收单片机发出的任何读写命令(读写命令也是来自SCSI命令集)。如果你有兴趣知道USB协议一些更细节的内容,请往下看。否则可以直接跳到第四章 的文件系统部分。3.4 USB1.1 协议本节内容主要涵盖USB 1.1 Specification的第4、5、8、9章。并且主要描述代码中无 法注释或者在代码中注释会太麻烦的内容。USB是一种主从的结构。所有传输由Host来发起。当主机发起一次传输时,这次传输 的包(Packets)通常包括三个阶段。主机首先是发送一个Token Packet,内里包含本次传 输的命令类型(type)、方向(direction)、设备的地址(device address)以及端点号(Endpoint)紧接着是数据包(data packet),就是包含数据了。最后将由device返回握手 信号包(handshake packet),表示是正确收到了(ACK)还是其他的失败原因。三个包如 下图所示。SETUPADDREE2CRC5EOP000000010 xB4000 x083.00DATA。I_DATACRC16 EOP00000001 0 xC380J)6 00 01 00 00 40 00|0 xBB29|39|ACK000000010 x4B3.00USB的传输模型:Host和设备的某个端点之间可以看成有一条逻辑管道(pipe)。Pipe 分两种:业务数据流和信令消息。业务流即指纯粹的数据,信令流指控制信息。其实通信 协议很多都如此,分业务流和信令流,例如电信网中的7号信令。在信令管道中,有一条默认的管道,那就是零地址处的零号端点,这条管道在USB 设备上电复位或总线复位后就存在了,便于Host统一利用这个地址向USB设备进行配-6-第3章USB通信协议置。显然对于USB集线器,即使同时插入几个设备,Host也只能一次对一个设备进行配 置。USB设备只有配置(configured)后,才可使用。USB的传输类型有四种:控制传输(control transfer),通常只用于在设备复位后Host通过端点。进行配置。块传输(Bulk Transfer),譬如U盘的大量数据传输即用此方式。中断传输(Interrupt Transfer),一般用于人机设备如USB鼠标键盘等。等时传输(Isochronous Transfer),可以进行带宽控制的实时传输形式。3.4.1 重新认识枚举过程枚举过程事实上是USB设备复位后,恢复到。地址0号端点,然后主机通过一系列 控制传输命令对USB设备进行配置,同时也获取一些信息。使用BUS hound这个工具可以把完整的USB设备枚举过程抓下来。网上很容易找到 安装包。BUS软件的设置如下:可以确保抓下所有的数据包信息。-7-技术小结利用BUS hound的软件抓一下爱国者行业特供型1G的U盘,其在PC下的枚举过程 完全在下图中表现了出来。让我们逐一分析。由设备16.0抓到的数据包属于USB集线器的行为,在无使用集线器的单片机系统中 可以无视之。设备21.0的含义是:usb设备地址是21,目前管道是跟它的端点。打交道。数字1处是枚举过程的开始,主机用控制传输发送Get Descriptor获取设备描述符(具 体为何是设备描述符可以对照USB1.1技术规范的第九章来分析左边的那串08 06 00 02的二进制数据,下同),这条命令假设了未知设备的端点0的最大包长值64字节,然后在命令中要求设备返回0 x12(十进制18)个字节的device描述符,如图中圈起来的 12o值得一提的是,这条命令无论假设设备的端点0的最大包长(Payload)是8,16,32,64,都是可以获得想要的数据的(图中的40)。40H指明该设备的端点0的最大包长 是64字节,Host此后的控制传输可以使用64字节的数据包跟设备通信了。64字节数据包-8-第3章USB通信协议的细节后面会介绍。数字2处表示主机发送Get Descriptor获取配置描述符。但是类似设备描述符的处理方 法,主机也先试探性的获取配置描述符的前9个字节(图中带圈的09),以获悉整个配置 描述符有多长,因为长度信息就位于描述符的第3个字节,如图中带圈的20o根据此20h 的长度信息,数字3处开始正式请求设备完整的配置描述符了,可以在图中看到两个20是 对应的。整个配置描述符包含32个字节(一般的U盘都是这样)。这32个字节中,包含3部 分内容,包含设备的重要信息。数字5所代表的第一个框表示第一部分:配置描述符。数 字6代表第二部分:接口描述符(Interface)o其中第5字节02表示该设备有2个端点(BulkN和BulkJDUT),第6字节08代表这是大容量存储设备(Mass Storage Device)。第7字节06表示支持SCSI命令,不过我调试过MP3播放器这个地方是05,但也支持 SCSI命令。第8字节50表示数据只支持使用Bulk传输(Bulk Only)。(更详细的内容可参 考 USB Mass Storage Class Bulk-Only Transport Revision 1.0)o数字7和8代表第三部分:端点描述符。第三字节都是代表端点地址,一般情况是地 址1和地址2。留意图中8框第三字节是82H,这表示该端点地址就是地址2,最高位被置 1以表示这个端点是Bulk.IN端点,所以整个数值变成了 82HO但是并非地址2就一定是 Bulk.IN,不同的U盘不一样,所以在程序中要根据描述符的实际值,用变量记录下来 的,后面要用到。继续看8框,第五和第六字节组成一个16位的数值表示该端点的最大包 长度(payload)o对于只支持1.1协议的设备,第六字节是其高8位,都是0,第五字节才 是真正的payload数值,1.1协议规定只能是8,16,32,64之一,由厂家固定。至于我们 在上图看到第六字节是02第五字节是00,组合成200H=512,那是因为U盘控制器认出 了这个Host(PC电脑)支持usb2.0,所以就回应了 512,而不是64o Payload值非常重 要,后面要依据此值进行判断和计算。下面接着的4个get descriptor都是获取设备的string描述符。实际的单片机系统也许 不需要获取这些描述符,而且有些U盘也不支持获取这个描述符(返回STALL)o再往下 的就是 set_configuration set_interface get_MAX_LUN 等。有些 U 盘,在 set_interface 处 会stall;有些U盘,如果Host不发送set_interface命令,往后的命令都不响应,所以这个 牵涉到兼容性问题,后面再解释。在上图中可以看到,这个爱国者U盘在遇到 get_MAX_LUN时返回了 stall,PC的处理方法是clear feature,然后重试,三次后仍然stall 则跳过。这个牵涉到如何进行差错处理,后面再详细分析。3.4.2 基于SL811的USB底层传输函数实现要点前面枚举过程介绍的各种命令,如截图中的数字1处的“80 06 00 01 00 00 12 00到 底是如何发送出去的,这也许是大家比较感兴趣的问题。有必要先简单认识一下SL811的功能,虽然这跟USB1.1协议几乎无关。-9-技术小结SL811提供了 15个寄存器供使用,实际在进行USB传输时最少只需要用到其中6 个,另外还需要用到SL811内建的240字节RAM作为数据缓冲。启动SL8n发送/接收一次数据(注意不是一帧数据,一帧数据=一个数据包)的步骤 如下:目标U盘的端点地址和pid-SL811寄存器地址0 x03目标U盘的地址 一 地址0 x04811内部RAM中数据缓冲的地址 一 地址0 x01该次数据的长度(不是该帧数据的长度)一 地址0 x02Oxff 一 地址 OxOd启动发送的命令字 一 地址0 x05当这一次数据成功了后,如果pid是“发送”,则SL811内部RAM中缓冲处的数据都 被发出去了;如果pid是“接收”,则SL8n内部RAM中缓冲处会填满来自U盘的数据,长度等于上面黑体字第四行之设定。单片机应该在此时及时把SL811的内建RAM中这些 数据读出来,放进单片机自己开辟的内存区域。看的出来SL811对USB的物理层已经完全封装了,但是设计者还是需要关心很多细 节。对SL8n总线式的读写函数太简单,这里不提了。那是我们这种解决方案下最底层的 函数。关键是usb.c中的usbXfer()和epOXfer()这两个函数。3.4.3 usbXfer()函数借助分析这个函数的实现可以了解USB的传输模型及差错处理。分析一下它的输入参数。int usbXfer(BYTE usbaddr,/USB设备地址0T27BYTE endpoint,BYTE pid,/端点的地址0T5,对于U盘无非就是0,1,2/数据包 token 类型,包括 setup,pid IN,pid OUTBYTE iso,/是否使用等时传输,对于U盘,该项恒否WORD wPayload,/本数据包的最大包长,又称净荷。WORD wLen,/待发送或接收的数据的实际长度BYTE buffer/待发送的数据的缓冲首址,或者将要接收数据的缓冲首址)首先要了解pido Setup型的pid只出现在控制传输阶段,即usb设备复位配置阶段。PidN和pid_OUT可能出现在控制传输阶段和此后的Bulk传输阶段。顾名思义,pid_IN 表示Host打算通过这次usbXfer,从U盘读进来wLen长度的数据,放入buffer中。Pid.OUT表示Host打算向U盘控制器发送wLen长度的数据或者写入U盘wLen长度的数 据,这些数据已经在buffer准备好。-10-第3章USB通信协议其次是要理清wPayload和wLen的关系。wPayload传递过来的是该端点的最大包长,在前面枚举中分析那32字节的配置描述符时应该已经记录了下来。在USB1.1的规范里只 能是8,16,32,64这四个值其中之一,实际上我根据SL811的打印只见过8字节和64 字节payload的U盘,而且那些8字节U盘只是端点0是8字节,BULK端点也是64字节 的。应该目前来说多数U盘都是64字节的(1.1的范围内)。言归正传,由于USB设备的端点有最大包长的限制,SL811启动一次数据传输时必须 保证不超过这一限制,因此,在往SL811的0 x02地址发送数据长度前,应作一判断,取 wPayload和wLen之中的较小者。C代码为:xferLen=(BYTE)(wLen=wPayload)?wPayload:wLen);当wLen payload的时候,一帧数据就需要SL811启动多次传输才能完成。接着让我们根据代码来分析usbXfer()函数的流程。建议对照着附件中usb.c的代码 来看。代码的图在前,分析在后,下同。/*/USB传输子程序/返回值:/成功一返回值=0,等于实际收发的数据包的字节效/失败返回值 0 设络突然拔出或端口异常复位=-1/EP0_ERR0R-2/EPO2TIMEOUT-4 EPO二SEQUENCE-8 EPO二SETUP-10/EPO2OVERFLOU-20 EPO2NAK-40/EPO二STALL-80/!/备注:/对于STALL以及其他状态未进行处理,是否存在兼容问题还有待验证/*int usbXfer(BYTE usbaddr,BYTE endpoint,BYTE pid,BYTE iso,WORD wPayload,(xdata BYTE end,result,intr;xdata BYTE xferLen,bufLen,dataO,datal,dataX,addr,wLen_save;xdata WORD vRe try_NAK,time out;wLen_save=(BYTE)wLen;一二-简单的函数说明,列出了返回值意义。函数开始的变量定义,有些可以顾名思义,有 些等后面用到了再解释。-11-技术小结-/Default setting for usb trasnfer-bufLen=dataX=timeout=0;/reset alldataO=EP0_Buf;/DATAO buffer addressdatal=dataO+(BYTE)uPayload;/DATA1 buffer address is only forDATA_ST0P=TIME0UT_ERR=FALSE;/set default conditionsvRetry_NAK=0;/红框处需要解释一下。EP0_Buf值为0 x10,它的意义是SL811内建RAM的起始地 址。对于SL811的内建RAM只需要用到其中2Xpayload个字节,而且是掰开两半来轮换 使用。SL8n的应用笔记称之为乒乓缓冲。举个例子,假设手头遇到一个U盘其端点。是8字节payload的。在枚举U盘时host 请求U盘返回它的32字节配置描述符。此时属于wLen大于payload的情形,需要SL811 启动4次传输才能完全把32字节数据收回来。首先如上图所示,我们初始化dataO指向SL811的RAM起始地址,datal初始化为指 向dataO+payload处,并初始化SL811的缓冲指针寄存器为dataOo然后启动SL811发起 第一次传输,让SL811把第一批8字节收到dataO处,单片机立即读走这8字节;修改 SL811的缓冲指针寄存器为datal,启动第二次传输,把第二批8字节内容收到datal处,单片机也立即读走此8个字节。依次轮换,直到4次传输过后,32字节完全收了回来。平心而论这个作法有点多余,既然是单片机立即读走数据的,一直用dataO就可以 了。不过上述做法是Cypress公司提供的例程,很多人都照搬了大家知道是怎么回事 就行了。/-/Define data transfer payload/-D_E(printf(rr=USBXfer-wLen=%d vPayload=dnrr,wLen,wPayload);)if(when=wPayload)/select proper data payloadxferLen=(BYTE)wPayload;/limit to vPayload sizeelse/else take=wPayload)?wPayload:wLen);-12-第3章USB通信协议-/For IN token/-if(pid=PID_IN)(ifqFULL SPEED)end=sDATAO RD;/for current IN tokens 0010 0011(DataO+SOF+IN+Enable+Arm)0000 0011(DataO 4-IN+Enable+Arm)else emd=DATA0_RD;/-Cmd变量是等会要写入SL811的控制寄存器0 x00地址的值。对于PidN的token类 型在这里进行预置。sDataO_RD=00100011B,其含义指:该次数据包的sequence bit定为 DATA0,产生同步帧SOF,数据方向是IN(即读U盘),使能传输(Enable+ARM)。具 体参考SL811的数据手册。解释一下其中的Sequence bit,USB1.1协议规定,每个数据包(data packet)都必须包 含一个sequence bit,用于纠错。收发双方的软件也要各自维护自己的sequence bit。Sequence位要么是DATA0,用0表示,要么是DATA1,用1表示(注意此DATA0与前面 的乒乓缓冲的dataO没有半点关系,重名纯属巧合)。USB1.1协议第185页描述了何为一 次成功的数据包收发。Transfer i Transfer/+1在第i个数据包发送前(左图),TX方的seq bit是DATAO,于是它填充数据包的seq bit为DATAOo RX方成功收到了这个数据包,于是将自己的seq bit切换到1即DATA1,并返回一个ACK应答表示成功收到了。当TX方收到ACK后,也切换自己的seq bit到 DATAlo右图的第i+1个数据包就类似了。1.1协议在第186页描述了一次重发数据包的情形。-13-技术小结DATAOTransfer/同样先看左图,第i个数据包由TX发出,但是由于各种原因RX收到坏(corrupted)了的包。于是RX方拒绝切换自己的seq bit,并返回NAK给TX方(返回stall或者 timeout等情形类似NAK)。此时TX方由于收到的不是ACK,不能切换seq bit,只能仍然 以DATA0的seq bit组装数据包重发,若如右图RX方接收了这个包了,这才是一次成功 的收发,RX和TX各自的seq bit发生切换。如下图是一次控制传输涉及到的三个packet,其中中间那个是data packet,红框处就 是其 sequence bit。SETUP ADDR000000010 xB4 j 0o0 x083.00l)ATA0ESS000000010 xC30 xBB291 1继续分析代码。SyncACKjob000000010 x4B3.00-14-第3章USB通信协议-/For OUT token/-else if(pid=PID_OUT)/_1_/for OUT tokensif(xferLen)SL811BufUrite(dataOzbufferzxferLen);/only when there are/data to transfer on USBif(FULL_SPEED)cmd=sDATA0_WR;else 2cmd=DATAO TO;/FS/FS on Hub,sync to sof/LS,no sync to sof for OUT/implement data togglebSeqbit=uDevusbaddr.bDatalendpoint;uDevusbaddr.bDatalendpoint=(uDevusbpayload,需要继续启动SL8n进行传输,并把数据放于乒乓缓冲中。请看代码。IN TOKENelse if(pid=PIDGIN)/for IN token onlywLen-=(UORD)xferLen;update remainding wLerend 人=0 x40;1 toggle DATAO/DATA1dataX+;/point to next dataX/If host requested for more data than the slave/have,and if the slave1s data len is a multiple/of its endpoint payload size/last xferLen.Do/not overwrite data in previous buffer.-if(remainder=xferLen)bufLen=0;else 2bufLen=xferLen;/empty data detected/do not overwriten pre/reset bufLen to zero/update previous buffei-/Arm for next data transfer when requested data/length have not reach zero,i.e.wLen!=0,and/last xferlen of data vas completed,i.e./remainder is equal to zero,not a short pkt/-if(!remainder&wLen)/remainder=0 when last/was all completed or 口addr=(dataX&1)?datal:dataO;/select next address fcxferLen=(BYTE)(wLen=wPaYload)?wPayload:wLen);/get data lengtif(FULL/PEED)end I=0 x20;SL811Write(EPOXferLen,xferLen);SL811Write(EPOAddress,addr);SL811rite(IntStatuszIWT_CLEAR);SL811Write(EP0Controlzcmd);/sync with SOF transfer/always sync SOF when I/select next xfer lengt/data buffer addr is a LS is on Hub./Enable USB transfer ar/-数字1处,修正剩余的wLen,切换seqbit,dataX加1。dataX的作用就是用来计算接 下来应该使用乒乓缓冲中的dataO还是datal o数字2处,判断之前的传输是否一个字节都没收到(应该不会出现这种情况)。否则认 为之前的传输是成功的,xferlen长度的数据全部收到了 SL811的RAM缓冲中,赋值给 buflen,告诉单片机从SL811的缓冲中读取buflen长度的内容。数字3处,仅当wLen还有剩余,且上次的传输已经全部完成remainder为0的情况 下,再次启动SL811的传输。判断dataX的奇偶性就可以判断应该使用乒乓缓冲的datal-17-技术小结还是dataO o-/Copy last IN token data pkt from prev transfer/Check if there vas data available during the/last data transfer/-if(bufLen)(DB(printf(rrIN token bufLen=%binrrzbufLen);)SL811BufRead(dataX&l)?dataO:datal),buffer,bufLen);DB(printBuf(rrIN token S1811 read buffer:rbuffer,bufLen);)buffer+=bufLen;)/if 0/-/Terminate on short packets,i.e.remainder=0/a short packet or empty data packet OR when/requested data len have completed,i.e.wLen=0/For a LOWSPEED device,the 1st device descp,/wPayload is default to 64-byte,LS device will/only send back a max of 8-byte device descp,/and host detect this as a short packet,and/terminate with OUT status stage/-if(remainder I I!wLen)2(DB(puts(rr=USBXfer-Terminate on short packets!rr);)return wLen_save;)数字1处,单片机及时从SL811的RAM中读取刚收下来的内容到buffer中,并更新 buffer的位置。数字2处,如果wlen或者remainder等于0,认为本次usbXfer已经IN 了全部需要 的数据,函数成功返回wLeno假如仍未收完,则会返回到前面的while,等待下一次的 DONE完成,再重复对result的判断,直至函数从这里返回。2.返回NAKNAK意味着USB设备暂时无法返回数据给hosto根据USB的协议,返回NAK的可能原因有如下:A)设备端处于流量控制状态。目前host的数据发送太频密,为防止设备自己的缓冲 溢出,设备向host发送NAK,意为暂时不要再发数据过来,等设备缓一口气。B)设备的端点正在忙(Busy)C)端点没有数据可传输给host。D)端点进入了 halt状态对于A和B情形,host应该继续尝试向设备重新发包。C情形一般出现在中断传输 里。D情形,host在重试了足够次数后,应该尝试使用clear feature命令清除端点的状态。-18-第3章USB通信协议-NAK-if(result&EPO_NAK)/NAK Detected(if(wRetry_NAK=1000)(DB(puts(rr=USBXfer-Terminate on NAK!rr);)return-40;)ShortDelayf);uRetrY_NAK-H-;SL811Write(IntStatus,INT_CLEAR);/clear interruptSL811Urite(EPOControl,cmd);/re-arm and requresult=0;DB(puts(rr=USBXfer-Retry on NAK!);)-TIMEOUT-程序段判断对NAK的重发次数是否已到达1000次,否则稍稍延时一下,然后重新发 送。重发超过1000次后,返回一40,表明这是NAK重发失败的返回。我之前调试的时候 NAK只设置为最大重发20次,每次延时5毫秒,结果有些U盘很容易返回NAK错误。现在看来,NAK的重试次数可以很多,重试间隔可以很短,但太多也不适宜,会让人感觉 到U盘的初始化时间很长。任何时候都不要无限重发,会造成死循环。3.返回 Timeout设备返Hl timeout的原因一般是收到无法识别的包(unrecognized)或者坏包(corrupted
展开阅读全文