cancel
Showing results for 
Search instead for 
Did you mean: 

Yet another Virtual COM Port Issue/Question with STM32F103

Popcorn
Associate II
Posted on March 15, 2011 at 16:39

Yet another Virtual COM Port Issue/Question with STM32F103

6 REPLIES 6
tsuneo
Senior
Posted on May 17, 2011 at 14:28

> When I try to send a string of bytes from the STM32, for some reason the Laptop only receives a single byte. On an infrequent basis it will receive the full string of information.

Sound like the endpoint buffer is overwritten by the next USB_SIL_Write() call, before current transaction finishes. Do you call your USB_TX() repeatedly, without checking the endpoint status?

Tsuneo

Popcorn
Associate II
Posted on May 17, 2011 at 14:28

Yes, I do call the USB_TX repeatedly, but not always in a very short period of time. There is usually a delay of at least a minute between calls to USB_TX.

This sometimes sends one byte when requesting a full string, even on the first transmission after startup.

I've changed my USB_TX around to try and work with what you're thinking of but I have the same results. It intermittently sends only one byte.

void USB_TX(u8 * sendData, int length)

{

    while (GetEPTxStatus(ENDP1) != EP_TX_VALID)

    {

        SetEPTxValid(ENDP1);

    }

    USB_SIL_Write(EP1_IN, sendData, length);

}

What do you think? Should I change this around differently?

tsuneo
Senior
Posted on May 17, 2011 at 14:28

> Should I change this around differently?

Here it is,

void USB_TX(u8 * sendData, int length)

{

    while (GetEPTxStatus(ENDP1) != EP_TX_NAK); // wait until the last transaction finishes

                                               // now, the endpoint buffer is available

    USB_SIL_Write(EP1_IN, sendData, length);   // copy data to the endpoint buffer

    SetEPTxValid(ENDP1);                       // make the endpoint ready for transaction

}

As of the background,

1) IN endpoint has a dedicated buffer. USB_SIL_Write() copies data to this buffer.

2) SetEPTxValid() notifies to the USB engine, that the endpoint is ready. The endpoint status goes to EP_TX_VALID

3) After this call, the USB engine waits for an IN transaction from the host.

4) Upon an IN transaction, the USB engine sends the data on the buffer to the bus.

5) Just when this transaction finishes, the endpoint status changes to EP_TX_NAK. Now, the endpoint buffer is empty. We may write a next packet to the buffer.

Above code polls the endpoint status using GetEPTxStatus()

You may use the endpoint interrupt to know the timing when the transaction completes.

IN endpoint interrupt is often used to send large data, greater than 64 bytes, splitting into full-size (64 bytes) transactions and the last short packet.

Tsuneo

Popcorn
Associate II
Posted on May 17, 2011 at 14:28

Unfortunately it performs the same way after the function change.

What is the Endpoint Interrupt?

Is it the Callback Function? For example, EP1_IN_Callback(void)?

tsuneo
Senior
Posted on May 17, 2011 at 14:28

How many bytes do you send with single USB_SIL_Write() call?

Above code supposes up to 64 bytes for single call.

To send just 64 bytes, the second USB_TX() call is required to terminate the transfer with ZLP (Zero-Length Packet)

USB_TX( buffer, 64 );   // send 64 bytes transaction

USB_TX( NULL, 0 );      // send ZLP

To send more than 64 bytes, the transfer is split into transactions.

For example of 100 bytes,

USB_TX( buffer, 64 );      // send the first 64 bytes

USB_TX( buffer + 64, 36 ); // send the next 36 bytes

But if you need to call multiple USB_TX() contiguously to send large data, like above examples, the endpoint interrupt (callback) gives better implementation.

//

// send greater than 64 bytes, too

//

#define CDC_BULK_IN_EP_SIZE   64

typedef struct {

    struct {

        unsigned occupied : 1;

        unsigned send_ZLP : 1;

    };

    u8 * buffer;

    int  length;

} t_CDC_bulk_IN;

volatile t_CDC_bulk_IN  cdc_bulk_IN;

void CDC_Block_TX(u8 * sendData, int length)

{

    int len;

    while (cdc_bulk_IN.occupied);              // wait until the last transaction finishes

                                               // now, the endpoint is available for this routine

                                               // send ZLP, when the transfer size is 

                                               // just the multiple of the wMaxPacketSize of the EP

    cdc_bulk_IN.send_ZLP = ((length & (CDC_BULK_IN_EP_SIZE - 1)) == 0);

                                               // calculate the first transaction size

    len = (length < CDC_BULK_IN_EP_SIZE) ? length : CDC_BULK_IN_EP_SIZE;

    cdc_bulk_IN.buffer = sendData + len;       // for the next transaction

    cdc_bulk_IN.length = length - len;

    cdc_bulk_IN.occupied = TRUE;

                                               // send the first transaction

    USB_SIL_Write(EP1_IN, sendData, len);      // copy data to the endpoint buffer

    SetEPTxValid(ENDP1);                       // make the endpoint ready for transaction

}

usb_conf.h

/* CTR service routines */

/* associated to defined endpoints */

// #define  EP1_IN_Callback   NOP_Process   // <------ comment this line

usb_endpoint.c

// send the second and later transactions

void EP1_IN_Callback( void )

{

    int len;

    if (! cdc_bulk_IN.occupied)  return;

                                               // calculate the next transaction size

    len = (cdc_bulk_IN.length < CDC_BULK_IN_EP_SIZE) ? cdc_bulk_IN.length : CDC_BULK_IN_EP_SIZE;

    if ( (len != 0) || cdc_bulk_IN.send_ZLP) {

        USB_SIL_Write(EP1_IN, cdc_bulk_IN.buffer, len);  // copy data to the endpoint buffer

        SetEPTxValid(ENDP1);                   // make the endpoint ready for transaction

    } else {

        cdc_bulk_IN.occupied = FALSE;          // finish transfer

        return;

    }

                                               // for the next transaction

    if ( len == 0 ) {

        cdc_bulk_IN.send_ZLP = FALSE;

    }

    cdc_bulk_IN.buffer += len;

    cdc_bulk_IN.length -= len;

}

usb_prop.c

void Virtual_Com_Port_SetConfiguration(void)

{

  DEVICE_INFO *pInfo = &Device_Info;

  if (pInfo->Current_Configuration != 0)

  {

    /* Device configured */

    bDeviceState = CONFIGURED;

    cdc_bulk_IN.occupied = FALSE;              // initialize variables for USB communication

  }

}

Tsuneo

Popcorn
Associate II
Posted on May 17, 2011 at 14:28

Wow, that's great. I'll try that out today.

I'm sending a variable amount. Right now it will not send more than a 40 Byte string, but I'm trying to get the USB sorted out so it can send as much as 1.5 KByte Strings.

The Zero Length Packet is very useful information. I didn't know that at all. In fact, when receiving information in excess of 64 Bytes, it sometimes receives Garbage information. One of the tasks of this device is to receive up to 161 KBytes of information during each run cycle through the USB from the computer, and during every transmission of such size there are a few bytes of information that come in as garbage. Do I need to implement some sort of a ZLP for when the STM32 receives as well?