Age | Commit message (Collapse) | Author |
|
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: #672
References: #1147
|
|
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 #1463 and replaces #1456.
|
|
The flush() method blocks until all characters in the serial buffer have
been written to the uart _and_ transmitted. This is checked by waiting
until the "TXC" (TX Complete) bit is set by the UART, signalling
completion. This bit is cleared by write() when adding a new byte to the
buffer and set by the hardware after tranmission ends, so it is always
guaranteed to be zero from the moment the first byte in a sequence is
queued until the moment the last byte is transmitted, and it is one from
the moment the last byte in the buffer is transmitted until the first
byte in the next sequence is queued.
However, the TXC bit is also zero from initialization to the moment the
first byte ever is queued (and then continues to be zero until the first
sequence of bytes completes transmission). Unfortunately we cannot
manually set the TXC bit during initialization, we can only clear it. To
make sure that flush() would not (indefinitely) block when it is called
_before_ anything was written to the serial device, the "transmitting"
variable was introduced.
This variable suggests that it is only true when something is
transmitting, which isn't currently the case (it remains true after
transmission is complete until flush() is called, for example).
Furthermore, there is no need to keep the status of transmission, the
only thing needed is to remember if anything has ever been written, so
the corner case described above can be detected.
This commit improves the code by:
- Renaming the "transmitting" variable to _written (making it more
clear and following the leading underscore naming convention).
- Not resetting the value of _written at the end of flush(), there is
no point to this.
- Only checking the "_written" value once in flush(), since it can
never be toggled off anyway.
- Initializing the value of _written in both versions of _begin (though
it probably gets initialized to 0 by default anyway, better to be
explicit).
|
|
This is slightly more clear than the previous explicit comparison.
|
|
The actual interrupt vectors are of course defined as before, but they
let new methods in the HardwareSerial class do the actual work. This
greatly reduces code duplication and prepares for one of my next commits
which requires the tx interrupt handler to be called from another
context as well.
The actual content of the interrupts handlers was pretty much identical,
so that remains unchanged (except that store_char was now only needed
once, so it was inlined).
Now all access to the buffers are inside the HardwareSerial class, the
buffer variables can be made private.
One would expect a program size reduction from this change (at least
with multiple UARTs), but due to the fact that the interrupt handlers
now only have indirect access to a few registers (which previously were
just hardcoded in the handlers) and because there is some extra function
call overhead, the code size on the uno actually increases by around
70 bytes. On the mega, which has four UARTs, the code size decreases by
around 70 bytes.
|
|
Previously, the constants to use for the bit positions of the various
UARTs were passed to the HardwareSerial constructor. However, this
meant that whenever these values were used, the had to be indirectly
loaded, resulting in extra code overhead. Additionally, since there is
no instruction to shift a value by a variable amount, the 1 << x
expressions (inside _BV and sbi() / cbi()) would be compiled as a loop
instead of being evaluated at compiletime.
Now, the HardwareSerial class always uses the constants for the bit
positions of UART 0 (and some code is present to make sure these
constants exist, even for targets that only have a single unnumbered
UART or start at UART1).
This was already done for the TXC0 constant, for some reason. For the
actual register addresses, this approach does not work, since these are
of course different between the different UARTs on a single chip.
Of course, always using the UART 0 constants is only correct when the
constants are actually identical for the different UARTs. It has been
verified that this is currently the case for all targets supported by
avr-gcc 4.7.2, and the code contains compile-time checks to verify this
for the current target, in case a new target is added for which this
does not hold. This verification was done using:
for i in TXC RXEN TXEN RXCIE UDRIE U2X UPE; do echo $i; grep --no-filename -r "#define $i[0-9]\? " /usr/lib/avr/include/avr/io* | sed "s/#define $i[0-9]\?\s*\(\S\)\+\s*\(\/\*.*\*\/\)\?$/\1/" | sort | uniq ; done
This command shows that the above constants are identical for all uarts
on all platforms, except for TXC, which is sometimes 6 and sometimes 0.
Further investigation shows that it is always 6, except in io90scr100.h,
but that file defines TXC0 with value 6 for the UART and uses TXC with
value 0 for some USB-related register.
This commit reduces program size on the uno by around 120 bytes.
|
|
Recent avr-libc releases define one, but this allows using it also on
older avr-libc releases.
|
|
This simplifies the baud rate calculation, removing the need for a goto
and shortening the code a bit. Other than that, this code should not use
any different settings than before.
Code was suggested by Rob Tillaart on github.
Closes: #1262
|
|
|
|
operator bool()
|
|
There are two begin methods, one which accepts just a baud rate and
uses the default bit settings and one which accepts both a baudrate and
a bit config. Previously, both of these contained a complete
implementation, but now the former just calls the latter, explicitely
passing the default 8N1 configuration.
Technically, this causes a small change: Before the UCSRC register was
untouched when calling begin(baud), now it is explicitely initialized
with 8N1. However, since this is the default configuration for at least
the Uno and the Mega (didn't check any others), probably for all avrs,
this shouldn't effectively change anything. Given that the Arduino
documentation also documents this as the default when none is passed,
explicitly setting it is probably a good idea in any case.
|
|
These functions do not modify the IPAddress object, but were not marked
as const. This meant that you could not do:
void set_ip(const IPAddress& ip) {
uint32_t copy = ip;
}
Since calling operator uint32_t() on ip would discard the constness of
the reference.
|
|
Fixed "runtime.hardware.path" and "runtime.platform.path" values
|
|
Added new EULER constant
|
|
Conflicts:
build/windows/dist/drivers/arduino.cat
|
|
https://github.com/arduino/Arduino/issues/1792#issuecomment-31650586
|
|
|
|
"runtime.hardware.path" now contains the path to the hardware folder
of the currently selected board and "runtime.platform.path" the path
to the specific platform.
This should fix #1176 and #1761.
|
|
|
|
Conflicts:
hardware/arduino/avr/cores/arduino/Print.cpp
|
|
On later versions of avr-libc, prog_char is deprecated. In 0acebeeff48
the one occurence of prog_char was replaced by "char PROGMEM", which is
not entirely correct (PROGMEM is supposed to be an attribute on a
variable, not on a type, even though this is how things work in older
libc versions). However, in 1130fede3a2 a few new occurences of
prog_char are introduced, which break compilation on newer libc versions
again.
This commit changes all these pointer types to use the PGM_P macro from
<avr/pgmspace.h>. This macro is just "const char *" in newer libc
versions and "const prog_char *" in older versions, so it should always
work.
References #795
|
|
Support both char* and uint8* in Stream and Print
|
|
|
|
Now that Print::write(const char*) is also available, these casts are no
longer needed.
|
|
The new functions just call their char* equivalents, but this allows
reading bytes into a buffer of uint8_t as well as chars.
|
|
The new function just calls Print::write(const uint8_t *, size_t), but
this allows writing out a buffer of chars (without having to learn about
casts).
|
|
|
|
|
|
|
|
|
|
|
|
Conflicts:
libraries/Ethernet/EthernetClient.cpp
|
|
This board has a "cpu" submenu to select either atmega328 or atmega168,
so it does not make sense to put atmega328 in the main board name.
|
|
|
|
|
|
|
|
|
|
|
|
This reverts commits:
3223d4fdca32ec03de4a3a2a0c22f2d40de5f374
77f8dd63ab102ab5d2929ac4edd5c00ae9d70493
|
|
This reverts commit 38c3bbbd3c83eda057d4857635fbd78a4785c3a4.
|
|
This reverts commit a31857688bdc270ed65307755ff3b73ef4867982.
|
|
|
|
Improved ADC speed on Arduino Due
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|