CDC_BulkIn in USB CDC Example?

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

CDC_BulkIn in USB CDC Example?

853 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by ktownsend on Mon Jan 10 03:46:50 MST 2011
I've been trying to get the USB CDC example working dependably, and the only way I've found so far is a shameless hack that's too slow and ugly for production use.

The board instantiates properly as a USB CDC device, and receiving data on the LPC1343 works fine (CDC_BulkOut in cdcuser.c), but CDC_BulkIn never seems to get called by itself to send buffered data out. The only 'solution' I've found is the following, which requires a huge delay to work:

extern void usbcdcSendByte(uint8_t c)
{
  // Massive ugly delay required
  uint32_t i, delay;
  delay = ((CFG_CPU_CCLK/SCB_SYSAHBCLKDIV) / 25000);
  for ( i = 0; i < delay; i++ )
  {
    __asm("nop");
  }
  // Send byte to EP
  USB_WriteEP (CDC_DEP_IN, (unsigned char *)&c, 1);
  CDC_DepInEmpty = 1;
}


Without the delay and manually writing to the EP, the output drops about 80% of the characters, but with the delay it's too hopelessly slow to do anything useful. Is there something evident I should be doing to get CDC_BulkIn to fire automatically, check the buffer and send data out by itself?

I would have expected to just make a buffer (instead of UART in the sample code), write to the buffer as required, and BulkIn would fire to output the buffered data on a regular basis. Obviously I've completely misunderstood how CDC_BulkIn works, but where should this method be called from then or when would be the appropriate time to check and empty buffered data into the EP?
0 Kudos
10 Replies

739 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by radu.moisan on Wed Sep 21 14:23:03 MST 2011
After some digging I found the missing puzzle.
[FONT=Courier New]USB_EndPoint3[/FONT] is supposed to be called on EP3 interrupt, that is for either IN or OUT events. The thing is, by default, the interrupt is triggered for "[I]successful transactions[/I]" and that is

[LIST]
[*]for bulk OUT endpoint we have a successful transaction if the data is received by the device without error
[*]for bulk IN endpoint we have a successful transaction if the data is send to host without error
[/LIST]
For bulk IN ep, however, we want the following behaviour. The host is periodically sending IN tokens and if the FIFO corresponding to that particular endpoint is empty, the device will return a NAK. Now, the usb core can trigger an endpoint interrupt at this point and offer us the chance to load our data into endpoint's FIFO (through CDC_BulkIn). Interrupt on NAK for bulk OUT endpoints is disabled by default so we need to enable it(see SetMode command in LPC1343 User Manual).
void USB_Reset (void) {

  LPC_USB->DevIntClr = 0x000FFFFF;
  /* Enable all eight(8) EPs, note: EP won't be ready until it's
  configured/enabled when device sending SetEPStatus command 
  to the command engine. */ 
  LPC_USB->DevIntEn  = DEV_STAT_INT | (0xFF<<1) |
               (USB_SOF_EVENT   ? FRAME_INT : 0);
  /* enable interrupt on NAK for bulk OUT endpoints */
  WrCmdDat(CMD_SET_MODE, DAT_WR_BYTE(INAK_AI));
  return;
}
0 Kudos

739 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by radu.moisan on Wed Sep 21 05:26:56 MST 2011
@crastin At a first look I could not see anything wrong with your code. If [FONT=Courier New]Buff[/FONT] is valid and contains [FONT=Courier New]numBytesWright[/FONT] bytes then it should work. However you could use [FONT=Courier New](uint8_t*)Buff [/FONT]instead of  [FONT=Courier New](unsigned char*)&Buff[0] [/FONT]it is the same thing after all but gives better readability.[FONT=Courier New]
[/FONT]
0 Kudos

739 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by radu.moisan on Wed Sep 21 05:15:02 MST 2011
@KTownsend You are right. As I understand it, [FONT=Courier New]CDC_BulkIn[/FONT] should be called automatically by usb code. [FONT=Courier New]CDC_BulkIn[/FONT] is called by [FONT=Courier New]USB_EndPoint3[/FONT] which is called from the interrupt routine. However the interrupt for IN event is not triggered. This is what I am looking into right now.

Keep me in the loop if you catch something I missed.
0 Kudos

739 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by crastin on Mon Apr 04 22:35:38 MST 2011
KTownsend please posted an example where you use a  CDC_BulkOut. When I use it MCU is down.
Fast USB Send to host looks like


Quote:

if (CDC_DepInEmpty) {
      CDC_DepInEmpty = 0;
      USB_WriteEP (CDC_DEP_IN, (unsigned char *)&Buff[0], numBytesWright);
    }

0 Kudos

