Hello,
I am in the search for missing transfer speed.
As I am understanding, the CDC implementation on the Due uses 512 Byte EPs for IN and OUT. That would allow up to 512kB/s unidirectional transfer speed. (512 Bytes per 1ms frame)
I get that this is only theoretically.
For a project I would need about 100-120kB/s (in one direction at a time) but instead I get just about 41kB/s. Additionally I can't seem to transfer 512 byte blocks.
Does anyone know the reason, or what I implemente wrong here to utilize the best possible speed?
My Test-Sketch (only the loop) will echo the incoming data up to 512 bytes transfer:
byte buffer[512] ;
void loop() {
int pos = 0 ;
if (!SerialUSB)
return ;
while (SerialUSB.available())
{
if (pos >= sizeof(buffer))
return ;
buffer[pos++] = SerialUSB.read();
}
if (pos)
SerialUSB.write(buffer, pos);
return ;
}
The PC side (.NET Core 3.1):
SerialPort Port;
Port = new SerialPort(portName, 2000000, Parity.None, 8, StopBits.One);
Port.Handshake = Handshake.RequestToSend;
Port.Open();
Port.DtrEnable = true;
byte[] buffer = new byte[511];
for (int i = 0; i < buffer.Length; i++)
buffer[i] = (byte)i;
DateTime start = DateTime.Now;
for (int i = 0; i < 256; i++)
{
Port.Write(buffer, 0, buffer.Length);
int pos = 0;
byte[] verify = new byte[buffer.Length];
while (pos < buffer.Length)
{
pos += Port.Read(verify, pos, Math.Min(buffer.Length - pos, Port.BytesToRead));
}
}
DateTime end = DateTime.Now;
TimeSpan ts = end - start;
PS: As you can see I am testing 511 Bytes / transfer. That is because 512 Bytes will not work, as the Due will not ack the RX after 512 Bytes as it waits for the 512th bytes to be placed in the ring buffer, which only can hold 511 Bytes. However the EP is configured as 512 Bytes and thus it is possible to fill that up completely with a single transfer without beeing able to drain that buffer:
ring_buffer *buffer = &cdc_rx_buffer;
uint32_t i = (uint32_t)(buffer->head+1) % CDC_SERIAL_BUFFER_SIZE;
// if we should be storing the received character into the location
// just before the tail (meaning that the head would advance to the
// current location of the tail), we're about to overflow the buffer
// and so we don't write the character or advance the head.
while (i != buffer->tail) {
uint32_t c;
if (!USBD_Available(CDC_RX)) {
udd_ack_fifocon(CDC_RX);
break;
}
c = USBD_Recv(CDC_RX);
// c = UDD_Recv8(CDC_RX & 0xF);
buffer->buffer[buffer->head] = c;
buffer->head = i;
i = (i + 1) % CDC_SERIAL_BUFFER_SIZE;
}
This is because the the difference of head and tail can only be in %512 and 0 is beeing used to signal an empty ring buffer. So the max usage of that buffer is 511 bytes < 512 bytes EP.
Instead the buffer should be able to contain any possible single transfer and set the line status to not ready until the buffer was drained. To prevent these type of lockups.