Cypress固件架构彻底解析及USB枚举

dscr51里放的是USB描述符表,EZ-USB在重枚举阶段会读取或设置相应的描述符:

db    DSCR_DEVICE_LEN          ;; Descriptor length

db    DSCR_DEVICE                  ;; Decriptor type

dw   0002H                                 ;; Specification Version (BCD)

db    00H                                    ;; Device class

db    00H                                    ;; Device sub-class

db    00H                                    ;; Device sub-sub-class

db    64                                       ;; Maximum packet size

dw   0B404H                              ;; Vendor ID

dw   0410H                                 ;; Product ID (Sample Device)

dw   0000H                                 ;; Product version ID

db    1                                        ;; Manufacturer string index

db    2                                        ;; Product string index

db    0                                        ;; Serial number string index

db    1                                        ;; Number of configurations

【1】db  DSCR_DEVICE_LEN――bLength段

指明整个设备描述符的长度,单位字节。

【2】db  DSCR_DEVICE――bDescriporType段

描述符类型值。DSCR_DEVICE=04H--设备描述符。

【3】dw 0002H――bcdUSB

表明该USB设备所遵循的USB协议版本,用bcd码表示,2字节。例如2.0版本,值为0200H,用bcd码表示,低字节在前,高字节在后,表示为0002H;同理,1.1版本,则表示为1001H。

【4】db  00H――bDeviceClass段

指明USB设备所属的设备类。

=0,表示USB各接口相互独立工作,分属不通的设备类,具体信息在接口描述符中说明;

=1~FEH,表明该USB设备属于某个明确的设备类,例如04H代表显示设备;

=FFH,厂商自定义的设备类。

【5】db  00H――bDeviceSubClass段

指明USB设备所述的设备子类。其值依赖bDeviceClass。

=0,此时bDeviceClass必须首先为0;

=1~FEH,详细的设备子类;

例如如果bDeviceClass=04H,是显示设备,则bDeviceSubClass=01H,表示CRT显示器;

=FFH,厂家自定义。

【6】db  00H――bDevicePortcol段

指明USB所使用的设备类协议。其值依赖bDeviceClass和bDeviceSubClass。

=0,表示该设备不使用任何设备类协议;

=1~FEH,则该USB必须属于某个明确的设备类和子设备类。如视频类协议(UVC),音频类协议(UAC)等;

=FFH,厂家自定义。

【7】db  64――bMaxPacketSize0段

指明该USB设备端点0控制传输所支持的最大数据包长度,单位字节。

【8】dw 0B404H――VID

【9】dw 0410H――PID

【10】dw 0000H――bcdDevice段

指明USB设备版本号。

【11】db 1――iManuFacture段

厂商信息字符串索引值,没有时为0。这里为1,即下面的“Cypress”字符串。

【12】db 2――iProduct段

产品信息字符串索引值,没有时为0。后面的“EZ-USB”字符串。

【13】db 0――iSerial段

USB设备序列号信息字符串索引值,没有时为0。

【14】db 1――bNumConfigurations段

指明USB设备所支持的配置数。如果USB设备支持两种传输速率,则该字段指出的是该速率下的配置数,而不是两种速率下的配置数和。

 

设备限定描述符:DeviceQualDscr

DeviceQualDscr:

db    DSCR_DEVQUAL_LEN      ;; Descriptor length

db    DSCR_DEVQUAL               ;; Decriptor type

dw   0002H                                 ;; Specification Version (BCD)

db    00H                                    ;; Device class

db    00H                                    ;; Device sub-class

db    00H                                    ;; Device sub-sub-class

db    64                                       ;; Maximum packet size

db    1                                        ;; Number of configurations

db    0                                        ;; Reserved

设备限定描述符,9个字段,共10字节。仅当该USB为高速USB设备,且设备既需支持高速(High Speed)又需支持全速(Full)时,就需要用到设备限定描述符。例如该高速USB设备目前工作于全速模式,则该描述符中包含高速模式的总体信息。

在设备请求处理函数SetupCommand(void)中,当收到读设备限定描述符请求时,会首先判断是否为高速USB设备。

【1】db  DSCR_DEVQUAL_LEN――bLength段

整个设备限定描述符的长度,单位字节,共10个字节。

