Skip to content

HardwareSerial flush problem #1463

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

Closed
xxxajk opened this issue Jun 10, 2013 · 8 comments
Closed

HardwareSerial flush problem #1463

xxxajk opened this issue Jun 10, 2013 · 8 comments
Labels
Component: Core Related to the code for the standard Arduino API
Milestone

Comments

@xxxajk
Copy link
Contributor

xxxajk commented Jun 10, 2013

HardwareSerial::flush() sits, spins, and never exits.
Mega 1280, Arduino 1.0.5

hardware/arduino/cores/arduino/HardwareSerial.cpp, line 457
replace
while (transmitting && ! (*_ucsra & _BV(TXC0)));
with
while (transmitting && _tx_buffer->head != _tx_buffer->tail);

fixes the issue.

@matthijskooijman
Copy link
Collaborator

Under what conditions are you calling flush()? With interrupts enabled? Or are you using a very high baudrate perhaps?

@xxxajk
Copy link
Contributor Author

xxxajk commented Jun 11, 2013

115200 baud. Write 1 char then call flush(), and it locks.
On Jun 11, 2013 5:38 AM, "Matthijs Kooijman" [email protected]
wrote:

Under what conditions are you calling flush()? With interrupts enabled? Or
are you using a very high baudrate perhaps?


Reply to this email directly or view it on GitHubhttps://github.com//issues/1463#issuecomment-19251412
.

@matthijskooijman
Copy link
Collaborator

I just did a quick try on my Arduino Uno, and the following sketch works
as expected (prints xs on the serial port).

void setup() {
  Serial.begin(115200);
}

void loop() {
  Serial.print("x");
  Serial.flush();
}

So it seems the problem is not as general as you describe it and thus
there is something else particular to your setup that is needed to cause
the problem.

Could you provide more details about your testcase, perhaps the
(reduced) failing sketch?

Does the above sketch work for you?

What hardware are you using?

@xxxajk
Copy link
Contributor Author

xxxajk commented Jun 11, 2013

Mega1280, yes the case is specific, and complicated. Add me on Google+ and
I can explain better, and provide all details. It will require additional
hardware as well. I also have ability of connecting an AVR dragon via jtag
if you need any other info.
On Jun 11, 2013 10:46 AM, "Matthijs Kooijman" [email protected]
wrote:

I just did a quick try on my Arduino Uno, and the following sketch works
as expected (prints xs on the serial port).

void setup() {
Serial.begin(115200);
}

void loop() {
Serial.print("x");
Serial.flush();
}

So it seems the problem is not as general as you describe it and thus
there is something else particular to your setup that is needed to cause
the problem.

Could you provide more details about your testcase, perhaps the
(reduced) failing sketch?

Does the above sketch work for you?

What hardware are you using?


Reply to this email directly or view it on GitHubhttps://github.com//issues/1463#issuecomment-19266353
.

@matthijskooijman
Copy link
Collaborator

Hmm, I don't actually have a Mega to test with, so this might get complicated indeed :-)

In any case, I would prefer it if you would explain the problem here in this ticket, so others can also read it. I'm just trying to get the problem clear, I might not be the one to actually investigate or fix it.

You suggested a fix in your original report, but that fix is not correct: It reduces the flush() call from "all bytes sent" to "all bytes written to the UART", so with your changes if someone calls flush() and then shuts down the TX pin, he might have missed one or two bytes.

I recently created a pile of fixes for HardwareSerial that haven't been merged yet. Perhaps you could see if they fix your problem as well? They're at #1369.

Alternatively, there is a deadlock fix for high baudrates, perhaps you're seeing the same problem at lower baudrates for some reason? The proposed fix is at #1456, could you see if that changes anything for you?

Having said that, if you think explaining through some messaging platform works better, feel free to join #arduino on the Freenode IRC network. My nickname is blathijs there.

@xxxajk
Copy link
Contributor Author

xxxajk commented Jun 11, 2013

I'll try that and let you know.
On Jun 11, 2013 11:07 AM, "Matthijs Kooijman" [email protected]
wrote:

Hmm, I don't actually have a Mega to test with, so this might get
complicated indeed :-)

In any case, I would prefer it if you would explain the problem here in
this ticket, so others can also read it. I'm just trying to get the problem
clear, I might not be the one to actually investigate or fix it.

You suggested a fix in your original report, but that fix is not correct:
It reduces the flush() call from "all bytes sent" to "all bytes written to
the UART", so with your changes if someone calls flush() and then shuts
down the TX pin, he might have missed one or two bytes.

I recently created a pile of fixes for HardwareSerial that haven't been
merged yet. Perhaps you could see if they fix your problem as well? They're
at #1369 #1369.

