bulkloop固件工程介绍
固件程序是指运行在芯片内置内核中的程序。固件程序的主要功能有功能设备的各硬件单元初始化、重新配置功能设备、中断处理、定义寄存器以及通信控制等等。
Cypress公司提供的固件框架已经完成和实现了对于设备的重列举、初始化和电源管理等功能。用户可以在提供的固件构架基础上添加或修改相应的程序代码,这样大大缩短了设计周期,提高了设计效率。
本文附上bulkloop例程核心代码及注释帮助大家对固件代码进行快速理解。
上图是Cpress公司提供的bulkloop例程的工程文件图。
fw.c包含固件程序主框架,实现USB总线协议,提供用户函数接口。
bulkloop.c是用户程序文件,可以根据自己的需求编写不同功能。
dscr.a51用来修改USB的描述符。
EZUSB.LIB是库代码文件。
USBJmpTb.OBJ定义USB中断跳转表。
在用户开发过程中主要修改的是fw.c、bulkloop.c和dscr.c三个文件。本文对这三个文件进行介绍。
Dscr.a51代码部分
Dscr.a51文件主要配置了设备描述符、设备限定描述符、高速配置描述符、全速配置描述符、接口描述符和端点描述符等。
以下为几个主要描述符的配置和注释:
DeviceDscr:
db DSCR_DEVICE_LEN ;; Descriptor length 指明整个设备描述符的长度,单位字节。18
db DSCR_DEVICE ;; Decriptor type 描述符类型值。1
dw 0002H ;; Specification Version (BCD) 表明该USB设备所遵循的USB协议版本,用bcd码表示,2字节。例如2.0版本,值为0200H,用bcd码表示,低字节在前,高字节在后,表示为0002H;
;;同理,1.1版本,则表示为1001H。
db 00H ;; Device class 指明USB设备所属的设备类。=0,表示USB各接口相互独立工作,分属不通的设备类,具体信息在接口描述符中说明;
db 00H ;; Device sub-class 指明USB设备所述的设备子类。其值依赖bDeviceClass。=0,此时bDeviceClass必须首先为0;
db 00H ;; Device sub-sub-class 指明USB所使用的设备类协议。其值依赖bDeviceClass和bDeviceSubClass。=0,表示该设备不使用任何设备类协议;
db 64 ;; Maximum packet size 指明该USB设备端点0控制传输所支持的最大数据包长度,单位字节。
dw 0B404H ;; Vendor ID VID
dw 0410H ;; Product ID (Sample Device) PID
dw 0000H ;; Product version ID 指明USB设备版本号。
db 1 ;; Manufacturer string index 厂商信息字符串索引值,没有时为0。这里为1,即下面的“Cypress”字符串。
db 2 ;; Product string index 产品信息字符串索引值,没有时为0。后面的“EZ-USB”字符串。
db 0 ;; Serial number string index USB设备序列号信息字符串索引值,没有时为0。
db 1 ;; Number of configurations 指明USB设备所支持的配置数。如果USB设备支持两种传输速率,则该字段指出的是该速率下的配置数,而不是两种速率下的配置数和。
DeviceQualDscr:
db DSCR_DEVQUAL_LEN ;; Descriptor length 整个设备限定描述符的长度,单位字节,共10个字节。
db DSCR_DEVQUAL ;; Decriptor type 指出该描述符类型。06H->设备限定描述符。
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 端点0控制传输所支持的最大数据包长度,单位字节。
db 1 ;; Number of configurations 另一速率所支持的配置数。
db 0 ;; Reserved 保留,必须为0。
HighSpeedConfigDscr:
db DSCR_CONFIG_LEN ;; Descriptor length 描述符长度,9字节。
db DSCR_CONFIG ;; Descriptor type 配置描述符。
db (HighSpeedConfigDscrEnd-HighSpeedConfigDscr) mod 256 ;; Total Length (LSB) 指明配置信息总长度,2字节表示。为配置描述符,接口描述符,端点描述符,设备类定义描述符,供应商自定义描述符长度的和。
;;在这里只有配置、接口和端点描述符。低有效位
db (HighSpeedConfigDscrEnd-HighSpeedConfigDscr) / 256 ;; Total Length (MSB) 高有效位。
db 1 ;; Number of interfaces 指明该配置所支持的接口数,最小为1。
db 1 ;; Configuration number 指明该配置的配置值。例如这里值为1,在重枚举时,主机发送Setconfiguration(x),当x=1时,就调用该配置。
db 0 ;; Configuration string 该配置的字符串索引值,没有时为0。
db 10000000b ;; Attributes (b7 - buspwr, b6 - selfpwr, b5 - rwu) 指明该配置的特性,8位。b0~b4,保留,必须为0;b5:远程唤醒选择。=1,支持远程唤醒;=0,不支持远程唤醒;
;;b6:是否总线电源选择。如果该USB设备外加了电源,=1,使用总线电源;=0,使用自供的电源;b7:必须为1。
db 50 ;; Power requirement (div 2 ma) 总线供电时,该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 4 ;; Number of end points 指明接口所使用的端点数,不包括端点0。
db 0ffH ;; Interface class 指明接口所使用的设备类。=0,保留;=1~FEH,表明该接口属于某个明确的USB设备类;=FFH,厂家自定义的设备类。
db 00H ;; Interface sub class
db 00H ;; Interface sub sub class
db 0 ;; Interface descriptor string index
;; Endpoint Descriptor
db DSCR_ENDPNT_LEN ;; Descriptor length
db DSCR_ENDPNT ;; Descriptor type
db 02H ;; Endpoint number, and direction 指明端点的端点号及传输方向。b0~b3:该端点的端点号。如0001端点1,0010端点2;b7:端点传输方向。1-IN传输;0-OUT传输。
db ET_BULK ;; Endpoint type 指明端点的一些特性。b0~b1:端点的传输类型。00-控制传输,01-同步传输,10-块传输,11-中断传输。
;;b2~b3: 当该端点为同步端点时,这两位指出同步类型。00-非同步,01-异步,10-自适应,11-同步。
;;b4~b5: 端点用法类型。00-数据端点,01-显示反馈端点,10-隐式反馈端点,11-保留。b6~b7:保留,必须为0。
db 00H ;; Maximun packet size (LSB)
db 02H ;; Max packect size (MSB)
db 00H ;; Polling interval
fw.c代码部分
Sleep = FALSE; // Disable sleep mode 初始化用户变量 休眠使能--禁止
Rwuen = FALSE; // Disable remote wakeup 远程唤醒--禁止
Selfpwr = FALSE; // Disable self powered
GotSUD = FALSE; // Clear "Got setup data" flag SetUp 令牌包到来标志
// Initialize user device
TD_Init();
// The following section of code is used to relocate the descriptor table. 下面的代码用于重新定位描述符表。
// The frameworks uses SUDPTRH and SUDPTRL to automate the SETUP requests 框架使用SUDPTRH和SUDPTRL来自动化设置请求
// for descriptors. These registers only work with memory locations 描述符。这些寄存器只对内存位置有效
// in the EZ-USB internal RAM. Therefore, if the descriptors are located 在EZ-USB内部RAM中。因此,如果描述符位于
// in external RAM, they must be copied to in internal RAM. 在外部RAM中,它们必须被复制到内部RAM中。
// The descriptor table is relocated by the frameworks ONLY if it is found 描述符表只有在找到时才会被框架重新定位
// to be located in external memory.
pDeviceDscr = (WORD)&DeviceDscr;
pDeviceQualDscr = (WORD)&DeviceQualDscr;
pHighSpeedConfigDscr = (WORD)&HighSpeedConfigDscr;
pFullSpeedConfigDscr = (WORD)&FullSpeedConfigDscr;
pStringDscr = (WORD)&StringDscr;
// Is the descriptor table in external RAM (> 16Kbytes)? If yes, 这段代码用来判断描述符表首址,如果是,则移除,然后将描述符表移到
// then relocate. 内部RAM区.描述符表位于外部RAM时,USB是不工作的。
// 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.
if ((WORD)&DeviceDscr & 0xC000) //外部RAM首地址
{
// first, relocate the descriptors 判断发现描述符表首址位于外部RAM后,就将外部RAM的描述符移到内部RAM。
IntDescrAddr = INTERNAL_DSCR_ADDR; //IntDescrAddr 保存内部RAM首址0X80
ExtDescrAddr = (WORD)&DeviceDscr; //ExtDescrAddr 保存获得的当前描述符表外部RAM的首址
DevDescrLen = (WORD)&UserDscr - (WORD)&DeviceDscr + 2; // 整个描述表的长度,从DeviceDscr段到UserDscr段,在dscrptr中有定义。
for (i = 0; i < DevDescrLen; i++) // 将从ExtDescrAddr地址开始的外部RAM中的数据逐个copy到从IntDescrAddr地址开始的内部RAM区。
*((BYTE xdata *)IntDescrAddr+i) = *((BYTE xdata *)ExtDescrAddr+i);
// update all of the descriptor pointers 完毕后更新描述符指针,指向内部RAM区,通过原指针减去一个偏移量得到。
pDeviceDscr = IntDescrAddr;
offset = (WORD)&DeviceDscr - INTERNAL_DSCR_ADDR;
pDeviceQualDscr -= offset;
pConfigDscr -= offset;
pOtherConfigDscr -= offset;
pHighSpeedConfigDscr -= offset;
pFullSpeedConfigDscr -= offset;
pStringDscr -= offset;
}
EZUSB_IRQ_ENABLE(); // Enable USB interrupt (INT2) EZUSB中断使能
EZUSB_ENABLE_RSMIRQ(); // Wake-up interrupt 使能远程唤醒中断
INTSETUP |= (bmAV2EN | bmAV4EN); // Enable INT 2 & 4 autovectoring 使能INT2,4自动向量跳转
USBIE |= bmSUDAV | bmSUTOK | bmSUSP | bmURES | bmHSGRANT; // Enable selected interrupts 使能所选择中断
EA = 1; // Enable 8051 interrupts 开8051中断
while(TRUE) // Main Loop 主循环
{
// Poll User Device
TD_Poll();
// Check for pending SETUP
if(GotSUD) // 等待SETUP令牌数据的到来
{
SetupCommand(); // Implement setup command 处理SETUP事务
GotSUD = FALSE; // Clear SETUP flag 清Setup标志
}
// 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.
if (Sleep)
{
if(TD_Suspend())
{
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.
}
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();
}
}
}
bulkloop.c代码部分
void TD_Init(void) // Called once at startup { // set the CPU clock to 48MHz CPUCS = ((CPUCS & ~bmCLKSPD) | bmCLKSPD1) ; //F:CLKSPD1=1且CLKSPD0=0 意思是48MHz // set the slave FIFO interface to 48MHz //F:时钟来源定为外部,内部FIFO\GPIF时钟设为48MHz,IFCLK输出端口为三态,IFCLK极性不翻转,同步方式,PE012为端口,ABD端口为端口模式 IFCONFIG |= 0x40;
EP1OUTCFG = 0xA0; EP1INCFG = 0xA0; SYNCDELAY; // see TRM section 15.14 EP2CFG = 0xA2; //F:1010 0010意思是:有效,OUT,Bulk,512,0,Double. SYNCDELAY; EP4CFG = 0xA0; //F:1010 0000意思是:有效,OUT,Bulk,512,0,00(4和8端点的末尾两位只能是0,在2和6都是Double情况下,意味着Double). SYNCDELAY; EP6CFG = 0xE2; //F:1110 0010意思是:有效,IN,Bulk,512,0,Double. SYNCDELAY; EP8CFG = 0xE0; //F:1110 0000意思是:有效,OUT,Bulk,512,0,00.
// out endpoints do not come up armed //F:输出端点一开始没有被arm. // since the defaults are double buffered we must write dummy byte counts twice //F:因为端点默认是双倍缓冲(512*2),我们必须用无用数据写两次字节计数,用来arm输出端点. SYNCDELAY; EP2BCL = 0x80; // arm EP2OUT by writing byte count w/skip. SYNCDELAY; EP2BCL = 0x80; SYNCDELAY; EP4BCL = 0x80; // arm EP4OUT by writing byte count w/skip. SYNCDELAY; EP4BCL = 0x80; // enable dual autopointer feature //F:使能自动指针 AUTOPTRSETUP |= 0x01; }
void TD_Poll(void) // Called repeatedly while the device is idle //F:重复调用 { WORD i; WORD count; if(!(EP2468STAT & bmEP2EMPTY)) //F:如果EP2的buff不空.EP2468STAT中的各个位其实就是EPxCS中的F和E位,标识满\空. { // check EP2 EMPTY(busy) bit in EP2468STAT (SFR), core set‘s this bit when FIFO is empty if(!(EP2468STAT & bmEP6FULL)) //F:如果EP6的buff不满. { // check EP6 FULL(busy) bit in EP2468STAT (SFR), core set‘s this bit when FIFO is full APTR1H = MSB( &EP2FIFOBUF ); //F:自动指针1指向EP2的buffer APTR1L = LSB( &EP2FIFOBUF ); AUTOPTRH2 = MSB( &EP6FIFOBUF ); //F:自动指针2指向EP6的buffer AUTOPTRL2 = LSB( &EP6FIFOBUF ); count = (EP2BCH << 8) + EP2BCL; //F:计算EP2有多少字节 // loop EP2OUT buffer data to EP6IN for( i = 0x0000; i < count; i++ ) { // setup to transfer EP2OUT buffer to EP6IN buffer using AUTOPOINTER(s) // F:利用自动指针进行EP2和EP6之间的数据转移 EXTAUTODAT2 = EXTAUTODAT1; // F:自动指针1指向的数据到自动指针2指向的空间 } EP6BCH = EP2BCH; //F:拷贝数据长度到EP6的计数,准备接下来的IN操作 SYNCDELAY; EP6BCL = EP2BCL; // arm EP6IN SYNCDELAY; EP2BCL = 0x80; // re(arm) EP2OUT } } if(!(EP2468STAT & bmEP4EMPTY)) { // check EP4 EMPTY(busy) bit in EP2468STAT (SFR), core set‘s this bit when FIFO is empty if(!(EP2468STAT & bmEP8FULL)) { // check EP8 FULL(busy) bit in EP2468STAT (SFR), core set‘s this bit when FIFO is full APTR1H = MSB( &EP4FIFOBUF ); APTR1L = LSB( &EP4FIFOBUF ); AUTOPTRH2 = MSB( &EP8FIFOBUF ); AUTOPTRL2 = LSB( &EP8FIFOBUF ); count = (EP4BCH << 8) + EP4BCL; // loop EP4OUT buffer data to EP8IN for( i = 0x0000; i < count; i++ ) { // setup to transfer EP4OUT buffer to EP8IN buffer using AUTOPOINTER(s) EXTAUTODAT2 = EXTAUTODAT1; } EP8BCH = EP4BCH; SYNCDELAY; EP8BCL = EP4BCL; // arm EP8IN SYNCDELAY; EP4BCL = 0x80; // re(arm) EP4OUT } } }
原文地址:https://www.cnblogs.com/gavin-gao1995/p/11684011.html