USB1.1? USB2.0 淘宝那种逻辑分析仪好像很多都分析不了
USB 2.0是包括高速(High-speed 480 Mb/s)、全速(Full-speed 12 Mb/s)和低速(Low-speed 1.5 Mb/s)三种传输速度的,而USB 1.1只涵盖了后两种,也就是说后两种设备可以选择1.1和2.0两种标准予以实现。
要分析高速USB 2.0的话当然就不能这么搞了,首先要有足够的采样率,其次还要正确分析高速传输状态下的差分信号(±400 mV),没有相应PHY的话通常搞不来的。
USB1.1? USB2.0 淘宝那种逻辑分析仪好像很多都分析不了
淘宝山上的 Saleae16 可以分析 USB 1.1(USB 2.0 FullSpeed)
就是那种传输速率为12Mbps的协议.
]]>
EP1:~1000 KB/s
EP2:~500 KB/s
EP3:~1000 KB/s
EP4:~500 KB/s
所以,在此次测试中,对速度产生影响的主要因素还是在于搬移数据导致的ISR执行时间过长,从而每两个数据包会有一个返回NAK。单双缓冲则影响不大。
大家也可以自己用电脑测试一下结果。
接下来说一说一个很坑的点:UIS_TRANSFER和U_IS_NAK。
在datasheet中,只提到了UIS_TRANSFER表示传输完成,而U_IS_NAK表示接收到NAK。但是,众所周知,Host并不会发送NAK,所以这里应该是用设备发送NAK更为准确?另外,这个传输完成到底是什么意思?NAK和STALL算传输完成吗?无响应呢?
带着这些问题,我测试了一下各种搭配,结果令人震惊(夸张了哈):
先说U_IS_NAK,很明显是在发送NAK的时候会置1,这倒没啥好惊讶的。当然,要实现这个功能,就应该事先置bUIE_DEV_NAK为1。
比较令人困惑的是UIS_TRANSFER。在bUIE_DEV_NAK为0时,只有接收/发送ACK的时候置1,NAK和STALL没有反应(没有测试无响应,猜测和STALL一致);但是当bUIE_DEV_NAK为1时,如果接收/发送NAK,UIS_TRANSFER也会置1(STALL仍然没反应)。这不得不说是比较有趣的行为。
另外,在bUIE_DEV_NAK为1,且对应端点的bUEP_AUTO_TOG为1时,如果使用设备的接收功能(对应Bulk OUT)会出现奇怪的bug,表现是U_IS_NAK没有正确设置,且自动切换TOG的功能错误(反应就是U_TOG_OK的值错误),如下图所示。但是,只要上述两个flag的任意一个为0(不使能NAK中断,或是手动切换TOG),则功能又恢复正常。而且,这个bug并不影响Bulk IN,或是没有发送NAK的情形。这一点也是令人困惑。
鉴于以上原因,在使用CH558的USB功能时,我建议:
不要使用U_IS_NAK,也就是置bUIE_DEV_NAK为0。一般来说,对于NAK我们也不需要进行特殊的操作,而是让它重新传输一次就行了。
在不打开bUIE_DEV_NAK的时候,自动切换TOG还是好用的,当然手动切换也并不麻烦,只需要异或一下TOG就好了。
由于STALL信号不触发中断,因此对于Control Transfer而言,不能在Token为OUT(Host到Device方向)的时候设置Device为回复STALL,否则无法正确接收到下一个Setup包。正确的选择是,在需要表明Request Error的情况下,对于Control Read应当在Data Stage阶段回复STALL,而对于Control Write则只能在Status Stage阶段回复STALL。也就是说,只能令UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_STALL,而不能同时使用UEP_R_RES_STALL。
另外,在UEP0_CTRL的STALL设置优先级高于bUC_INT_BUSY发出的NAK,也就是说即使bUC_INT_BUSY为1,在ISR中仍然会优先发送STALL信号。由于STALL信号仅需发送一个周期(下一个周期应当恢复到Setup包),而Setup包本身的处理逻辑又十分复杂,在发送STALL表明Request Error之后(典型例子是全速设备的Device_Qualifier),如果接收到Setup包(对于SETUP Token正确回复ACK)并且按原有路径在ISR中处理Setup包,则会因为当前Setup包的处理时间过长,从而对于当前的Request错误地回复STALL(也就是说在ISR中已经经历了Setup和Data两个Stage),造成逻辑错误。对此,解决方案是在接收到Setup包时第一步先将UEP0_CTRL的STALL去掉(见case UIS_TOKEN_SETUP下一行),之后正常处理便是(bUC_INT_BUSY在此时会正确地回复NAK)。
]]>
首先,时钟和休眠之类的倒还好(除了一开始忘了SAFE_MOD这回事儿懵逼了半天)。为了支持USB Suspend,需要在WAKE_CTRL开启USB唤醒,不过可以不用在中断里面开启Suspend,在我的代码里面是直接在loop部分判断bUMS_SUSPEND了,这样的好处是保证在当前不活动的情况下再进入休眠模式,不会影响当前的工作。
USB SIE的重置是一个小坑。原本我是在USB接收到Reset中断时对SIE进行重置(USB_CTRL = bUC_RESET_SIE | bUC_CLR_ALL,当然还有相关的初始化),但是发现这样处理之后SIE就会进入无响应状态,可能是因为重置需要一定时间。将重置改成只在Initialize阶段进行就解决了。
写这个程序的目的呢,除了实现USB协议之外,其实也有探究CH55x的USB实现的想法。所以,这里设置了4套端点(IN和OUT都有),分别对应以下几种情况:
EP1 IN/OUT:双缓冲,ISR中无复制
EP2 IN/OUT:双缓冲,ISR中有复制(使用memcpy,下同)
EP3 IN/OUT:单缓冲,ISR中无复制
EP4 IN/OUT:单缓冲,ISR中有复制
首先说说双缓冲机制。很明显,这是为了提升传输速度用的:毕竟这种级别的单片机主频不太高且执行周期长,移动速度未必比USB FS本身的读写快多少,如果没有缓冲的话,复制就要占据大部分时间,从而在复制完成前没办法准备下一阶段的传输,这通常意味着下一次传输通常只能NAK。对于没有PING机制的全速设备而言,Bulk OUT尤其是问题(毕竟需要传输一整个Packet后才能答复NAK)。双缓冲机制可以通过允许提前准备下一阶段的传输,从而比较有效地解决这个问题。
那么,在单缓冲的情况下,要实现数据的搬移,就不得不和ISR扯上关系了——要么直接在ISR中复制数据,要么直接在ISR中置为NAK,等到正常执行的阶段准备好下一阶段的传输之后再置为ACK,而后者肯定比前者更慢。因此,EP1和EP4应该是最为常用的两种情况,而EP2和EP3则是作为对照组存在。
当然,如果ISR的处理速度够快(通常中断都是在本次传输结束后开始的,因此对应的是本次传输结束到下次传输开始,当然理论上下次传输的Token Phase时也是来得及操作的),其实还有一种方案:自己维护缓冲区(通常是环形缓冲),端点缓冲只使用单缓冲,并且在ISR中切换当前缓冲。下面的测试将会看到,即使是CH558这种级别的单片机,也是完全来得及的。
#include "../CH558.H"
#include <string.h>
#define bMaxPacketSize0 64
#define wMaxPacketSize 64
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define HI(x) ((x) >> 8)
#define LO(x) ((x) & 0xFF)
#define WBVAL(x) LO(x), HI(x)
#define UIS_EP_IN(x) (UIS_TOKEN_IN | (x))
#define UIS_EP_OUT(x) (UIS_TOKEN_OUT | (x))
#define EP_IN(x) (USB_ENDP_DIR_MASK | (x))
#define EP_OUT(x) (x)
UINT8C DeviceDescriptor[] = {
18, // bLength
1, // bDescriptorType, DEVICE
WBVAL(0x0200), // bcdUSB
0x00, // bDeviceClass
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
bMaxPacketSize0, // bMaxPacketSize0
WBVAL(0x04B4), // idVendor
WBVAL(0x1003), // idProduct
WBVAL(0x0000), // bcdDevice
1, // iManufacturer
2, // iProduct
0, // iSerialNumber
1 // bNumConfigurations
};
UINT8C ConfigurationDescriptor[] = {
9, // bLength
2, // bDescriptorType, CONFIGURATION
WBVAL(74), // wTotalLength
1, // bNumInterfaces
1, // bConfigurationValue
0, // iConfiguration
0x80, // bmAttributes
250, // bMaxPower
// Interface 0
9, // bLength
4, // bDescriptorType, INTERFACE
0, // bInterfaceNumber
0, // bAlternateSetting
8, // bNumEndpoints
0xFF, // bInterfaceClass
0x00, // bInterfaceSubClass
0x00, // bInterfaceProtocol
0, // iInterface
// Endpoint 1 IN
7, // bLength
5, // bDescriptorType, ENDPOINT
0x81, // bEndpointAddress
0x02, // bmAttributes
WBVAL(wMaxPacketSize), // wMaxPacketSize
0, // bInterval
// Endpoint 1 OUT
7, // bLength
5, // bDescriptorType, ENDPOINT
0x01, // bEndpointAddress
0x02, // bmAttributes
WBVAL(wMaxPacketSize), // wMaxPacketSize
0, // bInterval
// Endpoint 2 IN
7, // bLength
5, // bDescriptorType, ENDPOINT
0x82, // bEndpointAddress
0x02, // bmAttributes
WBVAL(wMaxPacketSize), // wMaxPacketSize
0, // bInterval
// Endpoint 2 OUT
7, // bLength
5, // bDescriptorType, ENDPOINT
0x02, // bEndpointAddress
0x02, // bmAttributes
WBVAL(wMaxPacketSize), // wMaxPacketSize
0, // bInterval
// Endpoint 3 IN
7, // bLength
5, // bDescriptorType, ENDPOINT
0x83, // bEndpointAddress
0x02, // bmAttributes
WBVAL(wMaxPacketSize), // wMaxPacketSize
0, // bInterval
// Endpoint 3 OUT
7, // bLength
5, // bDescriptorType, ENDPOINT
0x03, // bEndpointAddress
0x02, // bmAttributes
WBVAL(wMaxPacketSize), // wMaxPacketSize
0, // bInterval
// Endpoint 4 IN
7, // bLength
5, // bDescriptorType, ENDPOINT
0x84, // bEndpointAddress
0x02, // bmAttributes
WBVAL(wMaxPacketSize), // wMaxPacketSize
0, // bInterval
// Endpoint 4 OUT
7, // bLength
5, // bDescriptorType, ENDPOINT
0x04, // bEndpointAddress
0x02, // bmAttributes
WBVAL(wMaxPacketSize), // wMaxPacketSize
0 // bInterval
};
UINT8C LanguageString[] = {
4, // bLength
3, // bDescriptorType, STRING
WBVAL(0x0409)
};
UINT8C ManufacturerString[] = {
9, // bLength
3, // bDescriptorType, STRING
'C', 'y', 'p', 'r', 'e', 's', 's'
};
UINT8C ProductString[] = {
11, // bLength
3, // bDescriptorType, STRING
'C', 'Y', '-', 'S', 't', 'r', 'e', 'a', 'm'
};
PUINT8C StringDescriptors[] = {
LanguageString,
ManufacturerString,
ProductString
};
#define STR_DESC_NUM (sizeof(StringDescriptors) / sizeof(PUINT8C))
static UINT8XV EP0Buf[64] _at_ 0x0;
static UINT8XV EP4Buf[2][64] _at_ 0x40;
static UINT8XV EP1Buf[4][64] _at_ 0x100;
static UINT8XV EP2Buf[4][64] _at_ 0x200;
static UINT8XV EP3Buf[2][64] _at_ 0x300;
static UINT8XV unused[64] _at_ 0x400;
struct {
UINT8 req;
UINT16 len;
PUINT8C desc;
} data setup;
#define SetupPacket ((const USB_SETUP_REQ xdata *)EP0Buf)
#define GET_CONFIGURED() (USB_DEV_AD & bUDA_GP_BIT)
#define SET_CONFIGURED() (USB_DEV_AD |= bUDA_GP_BIT)
#define CLR_CONFIGURED() (USB_DEV_AD &= ~bUDA_GP_BIT)
void Clock_Initialize(void)
{
SAFE_MOD = 0x55;
SAFE_MOD = 0xAA;
PLL_CFG = 7 << 5 | 28 << 0;
CLOCK_CFG = bOSC_EN_INT | 6 << 0;
SLEEP_CTRL = bSLP_OFF_USB | bSLP_OFF_ADC | bSLP_OFF_UART1 | bSLP_OFF_SPI0 | bSLP_OFF_TMR3 | bSLP_OFF_LED;
++SAFE_MOD;
}
void Port_Initialize(void)
{
PORT_CFG &= ~bP2_OC;
TNOW = 0;
P2_PU &= ~bTNOW;
P2_DIR |= bTNOW;
}
void USBD_Initialize(void)
{
TNOW = 1;
SAFE_MOD = 0x55;
SAFE_MOD = 0xAA;
SLEEP_CTRL &= ~bSLP_OFF_USB;
WAKE_CTRL |= bWAK_BY_USB;
++SAFE_MOD;
USB_CTRL = bUC_RESET_SIE | bUC_CLR_ALL;
USB_CTRL = bUC_DEV_PU_EN | bUC_INT_BUSY | bUC_DMA_EN;
UDEV_CTRL = bUD_DP_PD_DIS | bUD_DM_PD_DIS | bUD_PORT_EN;
UEP0_DMA = (UINT16)EP0Buf;
UEP1_DMA = (UINT16)EP1Buf;
UEP2_DMA = (UINT16)EP2Buf;
UEP3_DMA = (UINT16)EP3Buf;
// UEP4_DMA = UEP0_DMA + 64
UEP4_1_MOD = bUEP1_RX_EN | bUEP1_TX_EN | bUEP1_BUF_MOD | bUEP4_RX_EN | bUEP4_TX_EN;
UEP2_3_MOD = bUEP3_RX_EN | bUEP3_TX_EN | bUEP2_RX_EN | bUEP2_TX_EN | bUEP2_BUF_MOD;
USB_INT_EN = bUIE_TRANSFER | bUIE_BUS_RST;
USB_INT_FG = 0xFF;
IE_USB = 1;
TNOW = 0;
}
void USBD_ISR(void) interrupt INT_NO_USB using 1
{
TNOW = 1;
if (UIF_TRANSFER) {
if (U_TOG_OK)
switch (USB_INT_ST & (MASK_UIS_TOKEN | MASK_UIS_ENDP)) {
case UIS_EP_IN(1):
break;
case UIS_EP_OUT(1):
break;
case UIS_EP_IN(2):
memcpy((PUINT8XV)USB_DMA, unused, wMaxPacketSize);
break;
case UIS_EP_OUT(2):
memcpy(unused, (PUINT8XV)USB_DMA, USB_RX_LEN);
break;
case UIS_EP_IN(3):
break;
case UIS_EP_OUT(3):
break;
case UIS_EP_IN(4):
memcpy((PUINT8XV)USB_DMA, unused, wMaxPacketSize);
UEP4_CTRL ^= bUEP_T_TOG;
break;
case UIS_EP_OUT(4):
memcpy(unused, (PUINT8XV)USB_DMA, USB_RX_LEN);
UEP4_CTRL ^= bUEP_R_TOG;
break;
case UIS_EP_IN(0):
if ((setup.req & 0x80) == USB_REQ_TYP_IN) {
if ((setup.req & 0x7F) == USB_GET_DESCRIPTOR) {
UEP0_T_LEN = (UINT8)MIN(setup.len, bMaxPacketSize0);
memcpy(EP0Buf, setup.desc, UEP0_T_LEN);
setup.desc += UEP0_T_LEN;
setup.len -= UEP0_T_LEN;
UEP0_CTRL ^= bUEP_T_TOG;
}
} else {
// Status Stage finished
if ((setup.req & 0x7F) == USB_SET_ADDRESS)
USB_DEV_AD = *(UINT8D *)&setup.len;
setup.req = 0xFF;
UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
}
break;
case UIS_EP_OUT(0):
if ((setup.req & 0x80) == USB_REQ_TYP_OUT) {
// Not possible unless SET_DESCRIPTOR
UEP0_CTRL ^= bUEP_R_TOG;
} else {
// Status Stage finished
setup.req = 0xFF;
UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
}
break;
case UIS_TOKEN_SETUP:
// Avoid STALL because of slow Setup Packet handling
UEP0_CTRL = UEP0_CTRL & ~MASK_UEP_T_RES | UEP_T_RES_NAK;
setup.req = SetupPacket->bRequestType & 0x80 | SetupPacket->bRequest;
switch (SetupPacket->bRequest) {
case USB_GET_STATUS:
if ((SetupPacket->bRequestType & USB_REQ_RECIP_MASK) == USB_REQ_RECIP_DEVICE ||
(SetupPacket->bRequestType & USB_REQ_RECIP_MASK) == USB_REQ_RECIP_INTERF && SetupPacket->wIndexL == 0) {
EP0Buf[0] = 0x0;
} else if ((SetupPacket->bRequestType & USB_REQ_RECIP_MASK) == USB_REQ_RECIP_ENDP) {
if (!GET_CONFIGURED()) {
if (SetupPacket->wIndexL == 0)
EP0Buf[0] = 0x0;
else
goto REQUEST_ERROR;
} else {
switch (SetupPacket->wIndexL) {
case EP_IN(0):
case EP_OUT(0):
EP0Buf[0] = 0x0;
break;
case EP_IN(1):
EP0Buf[0] = !!((UEP1_CTRL & MASK_UEP_T_RES) == UEP_T_RES_STALL);
break;
case EP_OUT(1):
EP0Buf[0] = !!((UEP1_CTRL & MASK_UEP_R_RES) == UEP_R_RES_STALL);
break;
case EP_IN(2):
EP0Buf[0] = !!((UEP2_CTRL & MASK_UEP_T_RES) == UEP_T_RES_STALL);
break;
case EP_OUT(2):
EP0Buf[0] = !!((UEP2_CTRL & MASK_UEP_R_RES) == UEP_R_RES_STALL);
break;
case EP_IN(3):
EP0Buf[0] = !!((UEP3_CTRL & MASK_UEP_T_RES) == UEP_T_RES_STALL);
break;
case EP_OUT(3):
EP0Buf[0] = !!((UEP3_CTRL & MASK_UEP_R_RES) == UEP_R_RES_STALL);
break;
case EP_IN(4):
EP0Buf[0] = !!((UEP4_CTRL & MASK_UEP_T_RES) == UEP_T_RES_STALL);
break;
case EP_OUT(4):
EP0Buf[0] = !!((UEP4_CTRL & MASK_UEP_R_RES) == UEP_R_RES_STALL);
break;
default:
goto REQUEST_ERROR;
}
}
} else {
goto REQUEST_ERROR;
}
EP0Buf[1] = 0x0;
UEP0_T_LEN = 2;
UEP0_CTRL = bUEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_ACK;
break;
case USB_CLEAR_FEATURE:
switch (SetupPacket->wValueL) {
case 0: // ENDPOINT_HALT
if (!GET_CONFIGURED())
goto REQUEST_ERROR;
switch (SetupPacket->wIndexL) {
case EP_IN(1):
UEP1_CTRL = UEP1_CTRL & ~(MASK_UEP_T_RES | bUEP_T_TOG) | UEP_T_RES_ACK;
break;
case EP_OUT(1):
UEP1_CTRL = UEP1_CTRL & ~(MASK_UEP_R_RES | bUEP_R_TOG) | UEP_R_RES_ACK;
break;
case EP_IN(2):
UEP2_CTRL = UEP2_CTRL & ~(MASK_UEP_T_RES | bUEP_T_TOG) | UEP_T_RES_ACK;
break;
case EP_OUT(2):
UEP2_CTRL = UEP2_CTRL & ~(MASK_UEP_R_RES | bUEP_R_TOG) | UEP_R_RES_ACK;
break;
case EP_IN(3):
UEP3_CTRL = UEP3_CTRL & ~(MASK_UEP_T_RES | bUEP_T_TOG) | UEP_T_RES_ACK;
break;
case EP_OUT(3):
UEP3_CTRL = UEP3_CTRL & ~(MASK_UEP_R_RES | bUEP_R_TOG) | UEP_R_RES_ACK;
break;
case EP_IN(4):
UEP4_CTRL = UEP4_CTRL & ~(MASK_UEP_T_RES | bUEP_T_TOG) | UEP_T_RES_ACK;
break;
case EP_OUT(4):
UEP4_CTRL = UEP4_CTRL & ~(MASK_UEP_R_RES | bUEP_R_TOG) | UEP_R_RES_ACK;
break;
default:
goto REQUEST_ERROR;
}
UEP0_T_LEN = 0;
UEP0_CTRL = bUEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_ACK;
break;
case 1: // DEVICE_REMOTE_WAKEUP
case 2: // TEST_MODE
default:
goto REQUEST_ERROR;
}
break;
case USB_SET_FEATURE:
switch (SetupPacket->wValueL) {
case 0: // ENDPOINT_HALT
if (!GET_CONFIGURED())
goto REQUEST_ERROR;
switch (SetupPacket->wIndexL) {
case EP_IN(1):
UEP1_CTRL = UEP1_CTRL & ~MASK_UEP_T_RES | UEP_T_RES_STALL;
break;
case EP_OUT(1):
UEP1_CTRL = UEP1_CTRL & ~MASK_UEP_R_RES | UEP_R_RES_STALL;
break;
case EP_IN(2):
UEP2_CTRL = UEP2_CTRL & ~MASK_UEP_T_RES | UEP_T_RES_STALL;
break;
case EP_OUT(2):
UEP2_CTRL = UEP2_CTRL & ~MASK_UEP_R_RES | UEP_R_RES_STALL;
break;
case EP_IN(3):
UEP3_CTRL = UEP3_CTRL & ~MASK_UEP_T_RES | UEP_T_RES_STALL;
break;
case EP_OUT(3):
UEP3_CTRL = UEP3_CTRL & ~MASK_UEP_R_RES | UEP_R_RES_STALL;
break;
case EP_IN(4):
UEP4_CTRL = UEP4_CTRL & ~MASK_UEP_T_RES | UEP_T_RES_STALL;
break;
case EP_OUT(4):
UEP4_CTRL = UEP4_CTRL & ~MASK_UEP_R_RES | UEP_R_RES_STALL;
break;
default:
goto REQUEST_ERROR;
}
UEP0_T_LEN = 0;
UEP0_CTRL = bUEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_ACK;
break;
case 1: // DEVICE_REMOTE_WAKEUP
case 2: // TEST_MODE
default:
goto REQUEST_ERROR;
}
break;
case USB_SET_ADDRESS:
// USB Address should be changed after handshake packet, so temporarily kept in len
*(UINT8D *)&setup.len = SetupPacket->wValueL;
UEP0_T_LEN = 0;
UEP0_CTRL = bUEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_ACK;
break;
case USB_GET_DESCRIPTOR:
setup.len = (UINT16)SetupPacket->wLengthL << 0 | (UINT16)SetupPacket->wLengthH << 8;
switch (SetupPacket->wValueH) {
case USB_DESCR_TYP_DEVICE:
if (SetupPacket->wValueL > 0)
goto REQUEST_ERROR;
setup.desc = DeviceDescriptor;
if (setup.desc[0] < setup.len)
setup.len = setup.desc[0];
break;
case USB_DESCR_TYP_CONFIG:
if (SetupPacket->wValueL > 0)
goto REQUEST_ERROR;
setup.desc = ConfigurationDescriptor;
// Assuming wTotalLength < 256
if (setup.desc[2] < setup.len)
setup.len = setup.desc[2];
break;
case USB_DESCR_TYP_STRING:
if (SetupPacket->wValueL >= STR_DESC_NUM)
goto REQUEST_ERROR;
setup.desc = StringDescriptors[SetupPacket->wValueL];
if (setup.desc[0] < setup.len)
setup.len = setup.desc[0];
break;
case USB_DESCR_TYP_INTERF:
case USB_DESCR_TYP_ENDP:
case USB_DESCR_TYP_QUALIF:
case USB_DESCR_TYP_SPEED:
default:
goto REQUEST_ERROR;
}
UEP0_T_LEN = (UINT8)MIN(setup.len, bMaxPacketSize0);
memcpy(EP0Buf, setup.desc, UEP0_T_LEN);
setup.desc += UEP0_T_LEN;
setup.len -= UEP0_T_LEN;
UEP0_CTRL = bUEP_R_TOG | bUEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_ACK;
break;
case USB_GET_CONFIGURATION:
EP0Buf[0] = !!GET_CONFIGURED();
UEP0_T_LEN = 1;
UEP0_CTRL = bUEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_ACK;
break;
case USB_SET_CONFIGURATION:
switch (SetupPacket->wValueL) {
case 0:
CLR_CONFIGURED();
UEP1_CTRL = bUEP_AUTO_TOG | UEP_R_RES_STALL | UEP_T_RES_STALL;
UEP2_CTRL = bUEP_AUTO_TOG | UEP_R_RES_STALL | UEP_T_RES_STALL;
UEP3_CTRL = bUEP_AUTO_TOG | UEP_R_RES_STALL | UEP_T_RES_STALL;
UEP4_CTRL = UEP_R_RES_STALL | UEP_T_RES_STALL;
break;
case 1:
SET_CONFIGURED();
UEP1_T_LEN = wMaxPacketSize;
UEP1_CTRL = bUEP_AUTO_TOG | UEP_R_RES_ACK | UEP_T_RES_ACK;
UEP2_T_LEN = wMaxPacketSize;
UEP2_CTRL = bUEP_AUTO_TOG | UEP_R_RES_ACK | UEP_T_RES_ACK;
UEP3_T_LEN = wMaxPacketSize;
UEP3_CTRL = bUEP_AUTO_TOG | UEP_R_RES_ACK | UEP_T_RES_ACK;
UEP4_T_LEN = wMaxPacketSize;
UEP4_CTRL = UEP_R_RES_ACK | UEP_T_RES_ACK;
break;
default:
goto REQUEST_ERROR;
}
UEP0_T_LEN = 0;
UEP0_CTRL = bUEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_ACK;
break;
case USB_GET_INTERFACE:
if (!GET_CONFIGURED() || SetupPacket->wIndexL != 0)
goto REQUEST_ERROR;
EP0Buf[0] = 0;
UEP0_T_LEN = 1;
UEP0_CTRL = bUEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_ACK;
break;
case USB_SET_DESCRIPTOR:
// Unsupported, STALL at Status Stage
UEP0_CTRL = bUEP_R_TOG | UEP_R_RES_ACK | UEP_T_RES_STALL;
break;
case USB_SET_INTERFACE: // STALL to use default alt settings
case USB_SYNCH_FRAME:
default:
REQUEST_ERROR:
UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_STALL;
}
}
}
if (UIF_BUS_RST) {
UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
UEP1_CTRL = bUEP_AUTO_TOG | UEP_R_RES_STALL | UEP_T_RES_STALL;
UEP2_CTRL = bUEP_AUTO_TOG | UEP_R_RES_STALL | UEP_T_RES_STALL;
UEP3_CTRL = bUEP_AUTO_TOG | UEP_R_RES_STALL | UEP_T_RES_STALL;
UEP4_CTRL = UEP_R_RES_STALL | UEP_T_RES_STALL;
USB_DEV_AD = 0x00;
}
USB_INT_FG = 0xFF;
TNOW = 0;
}
void main(void)
{
Clock_Initialize();
Port_Initialize();
USBD_Initialize();
EA = 1;
for (;;) {
if (USB_MIS_ST & bUMS_SUSPEND)
PCON |= PD;
}
}