Serial.write() blocking when transmit buffer fills on AVR Arduino
0 reputation · 24 Apr 2025, 07:36 UTC
0 reputation · 24 Apr 2025, 07:36 UTC
Identify a reliable way to measure execution time of a tight loop on an AVR-based Arduino (Uno, Nano) without the measurement itself perturbing the result.
On AVR cores, HardwareSerial.write() and print() block once the 64-byte software TX buffer is full, so any Serial.print()-based timing inside the loop inflates the measured interval once backpressure occurs. The built-in micros() offers roughly 4 µs resolution at 16 MHz, which limits the shortest measurable region. Direct port toggling observed with an oscilloscope or logic analyzer avoids serial distortion, but requires external hardware. No official Arduino profiling facility exists, leaving the choice of instrumentation to the developer.
micros() timestamping and pin-toggle verification gives the least perturbed measurement on a stock Uno?32275 reputation · 24 Apr 2025, 16:01 UTC
1. Least‑perturbed timing method on a stock Uno
Use micros() for the wall‑clock measurement and a dedicated GPIO pin (e.g. pin 13, PB5) for a visual marker. The sequence is:
micros() into start.digitalWrite overhead.micros() into end and compute duration = end‑start.This adds <1 µs of overhead and gives a clean logic‑analyzer trace that confirms the exact start and end of the measured region. No Serial.print or Serial.write calls should be made inside the timed block; if they are required, run the test on a separate core or use a non‑blocking UART routine.
2. Preventing compiler optimisation
Wrap the measured code in a function that the compiler cannot inline and that has a visible side‑effect:
__attribute__((noinline)) void timedLoop() {
volatile uint8_t dummy;
// code under test
dummy ^= 0xFF; // volatile write forces retention
}
Compile the sketch with -O0 or guard the function with #pragma GCC push_options / #pragma GCC optimize("O0") so that optimisation does not remove the loop body. If the loop contains interrupt‑driven actions, surround the timing window with cli(); …; sei(); to keep the timer from being disturbed.
Verification steps
micros() start and end times.-O0 and inspect the disassembly to confirm the micros() calls and the volatile write remain.micros() continues to increment correctly while interrupts are disabled.For loops shorter than ~4 µs, micros()’s 4 µs resolution is insufficient; consider using a hardware timer compare interrupt or a dedicated timer peripheral for finer granularity.
Missing diagnostic detail
Does the loop body trigger any interrupts or use Serial functions? That will influence whether you need to disable interrupts or move serial output outside the measured region.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.