-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
USB Serial on 32U4 stops incrementing Serial.available() if serial buffer has ≥2 bytes in it and more are received #516
Comments
This issue triggered my curiosity, so I decided to quickly investigate this (but I won't have time for further followups, I expect). Turns out that, AFAICS, the behavior you are seeing is essentially unfixable with the current approach where data is only stored in dedicated USB buffer banks (DPRAM). It could be fixed by copying data from the USB buffers into regular RAM, but that would introduce an extra copy step and of course raise RAM usage, so I think that's not really an option. If you do really need to know the number of bytes pending in advance, you can always introduce such a buffer yourself and copy bytes into it while I think this makes this issue essentially unfixable. Maybe it would be good to document this behavior somewhere, if not already done. @per1234, maybe you have ideas about this? Basically, what currently happens:
Note that I could not find a way to ask the hardware how much data is pending in the second bank. But also note that this would not really help: if the data is split over more than 2 packets, there could be more data pending in the USB host that you'd never know about. Here's the path from ArduinoCore-avr/cores/arduino/CDC.cpp Lines 188 to 194 in 42fa4a1
ArduinoCore-avr/cores/arduino/USBCore.cpp Lines 223 to 227 in 44dc454
ArduinoCore-avr/cores/arduino/USBCore.cpp Lines 148 to 151 in 44dc454
What is also interesting is that the byte count is read from the UEBCLX register only, while there are three more bits in the UEBCHX register, meaning the available count could also be wrong if sending > 255 bytes. Given there are only 2 64-byte banks that can hold data, that should be ok. At first glance it seems a waste to use only 64-byte banks (since there is 832 bytes of dedicated USB RAM available), but it turns out that most endpoints are limited to 64-bytes, only endpoint 1 supports up to 256 bytes (weirdly enough the EPSIZE register allows configuring 512-byte endpoints?). I guess the RX endpoint could be configured to be 256 bytes, leaving TX at 64 (each has 2 banks, so 2x256+2x64=640, leaving 192 bytes for the control endpoint and other USB functions), but maybe there is some other limitation, or this was just not implemented to keep things simple. The BYCT field in the UEBCLX/UEBCHX registers is a bit weird. The datasheet section about the register suggest it just keeps a total count of available bytes (across all banks), but the "OUT endpoint management" section suggests that you need to read this many bytes before releasing the bank, implying that it contains the number of bytes in the current bank only. Then here's the path taken by ArduinoCore-avr/cores/arduino/CDC.cpp Lines 203 to 211 in 42fa4a1
ArduinoCore-avr/cores/arduino/USBCore.cpp Lines 231 to 256 in 44dc454
ArduinoCore-avr/cores/arduino/USBCore.cpp Lines 183 to 186 in 44dc454
And this shows the hardware is configured for 2 banks of 64 bytes each: ArduinoCore-avr/cores/arduino/USBCore.cpp Line 375 in 44dc454
ArduinoCore-avr/cores/arduino/USBAPI.h Lines 37 to 39 in 24e6edd
|
@matthijskooijman Coincidentally, I was looking at the buffer size for FTDI USB to RS232 chips, and found that FTDI also recommend 64 byte packets (62 of which are data). FTDI urges application developers not to send charcater by character, as I was doing with my 32U4 when I found this bug. I dont know if it's related, but it may be a USB CDC thing. This may be more of an undocumented limitation, but perhaps there is a way to get it to function like other boards |
Hm, I had missed that observation and also cannot immediately explain this. I thought maybe the peek buffer is involved (which could store one byte in RAM and keep the rest (second packet) in the USB fifo), but it seems that is only ever filled if you call peek(), which your sketch does not. Or maybe the fifo size returned by the |
Yes @matthijskooijman, if the buffer already has 0 or 1 bytes in it, you can send more bytes together and they will enter the buffer and be shown by available(). After that, you can send more bytes and they will go into the buffer (as confirmed by reading then out), but available() won't update |
After some investigation, I was able to narrow down my code to the minimum that reproduces the bug, then I added some code that helps me locate where it is. The problem appears to be that if more than one byte is sent, Serial.available() does not increment.
I did testing on a 32U4 where I could reproduce this bug, but I could not get this bug to show up on Uno with 9600, 115200, or 2,000,000 baud, so I think it only affects USB serial.
To reproduce the bug, make sure that Serial.available returns more than 1, then send data. It will remain at the previous value despite the data being in the buffer. If no bytes or one byte are in buffer, Serial.avaible works correctly upon sending data.
Here's my example code, which shows the changes in Serial.avaiable() and prints from the serial buffer even if Serial.available() is stuck:
The text was updated successfully, but these errors were encountered: