软件理解
当设置地址的setup包发送完之后,usb host主机会继续产生一个IN事务,进而进入这里,将setup包中设置的地址值,配置到usb从设备上pdiusbd12,对于PDIUSBD12来说,当为OUT事务数据包时,只有包数据接收完毕之后,才会触发PDIUSBD12的中断,当为IN事务时,一旦host发出IN事务的PID令牌值,PDIUSBD12会立即中断,mcu此时,把需要发送的数据送到USB总线即可.如果此次操作是OUT事务操作,那么当数据全部传输完毕之后,host主机会再产生一个IN事务操作,从机必须配合这个IN事务操作发送一个空数据包(实际51测试slave可以不用回发0字节的空数据包;但在at91rm9200下当没有待发的数据时,当出现IN事务时,at91rm9200固件驱动会发送0字节包来响应该IN事务),如果此次操作是IN事务操作,那么当数据全部传输完毕之后,host主机会再产生一个OUT事务操作,从机必须配合这个OUT事务操作返回正确ACK(ACK已经由usb控制器硬件自动完成),所以从这里看,IN事务也好,OUT事务也好,他们都是host主机以逻辑为出发点"乱搞"出来的,对于不负责任的从机,可能端点0,1,2之类一旦发生数据中断,它就发数据,它不管当前发的数据是为IN事务服务的,还是为OUT事务服务的,而对于负责任的从机,它会完全按照host提出来的IN、OUT事务的逻辑,配合IN、OUT事务,发送相应事务下的相应数据.
举一个例子:host下发一个数据,所以需要产生OUT事务,当host发送完成之后,host自己知道已经发完了,但host比较bt,他还想让slave做一个确认,让slave报告一个空数据包,他才解恨,slave怎么报告呢,slave不能主动传数据给host,因为usb主从模式的原因,一切数据的流动都是由host单方面主动提出发送、读取,slave只是被动的响应,就像前面的OUT事务一样host打算OUT数据时,绝不会事先知会一下slave,有了就发,比较粗鲁,大手大脚,当OUT事务完成之后,host又强迫slave上发一个空数据包给host,完全不顾及slave的感受,因为slave不能有脾气,如果slave有了脾气,不去理会host的IN事务,host以强硬的姿态让slave上发一个空数据包,slave就是不发,那没办法,slave你是真的不想混了,host会不带商量的把你踢出去--windows提示:"非法usb设备",所以slave为了能糊口,在windows的一亩三分地混口饭吃,那就只能顺着该死的host的规则来行事了,所以IN事务也好和OUT事务,以及setup事务,这些都是给这样一个过程起的一个代号,毕竟我们要和其他人交流,总不能说,"就是那个东西,数据发出去等着回答的那个东西,我觉得它有问题",那交流起来多麻烦,要是有个这个定义之后,大家就可以这么说,"我对IN事务不理解,我对OUT事务理解了",简洁明了.以上是我对usb--IN、OUT事务的理解.
硬件理解
<1>对于PDIUSBD12,对于OUT事务,只有当pc驱动程序把此次数据通过管道全部发送到usb设备端点
--PDIUSBD12的0或2的数据缓冲区和ACK之后,PDIUSBD12才会产生中断,通知cpu,处理接收到的OUT事务数据,
而不是在PDIUSBD12收到OUT令牌后就立即中断cpu,PDIUSBD12会等到OUT令牌之后的数据阶段
(也可能需要等到ACK握手阶段完成之后[gliethttp])完成之后,才触发中断通知cpu处理PDIUSBD12接收到的
数据缓冲区中的数据;
对于控制端点0、端点1和端点2的IN事务,只要IN令牌一收到,PDIUSBD12就会立即触发中断,
0xF4中断状态寄存器的bit1-控制输入端点状态位-置位,
以示cpu这次中断由于控制端点IN令牌包的收到而发生的,
固件驱动组织数据,之后发送给host主机.(gliethttp)
这样就比较麻烦,因为当固件驱动处理数据的时候,PDIUSBD12将主动返回NAK给host主机,host主机收到NAK
之后,会按固定时间间隔定时的发送IN事务令牌包,如果不将cpu的整个中断系统停掉的话,至少不把
PDIUSBD12的第14脚INT_N连接的cpu中断禁止的话,就会以很高的频繁对cpu进行INT_N中断,
当然也可以不使用中断,而是不停检测PDIUSBD12的第14脚INT_N是否为0.
<2>对于at91rm9200来说,只有如下几种情况才会引发中断:
(1.RXSETUP:当前端点收到了setup令牌包触发中断
(2.RX_DATA_BK0:当前端点收到了数据包并且已经安全放到了FIFO段0触发中断
(3.RX_DATA_BK1:当前端点收到了数据包并且已经安全放到了FIFO段1触发中断
(4.TXCOMP:当前端点上发的数据已经被host主机通过IN事务读取,同时PID_ACK也正常收到,触发TXCOMP中断
(5.STALLSENT:当前端点被挂起禁用触发中断
所以对于at91rm9200第1次收到"读取设备描述符"setup事务指令之后会引发cpu中断,在中断里边
usbEp0.DisptachSetup = AT91F_USB_DispatchRequest;回调函数中,得知是STD_GET_DESCRIPTOR-标准获取设备描述符,
固件驱动会立即把"设备描述符"送入控制端点的IN缓冲区,这个时候host主机可能还正在处理其他事情,没有时间来处理,
不怕,放到IN缓冲区中的数据at91rm9200的usb控制器不会在host主机没有发送PID_IN令牌之前,就把数据主动送到总线
上丢失掉,而是等待host主机对当前端点发送PID_IN令牌的时候,老早之前放到IN缓冲区的数据才会传输出去,
另外的那种情况就是,host主机速度快的惊人,刚刚发送完"读取设备描述符"setup事务指令,就立即又发送了PID_IN令牌,
这时at91rm9200的usb控制器还没有把"设备描述符"完全送到IN缓冲区,此时at91rm9200的usb控制器会自动向host主机
发送PID_NAK,作为PID_IN的握手回应.经过几次PID_NAK并且在没有超过50ms的前提下,at91rm9200固件驱动已经把数据放
到了IN缓冲区,并且使IN缓冲区生效,那么host定时再次发送过来的PID_IN令牌将能正常收到PID_DATA令牌下的数据了.
这种方式比较好,在OUT事务中,固件驱动得知是否有上传数据,然后直接在OUT中断中,把上传数据送到IN数据缓冲区,
当host正常读取IN缓冲区的数据之后,会触发IN中断,在IN中断中,固件驱动再去检查IN数据是否发送完毕,如果没有就
继续发送下去,这就好像"钟摆小球试验",当你轻轻给它一个"外力"--OUT事物中激活IN,之后,"小球"--IN事物中断,就开始
永远的"摆动"起来--IN事物中断,又有点类似自激震荡(gliethttp).
<3>usb通信时,每一次数据的传送都严格遵守3个阶段
[1]令牌PID阶段
[2]数据PID阶段
[3]握手PID阶段
PID域
-----------------------
令牌有4种:
PID_setup令牌--PID域值0xB4,用来标示数据阶段的数据为setup事务数据
PID_out令牌 --PID域值0x87,用来标示数据阶段的数据为out事务数据
PID_in令牌 --PID域值0x96,用来标示数据阶段的数据为in事务数据
PID_sof令牌 --PID域值0xA5,用来标示帧标号开始
-----------------------
数据阶段:
PID_DATA0数据--PID域值0xC3,用来标示此数据为PID偶分组数据
PID_DATA1数据--PID域值0xD2,用来标示此数据为PID奇分组数据
-----------------------
握手阶段有3种:
PID_ACK --PID域值0x4B,用来标示usb接收器硬件收到无误的数据分组
PID_NAK --PID域值0x5A,用来标示接收端不能接收数据(可能正在处理上一次接收到的数据)
或者发送端不能发送数据(固件驱动程序正在把IN数据放到管道端点缓冲区中)
PID_STALL --PID域值0x78,用来标示当前端点被禁用了.(gliethttp)
<4>一个usb设备只能有1个设备描述符,可以有多个配置描述符,
每个配置描述符可以有多个接口描述符,每个接口描述符可以管理多个端点.