Using the WriteEP function from the USB ROM drivers in LPCOpen

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

Using the WriteEP function from the USB ROM drivers in LPCOpen

2,595 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by psatyshur on Thu Nov 07 18:41:49 MST 2013
Hi,

I am building a project that will combine the freeRTOS example with the USB CDC example from LPCOpen V2. I have most of the basic stuff done, but I am having problems with the function WriteEP. Specifically, I am using the provided VCOM write function:

/* Virtual com port write routine*/
uint32_t vcom_write(uint8_t *pBuf, uint32_t len)
{
VCOM_DATA_T *pVcom = &g_vCOM;
uint32_t ret = 0;

if ( (pVcom->tx_flags & VCOM_TX_CONNECTED) && ((pVcom->tx_flags & VCOM_TX_BUSY) == 0) ) {
pVcom->tx_flags |= VCOM_TX_BUSY;

/* enter critical section */
NVIC_DisableIRQ(USB0_IRQn);
ret = USBD_API->hw->WriteEP(pVcom->hUsb, USB_CDC_IN_EP, pBuf, len);
/* exit critical section */
NVIC_EnableIRQ(USB0_IRQn);
}

return ret;
}


If I call this function slowly (as in, but a delay after each call to this function), everything works fine, but when I call it quickly, there are problems. It appears that the WriteEP function will only let me write to the endpoint once. All other writes are ignored until the computer requests the data from the endpoint. Is this correct?

If so, is there anything stopping me from putting the WriteEP function inside the VCOM_bulk_in_hdlr function, which (I assume) is called inside the USB interrupt? I would then need some sort of buffer that I can fill up with data, and then write it all to the endpoint when computer requests it.
Labels (1)
4 Replies

2,200 Views
vaezi57
Contributor I

Hi

before sending data to Vcom wait white busy flag is 1.

