// Task Dispatcher
从下面的代码开始,才真正开始我们自己的USB事务处理:
while(TRUE) // //Main Loop 主循环,一看就是个死循环
{
// Poll User Device
TD_Poll(); 用户调度程序
TD_Poll,也就是用户调度程序,USB空闲时调用
// Check for pending SETUP
if(GotSUD) 等待SETUP令牌数据的到来
{
SetupCommand(); // Implement setup command
在SetupCommand()函数中处理控制传输的内容,不管是USB标准
设备请求还是其他非标准请求还是Vendor命令等,
在调用SetupCommand()前首先判断了一个变量GotSUD是否为“真”,
查询的GotSUD变量,是在bulkloop.c文件中被设置为“真”的:
Setup Data Available Interrupt Handler
void ISR_Sudav(void) interrupt 0
{
GotSUD = TRUE; // Set flag
EZUSB_IRQ_CLEAR();
USBIRQ = bmSUDAV; // Clear SUDAV IRQ
}
这是一个中断服务程序。这个中断是怎么触发的呢?首先弄明白以下几点:
【1】USB设备和主机之间传输一次数据,叫做一次"USB事务处理".
【2】一个完整的控制传输,包含设置阶段,数据阶段,状态阶段:
例如主机要把“0X01,0X02”传送到下位机,那么设置阶段是一次USB事务处理,
数据阶段是两次USB事务处理等等。
【3】一般地,每个"USB事务处理"包含三个阶段:令牌阶段、数据阶段、握手阶段。
【4】每个阶段传输的数据,USB根据类型不同,打成不同的包,叫“封包”。例如,
令牌阶段传送令牌封包,数据阶段传送数据封包等。
【5】SETUP寄存中的内容即是在一次“USB事务处理”的“设置阶段”传送的,
具体包含在数据阶段的数据封包中。
然后,具体调用SetupCommand()的过程为:
(a)在USB枚举阶段,主机需要读取描述符的内容,于是发送标准请求,
例如case SC_GET_DESCRIPTOR读取描述符,将该请求打包,启动“USB”事务处理。
(b)进入“USB事务处理”的设置阶段,其中,在数据阶段,USB core的端点0收到
封包中的数据,并将其装载到SETUPDAT寄存器(8字节),如果顺利完成,即握手ACK后,
则USB core会发出SUDAV中断请求,于是进入void ISR_Sudav(void) interrupt 0
这个中断服务程序进行处理,设置GotSUD标志为“真”。
SUDAV,即是SetUp Arrived,SetUp数据到达。
SUDAV,在USBIRQ中定义USBIRQ.0=SUDAV。当8字节的SetUp数据从端点0发送到SETUPDAT寄存器
后,USB core 将该位置1,清除中断请求。
GotSUD = FALSE; Clear SETUP flag 清SETUP标记
GotSUD是令牌包标志,准确的说是“令牌阶段数据到来”,什么是令牌包?
首先,USB一连串的数据传输、处理、响应等就叫做USB事务。
例如,上位机要读取一个描述符,那么就会触发一次USB事务。
一个完整的USB事务处理有三个阶段:令牌阶段,数据阶段,握手阶段。
每个阶段数据传输是有各种包组成的,例如令牌阶段:同步字段+令牌包+EOP构成。
USB主机启动事务处理,开始发送令牌包,这个时候假如说我们当前的USB设备地址号
为2(重枚举时分配的),而主机发送的地址号也为2,那么这个USB设备硬件会产生
中断,进入中断处理。也就是periph.c文件中的void ISR_Sudav(void)函数,
在这个中断处理中,设置 GotSUD标志为TRUE,表示收到令牌数据,要启动USB传输了。
然后我们固件中判断if(GotSUD),GotSUD为真,则执行SetupCommand()函数,
在这里处理控制传输,读取描述符,设置特性,处理Vendor命令等。
完毕后置GotSUD = FALSE;然后检查USB各种状态并处理?
}
// check for and handle suspend.
// NOTE: Idle mode stops the processor clock. There are only two
// ways out of idle mode, the WAKEUP pin, and detection of the USB
// resume state on the USB bus. The timers will stop and the
// processor will not wake up on any other interrupts.
如果USB进入了休眠状态,这里Sleep是USB休眠标志,同GotSUD一样,USB休眠后产生中断,
进入void ISR_Susp(void)函数处理,置sleep标志为TRUE。
只有两种方式可以唤醒 USB 设备,一是 WAKEUP 引脚,二是 USB 总线探测到 resume 状态
if (Sleep) //检查sleep休眠使能标志
{
if(TD_Suspend()) 挂起处理
{
TD_Suspend()说明:此函数是在设备进入挂起状态前调用的,开发者在其中加入适当的代码,
对设备的工作状态进行配置,可使设备处于低功耗状态并返回真值。
但是开发者可以改动TD_Suspend()的程序代码,使其返回为假,这样可以使FX2不进入挂起状态。
Sleep = FALSE; // Clear the "go to sleep" flag. Do it here to prevent any race condition between wakeup and the next sleep.
在此清除睡眠模式标志是为了避免在唤醒下一个睡眠模式之间有任何的执行状况发生
do
{
EZUSB_Susp(); Place processor in idle mode.置8051为空闲状态
}
while(!Rwuen && EZUSB_EXTWAKEUP()); 如果唤醒
// above. Must continue to go back into suspend if the host has disabled remote wakeup
// *and* the wakeup was caused by the external wakeup pin.
// 8051 activity will resume here due to USB bus or Wakeup# pin activity.
EZUSB_Resume(); 从空闲状态中恢复 // If source is the Wakeup# pin, signal the host to Resume.//从空闲状态中恢复
TD_Resume();
TD_Resume()说明:当外部要求重新启动时(如,外界产生Wakeup中断或者USB总线有传输活动发生),
设备就会通过调用此函数来对处理器进行重启,也就是TD_Suspend()函数的逆操作。此时,
设备在正常电源下重新启动?
}
}
}
}
BOOL HighSpeedCapable()
{
HighSpeedCapable() 检测是否为高速USB芯片
// this function determines if the chip is high-speed capable.
// FX2 and FX2LP are high-speed capable. FX1 is not - it does
// not have a high-speed transceiver.
if (GPCR2 & bmFULLSPEEDONLY)
GPCR2、bmFULLSPEEDONLY 在FX2regs.h 的定义如下:
// Feature Registers (FX2LP/FX1 only)
EXTERN xdata volatile BYTE GPCR2 _AT_ 0xE50D; // Chip Features
/* Chip Feature Register (GPCR2) */
#define bmFULLSPEEDONLY bmBIT4
return FALSE;
else
return TRUE;
}
// Device request parser
void SetupCommand(void)
{
//etupCommand(void) 设备请求剖析器
void *dscr_ptr;
switch(SETUPDAT[1])
/判断何种设备请求,8字节,参阅usb 协议
SETUPDAT[1]包含请求,bRequest
根据setupdat[1]进行分别处理
Setupdat寄存器(只读)中的定义如下
◆SETUPDAT[0] = bmRequestType,USB设备接口或端点的状态
◆SETUPDAT[1] = bmRequest,请求类型, 8字节,这里是USB 11个标准请求之一
◆SETUPDAT[2:3] = wValue, wValue字段,对bmRequest字段的补充
◆SETUPDAT[4:5] = wIndex
◆SETUPDAT[6:7] = wLength
传输我们自定义的命令时,bmRequestType不用处理;
bmRequest为我们自定义的命令,上位机和下位机统一即可;
wValue和wIndex我们可以自由使用,共4个字节;
wLength为接下来的“数据阶段”的数据长度。
详见 <<USB设备请求.doc>>
{
case SC_GET_DESCRIPTOR: 命令是取得描述符表
if(DR_GetDescriptor()) 如果取得描述符表成功
switch(SETUPDAT[3]) // //判断是何种描述符
{
case GD_DEVICE: 是设备描述符
SUDPTRH = MSB(pDeviceDscr); 设备描述符的高位
SUDPTRL = LSB(pDeviceDscr); 设备描述符的低位
SUDPTR是指向主机请求的描述符的首地址
break;
case GD_DEVICE_QUALIFIER: 是设备限定描述符 Device Qualifier
// only retuen a device qualifier if this is a high speed
// capable chip.
if (HighSpeedCapable()) 如果是高速设备
{
SUDPTRH = MSB(pDeviceQualDscr); 设备限定描述符的高位
SUDPTRL = LSB(pDeviceQualDscr); 设备限定描述符的低位
}
else
{
EZUSB_STALL_EP0(); 停滞在端点0
}
break;
case GD_CONFIGURATION: 是配置描述符
SUDPTRH = MSB(pConfigDscr); 配置描述符的高位
SUDPTRL = LSB(pConfigDscr); 配置描述符的低位
break;
case GD_OTHER_SPEED_CONFIGURATION: 是其他速率配置描述符
SUDPTRH = MSB(pOtherConfigDscr); 其他速率配置描述符的高位
SUDPTRL = LSB(pOtherConfigDscr); 其他速率配置描述符的低位
break;
case GD_STRING: 是字符串描述符String
if(dscr_ptr = (void *)EZUSB_GetStringDscr(SETUPDAT[2]))
{ 如果取得字符串成功
SUDPTRH = MSB(dscr_ptr); 字符串描述符的高位
SUDPTRL = LSB(dscr_ptr); 字符串描述符的低位
}
else 如果是其它未知类型的描述符
EZUSB_STALL_EP0(); 中止在端点0 Stall End Point 0
break;
default: 如果是其它未知类型的描述符 (无效描述符)ivalid request
EZUSB_STALL_EP0(); /中止端点0 Stall End Point 0
}
break;
case SC_GET_INTERFACE: 命令是获得接口*** Get Interface
DR_GetInterface(); 获取接口子函数
break;
case SC_SET_INTERFACE: 命令是设置接口*** Set Interface
DR_SetInterface(); 设置接口子函数
break;
case SC_SET_CONFIGURATION: 命令是设置配置*** Set Configuration
DR_SetConfiguration(); 设置配置子函数
break;
case SC_GET_CONFIGURATION: 命令是获得配置 *** Get Configuration
DR_GetConfiguration(); 获取配置的子函数
break;
case SC_GET_STATUS: 命令是获得状态 *** Get Status
if(DR_GetStatus()) 如果获得状态成功
switch(SETUPDAT[0]) 判断是何状态(设备、接口或端点状态)
{
case GS_DEVICE: 是设备状态
EP0BUF[0] = ((BYTE)Rwuen << 1) | (BYTE)Selfpwr;
EP0BUF[1] = 0;
EP0BCH = 0;
EP0BCL = 2; 长度为2
break;
case GS_INTERFACE: 接口状态
EP0BUF[0] = 0;
EP0BUF[1] = 0;
EP0BCH = 0;
EP0BCL = 2;
break;
case GS_ENDPOINT: 端点状态
EP0BUF[0] = *(BYTE xdata *) epcs(SETUPDAT[4]) & bmEPSTALL;
EP0BUF[1] = 0;
EP0BCH = 0;
EP0BCL = 2;
break;
default: 未知状态(无效命令Invalid Command )
EZUSB_STALL_EP0(); 停滞在端点0 Stall End Point 0
}
break;
case SC_CLEAR_FEATURE: 命令是清除特性*** Clear Feature
if(DR_ClearFeature()) 如果清除特性的命令成功
switch(SETUPDAT[0]) 配置是何种类型
{
case FT_DEVICE: 设备 Device
if(SETUPDAT[2] == 1)
Rwuen = FALSE; 禁止远程唤醒 Disable Remote Wakeup
else
EZUSB_STALL_EP0(); // Stall End Point 0
break;
case FT_ENDPOINT: 端点 End Point
if(SETUPDAT[2] == 0)
{
*(BYTE xdata *) epcs(SETUPDAT[4]) &= ~bmEPSTALL; //???//
EZUSB_RESET_DATA_TOGGLE( SETUPDAT[4] ); //???//
}
else
EZUSB_STALL_EP0(); // Stall End Point 0
break;
}
break;
case SC_SET_FEATURE: 命令是配置特性 *** Set Feature
if(DR_SetFeature()) 如果配置特性的命令成功
switch(SETUPDAT[0]) 配置是何种类型
{
case FT_DEVICE: 设备 Device
if(SETUPDAT[2] == 1) //???//
Rwuen = TRUE; 开启远程唤醒 Enable Remote Wakeup
else if(SETUPDAT[2] == 2) //???//
// Set Feature Test Mode. The core handles this request. However, it is
// necessary for the firmware to complete the handshake phase of the
// control transfer before the chip will enter test mode. It is also
// necessary for FX2 to be physically disconnected (D+ and D-)
// from the host before it will enter test mode.
//???//
break;
else
EZUSB_STALL_EP0(); // Stall End Point 0
break;
case FT_ENDPOINT: 端点 End Point
*(BYTE xdata *) epcs(SETUPDAT[4]) |= bmEPSTALL; //???//
break;
default:
EZUSB_STALL_EP0(); // Stall End Point 0
}
break;
default: 未知命令(无效命令 *** Invalid Command )
if(DR_VendorCmnd()) 如果是自定义命令
EZUSB_STALL_EP0(); // Stall End Point 0
}
// Acknowledge handshake phase of device request
EP0CS |= bmHSNAK; //通过EP0CS |= bmHSNAK;发送ack进行确认
}
//唤醒中断的处理 Wake-up interrupt handler
void resume_isr(void) interrupt WKUP_VECT
{
EZUSB_CLEAR_RSMIRQ(); //???//
}