完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
dscr51里放的是USB描述符表,EZ-USB在重枚举阶段会读取或设置相应的描述符:
db DSCR_DEVICE_LEN ;; Descriptor lengthdb DSCR_DEVICE ;; Decriptor typedw 0002H ;; Specification Version (BCD)db 00H ;; Device classdb 00H ;; Device sub-classdb 00H ;; Device sub-sub-classdb 64 ;; Maximum packet sizedw 0B404H ;; Vendor IDdw 0410H ;; Product ID (Sample Device)dw 0000H ;; Product version IDdb 1 ;; Manufacturer string indexdb 2 ;; Product string indexdb 0 ;; Serial number string indexdb 1 ;; Number of configurations[url=][/url] 【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,厂商自定义的设备类。 |
|
|
相关推荐
|
|
|
【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相与? |
|
|
|
|
|
|
|
|
上图针对的是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得知,ezu***是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(); //处理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进入了休眠状态,这里Sleep是USB休眠标志,和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。此过程PC和HUB通信。
【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请求,为其分配一个新的,唯一的设备地址(1~127,总共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-u***…example或其他自定义设备。 为什么EZ-USB有重枚举过程呢?这就是为了可以让主机在前期固件未加载前自动枚举,识别USB,从而可以从上位机加载固件到68013的RAM中,而无需使用ROM, EEPROM, FLASH等内存。实现“软件”架构加载程序代码,随时修改固件。 |
|
|
|
|
|
|
|
|
EZ-USB如何控制重枚举?
首先,EZ-USB有一个寄存器USBCS,其中第1位USBCS.1为Renum控制枚举重枚举。在USB上电未加载固件代码前,Renum=0,表示使用“EZUSB核心”对芯片的初始配置,处理主机设备请求,并负责把固件下载到RAM中,这个过程就是“枚举”,完成该枚举过程的设备叫做“缺省USB设备(地址号0)”,即驱动显示为“…missing”的设备。当固件被下载到8051 RAM后,此时Renum=1,表示使用“增强8051核心”处理主机设备请求,并按照固件的代码(读取所有描述符)重新配置USB设备,这个过程就叫做“重枚举”,完成重枚举后,显示自定义的USB设备“…example”,实际是模拟断开与连接的过程。 在重枚举完成后,对控制端点0的设备请求可以由“EZUSB核心”处理或由“增强8051核心”处理,由Renum的值决定。芯片上电时Renum=0,由“EZUSB核心”处理;一旦8051开始运行,就可以设置Renum=1,由“增强8051核心”处理,表示按照8051下载的固件代码处理。 当然,这时也可以设置Renum=0,让“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 U***Disconnect(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_u***_fx2lp_development_kit_15.exe 网址:http://www.cypress.com/?rID=14321 |
|
|
|
|
|
|
|
528 浏览 0 评论
654 浏览 0 评论
858 浏览 0 评论
837 浏览 0 评论
RT-Thread与英飞凌(infineon)合作得板子PSOC 6 板子学习
770 浏览 0 评论
【youyeetoo X1 windows 开发板体验】少儿AI智能STEAM积木平台
17064 浏览 31 评论
浏览过的版块 |
/9
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2025-12-14 23:36 , Processed in 1.052206 second(s), Total 72, Slave 64 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191

淘帖