【2】dbDSCR_DEVQUAL――bDescriptorType段

指出该描述符类型。06H->设备限定描述符。

【3】dw 0002H ――bcdUSB段

USB协议版本号。

【4】db  00H――bDeviceClass段

该USB设备所属的USB设备类。

【5】db  00H――bDeviceSubClass段

所属子类。对bDeviceClass的进一步细化分类说明。

【6】db  00H――bDeviceProtocol段

该设备所使用的设备类协议。

【7】db  64――bMaxPacketSize0段

端点0控制传输所支持的最大数据包长度,单位字节。

【8】db  1――bNumConfigurations段

另一速率所支持的配置数。

【9】db  0――bReserved段

保留,必须为0。

 

配置描述符:HighSpeedConfigDscr/FullSpeedConfigDscr

db    DSCR_CONFIG_LEN          ;; Descriptor length

db    DSCR_CONFIG                  ;; Descriptor type

db    (HighSpeedConfigDscrEnd-HighSpeedConfigDscr) mod 256

;; Total Length (LSB)

db    (HighSpeedConfigDscrEnd-HighSpeedConfigDscr)/256

;; Total Length (MSB)

db    1                                        ;; Number of interfaces

db    1                                        ;; Configuration number

db    0                                        ;; Configuration string

db    10000000b                          ;; Attributes (b7 - buspwr,b6 - selfpwr,b5 - rwu)

db    100                                     ;; Power requirement (div 2 ma)

配置描述符包含8个字段,共9字节。所有的USB设备至少包含一个配置描述符,例如这里包含两个配置描述符高速HighSpeedConfigDscr和全速FullSpeedConfigDscr

【1】db  DSCR_CONFIG_LEN――bLength段

描述符长度,9字节。

【2】db  DSCR_CONFIG――bDescriptorType段

描述符类型。

【3】db  (HighSpeedConfigDscrEnd-HighSpeedConfigDscr) mod 256

db    (HighSpeedConfigDscrEnd-HighSpeedConfigDscr) / 256――wTotalLength段

指明配置信息总长度,2字节表示。为配置描述符,接口描述符,端点描述符,设备类定义描述符,供应商自定义描述符长度的和。在这里只有配置、接口和端点描述符。

【4】db  1――bNumInterface段

指明该配置所支持的接口数,最小为1。

【5】db  1――bConfigurationValue段

指明该配置的配置值。例如这里值为1,在重枚举时,主机发送Setconfiguration(x),当x1时,就调用该配置。

【6】db  00――iConfiguration段

该配置的字符串索引值,没有时为0。

【7】db  10000000b――bmAttributes段

指明该配置的特性,8位。

b0~b4,保留,必须为0;

b5:远程唤醒选择。=1,支持远程唤醒;=0,不支持远程唤醒;

b6:是否总线电源选择。如果该USB设备外加了电源,=1,使用总线电源;=0,使用自供的电源;

b7:必须为1。

在主机设备请求case SC_GET_STATUS,case GS_DEVICE中获得该信息。

【8】db  100――bMaxPower段

总线供电时,该USB设备可获得的最大电流。单位2mA,所以最大值为250。如果该电流得不到满足,USB将不能使用这个配置。

 

接口描述符:

;; Interface Descriptor

db    DSCR_INTRFC_LEN           ;; Descriptor length

db    DSCR_INTRFC                   ;; Descriptor type

db    0                                        ;; Zero-based index of this interface

db    0                                        ;; Alternate setting

db    6                                        ;; Number of end points

db    0ffH                                          ;; Interface class

db    00H                                    ;; Interface sub class

db    00H                                    ;; Interface sub sub class

db    0                                        ;; Interface descriptor string index

接口描述符有9个字段,共9字节。注意,主机不能用SetDescriptionGetDescription来设置和读取接口描述符,它只能作为配置描述符的一部分信息返回,在主机发送case SC_GET_DESCRIPTOR,且case GD_CONFIGURATION时一并读取。所以我们看到,在fw.c文件中并没有对接口描述符的判断。

【1】db  DSCR_INTRFC_LEN――bLength段

描述符长度。

【2】db  DSCR_INTRFC――bDescriptorType段

描述符类型。

【3】db  0――bInterfaceNumber段

