arduino / arduino/ArduinoCore-avr

Arduino Micro USB Serial cannot receive exactly 64 bytes at once

Open
#112 10 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
C
Stars
1.5k
Forks
1.1k
PR merge metrics
No merged PRs in 30d

Description

This issue was first reported in my project here https://github.com/kervinck/gigatron-rom/issues/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 https://github.com/arduino/Arduino/pull/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 https://github.com/kervinck/gigatron-rom/commit/94d167dd755bdad22af6ae818f879f1d3e961e86 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.

Contributor guide

No contributing guide indexed for this repository

Research direction

Reproduce the 64-byte failure with the example Arduino sketch and Python serial program, then inspect USBCore.cpp, especially USB_Available, USB_Recv, and ReleaseRX. Compare the receive sequence with the cited datasheet behavior and verify that messages sent after an exact 64-byte line are received and reported like the Arduino Uno output.

Written by the indexing model from the issue text.

Assessment

Tech stack
arduino, c
Domain
embedded-iot
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.