Hello,
I'm using a MAX3420E in order to comm a PCB with Atmel microcontroller, with a PC using USB. In windows vista, the PC recognizes a Serial (Virtual) Port and works right, but when I try it in Windows XP, it seems that PC doesn't recognizes the device.
It appears in the device manager as "unknown device" with VID_0000&PID_0000...
I've been searching for a similar post, and found one that had the same problem VID_0000, but the last message does't give any solution...
Any clue? Thank you very much in advance.
The descriptors I use are list below:
// DESCRIPTORS const unsigned char DD[]= // DEVICE Descriptor {0x12, // bLength = 18d 0x01, // bDescriptorType = Device (1) 0x10,0x01, // bcdUSB(L/H) USB spec rev (BCD) 0x02,0x00,0x00, // bDeviceClass, bDeviceSubClass, bDeviceProtocol (descrito en Interface Descriptor) 0x40, // bMaxPacketSize0 EP0 is 64 bytes 0x6A,0x0B, // idVendor(L/H)--Maxim is 0B6A (2922 en decimal) 0x46,0x53, // idProduct(L/H)--5346 (21318 en decimal) 0x00,0x01, // bcdDevice--1234 1,2,3, // iManufacturer, iProduct, iSerialNumber 1}; // bNumConfigurations
const unsigned char CD[]= // CONFIGURATION Descriptor {0x09, // bLength 0x02, // bDescriptorType = Config 67,0x00, // wTotalLength(L/H) = Nº Bytes (67[0x43}) (Sin cabeceras 48[0x30]) 0x4300 0x02, // bNumInterfaces = 2 0x01, // bConfigValue 0x00, // iConfiguration 0xA0, // bmAttributes. b7=1 b6=NO self-powered b5=RWU supported (1 1 1 0 0 0 0 0) 0xFA, // MaxPower is 200 ma
// INTERFACE Descriptor 0x09, // length = 9 0x04, // type = IF 0x00, // InterFace Number = 0 0x00, // bAlternate Setting 0x01, // bNum Endpoints = 1 IN 0x02, // bInterfaceClass = 2 Communication 0x02, // bInterfaceSubClass = 2 0x01, // bInterfaceProtocol =1 (SubClass ACM=Abstract Control Mode, InterfaceProtocol=V.25ter, common AT commands) 0x00, // iInterface
// Header Functional Descriptor (marks beginning of the concatenated set of Functional Descriptors) 0x05, // bFunctionLength, Descriptor size in bytes --[18] --[48] 0x24, // bDescriptorType, CS_INTERFACE 0x00, // bDescriptorSubtype, Header Functional Descriptor 0x10,0x01, // bcdCDC, CDC specification release number in BCD format ([0x10, 0x01])
// Call Management Functional Descriptor 0x05, // bFunctionLength, Descriptor size in bytes 0x24, // bDescriptorType, CS_INTERFACE 0x01, // bDescriptorSubtype, Call Management Functional Descriptor 0x03, // bmCapabilities, Device doesn't call management itself (0->3) 0x01, // bDataInterface, Interface used for call management
// Abstract Control Management Functional Descriptor 0x04, // bDescriptorLength, Descriptor size in bytes 0x24, // bDescriptorType, CS_INTERFACE 0x02, // bDescriptorSubtype, Abstract Control Management Functional Descriptor 0x06, // bmCapabilities, Support for the GET/SET_LINE_CODING, BREAK & SET_CONTROL_LINE_STATE
// Union Functional Descriptor 0x05, // bFunctionLength, Descriptor size in bytes 0x24, // bDescriptorType, CS_INTERFACE 0x06, // bDescriptorSubtype, Union Functional Descriptor 0x00, // bMasterInterface, The controlling interface for the union (bInterfaceNumber of a Communication or Data Class interface in this configuration) 0x01, // bSlaveInterface0, The controlled interace in the union (bInterfaceNumber of an interface in this configuration)
Functional Descriptor***************** // Endpoint Descriptor EP3-IN 0x07, // bLength 0x05, // bDescriptorType (Endpoint) 0x83, // bEndpointAddress (EP3 IN) 0x03, // bmAttributes (interrupt = 3) 0x40,0x00, // wMaxPacketSize (64[0x0040]) 0x02, // bInterval, Maximum latency (0x02)
// INTERFACE Descriptor 0x09, // length = 9 --[44] 0x04, // type = IF 0x01, // InterFace Number = 1 0x00, // bAlternate Setting 0x02, // bNum Endpoints = 2 (IN&OUT) 0x0A, // bInterfaceClass = A (Data) 0x00,0x00, // bInterfaceSubClass, bInterfaceProtocol (SubClass ACM=Abstract Control Mode, InterfaceProtocol=V.25ter, common AT commands) 0x00, // iInterface
// Endpoint Descriptor EP1-OUT 0x07, // bLength 0x05, // bDescriptorType (Endpoint) 0x01, // bEndpointAddress (EP1-OUT) 0x02, // bmAttributes (bulk = 2) 0x40,0x00, // wMaxPacketSize (64[0x40]) 0x00, // bInterval (0x00)
// Endpoint Descriptor EP2-IN 0x07, // bLength 0x05, // bDescriptorType (Endpoint) 0x82, // bEndpointAddress (EP2-IN) 0x02, // bmAttributes (bulk = 2) 0x40,0x00, // wMaxPacketSize (64[0x40]) 0x00, // bInterval (0x00) };
// STRING descriptors. An array of string arrays
const unsigned char strDesc[][64]= { // STRING descriptor 0--Language string { 0x04, // bLength 0x03, // bDescriptorType = string 0x09,0x04 // wLANGID(L/H) = English-United Sates }, // STRING descriptor 1--Manufacturer ID { 34, // bLength (0x2C para 23 (incluyendo tamaño y tipo??) 0x03, // bDescriptorType = string 'x',0, 'x',0, 'x',0, 'x',0, ' ',0, 'x',0, 'x',0, 'x',0, 'x',0, 'x',0, 'x',0, 'x',0, 'x',0, 'x',0, 'x',0, 'x',0,// text in Unicode }, // STRING descriptor 2 - Product ID { 8, // bLength 0x03, // bDescriptorType = string 'X',0, 'X',0, 'X',0, },
// STRING descriptor 3 - Serial Number ID { 8, // bLength 0x03, // bDescriptorType = string 'X',0, 'X',0, 'X',0, }};
const unsigned char SerialConf[]= { 0x00,0xE8,0x03,0x00, // dwDTFRate. Baudrate (9600hex->38400 baud) 0, // bCharFormat. 1 Stop bit 0, // bParityType: none 8, // Number of data bits: 8 };
Hello Tsuneo,
I'm not sure to understand debug outputs for enumeration"...What does this mean? I don't use variables for debugging, just the program to enumerate the device....
The program is too extensive to copy here without write too much posts, if you want, I can send you to email. I did it from a example for a HID device from maxim web, www.maxim-ic.com/.../3690 , but fitted it to my application...
Greetings, Piru
"I'm not sure to understand debug outputs for enumeration"...What does this mean?"
I thought this debug output comes from the device, don't it?
128 6 0 1 0 0 64 0 0 5 12 0 0 0 0 0 ->Set address 128 6 0 1 0 0 18 0 128 6 0 1 0 0 9 0 128 6 0 1 0 0 64 0 ->Again first...
This code is the bus reset handler, excerpted from the Maxim example. www.maxim-ic.com/.../3690 - Did you insert any heavy routine here ?
EnumAppNote_BF1.C void service_irqs(void) { ... if(rreg(rUSBIRQ)& bmURESIRQ) { L1_ON // turn the BUS RESET light on L2_OFF // Suspend light off (if on) wreg(rUSBIRQ,bmURESIRQ); // clear the IRQ } if(rreg(rUSBIRQ) & bmURESDNIRQ) { L1_OFF // turn the BUS RESET light off wreg(rUSBIRQ,bmURESDNIRQ); // clear the IRQ bit Suspended=0; // in case we were suspended ENABLE_IRQS // ...because a bus reset clears the IE bits }
This excerpt shows the main (super) loop of the Maxim example. While the user task is a light one like this example, it works well. But when the user tasks grow and become heavy, the service to the USB tasks delays. And the timing issue occurs, as you are seeing now. To solve this problem, handle the service_irqs() in an interrupt.
Connect MAX3420E INT pin to the external interrupt pin (INT0 or INT1) of your MCU. In the ISR (Interrupt Service Routine) of the external INT, call service_irqs().
EnumAppNote_BF1.C void main(void) { initialize_MAX(); while(1) // endless loop { // ------------------- USB tasks if(Suspended) check_for_resume(); if (MAX_Int_Pending()) // <--- polling MAX3420E interrupt pin. service_irqs(); // ------------------- user task(s) msec_timer++; if(msec_timer==TWENTY_MSEC) { msec_timer=0; if((rreg(rGPIO) & 0x10) == 0) // Check the pushbutton on GPI-0 { inhibit_send = 0x00; // Tell the "do_IN3" function to send the text string L0_ON // Turn on the SEND light } blinktimer++; // blink the loop active light every half second if(blinktimer==BLINKTIME) { blinktimer=0; L3_BLINK } }// msec_timer==ONE_MSEC } // while(1) }// main
Tsuneo
I get to simplify the code text to fit in just a couple of posts...
BYTE SUD[9]; BYTE ep3stall=0, ep2stall=0, ep1stall=0; BYTE RWU_enabled; bool USB_ini() { USB_IO(); SPI_ini(0); // software flags configval=0; // No configured PC_DTR=0; Suspended=1; // USB suspended RWU_enabled=0; // Set by host Set_Feature(enable RWU) request USB_rcv_length=0; USB_command_EOF=0; USB_send_length=0; USB_CSE; wreg(rPINCTL,(bmFDUPSPI+gpxOPERATE)); // MAX3420: SPI=full-duplex, INT=Edge Mode, POSINT=0(neg.edge xa menor consumo), GPX=OPERATE wreg(rUSBCTL,0); wreg(rCPUCTL,bmIE); // Enables the INT pin ENABLE_IRQS; USB_CSD; return TRUE; } void check_for_resume(void) { SPI_ini(0); USB_CSE; if(rreg(rUSBIRQ) & bmBUSACTIRQ) // THE HOST RESUMED BUS TRAFFIC Suspended=0; // no longer suspended USB_CSD; } void service_irqs(void) { if(SPI_mode==1){SPI_CLOSE;}if(SPI_init==0){SPI_ini(0);} USB_CSE; BYTE itest1,itest2; itest1 = rreg(rEPIRQ); // Check the EPIRQ bits itest2 = rreg(rUSBIRQ); // Check the USBIRQ bits if(itest1 & bmSUDAVIRQ) { wreg(rEPIRQ,bmSUDAVIRQ); // clear the SUDAV IRQ do_SETUP(); } if(itest1 & bmOUT1DAVIRQ) // Rcved EP1 (Bulk) TOS_post(USB_Rcv_Data); if((configval != 0) && (itest2&bmSUSPIRQ)) // HOST suspended bus for 3 msec { wreg(rUSBIRQ,(bmSUSPIRQ+bmBUSACTIRQ)); // clear the IRQ and bus activity IRQ Suspended=1; // signal the main loop } if(rreg(rUSBIRQ)& bmURESIRQ) wreg(rUSBIRQ,bmURESIRQ); // clear IRQ if(rreg(rUSBIRQ) & bmURESDNIRQ) { wreg(rUSBIRQ,bmURESDNIRQ); // clear IRQ bit Suspended=0; // in case were suspended ENABLE_IRQS; } USB_CSD; } void do_SETUP(void) { readbytes(rSUDFIFO,8,SUD); // got a SETUP packet switch(SUD[bmRequestType]&0x60) // Parse the SETUP packet { case 0x00: std_request(); break; case 0x20: class_request(); break; default: STALL_EP0 // unrecognized } } void std_request(void) { switch(SUD[bRequest]) { case SR_GET_DESCRIPTOR: send_descriptor(); break; case SR_SET_INTERFACE: set_interface(); break; case SR_GET_INTERFACE: get_interface(); break; case SR_GET_CONFIGURATION: get_configuration(); break; case SR_SET_CONFIGURATION: set_configuration(); break; case SR_SET_ADDRESS: rregAS(rFNADDR); break; // discard return value default: STALL_EP0 } } void set_configuration(void) { configval=SUD[wValueL]; if(SUD[wValueL]!=0) { wreg(rCLRTOGS,bmCTGEP3IN); // Clear toggle data EP3 IN (Interrupt) wreg(rCLRTOGS,bmCTGEP1OUT); // Clear toggle data EP1 OUT (Bulk) SETBIT(rUSBIEN, bmSUSPIE); // start looking for SUSPEND interrupts rregAS(rFNADDR); // dummy read to set the ACKSTAT bit } } void get_configuration(void) { wreg(rEP0FIFO,configval); // Send the config value wregAS(rEP0BC,1); } void set_interface(void) // All we accept are Interface=0 and AlternateSetting=0, otherwise send STALL { if((SUD[wValueL]==0) // wValueL=Alternate Setting index &&(SUD[wIndexL]==0)) // wIndexL=Interface index rregAS(rFNADDR); // dummy read to set the ACKSTAT bit else STALL_EP0 } void get_interface(void) // Check for Interface=0, always report AlternateSetting=0 { if(SUD[wIndexL]==0) // wIndexL=Interface index { wreg(rEP0FIFO,0); // AS=0 wregAS(rEP0BC,1); // send one byte, ACKSTAT } else STALL_EP0 }
I add thesecond part... First...I should have commented you that the program runs over API based on tinyOS (meshnetics), and the main bucle (my aplication) is called every Xms, I say 'X', because I can configures it with '0 ms' to make it runs 'as fast as possible'...but don't know the exact time. About the debug outputs, I just copy the variable's values meanwhile I debug.
void send_descriptor(void) { WORD sendlen,desclen; BYTE *pDdata=NULL; // pointer to ROM Descriptor data to send bool send_ZLP=FALSE; desclen = 0; // check for zero as error condition (no statements satisfied) switch (SUD[wValueH]) // wValueH is descriptor type { case GD_DEVICE: desclen = DD[0]; // descriptor length pDdata = (BYTE *) DD; break; case GD_CONFIGURATION: desclen = CD[2]; // Config descriptor includes interface, header and ep descriptors (67 Bytes [0x0043]) pDdata = (BYTE *) CD; break; case GD_STRING: desclen = strDesc[SUD[wValueL]][0]; // wValueL=string index, array[0] is the length pDdata = (BYTE *) strDesc[SUD[wValueL]]; // point to first array element break; case CS_INTERFACE: if(SUD[wIndexL]==0) // Interface Number=0. Del EndPoint 2 (interrupción), Communication { desclen = CD[9]; pDdata = (BYTE *) &CD[9]; } else if (SUD[wIndexL]==1) // Interface Number=1. Del EndPoint 1 (bulk), Datos { desclen = CD[44]; pDdata = (BYTE *) &CD[44]; } break; } // end switch on descriptor type // if (desclen!=0) // one of the case statements above filled in a value { sendlen = desclen; // Send Lower data if((desclen % 64) == 0) send_ZLP=TRUE; // Flag to send ZLP while(desclen>0) { if(desclen>64) sendlen=64; // if packet>64 bytes else sendlen=desclen; // if packet<=64 bytes while(!(rreg(rEPIRQ) & bmIN0BAVIRQ));// While out buff. isn't free writebytes(rEP0FIFO,sendlen,pDdata);// Write data in FIFO (EP_CONTROL) pDdata=(pDdata+sendlen); if(desclen<64) wregAS(rEP0BC,sendlen); // load EP0BC to arm the EP0-IN transfer & ACKSTAT else wreg(rEP0BC,sendlen); // load EP0BC to arm the EP0-IN transfer & ACKSTAT desclen-=sendlen; // Resta los datos ya enviados } if(send_ZLP) wregAS(rEP0BC,0); // load EP0BC to arm the EP0-IN transfer & ACKSTAT } else STALL_EP0; // none of the descriptor types match } void class_request(void) { switch (SUD[bRequest]) { case SEND_ENCAPSULATED_COMMAND: wregAS(rEP0BC, 0); break; // send_encapsulated_command(); break; case GET_ENCAPSULATED_RESPONSE: wregAS(rEP0BC, 0); break; // send_encapsulated_command(); break; // get_encapsulated_response(); break; case SET_COMM_FEATURE: break; case GET_COMM_FEATURE: break; case CLEAR_COMM_FEATURE:break; case SET_LINE_CONDING: { wregAS(rEP0BC, 0); // EnvÃa un paquete de longitud 0 para la etapa de 'STATUS' (?) if(SUD[wLengthL]>0) { while(!(rreg(rEPIRQ) & bmOUT0DAVIRQ) && SUD[7]<=5) SUD[7]++; readbytes(rEP0FIFO,SUD[wLengthL],(uint8_t *)&SUD[0]); // Lee la configuración serie por el EP0 (hay que cambiar algo para que lea?) } } break; case GET_LINE_CODING: { wregAS(rEP0BC, 0); // EnvÃa un paquete de longitud 0 para la etapa de 'STATUS' (?) writebytes(rEP0FIFO,SUD[wLengthL],(uint8_t *)&SerialConf[0]); wreg(rEP0BC, SUD[wLengthL]); } break; case SET_CONTROL_LINE_STATE: { if(configval) { USB_init=1; // Si llega a este punto, el dispositivo ha terminado de inicializarse if((SUD[wValueL]&0x01)==0x01)// PC listo para aceptar tramas del AZC PC_DTR=1; if(SUD[wValueL]==0x00) // Se ha cortado la comunicación con el PC PC_DTR=0; } wregAS(rEP3INBC, 0); } break; case SEND_BREAK: break; default: STALL_EP0; // none of the descriptor types match } } void wreg(BYTE reg, BYTE dat) { uint8_t data[2]; USB_CSE; data[0]=reg+2; data[1]=dat; spi_readWrite(data, 2); USB_CSD; } // Write a MAX3410E register with the "ACK STATUS" bit set in the command byte void wregAS(BYTE reg, BYTE dat) { uint8_t data[2]; USB_CSE; data[0]=reg+3; data[1]=dat; spi_readWrite(data, 2); USB_CSD; } // Read a register, return its value. uint8_t rreg(BYTE reg) { uint8_t dat[2]; dat[0]=reg; USB_CSE; spi_readWrite(&dat[0], 2); USB_CSD; return(dat[1]); } // Read a byte (as rreg), but also set the AckStat bit in the command byte. BYTE rregAS(BYTE reg) { uint8_t dat[2]; dat[0]=reg+1;; USB_CSE; spi_readWrite(&dat[0], 2); USB_CSD; return(dat[1]); } void readbytes(BYTE reg, BYTE N, uint8_t *p) { // uint8_t ind_read=0; p[0]=reg; USB_CSE; spi_readWrite(&p[0], N+1); USB_CSD *(uint64_t*)p=*(uint64_t*)(p+1); // for(ind_read=0;ind_read<N;ind_read++) // p[ind_read] = rreg(reg); } void writebytes(BYTE reg, BYTE N, uint8_t *p) { uint8_t ind_write=0; for(ind_write=0;ind_write<N;ind_write++) wreg(reg, p[ind_write]); }
"About the debug outputs, I just copy the variable's values meanwhile I debug."
I see.
As of your above code, - Bus reset handler is fine. No problem. - Unfortunately, you didn't include the super loop which calls service_irqs() The problem lies in the way how service_irqs() is called.
"I should have commented you that the program runs over API based on tinyOS (meshnetics), and the main bucle (my aplication) is called every Xms, I say 'X', because I can configures it with '0 ms' to make it runs 'as fast as possible'...but don't know the exact time."
I suppose service_irqs() is called from your "main bucle" using polling, like Maxim example. Is your tinyOS support interrupt? Then, move service_irqs() call to external INT pin ISR, which is connected to MAX3420E INT pin.
Hi, do you debug while plugging it in? I have the problem, that USB enumeration does not work while debugging (even without any breakpoint). The device sends vendor and device ID 0000. When I do not debug (load/start the app out of DataFlash) the enumeration works fine on Windows XP.
"I have the problem, that USB enumeration does not work while debugging (even without any breakpoint)."
This problem often occurs on the board which has a fixed USB D+ (or D-) pull-up resistor, like MCB2140 - R35 MCBSTR7 - D+ resistor is included in USBUF01W6 - fixed
When the pull-up resistor is controlled by MCU, the problem doesn't occur (as long as the firmware handles it properly).
These boards are fine at this point. MCB2300 - R3 MCB2460 - OTG - controlled by ISP1301 MCBSTR750 - R29 MCBSTR9 - R29 MCBSTM32(E) - R29
Yes, I try to run it without debuging of course, but don't work anyway...
----
About the interrupts, yes it's supported (as far as I know). 'service_irqs()' is called every time that MAXIM3420 actives interrupt, is not inside a (super) loop...directly...Maybe it's inside a superloop because it is (more than) possible that API has a task manager (that is the reason I have to define a 'user loop call time'). Now I don't use interrupts, put 'service_irqs()' into 'user entry' function where I have to define the time that the main loop is called. Just a while loop calling 'service_irqs()', nothing more to do...in Vista it works, but in XP it don't work yet...
I spent some time looking for a solution without results, but I found other things, that maybe gives any clue to anybody that have suffered the same problem...
It seems that it works in a laptop with Vista, many USB controller OHCI, and a EHCI (PCI to USB Enhanced Host Controller). It has the same resources than the PC, but the controller is OpenHCD, and the PC uses "SiS 7001".
I found many forums where peoples have many problems with this controller "SiS 7001", I don't know if somebody knows something about this...
I try to disable the Enhanced controller, but nothing, only says that memory pen can works faster because is USb 2.0...
The USBView utility tells me that my device has a Low Bus Speed (this application can read PID and VID also)...any clue...?
thank you in advance, and sorry for the insistence...
I reviewed this problem, and another possibility. It may relate to suspend after connection, before bus reset. To confirm it, disable suspend temporarily.
bool USB_ini() { ... // Suspended=1; // USB suspended <---- disable suspend temporarily
Finally I found the error!! Now I hace an initialization error, but it recognizes the device.
Looking for the differences between descriptors requeriments of Vista and XP, I found that in XP you should return the exact number of Bytes of the descriptors requested, but if the request is higher, you have to truncate the number to the exact number of descriptor types... I neglected this...
Sorry for the inconveniences, and than you very much for your time!
I hope this error be useful to more people...
Greetings!
Well, now the device works all right.
I would like to explain exactly the problem to help to anyone who needs it.
When PC ask me for a configuration device, I returned the length of the descriptor, but sometimes the machine just ask for a part of the descriptor...
when ask for 255 bytes, return 67 bytes that I defined...when ask for 9 I also returned 67...but it's wrong, you have to return the bytes for the descriptor part that PC ask you, it could be 9 or 67, depending of the request... well, I think it's no necessary that I explain the full real example ...
I seem to be having very similar problems and I can't get the hardware working with the instructions in the last post. Would you mind posting your send_descriptor function so I can track down whats going wrong.
Thanks
I just copy send descriptor function and try to change tabulations...but it's not very 'readable', but if you copy the code to an empty text file, you could read it clear. I Hope this will be useful ;)
Greetings.
void send_descriptor(void) { static WORD sendlen,desclen; BYTE *pDdata=NULL; // pointer to ROM Descriptor data to send bool send_ZLP=FALSE; // Zero Length packet Flag desclen = 0; // check for zero as error condition (no case statements satisfied) switch (SUD[wValueH]) // wValueH is descriptor type { case GD_DEVICE: /*if(SUD[wLengthL]>DD[0])*/ desclen = DD[0]; // descriptor length //else desclen = SUD[wLengthL]; pDdata = (BYTE *) DD; break; case GD_CONFIGURATION: if(SUD[wLengthL]>CD[2]) desclen = CD[2]; // Config descriptor includes interface, header and ep descriptors (67 Bytes [0x0043]) else desclen = SUD[wLengthL]; if(SUD[wLengthH]!=0) desclen = CD[2]; // wLengthH=1 (last byte) pDdata = (BYTE *) CD; break; case GD_STRING: desclen = strDesc[SUD[wValueL]][0]; // wValueL=string index, array[0] is the length pDdata = (BYTE *) strDesc[SUD[wValueL]]; // point to first array element break; case CS_INTERFACE: if(SUD[wIndexL]==0) // Interface Number=0. Del EndPoint 2 (interrupción), Communication { /*if(SUD[wLengthL]>CD[9]) */desclen = CD[9]; //else desclen = SUD[wLengthL]; pDdata = (BYTE *) &CD[9]; } else if (SUD[wIndexL]==1) // Interface Number=1. Del EndPoint 1 (bulk), Datos { /*if(SUD[wLengthL]>CD[44])*/ desclen = CD[44]; //else desclen = SUD[wLengthL]; pDdata = (BYTE *) &CD[44]; } break; } // end switch on descriptor type // if (desclen!=0) // one of the case statements above filled in a value { sendlen = desclen; // Send lower size of data if((desclen % 64) == 0) send_ZLP=TRUE; // Send zero length packet Flag Flag para enviar paquete de tamaño 0 while(desclen>0) // Meanwhile there are data to be sent { if(desclen>64) sendlen=64; // If there are more than 64 bytes in the packet else sendlen=desclen; // If there are less than 64 bytes in the packet while(!(rreg(rEPIRQ) & bmIN0BAVIRQ));// Meanwhile output buffer isn't free writebytes(rEP0FIFO,sendlen,pDdata);// Write data to EP_CONTROL FIFO pDdata=(pDdata+sendlen); // Increments data pointer !! ********(VERY IMPORTANT)******* if(desclen<64) wregAS(rEP0BC,sendlen); // load EP0BC to arm the EP0-IN transfer & ACKSTAT else wreg(rEP0BC,sendlen); // load EP0BC to arm the EP0-IN transfer & ACKSTAT desclen-=sendlen; // Substract sent data } if(send_ZLP) wregAS(rEP0BC,0); // load EP0BC to arm the EP0-IN transfer & ACKSTAT } else STALL_EP0; // none of the descriptor types match }