指明该接口的接口号。

【4】db  0――bAlternateSetting段

指明接口的可替换设置值。

【5】db  6――bNumberEndpoints段

指明接口所使用的断点数,不包括端点0

【6】db  0ffH――bInterfaceClass段

指明接口所使用的设备类。

=0,保留;

=1~FEH,表明该接口属于某个明确的USB设备类;

=FFH,厂家自定义的设备类。

【7】db  00H――bInterfaceSubClass段

该接口所属的USB设备子类。

【8】db  00H――bInterfaceProtocol段

该接口所使用的设备类协议。

【9】db  0――iInterface段

接口字符串描述符的索引值,没有时为0。

 

;; Endpoint Descriptor

db    DSCR_ENDPNT_LEN         ;; Descriptor length

db       DSCR_ENDPNT                  ;; Descriptor type

db    02H                                    ;; Endpoint number, and direction

db    ET_BULK                           ;; Endpoint type

db    00H                                    ;; Maximun packet size (LSB)

db    02H                                    ;; Max packect size (MSB)

db    00H                                    ;; Polling interval

端点描述符有6个字段,共7字节。和接口描述符一样,也不能由主机通过发送GetDedcription()请求读取,只能作为配置信息case GD_CONFIGURATION的一部分返回给主机。

【1】db  DSCR_ENDPNT_LEN――bLength段

该描述符长度,单位字节。

【2】db  DSCR_ENDPNT――bDescriptorType段

该描述符类型。

【3】db  02H――bEndpointAddress段

指明端点的端点号及传输方向。

b0~b3:该端点的端点号。如0001端点1,0010端点2;

b4~b6:保留,必须为0;

b7:端点传输方向。1-IN传输;0-OUT传输。

【4】db  ET_BULK――bmAttributes段

指明端点的一些特性。

b0~b1:端点的传输类型。00-控制传输,01-同步传输,10-块传输,11-中断传输。

b2~b3:当该端点为同步端点时,这两位指出同步类型。00-非同步,01-异步,10-自适应,11-同步。

b4~b5:端点用法类型。00-数据端点,01-显示反馈端点,10-隐式反馈端点,11-保留。

b6~b7:保留,必须为0。

【5】db  00H――wMaxpacketSize段(LSB)

指明端点所支持的最大数据包长度,共16位。

b0~b10:端点所支持的最大数据包长度。

b11~b12:当该端点为高速中断端点或同步端点时,这两位指出每小帧中最多传输的事务数。00-每小帧1次(默认),01-每小帧2次(附加一次),10-每小帧3次(附加2次),11-保留。

b13~b15:保留,必须为0。

【5】db  02H――wMaxpacketSize段(MSB)

高8位。数据包大小为:0000 0010 0000 0000,取0~10位,还是0200,512字节。

【6】db  00H――bInterval段

指明端点数据传输的访问间隔。

低速中断端点:=10~255ms;

全速中断端点:=1~255ms;

高速中断端点:=1~16,访问间隔为2(bInterval-1)(幂)×1us;

全速/高速同步端点:=1~16,访问间隔为2(bInterval-1)(幂)×1ms 和2(bInterval-1)(幂)×1us;

高速块/控制out端点:指明其最大NAK握手包发送速率。=0,表示该端点永远不会发出NAK握手包;=其他值,表示每个bInterval时间内,该端点最多只能发送一次NAK握手包。

其他类型端点:该字段无效。

 

字符串描述符:StringDscr1:

StringDscr1:

db    StringDscr1End-StringDscr1 ;; String descriptor length

db    DSCR_STRING

db    'C',00

db    'y',00

db    'p',00

db    'r',00

db    'e',00

db    's',00

db    's',00

StringDscr1End:

3个自独,长度是变化的(字节)。

偏移量

     域

大小

     描述

0

bLength

1

N+2

此描述表的字节数

1

bDescriptorType

1

常量

字串描述表类型

2

wLANGID[0]

2

数字

语言标识(LANGID)码0

     

N

wLANGID[x]

2

数字

语言标识(LANGID)码X

【1】db   StringDscr1End-StringDscr1――bLength段

描述符长度

【2】db   DSCR_STRING―― bDescriptorType段

描述符类型。

