PDA

View Full Version : Teensy 3.0 Serial.available() bug?



Bill Greiman
12-25-2013, 06:02 PM
This sketch:


void setup() {
Serial.begin(9600);
while(!Serial);
}
void loop() {
Serial.println(Serial.available());
Serial.read();
delay(1000);
}

Produces these strange values for Serial.available()


0
57344
49152
40960
32768
24576
16384
8192
0
57344
49152
40960
32768
24576
16384
8192

eberkund
02-27-2014, 12:02 AM
Did you fix this?

stevech
02-27-2014, 12:49 AM
I think your context assumes a boolean result from Serial.available() but indeed it returns an int. Or some such, with the way the class is written.
Look at, try this code. It works as you'd expect

void setup() {
Serial.begin(9600);
while(!Serial)
{}; // for clarity
}

void loop() {
int n;
n = Serial.available();
if (n)
while (n)
Serial.printf("n=%d data=%02X\n", n--, Serial.read());
else
Serial.printf("no data available\n");
delay(1000);
}

stevech
02-27-2014, 01:03 AM
this code works as you'd expect as well

void setup() {
Serial.begin(9600);
while(!Serial)
{}; // for clarity
}

void loop() {
while (Serial.available())
Serial.printf("data=0x%02X\n", Serial.read());
delay(1000);
Serial.printf("no data at tick:%d\n", millis());
}

nox771
02-27-2014, 04:10 AM
You are right, this is some kind of bug. I also get bogus numbers with a case like yours (different numbers though). The problem seems to be the Serial.read(). In your case if the Serial.read() is issued when there is no data, then somehow it is screwing up Serial.available().

If I make the following change, only reading when data exists, then it seems to work fine.


This sketch:


void setup() {
Serial.begin(9600);
while(!Serial);
}
void loop() {
Serial.println(Serial.available());
if(Serial.available())
Serial.read();
delay(1000);
}

Produces these strange values for Serial.available()

stevech
02-27-2014, 04:37 AM
From your post #5, I'm confused.
The two examples I posted, on my hardware, seem to run correctly.
Are you saying the same code does not, you your devices and versions?

nox771
02-27-2014, 04:53 AM
No, it was a reply to the top post. If I run the top post code as-is, I see the same odd behavior (well different but similar, my output alternates between 0, 32768, 0, 32768, ...). The problem can be traced to the Serial.read(), if it is blocked when there is no available data then everything works. Notice both your examples block read() unless data is available. Try doing a read() regardless of data available and see what you get.

nox771
02-27-2014, 04:58 AM
Here, using your code, try running with this change instead:




void setup() {
Serial.begin(9600);
while(!Serial)
{}; // for clarity
}

void loop() {
while (Serial.available())
Serial.printf("data=0x%02X\n", Serial.read());
delay(1000);
Serial.printf("no data at tick:%d\n", millis());
Serial.read();
}

stevech
02-27-2014, 05:15 AM
Hmm. the added Serial.read() in the bottom should get back an undefined value, or -1 rather than a 8 bit value, since the read is done without regard to whether or not data is available.
But adding that causes mine to display, at the first Serial.read(), -1 in hex, 32 bits, with Serial.available() erroneously returning a non-zero value (true), to satisfy the while().
funny, the code cycles, with the correct value every 7 iterations.

nox771
02-27-2014, 05:38 AM
Hrm, just a guess here, but I traced this down into the code, perhaps the bug is here (in usb_dev.c):


usb_packet_t *usb_rx(uint32_t endpoint)
{
usb_packet_t *ret;
endpoint--;
if (endpoint >= NUM_ENDPOINTS) return NULL;
__disable_irq();
ret = rx_first[endpoint];
if (ret) rx_first[endpoint] = ret->next;
usb_rx_byte_count_data[endpoint] -= ret->len;
__enable_irq();
//serial_print("rx, epidx=");
//serial_phex(endpoint);
//serial_print(", packet=");
//serial_phex32(ret);
//serial_print("\n");
return ret;
}


should this be instead:


usb_packet_t *usb_rx(uint32_t endpoint)
{
usb_packet_t *ret;
endpoint--;
if (endpoint >= NUM_ENDPOINTS) return NULL;
__disable_irq();
ret = rx_first[endpoint];
if (ret)
{
rx_first[endpoint] = ret->next;
usb_rx_byte_count_data[endpoint] -= ret->len;
}
__enable_irq();
//serial_print("rx, epidx=");
//serial_phex(endpoint);
//serial_print(", packet=");
//serial_phex32(ret);
//serial_print("\n");
return ret;
}


otherwise usb_rx_byte_count_data[endpoint] is getting subtracted regardless of whether ret is a valid pointer or not.

nox771
02-27-2014, 05:42 AM
Yep, above modification fixed it on mine.

stevech
02-27-2014, 06:25 AM
RE the below... I see that there are posts here above this one; I wrote this before noticing the new ones above.

Further testing.. seems to me there's a subtle coding error, in the interface between usb_serial.c and stream.cpp.

It's somewhere in the arrangement of virtual functions alternatives for read() and readBytes(), if called when the buffer is empty, cause the next available() to return wrong value.
Somehow related to timeout too, as the value seen by available() changes every few loop iterations.

The c to cpp interfaces and virtual overrides aren't something I can find/follow (I hate that about c++ where there are no comments).

PaulStoffregen
02-27-2014, 10:28 AM
otherwise usb_rx_byte_count_data[endpoint] is getting subtracted regardless of whether ret is a valid pointer or not.

Yes, you're absolutely right, it is a bug.

I've committed the fix on github, so this will be in the next release...

https://github.com/PaulStoffregen/cores/commit/482a1f2b5f4dc20a9f092d81f8e9a4ba979dfd27

stevech
02-27-2014, 05:02 PM
I made the change per commit 482a1f2.
Then tested.
Cured the edge case problem caused if a usb read() is done when there is no data available.