上篇介绍了基于libusb的无驱动设计,上位机没问题了,现在还留下个下位机的问题,该项目中USB下位机采用的Cypress的CY7C68013A控制芯片, 下面来仔细看如何编写下位机的固件程序(firmware)。
先看看工程的结构,如下图所示(开发工具为Keil uVision3):
其中:
(1): fw.c: 这个文件是整个USB的固件的根本,USB协议方面的通信都是在这里完成的,包括上电枚举,重枚举,唤醒以及调用用户自己的程序和控制命令等等。 一般的开发,该文件无需修改。固件的main程序就位于该文件中,概括来说,它首先进行一系列通用初始化设置,并调用void TD_Init(void)函数来实现用户的特殊初始化设置。之后,程序会一直监视是否有指令收到,并在空闲的时候反复调用TD_Poll()函数来实现我们用户的功能。代码如下:
//-----------------------------------------------------------------------------
// File: fw.c
// Contents: Firmware frameworks task dispatcher and device request parser
// source.
//
// indent 3. NO TABS!
//
// $Revision: 18 $
// $Date: 12/04/01 5:33p $
//
// Copyright (c) 1997 AnchorChips, Inc. All rights reserved
//
//-----------------------------------------------------------------------------
#include ".\Inc\fx2.h"
#include ".\Inc\fx2regs.h"
//-----------------------------------------------------------------------------
// Constants
//-----------------------------------------------------------------------------
#define DELAY_COUNT 0x9248*8L // Delay for 8 sec at 24Mhz, 4 sec at 48
#define _IFREQ 48000 // IFCLK constant for Synchronization Delay
#define _CFREQ 48000 // CLKOUT constant for Synchronization Delay
//-----------------------------------------------------------------------------
// Random Macros
//-----------------------------------------------------------------------------
#define min(a,b) (((a)<(b))?(a):(b))
#define max(a,b) (((a)>(b))?(a):(b))
// Registers which require a synchronization delay, see section 15.14
// FIFORESET FIFOPINPOLAR
// INPKTEND OUTPKTEND
// EPxBCH:L REVCTL
// GPIFTCB3 GPIFTCB2
// GPIFTCB1 GPIFTCB0
// EPxFIFOPFH:L EPxAUTOINLENH:L
// EPxFIFOCFG EPxGPIFFLGSEL
// PINFLAGSxx EPxFIFOIRQ
// EPxFIFOIE GPIFIRQ
// GPIFIE GPIFADRH:L
// UDMACRCH:L EPxGPIFTRIG
// GPIFTRIG
// Note: The pre-REVE EPxGPIFTCH/L register are affected, as well...
// ...these have been replaced by GPIFTC[B3:B0] registers
#include ".\Inc\fx2sdly.h" // Define _IFREQ and _CFREQ above this #include
//-----------------------------------------------------------------------------
// Global Variables
//-----------------------------------------------------------------------------
volatile BOOL GotSUD;
BOOL Rwuen;
BOOL Selfpwr;
volatile BOOL Sleep; // Sleep mode enable flag
WORD pDeviceDscr; // Pointer to Device Descriptor; Descriptors may be moved
WORD pDeviceQualDscr;
WORD pHighSpeedConfigDscr;
WORD pFullSpeedConfigDscr;
WORD pConfigDscr;
WORD pOtherConfigDscr;
WORD pStringDscr;
//-----------------------------------------------------------------------------
// Prototypes
//-----------------------------------------------------------------------------
void SetupCommand(void);
void TD_Init(void);
void TD_Poll(void);
BOOL TD_Suspend(void);
BOOL TD_Resume(void);
BOOL DR_GetDescriptor(void);
BOOL DR_SetConfiguration(void);
BOOL DR_GetConfiguration(void);
BOOL DR_SetInterface(void);
BOOL DR_GetInterface(void);
BOOL DR_GetStatus(void);
BOOL DR_ClearFeature(void);
BOOL DR_SetFeature(void);
BOOL DR_VendorCmnd(void);
// this table is used by the epcs macro
const char code EPCS_Offset_Lookup_Table[] =
{
0, // EP1OUT
1, // EP1IN
2, // EP2OUT
2, // EP2IN
3, // EP4OUT
3, // EP4IN
4, // EP6OUT
4, // EP6IN
5, // EP8OUT
5, // EP8IN
};
// macro for generating the address of an endpoint's control and status register (EPnCS)
#define epcs(EP) (EPCS_Offset_Lookup_Table[(EP & 0x7E) | (EP > 128)] + 0xE6A1)
//-----------------------------------------------------------------------------
// Code
//-----------------------------------------------------------------------------
// Task dispatcher
void main(void)
{
DWORD i;
WORD offset;
DWORD DevDescrLen;
DWORD j=0;
WORD IntDescrAddr;
WORD ExtDescrAddr;
// Initialize Global States
Sleep = FALSE; // Disable sleep mode
Rwuen = FALSE; // Disable remote wakeup
Selfpwr = FALSE; // Disable self powered
GotSUD = FALSE; // Clear "Got setup data" flag
// Initialize user device
TD_Init();
// The following section of code is used to relocate the descriptor table.
// Since the SUDPTRH and SUDPTRL are assigned the address of the descriptor
// table, the descriptor table must be located in on-part memory.
// The 4K demo tools locate all code sections in external memory.
// 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;
if ((WORD)&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) = 0xCD;
for (i = 0; i < DevDescrLen; i++)
*((BYTE xdata *)IntDescrAddr+i) = *((BYTE xdata *)ExtDescrAddr+i);
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_ENABLE_RSMIRQ(); // Wake-up interrupt
INTSETUP |= (bmAV2EN | bmAV4EN); // Enable INT 2 & 4 autovectoring
USBIE |= bmSUDAV | bmSUTOK | bmSUSP | bmURES | bmHSGRANT; // Enable selected interrupts
EA = 1; // Enable 8051 interrupts
#ifndef NO_RENUM
// Renumerate if necessary. Do this by checking the renum bit. If it
// is already set, there is no need to renumerate. The renum bit will
// already be set if this firmware was loaded from an eeprom.
if(!(USBCS & bmRENUM))
{
EZUSB_Discon(TRUE); // renumerate
}
#endif
// 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 to 0 (after renumeration)
// clear the Sleep flag.
Sleep = FALSE;
// Task Dispatcher
while(TRUE) // Main Loop
{
if(GotSUD) // Wait for SUDAV
{
SetupCommand(); // Implement setup command
GotSUD = FALSE; // Clear SUDAV flag
}
// Poll User Device
// 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());
// 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_Poll();
}
}
// Device request parser
void SetupCommand(void)
{
void *dscr_ptr;
switch(SETUPDAT[1])
{
case SC_GET_DESCRIPTOR: // *** Get Descriptor
if(DR_GetDescriptor())
switch(SETUPDAT[3])
{
case GD_DEVICE: // Device
SUDPTRH = MSB(pDeviceDscr);
SUDPTRL = LSB(pDeviceDscr);
break;
case GD_DEVICE_QUALIFIER: // Device Qualifier
SUDPTRH = MSB(pDeviceQualDscr);
SUDPTRL = LSB(pDeviceQualDscr);
break;
case GD_CONFIGURATION: // Configuration
SUDPTRH = MSB(pConfigDscr);
SUDPTRL = LSB(pConfigDscr);
break;
case GD_OTHER_SPEED_CONFIGURATION: // 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(); // Stall End Point 0
break;
default: // Invalid request
EZUSB_STALL_EP0(); // 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: // Device
EP0BUF[0] = ((BYTE)Rwuen << 1) | (BYTE)Selfpwr;
EP0BUF[1] = 0;
EP0BCH = 0;
EP0BCL = 2;
break;
case GS_INTERFACE: // Interface
EP0BUF[0] = 0;
EP0BUF[1] = 0;
EP0BCH = 0;
EP0BCL = 2;
break;
case GS_ENDPOINT: // End Point
EP0BUF[0] = *(BYTE xdata *) epcs(SETUPDAT[4]) & bmEPSTALL;
EP0BUF[1] = 0;
EP0BCH = 0;
EP0BCL = 2;
break;
default: // Invalid Command
EZUSB_STALL_EP0(); // 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;
}
break;
default: // *** Invalid Command
if(DR_VendorCmnd())
EZUSB_STALL_EP0(); // Stall End Point 0
}
// Acknowledge handshake phase of device request
EP0CS |= bmHSNAK;
}
// Wake-up interrupt handlear
void resume_isr(void) interrupt WKUP_VECT
{
EZUSB_CLEAR_RSMIRQ();
}
(2):dscr.a51: 这个文件是USB描述符文件,包括了设备描述符,接口描述符,端点描述符,字符串等等。如要修改从PC端看到的该USB设备的名字,可通过修改该文件中的字符串内容来实现。
(3):GPIF.c:本项目中用到了GPIF功能(Cypress定义的一种接口),(安装gpif designer软件,根据自己需要完成相应时序图,即可自动生成GPIF.c文件)。
// GPIF Program Code
// DO NOT EDIT ...
#include "fx2.h"
#include "fx2regs.h"
#include "fx2sdly.h" // SYNCDELAY macro
// END DO NOT EDIT
// DO NOT EDIT ...
const char xdata WaveData[128] =
{
// Wave 0
/* LenBr */ 0x01, 0x01, 0x9A, 0x01, 0x01, 0x01, 0x01, 0x07,
/* Opcode*/ 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02,
/* Output*/ 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x07,
/* LFun */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F,
// Wave 1
/* LenBr */ 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x07,
/* Opcode*/ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
/* Output*/ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
/* LFun */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F,
// Wave 2
/* LenBr */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x07,
/* Opcode*/ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
/* Output*/ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
/* LFun */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F,
// Wave 3
/* LenBr */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x07,
/* Opcode*/ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
/* Output*/ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
/* LFun */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F,
};
// END DO NOT EDIT
// DO NOT EDIT ...
const char xdata FlowStates[36] =
{
/* Wave 0 FlowStates */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* Wave 1 FlowStates */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* Wave 2 FlowStates */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* Wave 3 FlowStates */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};
// END DO NOT EDIT
// DO NOT EDIT ...
const char xdata InitData[7] =
{
/* Regs */ 0xE0,0x10,0x00,0x07,0xEE,0x4E,0x00
};
// END DO NOT EDIT
// TO DO: You may add additional code below.
void GpifInit( void )
{
BYTE i;
// Registers which require a synchronization delay, see section 15.14
// FIFORESET FIFOPINPOLAR
// INPKTEND OUTPKTEND
// EPxBCH:L REVCTL
// GPIFTCB3 GPIFTCB2
// GPIFTCB1 GPIFTCB0
// EPxFIFOPFH:L EPxAUTOINLENH:L
// EPxFIFOCFG EPxGPIFFLGSEL
// PINFLAGSxx EPxFIFOIRQ
// EPxFIFOIE GPIFIRQ
// GPIFIE GPIFADRH:L
// UDMACRCH:L EPxGPIFTRIG
// GPIFTRIG
// Note: The pre-REVE EPxGPIFTCH/L register are affected, as well...
// ...these have been replaced by GPIFTC[B3:B0] registers
// 8051 doesn't have access to waveform memories 'til
// the part is in GPIF mode.
IFCONFIG = 0xEE;
// IFCLKSRC=1 , FIFOs executes on internal clk source
// xMHz=1 , 48MHz internal clk rate
// IFCLKOE=0 , Don't drive IFCLK pin signal at 48MHz
// IFCLKPOL=0 , Don't invert IFCLK pin signal from internal clk
// ASYNC=1 , master samples asynchronous
// GSTATE=1 , Drive GPIF states out on PORTE[2:0], debug WF
// IFCFG[1:0]=10, FX2 in GPIF master mode
GPIFABORT = 0xFF; // abort any waveforms pending
GPIFREADYCFG = InitData[ 0 ];
GPIFCTLCFG = InitData[ 1 ];
GPIFIDLECS = InitData[ 2 ];
GPIFIDLECTL = InitData[ 3 ];
GPIFWFSELECT = InitData[ 5 ];
GPIFREADYSTAT = InitData[ 6 ];
// use dual autopointer feature...
AUTOPTRSETUP = 0x07; // inc both pointers,
// ...warning: this introduces pdata hole(s)
// ...at E67B (XAUTODAT1) and E67C (XAUTODAT2)
// source
AUTOPTRH1 = MSB( &WaveData );
AUTOPTRL1 = LSB( &WaveData );
// destination
AUTOPTRH2 = 0xE4;
AUTOPTRL2 = 0x00;
// transfer
for ( i = 0x00; i < 128; i++ )
{
EXTAUTODAT2 = EXTAUTODAT1;
}
// Configure GPIF Address pins, output initial value,
PORTCCFG = 0xFF; // [7:0] as alt. func. GPIFADR[7:0]
OEC = 0xFF; // and as outputs
PORTECFG |= 0x80; // [8] as alt. func. GPIFADR[8]
OEE |= 0x80; // and as output
// ...OR... tri-state GPIFADR[8:0] pins
// PORTCCFG = 0x00; // [7:0] as port I/O
// OEC = 0x00; // and as inputs
// PORTECFG &= 0x7F; // [8] as port I/O
// OEE &= 0x7F; // and as input
// GPIF address pins update when GPIFADRH/L written
SYNCDELAY; //
GPIFADRH = 0x00; // bits[7:1] always 0
SYNCDELAY; //
GPIFADRL = 0x00; // point to PERIPHERAL address 0x0000
// Configure GPIF FlowStates registers for Wave 0 of WaveData
FLOWSTATE = FlowStates[ 0 ];
FLOWLOGIC = FlowStates[ 1 ];
FLOWEQ0CTL = FlowStates[ 2 ];
FLOWEQ1CTL = FlowStates[ 3 ];
FLOWHOLDOFF = FlowStates[ 4 ];
FLOWSTB = FlowStates[ 5 ];
FLOWSTBEDGE = FlowStates[ 6 ];
FLOWSTBHPERIOD = FlowStates[ 7 ];
}
(4):periph.c: 这个就是我们用户实现功能文件,根据用户要实现的功能在此添加相应代码即可。主要函数如下:
void TD_Init(void):这个函数只会在USB启动后调用一次。在这个函数里添加自己的初始化代码,也就是传输数据前要处理的,例如IO口配置,时钟,端点,FIFO的选择等,本例中详细代码如下:
void TD_Init(void) // Called once at startup
{
// set the CPU clock to 48MHz
CPUCS = ((CPUCS & ~bmCLKSPD) | bmCLKSPD1) ;
SYNCDELAY;
REVCTL = 0x03; //REVCTL.0 and REVCTL.1 set to 1
//Endpoint configuration
SYNCDELAY; // see TRM section 15.14
EP2CFG = 0xA2; //valid, Type is BULK OUT, double buffer, size is 512bytes
SYNCDELAY;
EP6CFG = 0xE2;
SYNCDELAY;
EP1INCFG = 0xB0; //valid, Type is INTERRUPT
//FIFO intialization(Restore FIFOs to default state)
SYNCDELAY;
FIFORESET = 0x80; // activate NAK-ALL to avoid race conditions
SYNCDELAY;
FIFORESET = 0x02; // reset, FIFO
SYNCDELAY;
FIFORESET = 0x00; // deactivate NAK-ALL
SYNCDELAY;
OUTPKTEND = 0x82; //Arm both EP2 buffer
SYNCDELAY;
OUTPKTEND = 0x82;
// enable dual autopointer feature
AUTOPTRSETUP |= 0x01;
Rwuen = TRUE; // Enable remote-wakeup
// NAK INT Enable
SYNCDELAY;
NAKIRQ = bmBIT0; // Clear the global IBN IRQ
SYNCDELAY;
NAKIE |= bmBIT0; // Enable the global IBN IRQ
SYNCDELAY;
IBNIRQ = 0xFF; // Clear any pending IBN IRQ
SYNCDELAY;
IBNIE |= bmEP1IBN; // Enable the IBN interrupt for EP1
memset(gpio_expan1_save, 0xFF, 2);
memset(gpio_expan2_save, 0xFF, 2);
memset(gpio_expan3_save, 0xFF, 2);
EZUSB_InitI2C(); // Initialize EZ-USB I2C controller
// Configure GPIF from GPIFTool generated waveform data
GpifInit();
OEA = 0xFF; // Port A Output Enable
PORTACFG = 0; // use Port A
}
void TD_Poll(void):这个函数就是用户调度程序,USB会在空闲的时候反复调用该函数,所以我们把自己需要反复执行的代码放在这里。本例里,该函数根据收到的指令,通过GPIF接口实现对FPGA的配置,数据读写等功能,具体代码如下:
void TD_Poll(void) // Called repeatedly while the device is idle
{
WORD i;
WORD count;
WORD *temp_buff;
DWORD *send_buff;
DWORD *recv_buff;
DWORD temp_size;
WORD status = 0;
/******** USB data write mode *******/
if (usb_data_write_flag) {
//EP2E = 0, when endp FIFO not emptry , it means the data is commming
if (!(EP2468STAT & bmEP2EMPTY)) {
count = ((EP2BCH << 8) + EP2BCL);
SYNCDELAY;
send_buff = (DWORD *)EP2FIFOBUF;
if (usb_access_type == TYPE_REG) { // register write
FpgaRegWrite(ddr3_start_add, *send_buff);
usb_data_write_flag = 0;
} else { // memory write
for (i = 0; i < count/4; i++) {
//parse the address information
ddr3_temp_add = ddr3_start_add & 0xFFFFF000;
if ((ddr3_first_flag == 1) || (ddr3_start_add == ddr3_temp_add )) {
FpgaRegWrite(FPGAE_DDR3_BASE_ADDR_REG, ddr3_temp_add);
ddr3_first_flag = 0;
}
ddr3_temp_add = ddr3_start_add & 0x00000FFF;
FpgaRegWrite(FPGAE_DDR3_WINDOW_AREA_REG + ddr3_temp_add, *send_buff);
send_buff ++;
ddr3_start_add += 4;
}
left_size -= count;
if (left_size == 0) {
ddr3_temp_add = 0;
usb_data_write_flag = 0;
}
}
SYNCDELAY;
EP2BCL = 0x00; // re(arm) EP2OUT
}
}
/******** USB data read mode ********/
if (usb_data_read_flag) {
if(!(EP2468STAT & bmEP6FULL)) {
recv_buff = (DWORD *)EP6FIFOBUF;
if (usb_access_type == TYPE_REG) { // register read
FpgaRegRead(ddr3_start_add, recv_buff);
usb_data_read_flag = 0;
} else { //memory read
temp_size = left_size;
if (left_size >= BLOCK_SIZE) { /* left_size is larger than buffer size(512) */
for (i = 0; i < BLOCK_SIZE/4; i ++) {
ddr3_temp_add = ddr3_start_add & 0xFFFFF000;
if ((ddr3_first_flag) || (ddr3_start_add == ddr3_temp_add )) {
FpgaRegWrite(FPGAE_DDR3_BASE_ADDR_REG, ddr3_temp_add);
ddr3_first_flag = 0;
}
ddr3_temp_add = ddr3_start_add & 0x00000FFF;
FpgaRegRead(FPGAE_DDR3_WINDOW_AREA_REG + ddr3_temp_add, recv_buff);
recv_buff ++;
ddr3_start_add += 4;
}
left_size -= BLOCK_SIZE;
} else { /* left_size is less than buffer size(512) */
for (i = 0; i < left_size/4; i ++) {
ddr3_temp_add = ddr3_start_add & 0xFFFFF000;
if ((ddr3_first_flag) || (ddr3_start_add == ddr3_temp_add )) {
FpgaRegWrite(FPGAE_DDR3_BASE_ADDR_REG, ddr3_temp_add);
ddr3_first_flag = 0;
}
ddr3_temp_add = ddr3_start_add & 0x00000FFF;
FpgaRegRead(FPGAE_DDR3_WINDOW_AREA_REG + ddr3_temp_add, recv_buff);
recv_buff ++;
ddr3_start_add += 4;
}
left_size = 0;
}
}
if (left_size == 0) {
ddr3_temp_add = 0;
usb_data_read_flag = 0;
}
if (usb_access_type == TYPE_REG) { // register read
SYNCDELAY;
EP6BCH = 0x00;
SYNCDELAY;
EP6BCL = 0x04; // re(arm) EP6IN
} else {
if (temp_size >= BLOCK_SIZE) { // memory read
SYNCDELAY;
EP6BCH = 0x02;
SYNCDELAY;
EP6BCL = 0x00; // re(arm) EP6IN
} else {
SYNCDELAY;
EP6BCH = 0x00;
SYNCDELAY;
EP6BCL = temp_size; // re(arm) EP6IN
}
}
}
}
/******** Configurate mode *********/
if (fpga_config_start_flag ) {
if (!(EP2468STAT & bmEP2EMPTY)) { //EP2E = 0, when endp FIFO not emptry , it means the config data is commming
count = ((EP2BCH << 8) + EP2BCL);
SYNCDELAY;
temp_buff = (WORD *)EP2FIFOBUF;
for (i = 0; i < count/2; i++) {
//wait until the data bus is idle
CpldRegWrite(CPLD_CONFIG_DATA_REG, *temp_buff);
while(!(status & USB_CONFIG_READY)) {
CpldRegRead(CPLD_CONFIG_STATUS_REG, &status);
EZUSB_Delay(1);
}
temp_buff ++;
left_size -= 2;
}
if (left_size == 0) { //it means all config data send
fpga_config_start_flag = 0;
config_flag &= ~USB_CONFIG_START;
CpldRegWrite(CPLD_CONFIG_CONTROL_REG, config_flag);
}
SYNCDELAY;
EP2BCL = 0x00; // re(arm) EP2OUT
}
}
}
BOOL DR_VendorCmnd(void):这个函数就是自定义命令代码的书写处。我们的Vendor命令都会写在这里,fw.c固件会自动调用我们的代码来解析指令。
本项目中该函数的部分代码如下:
BOOL DR_VendorCmnd(void)
{
BYTE gpio_value;
switch (SETUPDAT[1])
{
case FPGA_ID_SELECT:
fpga_id = SETUPDAT[2]; //get the FPGA ID number which will be configurated
config_flag = (USB_CONFIG_MODE | USB_CONFIG_START) | ((WORD)fpga_id);
CpldRegWrite(CPLD_CONFIG_CONTROL_REG, config_flag);
break;
case FPGA_CONFIG_DATA_SIZE_SET:
fpga_config_file_size = (DWORD)SETUPDAT[2];
fpga_config_file_size |= ((DWORD)SETUPDAT[3]) << 8;
fpga_config_file_size |= ((DWORD)SETUPDAT[4]) << 16;
fpga_config_file_size |= ((DWORD)SETUPDAT[5]) << 24;
left_size = fpga_config_file_size;
break;
case FPGA_CONFIG_DATA_TRANS_START:
fpga_config_start_flag = 1;
break;
case FPGA_CONFIG_DATA_TRANS_STOP:
fpga_config_start_flag = 0;
break;
case USB_DATA_READ:
if (SETUPDAT[7] != 0 || SETUPDAT[6] != 0) {
EP0BCH = SETUPDAT[7];
SYNCDELAY;
EP0BCL = SETUPDAT[6];
SYNCDELAY;
while(EP0CS & bmEPBUSY);
}
usb_access_type = SETUPDAT[4];
ddr3_start_add = (DWORD)EP0BUF[0];
ddr3_start_add |= ((DWORD)EP0BUF[1]) << 8;
ddr3_start_add |= ((DWORD)EP0BUF[2]) << 16;
ddr3_start_add |= ((DWORD)EP0BUF[3]) << 24;
ddr3_trans_size = (DWORD)EP0BUF[4];
ddr3_trans_size |= ((DWORD)EP0BUF[5]) << 8;
ddr3_trans_size |= ((DWORD)EP0BUF[6]) << 16;
ddr3_trans_size |= ((DWORD)EP0BUF[7]) << 24;
left_size = ddr3_trans_size;
ddr3_first_flag = 1;
usb_data_read_flag = 1;
break;
case USB_DATA_WRITE:
if (SETUPDAT[7] != 0 || SETUPDAT[6] != 0) {
EP0BCH = SETUPDAT[7];
SYNCDELAY;
EP0BCL = SETUPDAT[6];
SYNCDELAY;
while(EP0CS & bmEPBUSY);
}
usb_access_type = SETUPDAT[4];
ddr3_start_add = (DWORD)EP0BUF[0];
ddr3_start_add |= ((DWORD)EP0BUF[1]) << 8;
ddr3_start_add |= ((DWORD)EP0BUF[2]) << 16;
ddr3_start_add |= ((DWORD)EP0BUF[3]) << 24;
ddr3_trans_size = (DWORD)EP0BUF[4];
ddr3_trans_size |= ((DWORD)EP0BUF[5]) << 8;
ddr3_trans_size |= ((DWORD)EP0BUF[6]) << 16;
ddr3_trans_size |= ((DWORD)EP0BUF[7]) << 24;
left_size = ddr3_trans_size;
ddr3_first_flag = 1;
usb_data_write_flag = 1;
break;
//..........
//..........
//..........
default:
return(TRUE);
}
return(FALSE);
}
void ISR_Ep0in(void) interrupt 0~void ISR_Ep8inout(void) interrupt 0:这几个函数是当使用端点中断传输时,中断代码的书写处,可根据需要添加相应代码。
(5):resume.c: USB设备唤醒时调用。(6):delay.c: 延时调用。
(7):discon.c: 设备连接断开时进行相应处理。
(8):i2c.c 和i2c_rw.c都是用于i2c的相关函数。不需要该功能的,不包含进来即可。