I once had to implement a UART-like protocol on top of a custom 2400 baud FSK modem. There are some subtleties.
Consider the infinite stream of bits that is a UART signal. 1 is idle. 0 is the start bit which means the next 8 bits are data. The bit after that is the stop bit which should be 1; otherwise you have a break. It’s about the simplest way of sending bytes there is.
Now: how does the receiver know the difference between a start bit and a 0 data bit?
It doesn’t. Whether 0 is a start bit or data bit depends only on the receiver’s state. If it’s idle it knows 0 is a start bit. If it’s active it knows 0 is a data bit. That’s all. If the receiver picks up in the middle of a byte, it will receive garbage. What’s more, an unbroken stream of bytes might never stop being garbage. Only after a byte’s worth of idle time (10 bits) is synchronization guaranteed.
That’s fine for serial consoles, because the nature of a console makes for frequent idle time and occasional garbage is no big deal. Not so for arbitary data. Suppose you’re sending a few megabytes over some 9600 baud throwback to ancient times. A glitch could corrupt all that follows.
The solution is to insert idle time occasionally. Maybe your UART has hardware for that. I’ve never seen it, but it would be a reasonable feature to have. Software won’t even know unless it measures how quickly the FIFO drains.
If you insert idle time in software, you will waste bits.
usleep(10*1000000/9600)
can sleep for more than 1042 μs on a PC and you
will have a hell of a time preventing that. Sure, you could busywait. On a
microcontroller that might be the best way. Less so on a PC.
If you’re poking a GPIO pin on a microcontroller, you have the option of doing as the hypothetical hardware would… assuming you can spare the cycles. That’s what I did. There was another reason to bang bits: a need for 2 extra framing bits per byte. I’ve never seen a hard UART that supported 10 data bits. 7, sure. Ye olde Baudot code had 5.
Even if you have a hard UART, you might consider banging bits just so you can control timing. Trade code efficiency for transmit efficiency. Of course, idle time isn’t enough: if you care about guaranteed sync you probably care about ACKs and integrity, and idle time won’t help you there.
Alternatively, you could just not worry about it. If your line idles a lot in practice, maybe it’s not worth fixing. Some things are like that.