Alternatively, there is a deadlock fix for high baudrates, perhaps you're
seeing the same problem at lower baudrates for some reason? The proposed
fix is at #1456 #1456, could
you see if that changes anything for you?

Having said that, if you think explaining through some messaging platform
works better, feel free to join #arduino on the Freenode IRC network. My
nickname is blathijs there.


Reply to this email directly or view it on GitHubhttps://github.com//issues/1463#issuecomment-19267599
.

@xxxajk
Copy link
Contributor Author

xxxajk commented Jun 12, 2013

#1456 fixes it for me. My guess is that since I am not operating on internal RAM, but instead external RAM, that the extra clock cycles trip the deadlock easier. Consider this a verification that it can happen at lower baud rates under the right conditions, such as mine.

Oh and before I forget, The other factor involved here is that I am multitasking on the external RAM as well.

Here is the successful output.

Task A
Waiting for '/' to mount......Starting test...
File opened OK, displaying contents...
Hello world!

Read completed, last read result = -1 (20).
File closed, result = 0.
Remove and insert media...

matthijskooijman added a commit to matthijskooijman/Arduino that referenced this issue Dec 18, 2013
…hile

It turns out there is an additional corner case. The analysis in the
previous commit wrt to flush() assumes that the data register is always
kept filled by the interrupt handler, so the TXC bit won't get set until
all the queued bytes have been transmitted. But, when interrupts are
disabled for a longer period (for example when an interrupt handler for
another device is running for longer than 1-2 byte times), it could
happen that the UART stops transmitting while there are still more bytes
queued (but these are in the buffer, not in the UDR register, so the
UART can't know about them).

In this case, the TXC bit would get set, but the transmission is not
complete yet. We can easily detect this case by looking at the head and
tail pointers, but it seems easier to instead look at the UDRIE bit
(the TX interrupt is enabled if and only if there are bytes in the
queue). To fix this corner case, this commit:
 - Checks the UDRIE bit and only if it is unset, looks at the TXC bit.
 - Moves the clearing of TXC from write() to the tx interrupt handler.
   This (still) causes the TXC bit to be cleared whenever a byte is
   queued when the buffer is empty (in this case the tx interrupt will
   trigger directly after write() is called). It also causes the TXC bit
   to be cleared whenever transmission is resumed after it halted
   because interrupts have been disabled for too long.

As a side effect, another race condition is prevented. This could occur
at very high bitrates, where the transmission would be completed before
the code got time to clear the TXC0 register, making the clear happen
_after_ the transmission was already complete. With the new code, the
clearing of TXC happens directly after writing to the UDR register,
while interrupts are disabled, and we can be certain the data
transmission needs more time than one instruction to complete. This
fixes arduino#1463 and replaces arduino#1456.
cmaglie pushed a commit to cmaglie/Arduino that referenced this issue Jan 22, 2014
…hile

It turns out there is an additional corner case. The analysis in the
previous commit wrt to flush() assumes that the data register is always
kept filled by the interrupt handler, so the TXC bit won't get set until
all the queued bytes have been transmitted. But, when interrupts are
disabled for a longer period (for example when an interrupt handler for
another device is running for longer than 1-2 byte times), it could
happen that the UART stops transmitting while there are still more bytes
queued (but these are in the buffer, not in the UDR register, so the
UART can't know about them).

In this case, the TXC bit would get set, but the transmission is not
complete yet. We can easily detect this case by looking at the head and
tail pointers, but it seems easier to instead look at the UDRIE bit
(the TX interrupt is enabled if and only if there are bytes in the
queue). To fix this corner case, this commit:
 - Checks the UDRIE bit and only if it is unset, looks at the TXC bit.
 - Moves the clearing of TXC from write() to the tx interrupt handler.
   This (still) causes the TXC bit to be cleared whenever a byte is
   queued when the buffer is empty (in this case the tx interrupt will
   trigger directly after write() is called). It also causes the TXC bit
   to be cleared whenever transmission is resumed after it halted
   because interrupts have been disabled for too long.

As a side effect, another race condition is prevented. This could occur
at very high bitrates, where the transmission would be completed before
the code got time to clear the TXC0 register, making the clear happen
_after_ the transmission was already complete. With the new code, the
clearing of TXC happens directly after writing to the UDR register,
while interrupts are disabled, and we can be certain the data
transmission needs more time than one instruction to complete. This
fixes arduino#1463 and replaces arduino#1456.
@cmaglie
Copy link
Member

cmaglie commented Jan 28, 2014

Merged Matthijs' patch, this should be fixed in 1.5.6.

@cmaglie cmaglie closed this as completed Jan 28, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Component: Core Related to the code for the standard Arduino API
Projects
None yet
Development

No branches or pull requests

3 participants