-
-
Notifications
You must be signed in to change notification settings - Fork 7k
Serial bugs, non-intuitive programming default, during timer interrupts #1147
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
Comments
I'm not sure what all the problems are you're trying to describe, but the "Adding flush seems to grind my Arduino to a halt" is caused by a deadlock inside HardwareSerial, see #672. |
Yes, but there are other problems as well. The serial output is often cut off mid-report. Even if the serial output cannot all be sent at once, everything should at least be sent in the right order. Since serial is the primary means of debugging in Arduino, and Interrupts are a critical component to certain kinds of embedded development, correcting serial performance inside an interrupt should be made a priority. Ryan Pettigrew On Apr 16, 2013, at 9:53 AM, Matthijs Kooijman [email protected] wrote:
|
Hi Ryan,
I suggested some changes in the other ticket I referred to. I'll see Gr. Matthijs |
Run the tests in the sequence in the document on GitHub. It should show you the various problems I'm talking about. Hopefully, most will not be too hard to fix. Ryan Pettigrew On Apr 16, 2013, at 2:24 PM, Matthijs Kooijman [email protected] wrote:
|
I've tried running your sketch, but I'm not sure what to look for. At first, nothing seemed to happen. I tried changing the register settings (which seemed wrong, they were set to a PWM mode, probably because I'm running on an Arduino UNO with a different microcontroller). I changed them to: #define T1CRA 0b00000000 // COM1A = 0, COM1B = 0, WGM[1:0] = 0; This made the timer interrupt at least do something, I got a blinking led with the toggle pin 13 example, but it only lasts for half a second. Is this one of the problems you saw? Other than this, I'm really not sure what problems you were trying to illustrate with your example. It would help if you could describe a bit more clearly what happens and what you would have expected to happen, for each of the tests in your program. |
Ah, found the problem. There were /* */ around both the TIMSK1 settings in your sketch, which I hadn't noticed, so I was never enabling the interrupt. With that out of the way, I still don't see all those problems your are seeing (at least I think so). I tried:
So, are there any other bugs/problems apart from the flush issue? Your code talks about workarounds for "the bug". Which is that? |
When interrupts are disabled, writing to HardwareSerial could cause a lockup. When the tx buffer is full, a busy-wait loop is used to wait for the interrupt handler to free up a byte in the buffer. However, when interrupts are disabled, this will of course never happen and the Arduino will lock up. This often caused lockups when doing (big) debug printing from an interrupt handler. Additionally, calling flush() with interrupts disabled while transmission was in progress would also cause a lockup. When interrupts are disabled, the code now actively checks the UDRE (UART Data Register Empty) and calls the interrupt handler to free up room if the bit is set. This can lead to delays in interrupt handlers when the serial buffer is full, but a delay is of course always preferred to a lockup. Closes: arduino#672 References: arduino#1147
This prevents a potential race condition where for example write() would detect that there is room in the tx buffer, an interrupt would trigger where the interrupt handler writes to Serial filling up that room, and write() would then reset the head pointer, effectively throwing away the bytes written by the interrupt handler. References: arduino#1147
Sorry for the delay; I had to prioritize my Robotics conference. Both the Pro Mini and the UNO use the Atmega 328, so they should be identical, as far as the test is concerned. The default test fails because there is no default ISR vector when an interrupt is enabled but no ISR vector specified. This would easily happen when someone is commenting out code to try and debug a problem. The assertion of my colleagues and myself is that ISR vectors should be loaded with a default, and that default should be to return without doing anything. Compiler/IDE warnings should probably also be thrown. The second, third, fourth, and fifth tests merely confirm this is the case by disabling & reenabling the interrupt enable, and enabling an empty ISR and a valid ISR, showing that the problems are confined to the interrupt with no default vector and nothing more. The last two are the serial problems. Serial code in an interrupt behaves badly. Serial Writes do not reserve space; when an interrupt comes along, the first write is unfinished, and the write from the interrupt cuts it off to perform its own write, and the previous write continues after the interrupt finishes. The real problem here is not the interrupt behaving like an interrupt should, by interrupting, but by serial not reserving the first write's "place in line". When the flush happens (or, in this case, should happen, but doesn't), the previous write from outside of the ISR should finish where it left off before the ISR write begins. I see you have fixed some issues relating to the atomicity of serial writes; has this problem now been fixed? I have not found any information on how better to submit test cases like these to Arduino; is there one somewhere? |
Great, your explanation helps to understand your concerns. As for how to report these bugs, your example combined with the explanation looks good to me. For clarity, it might be useful to separate both issues at some point, but let's see where this leads. As for the absence of a default timer ISR, I'm not really sure what the policy is here. I guess a default empty ISR declared as "weak" should be possible, but perhaps one of the official developers can comment. As for the Serial issues: my fixes make sure that bytes that are printed are actually printed, even when printing from both an ISR and regular code, without lockups. However, there is no concept of "reserving space", so if an interrupt occurs halfway through a print, the ISR's print will be in the middle of the original print. This is something that is pretty impossible to fix, because:
If you care about not getting mixed messages, you should disable interrupts whenever you write a message to the Serial console (which, for messages longer than the serial buffer, can mean that interrupts are disabled for quite some time...). I'm entirely sure if I understand your issue correctly, or if you refer to some other (now fixed) race condition, though. |
Yes, I was thinking of weak declared ISRs too, but I'm not sure how that might fit against other concerns. You seem to be addressing my concerns with the serial, yes. I've thought for a while that maybe a "prebuffer" with something like a linked list of the heads and tails of things to be sent out by serial might be a potential approach to solve the serial sequencing issue. Serial writes would add a head and tail to the prebuffer. The serial interrupt (and flush in an interrupt) will start at the first head and add data to the serial buffer, updating the head appropriately until the transmission completes, and moving on to the next entry head. New writes always get added to the end of the prebuffer list, leaving the sequence intact. I've wondered if the relevant memories are fast enough compared to the serial itself that you could maybe buffer the transmission in-place without a separate buffer in memory, but I've not considered the performance issues or tradeoffs. |
When interrupts are disabled, writing to HardwareSerial could cause a lockup. When the tx buffer is full, a busy-wait loop is used to wait for the interrupt handler to free up a byte in the buffer. However, when interrupts are disabled, this will of course never happen and the Arduino will lock up. This often caused lockups when doing (big) debug printing from an interrupt handler. Additionally, calling flush() with interrupts disabled while transmission was in progress would also cause a lockup. When interrupts are disabled, the code now actively checks the UDRE (UART Data Register Empty) and calls the interrupt handler to free up room if the bit is set. This can lead to delays in interrupt handlers when the serial buffer is full, but a delay is of course always preferred to a lockup. Closes: arduino#672 References: arduino#1147
This prevents a potential race condition where for example write() would detect that there is room in the tx buffer, an interrupt would trigger where the interrupt handler writes to Serial filling up that room, and write() would then reset the head pointer, effectively throwing away the bytes written by the interrupt handler. References: arduino#1147
When interrupts are disabled, writing to HardwareSerial could cause a lockup. When the tx buffer is full, a busy-wait loop is used to wait for the interrupt handler to free up a byte in the buffer. However, when interrupts are disabled, this will of course never happen and the Arduino will lock up. This often caused lockups when doing (big) debug printing from an interrupt handler. Additionally, calling flush() with interrupts disabled while transmission was in progress would also cause a lockup. When interrupts are disabled, the code now actively checks the UDRE (UART Data Register Empty) and calls the interrupt handler to free up room if the bit is set. This can lead to delays in interrupt handlers when the serial buffer is full, but a delay is of course always preferred to a lockup. Closes: arduino#672 References: arduino#1147
Since the serial issues here are either fixed, or tricky to solve, I'm closing this issue. If you feel strongly about the empty ISRs, I would advice collecting some opinions on the mailing list and perhaps opening a pullrequest. |
The text was updated successfully, but these errors were encountered: