Languages

USB控制传输在EZ-USB(68013)总结

控制传输在交换少量控制信息的时候很有用,例如传送命令、获取下位机状态等,通过Cypress标准固件框架可以很方便的使用控制传输,仅需在DR_VendorCmnd中加入自己的处理过程即可。如:

BOOL DR_VendorCmnd(void) {
switch(SETUPDAT[1]) {
case VR_1: //自定义命令1
do_something();
break;
case VR_2: //自定义命令2
do_something_else();
break;
default:
return(TRUE);
}
return(FALSE); // no error; command handled OK
}

控制传输分为三个阶段。

1、SETUP阶段,通过SETUPDAT的8字节寄存器进行,

SETUPDAT[0] = bmRequestType
SETUPDAT[1] = bmRequest
SETUPDAT[2:3] = wValue
SETUPDAT[4:5] = wIndex
SETUPDAT[6:7] = wLength

传输我们自定义的命令时,bmRequestType不用处理;bmRequest为我们自定义的命令,上位机和下位机统一即可;wValue和wIndex我们可以自由使用,共4个字节;wLength为接下来的“数据阶段”的数据长度。

2、数据阶段。本阶段是可选的,如果我们传输的数据wValue和wIndex容纳不下,或者需要从设备读取信息,就需要通过数据阶段进行,数据阶段通过EP0BUF、EP0BCH、EP0BCL寄存器进行,数据阶段一次可以传输64字节的数据。

本阶段常常被错误的使用,特别是从上位机传输数据到下位机时,错误使用造成一个奇怪的现象:需要连续传输两次才能在缓冲区中得到正确的数据。以下是错误使用的例子:

case VR_1:
a = EP0BUF[0];
b = EP0BUF[1];
c = EP0BUF[2];
d = EP0BUF[3];
EP0BCH = 0;
SYNCDELAY;
EP0BCL = 0;
SYNCDELAY;
break;

这本意是从上位机得到一些数据,并且通过最后的EP0BCH = 0;EP0BCL = 0;告诉ez-usb已经读取完毕,但实际上,这会导致上述的问题。仔细看TRM,发现如下的一段话:

Some CONTROL transfers do not have a DATA stage. Therefore the 8051 code that processes the SETUP data should check the length field in the SETUP data (in the 8-byte buffer at SETUPDAT) and arm endpoint zero for the DATA phase (by loading IN0BC or OUT0BC) only if the length is non-zero.[1]

至此,传输两次才能得到正确数据的疑惑解开了——下行数据需要通过EP0BCH和EP0BCL告知ez-usb,以启动数据阶段!改为下述代码:

case VR_1:
//start data phase, see TRM 7.2
if (SETUPDAT[7] != 0 || SETUPDAT[6] != 0) {
EP0BCH = SETUPDAT[7];
SYNCDELAY;
EP0BCL = SETUPDAT[6];
SYNCDELAY;
while(EP0CS & bmEPBUSY);
}
a = EP0BUF[0];
b = EP0BUF[1];
c = EP0BUF[2];
d = EP0BUF[3];
break;

另外,无数据阶段的传输不需要在最后使用EP0BCH = 0; EP0BCL = 0来“告诉ez-usb已经读取完毕”。

3、状态阶段。通过EP0CS寄存器,告知上位机命令是否处理等信息。如果未处理,通过EZUSB_STALL_EP0()告知上位机;通过EP0CS |= bmHSNAK;发送ack进行确认。(此阶段不需我们处理,fw.c中已处理)

[1] EZ-USB TRM 7.2, P115