【3】Unicode编码的字符串。

 

FW.C文件,是比较难看懂的了,这个要逐字逐句研读,我当初整整看了一个星期,边理解,边一行一行的注释,可以说,看懂了,USB协议部分也就差不多了。从main()函数开始看:

DWORD              i;

WORD          offset;

DWORD              DevDescrLen;

WORD          IntDescrAddr;

WORD          ExtDescrAddr;

Sleep = FALSE;           //初始化用户变量 休眠使能--禁止

Rwuen = FALSE;        //远程唤醒--禁止

Selfpwr = FALSE;

GotSUD = FALSE;      //SetUp令牌包到来标志

定义了一些变量,具体用途在后面;第二段同时对变量进行初始化,从名字可以看出其用途。

TD_Init();

紧接着调用TD_Init()函数,是一些我们自己的初始化配置。

//定向USB描述符

pDeviceDscr = (WORD)&DeviceDscr;

pDeviceQualDscr = (WORD)&DeviceQualDscr;

pHighSpeedConfigDscr = (WORD)&HighSpeedConfigDscr;

pFullSpeedConfigDscr = (WORD)&FullSpeedConfigDscr;

pStringDscr = (WORD)&StringDscr;

这段代码用来获取USB的各个描述符在68013内存中的地址,准确说是在RAM中的地址,在dscrpt.a51文件中有定义,所有的描述符组成了整个的描述符表,后面会用到。

if ((WORD)&DeviceDscr & 0xE000)

这段代码及以后的,在固件中解释是:

Is the descriptor table in external RAM (> 8Kbytes)? If yes, then relocate. Note that this code only checks if the descriptors START in external RAM. It will not work if the descriptor table spans internal and external RAM.

意思是说,这段代码用来判断描述符表首址,也就是前面的DeviceDscr、DeviceQualDscr等是否位于68013的外部RAM区,如果是,则移除,然后将描述符表移到内部RAM区,为什么要移到内部RAM区,因为当描述符表位于外部RAM时,USB是不工作的。那么如何判断描述符地址是否超出内部RAM的地址呢?首先,&DeviceDscr取得整个描述符表的首地址(它也是DeviceDscr设备描述的首址),然后和0xE000相与,为什么要和0xE000相与?这就牵涉到68013 FX2的内部结构图:

上图针对的是128pin的FX2,如果是56或100pin的,那么没有外部RAM,只有内部RAM。可以看到,FX2内部RAM从0000-FFFF,其他为外部RAM。而内部RAM中,只有从0000-1FFF和从E000-FFFF的区域可用,其他为系统保留。从0000-1FFF这8K bytes的内部RAM空间,叫做主RAM,对56,100,128pin来说,都可以同时作为程序或数据存储器(对128pin来说,EA=0)。再看&DeviceDscr & 0xE000的结果,要为“真”的话,显然,必须&DeviceDscr>=0X2000,也就是说判断的是描述表首址&DeviceDsc是否大于1FFF,刚好是主RAM区的大小,这就是为什么要用&DeviceDscr 和 0xE000相与就来判断实现了描述符表首地址的原因了。

// 重定向描述符

IntDescrAddr = INTERNAL_DSCR_ADDR;

ExtDescrAddr = (WORD)&DeviceDscr;

DevDescrLen = (WORD)&UserDscr - (WORD)&DeviceDscr + 2;

for (i = 0; i < DevDescrLen; i++)

*((BYTE xdata *)IntDescrAddr+i) = *((BYTE xdata *)ExtDescrAddr+i);

判断发现描述符表首址位于外部RAM的后,紧接着就将外部RAM的描述符移到内部RAM。这里就用到了前面定义的变量,IntDescrAddr保存内部RAM首址0X80,ExtDescrAddr保存我们获得的当前描述符表外部RAM的首址 ,DevDescrLen是整个描述表的长度,从DeviceDscr段到UserDscr段,在dscrptr中有定义。然后for循环,将从ExtDescrAddr地址开始的外部RAM中的数据逐个copy到从IntDescrAddr地址开始的内部RAM区。

// 更新描述符指针

pDeviceDscr = IntDescrAddr;

offset = (WORD)&DeviceDscr - INTERNAL_DSCR_ADDR;

