2011-03-15 08:39 AM
Yet another Virtual COM Port Issue/Question with STM32F103
2011-05-17 05:28 AM
> 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
2011-05-17 05:28 AM
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?2011-05-17 05:28 AM
> 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
2011-05-17 05:28 AM
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)?2011-05-17 05:28 AM
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
2011-05-17 05:28 AM
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?