Description
This issue was first reported in my project here kervinck/gigatron-rom#36 but I now believe the root cause might be in USBCore.cpp
Tested in Ardiuno IDE 1.8.1 and 1.8.6 (arduino-PR-6886-BUILD-734-macosx.zip)
BN: Arduino/Genuino Micro
VID: 2341
PID: 8037
SN: Upload any sketch to obtain it
The example sketch just receives lines of text and reports their length:
void setup() {
Serial.begin(115200);
}
void loop() {
static int lineLength = 0;
if (Serial.available()) {
char next = Serial.read();
lineLength++;
if (next == '\n' || next == '\r') {
Serial.println(lineLength);
lineLength = 0;
}
}
}
Test Python program to demonstrate that the issue is with data messages of exactly 64 bytes:
import serial
from time import sleep
tty = '/dev/tty.usbmodem1411' # Adapt for your own computer
ser = serial.Serial(tty, baudrate=115200, timeout=1)
sleep(1) # For Arduinos that reboot
for length in [10, 70, 65, 64, 10, 10]:
line = (length-1)*'A' + '\n'
print('> %s (%d bytes)' % (repr(line), len(line)))
ser.write(line)
reply = ser.readline()
print('< %s %s' % (repr(reply), 'OK' if reply else 'TIMEOUT'))
Output shows that the Arduino Micro stops receiving data after having received the 64-byte "killer" line. It does see the 64-byte message itself and it is still able to send. But it doesn't see new data anymore.
> 'AAAAAAAAA\n' (10 bytes)
< '10\r\n' OK
> 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n' (70 bytes)
< '70\r\n' OK
> 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n' (65 bytes)
< '65\r\n' OK
> 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n' (64 bytes)
< '64\r\n' OK
> 'AAAAAAAAA\n' (10 bytes)
< '' TIMEOUT
> 'AAAAAAAAA\n' (10 bytes)
< '' TIMEOUT
Expected output is what the Arduino Uno does:
> 'AAAAAAAAA\n' (10 bytes)
< '10\r\n' OK
> 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n' (70 bytes)
< '70\r\n' OK
> 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n' (65 bytes)
< '65\r\n' OK
> 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n' (64 bytes)
< '64\r\n' OK
> 'AAAAAAAAA\n' (10 bytes)
< '10\r\n' OK
> 'AAAAAAAAA\n' (10 bytes)
< '10\r\n' OK
Somewhat related to https://github.com/arduino/Arduino/issues/6669 ("Arduino Micro USB Serial cannot receive more than 255 data once") and arduino/Arduino#6886 ("Handle receiving a ZLP in USB_Available"), however the proposed solution of that issue doesn't solve this.
I suspect the problem is caused by not following the datasheet's sequence, specifically:
RXOUTI shall always be cleared before clearing FIFOCON
The way USB_Recv is written I don't think ReleaseRX will ever be called twice in a row. And that is exactly what gets me out of the hangup situation. In my own project I have integrated a workaround kervinck/gigatron-rom@94d167d that seems to work by clearing FICOCON after Serial.read() and observing that UEBCLX has become 0. But I haven't considered every possible scenario (race conditions?).
I have some general remarks about USBCore.cpp, and this is very subjective, so please take no offense. But having studied the code with an actual problem, the Arduino Micro hooked up and the datasheets at hand, I have the impression some more issues might be lurking here. It is not obvious (to me) that the code and the datasheet match.
- Why not use RWAL in USB_Available instead of UEBCLX. Tracing its value in different scenarios, UEBCLX doesn't always seem quite the right indicator. It can sometimes momentarily read 0 in the middle of a transfer sequence. (Besides the question what to to with UEBCHX in case that ever becomes relevant)
- What is the magic constant 0x6B in ReleaseRX? Why not address the desired bits explicitly with masking operations.
- USB_Recv() does some interesting things with its n and len, and the condition before ReleaseRX seems a bit suspect to me.