pDeviceQualDscr -= offset;

pConfigDscr -= offset;

pOtherConfigDscr -= offset;

pHighSpeedConfigDscr -= offset;

pFullSpeedConfigDscr -= offset;

pStringDscr -= offset;

完毕后更新描述符指针,指向内部RAM区,通过原指针减去一个偏移量得到。

然后是USB的一些初始状态设置:

EZUSB_IRQ_ENABLE();                  // EZUSB中断使能

EZUSB_ENABLE_RSMIRQ();               // 使能远程唤醒中断

INTSETUP |= (bmAV2EN | bmAV4EN);   //使能INT2,4自动向量跳转

USBIE |= bmSUDAV | bmSUTOK | bmSUSP | bmURES | bmHSGRANT;// 使能所选择中断

EA = 1;                                // 开8051中断

EZUSB_IRQ_ENABLE();预定义是EZUSB=1,查TRM得知,ezusb是EIE寄存器的第0位,EIE.0=1,使能USB中断;EZUSB_ENABLE_RSMIRQ();EICON |= 0x20,EICON.5=1,使能远程唤醒中断;INTSETUP |= (bmAV2EN | bmAV4EN);使能INT2,4自动向量跳转;USBIE |= bmSUDAV | bmSUTOK | bmSUSP | bmURES | bmHSGRANT;使能所选择中断,相关的中断意义,我也一知半解,后面慢慢学习补充;EA = 1;开8051中断。

从下面的代码开始,才真正开始我们自己的USB事务处理:

while(TRUE)                             // Main Loop主循环