739 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Ex-Zero on Tue Jan 11 17:57:33 MST 2011
In http://www.usbmadesimple.co.uk/ums_6.htm

1 ms Frame (and a lot of basic USB stuff) is described.
0 Kudos

739 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by ktownsend on Tue Jan 11 16:49:08 MST 2011
I spent a bit of time playing with this today and you indeed can't send more than one frame per ms ... buffering all the incoming content and then writing it out one frame per millisecond works fine, though I'll need to find the right balance between filling the buffer and sending the contents out in a timely way.  Thanks again for the clarification.
0 Kudos

739 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by ktownsend on Mon Jan 10 12:02:31 MST 2011

Quote: Zero
I like to work with REDLIB, AFAIK NEWLIB is larger.
Of course printf (with CR_INTEGER_PRINTF option) costs me a few kB,
but makes developing easier and therefore faster :)



Sorry ... I noticed it wasn't newlib and modified the post. ;) I'll have to play around with Redlib a bit ... with 32KB even 1-2KBs helps. I actually kind of like playing with these 32KB parts, though, compared to the larger 1700s, etc. It makes you think a lot harder about what you're writing and whether you can do it a bit better, though the code size on the M3/M0 is also visibly better than ARM7 which helps.
0 Kudos

739 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Ex-Zero on Mon Jan 10 11:30:14 MST 2011
I like to work with REDLIB, AFAIK NEWLIB is larger.
Of course printf (with CR_INTEGER_PRINTF option) costs me a few kB,
but makes developing easier and therefore faster :)
0 Kudos

739 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by ktownsend on Mon Jan 10 06:25:27 MST 2011
Zero:

Thanks ... I noticed that when I buffer it that it works better, I just wish I knew what was going on under the hood with the BulkIn and BulkOut methods and how to get BulkIn firing automatically. The code isn't terribly clear at times. I'll probably just make a buffer and call the endpoint from the 1ms systick timer. I don't really like it, but I know there are more than enough free ticks between timer interrupts to handle a few bytes of data over USB.

The license is also very unclear. You can use the USB code in commercial products as long as it's an NXP processor, but there is absolutely no provision for sharing the source of your project if you use the Keil code. I'm really in limbo about whether I can include this with an otherwise BSD-licensed project, for example (keeping the same license terms for the USB code). :confused:

Kevin

Update:

I suppose I could also handle this the same way as the example you gave. I'm doing something almost identical, but on the __putchar level which is obviously what's causing the problem.

/**************************************************************************/
/*! 
    @brief Sends a single byte to a pre-determined peripheral (UART, etc.).
    @param[in]  byte
                Byte value to send
*/
/**************************************************************************/
void __putchar(const char c) 
{
  #ifdef CFG_PRINTF_UART
    // Send output to UART
    uartSendByte(c);
  #endif
  #ifdef CFG_PRINTF_USBCDC
    // Send output to USB if connected
    if (USB_Configuration) {
      usbcdcSendByte(c); 
    }
  #endif
  #ifdef CFG_PRINTF_CWDEBUG
    // Send output to the CW debug interface
    debug_printf("%c", c);
  #endif
}
0 Kudos

739 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Ex-Zero on Mon Jan 10 05:02:39 MST 2011
Sorry, can't explain this USB stuff, don't understand it myself.

Found a 1343 code I worked at month ago to use wonderful printf and switch from UART to USB output.

I'm no USB expert, but as far as I remember it was a stupid idea to write a single character in 64 byte USB buffer.

AFAIR USB reads this IN buffer every 1ms (could cause your problems) and so I decided to write my data in a buffer and let USB do it's job.

Worked fine for me

//use printf output to write UART or USB 
#define USB_OUTPUT
 
//globals
volatile unsigned int string_counter;
volatile char string_buffer[64];
 
#ifdef USB_OUTPUT
int __write (int iFileHandle, uint8_t *pcBuffer, uint8_t iLength)
{
 unsigned char len_count;      //len counter
 unsigned char print=0;       //print flag
 for(len_count=0;len_count< iLength;len_count++)//read loop
 {
  string_buffer[string_counter]= *pcBuffer;  //fill buffer
  if(*pcBuffer == 0x0D)print=1;     //check return
  pcBuffer++;         //inc buffer pointer
  string_counter++;        //inc counter
 }
 if((string_counter>63) || print)    //if max buffer or return
 {
  USB_WriteEP (CDC_DEP_IN, (unsigned char *)&string_buffer[0], string_counter);
  string_counter=0;        //reset counter
 }
 return iLength;
}
 
#else
// Function __write() ->printf
int __write (int iFileHandle, uint8_t *pcBuffer, uint8_t iLength)
{
 UARTSend(pcBuffer,iLength);
 return iLength;
}
#endif
0 Kudos