Teensy 4.1 serialUSB1.read()

bxwen

Member
Following instruction from https://www.pjrc.com/teensy/td_serial.html, I use this code to read data from PC host:
Code:
     unsigned char bytecount = 0;
      
      while (SerialUSB1.available() && bytecount< 128) {
        
        toggle=!toggle;
        digitalWriteFast(TESTPin,toggle);  
        readBuffer[bytecount]= SerialUSB1.read();
        //SerialUSB1.readBytes();
        bytecount++;
      }
      if(bytecount>0){
        ProcessInput(readBuffer,bytecount);     
      }

Two observations:

1. by measuring the testpin, I found the data rate is about 5Mbyte/second.
2. the number 128 in bytecount< 128 cannot be too large. If I set it to such as 256, I start to loose data.

I am trying to get fast data throughput. I have tested it is very fast to send data from Teensy to PC, but the direction from PC to Teensy seems to be slower. Is this by design?
 
Many complex factors impact the overall speed.

A couple years ago, quite a lot of work did go into speeding up the Teensy-to-PC code. Less optimization work has (so far) been done on the PC-to-Teensy path, so yes, it may simply be slower. I suppose you could call that by design. Further USB software optimization has been considered a much lower priority than so many other projects, like the filesystem integration stuff.

But how your code on the PC transmits data also can have a huge impact, especially if using Windows or Linux. Those systems have fairly simple USB drivers which do not combine writes together to pack outgoing data efficiently into maximum size USB packets (as Teensy's USB code does when sending data to your PC). So if your code on the PC side is not writing in large buffers, it could be causing the not-so-smart USB serial driver to make inefficient use of the USB bandwidth as it transmits to Teensy.
 
Is there any way to speed up the Serial.read()? From what I can see on the TESTPin, the while loop run 5 to 6 times each micro seconds. If we can find out how many bytes available, can we use a memcpy to get all the bytes?
 
This has exactly the same speed. It can't read too many bytes each time also. Is there source code available? I suspect internally SerialUSB1.readBytes calls SerialUSB1.read() many times in a loop.

Code:
int handleDataTransfer(){
  
     unsigned char bytecount = 0;
      
      if(SerialUSB1.available()) {
        
        toggle=!toggle;
        digitalWriteFast(TESTPin,toggle);  
        bytecount= SerialUSB1.readBytes(readBuffer,128);
        
      
        if(bytecount>0){
          ProcessInput(readBuffer,bytecount);     
        }
      }
  }
 
This has exactly the same speed. It can't read too many bytes each time also. Is there source code available? I suspect internally SerialUSB1.readBytes calls SerialUSB1.read() many times in a loop.

...

Any install of TeensyDuino brings all the sources in that directory.
 
Is there source code available?

It's already on your computer because of the installer, and also on github.

https://github.com/PaulStoffregen/c...b275c0b546974be11ea/teensy4/usb_serial.c#L183

Look for usb_serial_read() in usb_serial.c.


I suspect internally SerialUSB1.readBytes calls SerialUSB1.read() many times in a loop.

When you look at usb_serial.h, you'll see they both call usb_serial_read() which reads a block of data. The single byte() read actually calls another function which calls usb_serial_read() with the length parameter set to 1.
 
As an example of this, you might try looking at the Example: Teensy->USB_SERIAL->USBtoSerial

It shows you how in the loop, it uses the Serial.available() to help determine how many bytes to read.
In addition to this information, it uses the size of the buffer as well as seeing how may bytes it can
write to the output Serial port without having the code need to wait in the HWSERIAL.write(...) call.

As Paul mentioned, what code you are running on the other side can also make a big impact.

For example, several years ago when I first started playing around with plugging a teensy into a RPI board to use to drive 18 servos.

I found that at least at the time on an RPI, when I finished outputting a packet to the Teensy, I wanted to flush it out as quick as possible as to
reduce latency...

So on the linux side I used tcdrain call which is similar to Serial.flush(). Earlier when I was doing this on the RPI to talk to another board (Arbotix Pro), which used an FTDI chip for the USB, this helped a lot. However I found when talking to other devices /dev/ttyACMx the tcdrain, appeared like it had now support in the driver and just put a long delay in, which he figured a lot longer than the time it should take for the bytes to output.... So I removed that call!...

The main point is, what you call on the other side makes a difference.
 
Ok I think the problem is SerialUSB1.readBytes() itself. I am sending a big chunk of 32K bytes from pc.
Code:
int handleDataTransfer(){
  
     unsigned char bytecount = 0;
     int rd=SerialUSB1.available();
      if( rd>0 ) {
        DLOG1("available bytes: %i \r\n",rd);
        toggle=!toggle;
        digitalWriteFast(TESTPin,toggle);  
        bytecount= SerialUSB1.readBytes(readBuffer,200);
        
        DLOG1("read bytes: %i \r\n",bytecount);
      
        if(bytecount>0){
          ProcessInput(readBuffer,bytecount);     
        }
      }
  }
if I limit the number of bytes to read at 200, then everything is OK. This is the output:
Code:
available bytes: 512 
read bytes: 200 
pointer=0 
available bytes: 312 
read bytes: 200 
pointer=200 
available bytes: 624 
read bytes: 200 
pointer=400 
available bytes: 936 
read bytes: 200 
pointer=600 
available bytes: 1248 
read bytes: 200 
pointer=800 
available bytes: 1560 
read bytes: 200 
pointer=1000

But if I try to read 512 bytes, which is what becomes available each time:
Code:
int handleDataTransfer(){
  
     unsigned char bytecount = 0;
     int rd=SerialUSB1.available();
      if( rd>0 ) {
        DLOG1("available bytes: %i \r\n",rd);
        toggle=!toggle;
        digitalWriteFast(TESTPin,toggle);  
        bytecount= SerialUSB1.readBytes(readBuffer,rd);
        
        DLOG1("read bytes: %i \r\n",bytecount);
      
        if(bytecount>0){
          ProcessInput(readBuffer,bytecount);     
        }
      }
  }
the output is:
Code:
available bytes: 512 
read bytes: 0 
available bytes: 512 
read bytes: 0 
available bytes: 512 
read bytes: 0 
available bytes: 512 
read bytes: 0 
available bytes: 512 
read bytes: 0 
available bytes: 512 
read bytes: 0 
available bytes: 512 
read bytes: 0 
available bytes: 512 
read bytes: 0

nothing read in by the function SerialUSB1.readBytes
 
When you try larger reads, is "bytecount" still only an 8 bit number? Storing the result into only 8 bits would explain why you get 0 when you should get 512.

If this doesn't help, maybe you could consider showing a complete program and code or application on the PC side, so anyone can reproduce the problem. Seeing only these code fragments, rather than the complete actual program leads to a lot of guesswork, like the variable types actually used.

It's possible you've found a previously unknown bug in the USB serial code. But unless a complete program and enough info including the PC side is given to reproduce the problem, no serious investigation will ever happen. Reproducible test cases are the way these sorts of issues get investigated and fixed.
 
Paul,
I think you just pointed out the problem. I copied the code from https://www.pjrc.com/teensy/td_serial.html, and didn't notice the bytecount is defined as unsigned char, while the available byte counts can be more than 256.
I feel I got way better technical support from here than from NXP. Thank you, KurtE, and defragster for answering my questions so promptly.
 
Even though the example bytecount isn't assigned from readBytes(), I've updated the web page to show unsigned int type rather than unsigned char.

I'm curious, was this question also asked on NXP's community forum? If so, could you give the link to the public conversation?
 
I didn't ask this question in NXP forum. But the response there usually takes days, not like hours here.
 
Back
Top