/* Virtual com port write routine*/
uint32_t vcom_write(uint8_t *pBuf, uint32_t len)
{
 VCOM_DATA_T *pVcom = &g_vCOM;
 uint32_t ret = 0;
 int retry=0;
 
 // if ((pVcom->tx_flags & VCOM_TX_CONNECTED) && ((pVcom->tx_flags & VCOM_TX_BUSY) == 0))
 if (pVcom->tx_flags & VCOM_TX_CONNECTED)
 {
 retry=0;
 while((pVcom->tx_flags & VCOM_TX_BUSY) && retry<50000 ) 
 {
 retry++;
 }
 /* enter critical section */
 NVIC_DisableIRQ(USB0_IRQn);
 pVcom->tx_flags |= VCOM_TX_BUSY;
 ret = USBD_API->hw->WriteEP(pVcom->hUsb, USB_CDC_IN_EP, pBuf, len);
 /* exit critical section */
 NVIC_EnableIRQ(USB0_IRQn);
 }
return ret;
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos

2,200 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by brownm on Tue Nov 11 12:26:45 MST 2014
Works Great for me as well, thanks for posting

Just needed the following smalll additions in vcom_init


if (ret == LPC_OK) {
/* allocate transfer buffers */
g_vCOM.rx_buff = (uint8_t *) cdc_param.mem_base;
cdc_param.mem_base += VCOM_RX_BUF_SZ;
cdc_param.mem_size -= VCOM_RX_BUF_SZ;


/* allocate transfer buffers */
g_vCOM.tx_buff = (uint8_t *) cdc_param.mem_base;    //<-------- need to init the tx_buff 
cdc_param.mem_base += VCOM_TX_BUF_SZ;           //<--------this will need a #define added as well
cdc_param.mem_size -= VCOM_TX_BUF_SZ;





Regards
Marshall
0 Kudos

2,200 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by hetii on Sun Oct 19 15:17:26 MST 2014
Hi there

I just play with lpc1769 board and notice exactly the same behavior like you, about using multiple times of vcom_write.

General its a shame that NXP don`t put any explanation, for many topics like this...

I hope that comunity will grow up and maybe in the future we get better "know how" about nxp world.

BTW,
For me works also a blocking way where I wait till VCOM_TX_BUSY is cleared.

Its a nasty way but works for beginning.

/* Virtual com port write routine*/
uint32_t vcom_write(uint8_t *pBuf, uint32_t len)
{
VCOM_DATA_T *pVcom = &g_vCOM;
uint32_t ret = 0;

//if ( (pVcom->tx_flags & VCOM_TX_CONNECTED) && ((pVcom->tx_flags & VCOM_TX_BUSY) == 0) ) {
if ( (pVcom->tx_flags & VCOM_TX_CONNECTED)) {
while((pVcom->tx_flags & VCOM_TX_BUSY) != 0);
pVcom->tx_flags |= VCOM_TX_BUSY;

/* enter critical section */
NVIC_DisableIRQ(USB_IRQn);
ret = USBD_API->hw->WriteEP(pVcom->hUsb, USB_CDC_IN_EP, pBuf, len);
/* exit critical section */
NVIC_EnableIRQ(USB_IRQn);
}

return ret;
}


Regards.

2,200 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by psatyshur on Sun Nov 17 15:54:29 MST 2013
It's pretty quiet in here. I fixed the problem, and I thought I would respond to my own thread in case someone else has a similar problem and wants to know how to fix it.

I found a very helpful thread on this topic here (http://www.lpcware.com/content/forum/problem-with-subsequent-calls-to-writeep). Basically, the computer will poll the device at fixed intervals, and anything written to the endpoint using USBD_API->hw->WriteEP() is sent to the computer when this happens. However, WriteEP() can only be called once until the computer retrieves the data. So, how do we deal with a program that wants to write several small bits of text the the endpoint? Well, I don't know if this is the best way, but this way seems to work for me:

First, I modify the VCOM_DATA_T struct to contain a transmit buffer pointer and a transmit data length.

/**
 * Structure containing Virtual Comm port control data
 */
typedef struct VCOM_DATA {
USBD_HANDLE_T hUsb;
USBD_HANDLE_T hCdc;
uint8_t *rx_buff;
uint16_t rx_rd_count;
uint16_t rx_count;
uint16_t tx_buff_count;//
uint8_t *tx_buff;//
volatile uint16_t tx_flags;
volatile uint16_t rx_flags;
} VCOM_DATA_T;


The VCOM_TX_BUSY mask in the 'tx_flags' of that struct indicate that a write to the endpoint has occured and the computer has not yet read the data. In the example vcom_write() function, this causes the function to discard any writes until this flag is cleared. I modify the vcom_write() function to instead put this data into the transmit buffer.

/* Virtual com port write routine*/
uint32_t vcom_write(uint8_t *pBuf, uint32_t len)
{
VCOM_DATA_T *pVcom = &g_vCOM;
uint32_t ret = 0;

if ( (pVcom->tx_flags & VCOM_TX_CONNECTED))
{
if ((pVcom->tx_flags & VCOM_TX_BUSY) == 0)
{
//This flag is set to zero when in the bulk interrupt when no more data is ready to send
pVcom->tx_flags |= VCOM_TX_BUSY;
/* enter critical section */
NVIC_DisableIRQ(USB0_IRQn);
ret = USBD_API->hw->WriteEP(pVcom->hUsb, USB_CDC_IN_EP, pBuf, len);
/* exit critical section */
NVIC_EnableIRQ(USB0_IRQn);
}
else
{
if((len + (pVcom->tx_buff_count)) < VCOM_TX_BUF_SZ)
{
/* enter critical section */
NVIC_DisableIRQ(USB0_IRQn);
memcpy(&pVcom->tx_buff[pVcom->tx_buff_count], pBuf, len);
pVcom->tx_buff_count += len;
ret = pVcom->tx_buff_count;
/* exit critical section */
NVIC_EnableIRQ(USB0_IRQn);
}
}
}
return ret;
}


Note that the write to the buffer must be surrounded by NVIC_DisableIRQ(USB0_IRQn) and NVIC_EnableIRQ(USB0_IRQn) or a (I assume) race condition occurs as to whether the buffer is written before the computer polls the endpoint.

Next the VCOM_TX_BUSY flag is cleared in an interrupt called by the bulk in endpoint handler, VCOM_bulk_in_hdlr. (Side note: that's 'in' to the computer, not 'in' to your embedded device). It should also be noted that this interrupt is called only when the computer actually reads data from your device. If it polls the device and there is no new data, this interrupt does not fire. The original function simply clears the VCOM_TX_BUSY flag and returns. I modify this function to check if there is any data waiting to send in the transmit buffer, and to send that if there is. Only if there is no data waiting, does it clear the flag.

/* VCOM bulk EP_IN endpoint handler */
static ErrorCode_t VCOM_bulk_in_hdlr(USBD_HANDLE_T hUsb, void *data, uint32_t event)
{
VCOM_DATA_T *pVcom = (VCOM_DATA_T *) data;

if (event == USB_EVT_IN) {
if(pVcom->tx_buff_count > 0)
{
USBD_API->hw->WriteEP(pVcom->hUsb, USB_CDC_IN_EP, pVcom->tx_buff, pVcom->tx_buff_count);
pVcom->tx_buff_count = 0;
}
else
{
pVcom->tx_flags &= ~VCOM_TX_BUSY;
}
}
return LPC_OK;
}


It should be noted in my example that my 'tx_buff' size is 64 bytes. This is the packet size of the endpoint, so I only ever have to send one extra packet of data in 'VCOM_bulk_in_hdlr'. This seems to work just fine for my application of sending short status strings back to the computer for debug or human control of my device. If I wanted to make the buffer longer, a few things would have to change.

I am pretty much a noob when it comes to USB, so please correct me if my explanation is wrong or incomplete.