{

// Check for pending SETUP

if(GotSUD)                         //等待SETUP令牌数据的到来

{

SetupCommand();              // Implement setup command //处理SETUP事务

GotSUD = FALSE;      // Clear SETUP flag   清Setup标志

}

if(GotSUD),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各种状态并处理:

if (Sleep)

如果USB进入了休眠状态,这里SleepUSB休眠标志,和GotSUD一样,USB休眠后产生中断,进入void ISR_Susp(void)函数处理,置sleep标志为TRUE

if(TD_Suspend())

检测USB是否挂起

{

Sleep = FALSE;                                                     //清Sleep标志

do

{

EZUSB_Susp();                                              // 置8051为空闲状态.

}

while(!Rwuen && EZUSB_EXTWAKEUP());              //如果唤醒

EZUSB_Resume();                                                 //从空闲状态中恢复

TD_Resume();

到这里,一个事务处理完毕,等待下次事务处理中断的到来。

在继续看fw.c文件前,我觉得有必要先把USB的整个工作过程搞懂!搞懂了USB整个工作过程,对FW中的列举/重列举就比较好理解了。这也是我自己曾经学习的一个过程吧,我并没有一开始就去仔细推敲USB协议方面的东西,而是先看一个大概,然后学到哪里不懂的时候,再回过头去进一步研究,这样慢慢就理解深入了。个人觉得好多书里一上来就给你细细剖析协议的,完全是浪费脑细胞,越看越糊。

以下参照《USB原理与工程实践》这本书上讲的,我简化了一下,算是自己的理解吧:

【1】USB连上电脑(实际是集线器HUB),但是还没有上电。也就是说VCC还没有电压,到VCC上有5V电压有一个很短的延时过程。该过程主机PC和HUB通信。

【2】USB上电,但是还没有被复位。此时不能响应USB任何事务,也没有被分配到任何设备地址,包括默认地址。集线器通过检测D+,D-上的电压来判断是否有新的USB设备连接。

当检测到有新的USB设备连接时,报告给PC。该过程主机和HUB通信。

【3】复位USB总线。PC通知HUB复位USB总线,获得传输模式(低速?全速还是高速)。复位后,USB设备获得一个默认的设备地址号0。此过程PCHUB通信。

【4】USB设备获得地址号0后,可使用该地址号进行某些事务处理。

使用地址号0,控制传输,端点0,主机开始和USB设备功能层开始通信。

USB功能层USB总线结构中的一个,USB总线结构由USB功能层,USB设备层,USB接口层构成。USB功能层,主要负责数据传输操作,就是控制传输,中断传输,块传输,同步传输。

【5】主机开始获得USB设备的信息(例如刚开始要获得USB控制传输所支持的最大数据包长度),那么就要向USB设备发命令(发送GetDescription获得设备描述符信息),于是启动一个USB事务处理,而USB事务处理分为3个阶段:

令牌阶段

数据阶段

握手阶段

也就是说,这一步中,主机发送GetDescription请求读取设备描述符,获得USB控制传输所支持的最大数据包长度(只需读取前8个字节即可),这是一个USB事务,既然是事务,那么所有的USB事务必然从令牌包开始,于是USB固件首先等待令牌包到来,然后处理相应的命令。这样,主机通过发送GetDescription请求(USB 1.1中标准请求之一),读取设备描述符DeviceDscr,目的是获取控制传输支持的最大字节数(第8个字节),一旦检测到这个数,主机复位USB总线并开始进入枚举过程

【6】至此,开始进入枚举过程。主机向USB设备发送SetAddress请求,为其分配一个新的,唯一的设备地址(1127,总共127个)。以后,USB将使用这个新的地址号与主机通信。

【7】主机循环向USB设备发出GetDescription请求,读取所有的描述符,获得该USB设备的全部配置信息。首先读取设备描述符DeviceDscr全部字段,然后读取配置描述符Configuration,接口描述符,端点描述符,其他各种设备类描述符以及自定义描述符等。

【8】然后主机根据读取的产品识别码(PID)、供应商ID(VID)选择一个合适的驱动加载,如果第一次使用,则提示发现新硬件。

【9】加载了USB驱动后,主机发送SetConfigration()请求为该USB设备选择合适的配置,主机为该USB设备选择一个配置值,一个接口,一个可替换设置值。

【10】至此,USB枚举结束。

那么,什么是重枚举呢?

首先,上面的USB枚举是对通用USB来说的,一般USB设备只有枚举过程,没有重枚举过程。也就是说其实,对EZ-USB系列来说,上面的枚举举实际包含了EZ-USB枚举和重枚举两个过程:

对EZ-USB来说,枚举过程就是USB上电复位到加载固件这段过程,此时USB设备地址号为默认的0,枚举完成后,驱动为cypress...eeprom..missing(FX2/FX2LP来说)。然后加载固件,进行重枚举,重枚举完成后,显示驱动为cypress...ez-usb...example或其他自定义设备。

为什么EZ-USB有重枚举过程呢?这就是为了可以让主机在前期固件未加载前自动枚举,识别USB,从而可以从上位机加载固件到68013RAM中,而无需使用ROM, EEPROM, FLASH等内存。实现软件”架构加载程序代码,随时修改固件。

EZ-USB如何控制重枚举?

首先,EZ-USB有一个寄存器USBCS,其中第1位USBCS.1为Renum控制枚举重枚举。USB上电未加载固件代码前,Renum0,表示使用EZUSB核心对芯片的初始配置,处理主机设备请求,并负责把固件下载到RAM中,这个过程就是枚举”完成该枚举过程的设备叫做“缺省USB设备(地址号0)”,即驱动显示为...missing的设备。固件被下载到8051 RAM,此时Renum1表示使用“增强8051核心”处理主机设备请求,并按照固件的代码(读取所有描述符)重新配置USB设备,这个过程就叫做“重枚举”,完成重枚举后,显示自定义的USB设备“...example”,实际是模拟断开与连接的过程。

下表表示:

 

处理设备请求

Renum

8051动作

枚举

EZUSB核心

Renum=0

8051置Renum=1

重枚举

8051

Renum=1

8051重置Renum=0

在重枚举完成后,对控制端点0的设备请求可以由EZUSB核心”处理或由增强8051核心处理,由Renum的值决定。芯片上电时Renum=0,由“EZUSB核心”处理;一旦8051开始运行,就可以设置Renum=1,由“增强8051核心”处理,表示按照8051下载的固件代码处理。

当然,这时也可以设置Renum0,让“EZUSB核心”处理端点0的设备请求,而让8051完成具体的USB数据传输,这样做会大大简化8051固件代码。

然后再看接下来的代码:

注意,在这段代码之前,EZUSB已经枚举完成,当然,整个过程是自动的,我们看不见的。

#ifndef NO_RENUM

if(!(USBCS & bmRENUM))       //如果RENUM位为0,则重列举

{

EZUSB_Discon(TRUE);             //重枚举

}

#endif

这段代码即告诉USB进行重枚举,用软件设置,模拟USB断开与连接,EZUSB_Discon(TRUE)完成断开连接,具体实现在EZUSB.lib库中,大概代码是这样的:

void UsbDisconnect(BOOL renum)

{

if(renum)

USBCS |= (bmDISCON | bmRENUM);     

else

USBCS |= bmDISCON;

EZUSB_Delay(1500);

USBIRQ = 0xff;

EPIRQ = 0xff;

EZUSB_IRQ_CLEAR();

USBCS &=~bmDISCON;

}

将USBCS寄存器的DICON位置1,断开USB,同时如果RENUM位为0,则置1;然后重新连接USB。

// unconditionally re-connect. If we loaded from eeprom we are

// disconnected and need to connect. If we just renumerated this

// is not necessary but doesn't hurt anything

USBCS &=~bmDISCON;           //重新连接

CKCON = (CKCON&(~bmSTRETCH)) | FW_STRETCH_VALUE; // Set stretch

// clear the Sleep flag.

Sleep = FALSE;                         //清sleep标志

这段代码英文意思说的很清楚了,CKCON还不清楚干什么用的。

紧接着主机开始读描述符,并进行配置(发送SetConfiguration),加载驱动等,完成重枚举。

 

 

 

Cypress固件架构彻底解析

 

算是给所有正在学习USB,还徘徊着不得其门而入的朋友一个入门的契机吧,我也深知入门的痛苦,有些人入门就是抱着那什么USB协议,包定义,帧格式等啃来啃去的,结果啃不出个所以然来。

依我的经验来看,协议方面的东西,随便找本书,过一遍就行了;然后,你的终点应该放在你如何来写第一个成功的USB固件;而要写USB固件,那么了解Cypress固件架构是必要的,也是重中之重;再然后,等你积累了一些端点,控制,bulk,中断传输,SlaveFIFO,GPIF等等的经验后,再回过头去看协议方面的内容,就会有更加深刻的体会了;然后,你就可以试着更改fw.c文件了——这个时候你就是高手了。

【1】:首先,还是说下工具吧!

1:你要有一块68013的USB板子,淘宝随便买块好了,还送不少资料。

2:要准备开发工具,去Cypress官网下一个Cy3684的开发包,全称:

cy3684_ez_usb_fx2lp_development_kit_15.exe

网址:http://www.cypress.com/?rID=14321

3.安装开发包。工具就是Cypress USB Console了。怎么用不用我说了吧,这篇文章的主题是Cypress固件架构。

【2】好了,进入主题,固件架构。以一个3684开发包自带的例子讲解。

(1) 进入目录(个人找自己的):D:\Program Files\Cypress\USB\Examples\FX2LP\Bulkloop;

(2) 到D:\Program Files\Cypress\USB\Target文件价下把Cypress头文件Fx2.h,fx2regs.h,syncdly.h这三个头文件拷到bulkloop文件夹里。

(3) keil设置output里关掉Run User Program #1(前面的勾去掉)(别说不会用keil。。。)

(4) 检查keil C51文件路径是否正确。(如果你的keil是直接装载C:\Keil下,那不会有错误,否则,自行设置正确的路径。)

【3】现在可以打开bulkloop工程并编译链接正确了。

在工程下,有以下几个文件:

其中,USBJmpTb.OBJ和EZUSB.LIB基本上是每个工程都要添加的,是一些中断向量表,EZUSB的函数库等等,不用管它们。

现在重点看前面三个文件:

(1) fw.c:这个文件是整个USB的固件根本(FirmWare的缩写),USB协议方面的通信都是在这里完成的,包括上电枚举,重枚举,唤醒以及调用用户自己的程序和控制命令等等。基本上,如非必要,尽量不要动这个文件的内容,也不要在里面书写你自己的任何代码。

(2) bulkllop.c:这个就是用户自己的代码书写文件(原始名称:periph.c)。我们所有的代码都在这个文件里书写。Cypress已经给我们搭好了架构。

void TD_Init(void):这个函数只会在USB启动后调用一次。在这个函数里添加你自己的初始化代码,也就是传输数据前要处理的,例如IO口配置,时钟,端点,FIFO的选择等等。

我们看bulkloop的初始化,它在USB的in,out传输启动前进行了哪些初始化:

CPU时钟频率,USB工作模式选择,端点选择,端点传输方向,FIFO大小的配置等等。

void TD_Poll(void):Poll中文意思调度,这个函数就是用户调度程序,USB会在空闲的时候反复调用该函数,所以我们把自己需要反复执行的代码放在这里。例如在bulkloop里,它就实现了反复从端点2接收上位机数据然后传给端点6,再从端点6传给上位机(4,8端点一样)。

BOOL DR_VendorCmnd(void):这个函数就是自定义命令代码的书写处。我们的Vendor命令都会写在这里,fw.c固件会自动调用我们的代码。

void IO_Init(void);  //8051 IO 初始化

void REG_Init(void); //8051 寄存器初始化

BOOL TD_Suspend(void);   // 挂起处理

BOOL TD_Resume(void);    // 唤醒处理

  功能文件,处理各种中断。例如PERIPH.C 文件。8051 一般默认只有四个中断,这显然不够USB 使用,所以CYPRESS 引入了自动向量的概念,相当于软中断,大大扩展了现有的中断数量。主要的中断有:

 

void ISR_Sudav(void) interrupt 0  // 收到setup 包

 

void ISR_Sutok(void) interrupt 0 // 收到SETUP 令牌

 

void ISR_Sof(void) interrupt 0 // 收到起始帧

 

void ISR_Ures(void) interrupt 0 // 收到RESET

 

void ISR_Susp(void) interrupt 0  // 收到挂起信息

 

void ISR_Highspeed(void) interrupt 0 // 高速模式

 

void ISR_Ep0ack(void) interrupt 0   // 正常响应ACK

 

void ISR_Stub(void) interrupt 0

 

void ISR_Ep0in(void) interrupt 0

 

void ISR_Ep0out(void) interrupt 0

 

void ISR_Ep1in(void) interrupt 0

 

void ISR_Ep1out(void) interrupt 0 //EP1 输入中断

 

void ISR_Ep2inout(void) interrupt 0     //EP2 中断

 

void ISR_Ep4inout(void) interrupt 0

 

void ISR_Ep6inout(void) interrupt 0

 

void ISR_Ep8inout(void) interrupt 0

 

void ISR_Ibn(void) interrupt 0

特别是对于接受数据,一般都在中断中完成相应处理,“中断中适合进行少量简短的操作,不适合进行复杂操作”,这句话在
此依然有效。如果要进行复杂的操作可以在TD_POLL()中进行(多数操作都是在这个函数中完成的)。
另外非常重要的一点是,中断程序的结尾应该让中断复位,允许下一次中断,有些端点的计数器也要清零并允许接受新的中断请求。例如:
    EP1OUTBC = 0; //清空计数
    EZUSB_IRQ_CLEAR(); //USB中断复位
    EPIRQ = 0x08;     //允许EP1中断请求

 

 

 

void ISR_Ep0in(void) interrupt 0~void ISR_Ep8inout(void) interrupt 0:这几个函数是当使用端点中断传输时,中断代码的书写处,很少用。

以上,是经常会用到的几个函数;其他,基本不常用。

(4) :dscr.51:这个文件是USB描述符文件,包括了设备描述符,接口描述符,端点描述符,字符串等等。里面的英文都注释得很详细了,我就不多做介绍了,刚开始入门的时候,这个文件也不必改动。

【4】然后说下几个包含文件:

(1) fx2.h:预定义宏及函数声明

(2) fx2regs.h:68013的寄存器地址定义。

     (3):syncdly.h:同步延时。在其他文件里经常调用的一个函数SYNCDELAY就是这里定义的。

     (4):intrins.h:C51一些数据类型及函数定义。

好了,就写到这里,搞懂每个文件的作用非常非常重要,这样,你就可以知道自己的代码书写在什么地方,遇到不明的函数,定义可以到指定的位置查询,或者想修改某个设置(例如想把端点2设成IN,端点6设成out),知道到哪个文件里去修改。


转载:http://www.eefocus.com/sbogwxf230/blog/12-01/237595_b89c6.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值