Teensy ++2.0 - Serial.write and 64-byte USB packets

Hi everyone

I have spent days troubleshooting a Serial USB issue with my Teensy++2.0 and have either uncovered a bug (library or hardware) or have misunderstood something about how the USB packet buffers operate in the device.

Some details:
USB Host: Windows 7, tested with the latest and earlier USB Virtual-COM Port Drivers installed
TeensyDuino/Arduino versions tested: 1.5.3/1.18.13 and the latest v1.5.7/1.8.19

In short, when using Serial.write() followed by a Serial.send_now() of a stream of bytes that happens to be an exact multiple of 64-bytes in length, the USB packet never leaves the device, until a new byte is sent (which is my workaround.) I have verifided that nothing leaves the device by sniffing the USB packets in Wireshark.

Serial.send_now() DOES return (toggling a digital Pin immediately after send_now is observable) even though my (possibly flawed) understanding is that it should block until the USB host takes the packet - I rely on this blocking activity to maintain synchronisation between my applications on the Teensy and in Windows (actually, within a retro-computer emulation running in Windows.)

Workaround: If the length of the next packet to be sent to the USB Host is exactly divible by 64-bytes, I add an extra null-byte before issuing send_now(), and which is then dropped within the target application.

Here's a code snippet, if it's needed, including the workaround:

Code:
// Added DEBUG signalling
PORTB |= 0b10000000; // DEBUG_USB - 50us: USB_SEND_START
delayMicroseconds(50);
PORTB &= 0b01111111;

  Serial.write( MQ_SYNC, MQ_SYNC_LEN );
  Serial.write( &mQTable[nextReplyMQSlot * MQTABLE_ENTRY_SZ], MESSAGE_HLEN );

  if ( (mID & MQ_LONG_MASK) && (mLenPar2 > 0) ) {

    mBufPtr=getMQBufPtr(nextReplyMQSlot);

    Serial.write( &heap[mBufPtr], mLenPar2 );

    mBufPtr=releaseHeap(mBufPtr); // Release the heap-space used for the LONG reply

    // Workaround for fault in Serial.write/send_now for packets with lengths exactly divisble by 64!
    if ( (mLenPar2 + MQ_SYNC_LEN + MESSAGE_HLEN) % USB_PACKET_LEN == 0) Serial.write( NUL_CHAR );

  }

  Serial.send_now(); // On Teensy, will force (and wait for) all pending USB data to be transmitted

// Added DEBUG signalling
PORTB |= 0b10000000; // DEBUG_USB - 50us: USB_SEND_STOP
delayMicroseconds(50);
PORTB &= 0b01111111;

Is there a better approach?

Warm regards
 
I tried but could not reproduce this problem.

Here is the code I tested.

Code:
const char *longstring = "Serial.send_now() DOES return (toggling a digital Pin immediately after send_now is observable) even though my (possibly flawed) understanding is that it should block until the USB host takes the packet - I rely on this blocking activity to maintain synchronisation between my applications on the Teensy and in Windows (actually, within a retro-computer emulation running in Windows.)";

void setup() {
  Serial.begin(9600);
  while (!Serial) ; // wait for Arduino Serial Monitor
  delay(250);
  Serial.write(longstring, 64);
  Serial.send_now();
}

void loop() {
}

When I upload to a Teensy++ 2.0 board, this is the result I see in the Arduino Serial Monitor.

screenshot.png
 
There may still be a previously unknown (after all these years...) bug in the Teensy 2 USB code. But obviously a small simple program doesn't trigger it. If I should investigate, could you try this simple code and possibly extend it to whatever is needed to reproduce the problem?
 
Thank you for testing this Paul.

I shall try your simplified scenario and see how to reproduce the issue outside my rather more complex program.
 
Looks like there is indeed a problem.

Code:
const char *longstring = "Serial.send_now() DOES return (toggling a digital Pin immediately after send_now is observable) even though my (possibly flawed) understanding is that it should block until the USB host takes the packet - I rely on this blocking activity to maintain synchronisation between my applications on the Teensy and in Windows (actually, within a retro-computer emulation running in Windows.)";

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
  Serial.begin(9600);
  while (!Serial) ; // wait for Arduino Serial Monitor
  delay(250);
  Serial.write(longstring, 64);
  Serial.write(longstring, 64); // sometimes crashes?
  //Serial.write(longstring, 64); // crashes??
  //Serial.send_now();  // <-- crashes?
  digitalWrite(LED_BUILTIN, HIGH);
}

void loop() {
}

I've put this on my list of software issues to debug...
 
Thanks Paul - no hurry from my perspective as I have a (somewhat cludgy but effective) workaround for the time being.

Happy to test any library changes once ready.
 
Back
Top