Offtopic : serial flow control

56 views
Skip to first unread message

Adrian Godwin

unread,
Oct 23, 2015, 5:21:02 PM10/23/15
to devel...@arduino.cc

On Fri, Oct 23, 2015 at 4:12 PM, Cristian Maglie <c.ma...@arduino.cc> wrote:
Il 23/10/2015 15:39, Adrian Godwin ha scritto:
> It's a really bad solution for communicating between two fast things
> over a slow channel (happy to expand on that but in a different thread
> please)

Happy to see more on this topic from your experience, feel free to start
a new discussion on that! :-)


As Andrew said in the original thread, flow control is important. And there are devices that require some sort of RTS/CTS flow control, so it's useful to have a convenient way to access them.

But managing the flow-controlled link is difficult, and varies according to the nature of the slowdown so the library that handles it is either very simple (leaving all the work to the application), or very large (to handle all the possibilities).

A 'just do a simple buffer with flow control' sounds straightforward, but ask 10 people to do it and you'll have 10 perfect ways to communicate with specific hardware. Muddle them up and only a few of the combinations will work properly. And even when they do, they probably won't do what the users wanted.

Ignoring the implementation issues and timing, there's an underlying problem in that a library wants to hide the complexity beneath a simple interface. And automatic buffering is complex, and you want to know what it's doing.

History first. It might seem irrelevant but it explains why we have a situation that doesn't have a single simple solution.

Hardware flowcontrol was invented for the situation where two devices wanted to communicate and could be either very slow (a mechanical typewriter that can't print as fast as the bit rate), broken (off-line modem)  or stuck (half-duplex line can transmit or receive but not both at the same time). But sometimes you wanted to connect two terminals together with no intervening modems to reverse the signals, and so the null-modem cable was born.

More details :

http://www.lammertbies.nl/comm/info/RS-232_null_modem.html


So now we've got cross-connected wires that don't signify their original meanings at all : there's a new protocol that uses one or more of those pairs to indicate 'I'm too busy to listen to you'.

But what does that mean ?

The simplest meaning is 'I cannot accept the next character'. And a good implementation signals that just before its buffer fills, and clears it when there's a margin of space in the buffer. The sender checks before sending and holds back if busy, etc.

A bad implementation reads characters into a buffer and tries to deal with the contents. When it comes across something that will slow it down, it signals 'busy' and goes and does it. The sender, though, had already sent one character and has another in the UART before it sees the change. Perhaps one is lost. Now the pair are out of sync, and when the receiver returns and signals it's not busy again, the sender doesn't know what was received. It hopes everything was, and some of the time it's right.

With enough care and knowledge of the content, that might be made to work. But it will differ according to the timing of the receiver, and it may not be obvious to the application, so recovery may be difficult .

FIFOs, software or hardware, help take some of the problems away but need their own management.

This all assumes the delays are small and the CTS/RTS handshake is fast enough to avoid buffer overruns. What if there isn't a physical connection between the two, but a couple of bluetooth or USB links ?  So when the a radio asserts its busy, it's actually got 16 bytes in an internal buffer that hasn't been sent to the other end ? So it sends it to the other radio, which finds the receiving device busy and holds it in the buffer while the first radio refills from the other device, etc.

I'm not going to go through that in any more detail, but you can see how fragile it's getting and how the buffering can only work if everybody does it correctly, and to the same protocol. And when data doesn't appear, you have no idea if it's been delivered and discarded, or will pop up later, or has holes in it etc. And flowcontrol made it worse, not better. Without flowcontrol you would have sent data no faster than the link can handle, because otherwise it would fail instantly. With flowcontrol it didn't go any faster overall. it just got less predictable.

I'd be interested to hear what Paul's users expected when they asked for RTS/CTS flow control. Probably, they had some USB interfaces and an application that sometimes couldn't keep up. Or they had some radios that asserted CTS when their internal buffers were full. Both of these could be handled with the right control, but it wouldn't, I suggest, be the same control.

Is there a better way ?

Of course. If you have an intelligent radio like a bluetooth modem, you agree a size of packet that every part of the chain can always either accept totally or not at all. You protect that packet with framing characters, an escaping method (if necessary) and a CRC or checksum. Then you always deal in whole packets, and your protocol has a defence against lost packets - perhaps it asks for a retransmission, perhaps it can forward-error-correct a damaged one, perhaps it can safely discard it and wait for an update (that's always my preference). You can still deal with buffer full issues with RTS/CTS if you like, but you're probably far better off using some sort of control packets that tell you a lot more about what the situation is and keeping your control lines for some nice blinky blue leds.


There's also (character-level, in-band) software flowcontrol. All the above problems plus the inability to receive the control chars when the buffer is full ...

These are not new problems. Here's a thread from 1986 referring to comments made by RMS earlier still.

https://groups.google.com/forum/#!topic/comp.emacs/NTy_XTTtQ1Q

As someone in that thread mentions, there's a better way to do serial than async - HDLC. It can handle 8-bit binary characters and still have room for out-of-band signalling for framing and control. Sadly, although the USART isn't much more complex than the UART in hardware, it took another generation or two to get anything like it in normal circulation (ethernet, CAN, USB) and they're still not the lowest common denominator that async is.


-adrian

(far too cynical after working for years in industries where even a parity check is 'too much trouble' or 'inefficient for binary data', let alone checksums or CRCs. And programmers that claim 'it works' after testing it on a bench, and can't understand why it occasionally fails badly when the cables run past the ignition. Or the system 'just stops working' and you have to turn-it-off-and-on-again like something from Microsoft. etc. etc.)







Dennis Lee Bieber

unread,
Oct 23, 2015, 7:59:18 PM10/23/15
to devel...@arduino.cc
On Fri, 23 Oct 2015 22:20:55 +0100, Adrian Godwin
<artg...@gmail.com> declaimed the
following:

>As someone in that thread mentions, there's a better way to do serial than
>async - HDLC. It can handle 8-bit binary characters and still have room for
>out-of-band signalling for framing and control. Sadly, although the USART
>isn't much more complex than the UART in hardware, it took another
>generation or two to get anything like it in normal circulation (ethernet,
>CAN, USB) and they're still not the lowest common denominator that async is.
>
If one can ensure enough working software to implement HDLC, then
yes... it is useful.

But in ancient days, simple serial UARTs were seen by the processor as
maybe three registers (Tx data, Rx data, status register)... making the
minimal control logic rather simple:

Send:
while status&TxD
;
TxBuffer = byte;


Recv:
while not status&RxD
;
byte = RxBuffer

(presuming the UART clears/sets the status bit when data was read).

Advanced usage -- an interrupt linked to TxD and RxD status bits.



It seems to me that the argument of a chain of intermediates in the
data path is irrelevant... The RTS/CTS (or XON/XOFF) should only apply
between any two adjacent nodes. If each node can buffer its connecting end
points with flow control, there shouldn't be a propagation of drop-outs.
That is, if the "modem" at your end gets a "hold" signal from the other
end, it should have enough buffer space to handle the lag between
forwarding the "hold" to its source and said source stopping output to it.

Granted, at some of the obscene speeds being used on modern hardware
serial links -- they better have a fast response to "hold" signals. It
could mean triggering "hold" when its buffer is still 90% empty <G>

--
Wulfraed Dennis Lee Bieber AF6VN
wlf...@ix.netcom.com HTTP://wlfraed.home.netcom.com/

Paul Stoffregen

unread,
Oct 24, 2015, 7:44:21 AM10/24/15
to devel...@arduino.cc
My main goal here was and still is to talk about APIs. Everybody will
benefit, especially end users, if we work together for API compatibility.

Cristian did invite you to start this thread...

You raised many historical issues. The different definitions for these
signals the old standards (published in 1962 and 1969) and the many
varying practices in the 1970s to 1980s really aren't of any
consequence. For at least the last 25 years, RTS has been well
understood to be an output which indicates whether the RX pin is ready
to receive data, and CTS has been well understood to be an input which
receives that RTS signal, indicating to the TX pin whether it should
transmit to the other side's RX pin.

Likewise, XON/XOFF flow control is really a separate matter from
implementing hardware flow control.

You are correct that the world is filled with poor implementations.
Clearly that's very frustrating. Personally, I see this as a technical
challenge to overcome, especially dealing with devices which do not
immediately cease transmission in response to RTS.

You asked:
>
> I'd be interested to hear what Paul's users expected when they asked
> for RTS/CTS flow control.

The reality is only the most sophisticated users understand these issues
well enough to ask for flow control. Far more common are reports that
some of their data didn't transmit, or wasn't received, or reports that
some library is corrupting memory or interfering, or even suspicion that
limited bandwidth or electromagnetic interference issues are corrupting
their signals. If you answer and troubleshoot end user problems on a
daily basis, as I do, eventually you start to recognize common causes.

What end users ultimately expect is that we developers who create these
platforms and libraries have done our homework! They expect to be able
to buy a Bluefruit or XBee or ESP8266 and have it work properly when
they wire it up to any Arduino compatible board and use the correct
Serial object to access it. Likewise for connecting 2 boards to each
other. The Arduino platform and its core library is meant to abstract
the hardware, so even utter novices can make complex things work. They
expect we've taken care of these troublesome details.

Well, maybe "expect" is too strong a word? Certainly users "want" this,
for things to just work automatically, even when combined together with
other 3rd party libraries and hardware.

Other synchronous protocols were mentioned as superior to adding
hardware flow control to asynchronous serial. That is true, and indeed
Arduino does support USB and libraries do exist for Ethernet, CAN and
others. But the existence of those other protocols isn't an answer for
a user who's purchased a XBee or Bluefruit module!

Asynchronous serial devices with hardware flow control are widespread,
and I personally want to deliver the highest quality user experience
possible for users who choose those products. I also wish to
collaborate with Arduino on API compatibility, which was the main point
of this conversation.

Andrew Kroll

unread,
Oct 24, 2015, 8:47:44 AM10/24/15
to devel...@arduino.cc

So, the questions to ask In order to shape the API are:
How do these other devices behave with respect to RTS/CTS?
Will a device behave well, and stop transmitting, or does it spill the fifo?
If a device has a fifo that dumps, how much  incoming data should be expected?

--
You received this message because you are subscribed to the Google Groups "Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to developers+...@arduino.cc.

William Westfield

unread,
Oct 24, 2015, 10:06:14 PM10/24/15
to devel...@arduino.cc
> So, the questions to ask In order to shape the API are:
> How do these other devices behave with respect to RTS/CTS?
> Will a device behave well, and stop transmitting, or does it spill the fifo?
> If a device has a fifo that dumps, how much incoming data should be expected?

Actually “do other devices work with the XYZ implementation of serial flow control” is an entirely separate question from “is this API reasonable for turning on XYZ’s flow control implementation.”

Likewise, discussions of exactly how flow control should work on a chip with certain HW characteristics and certain driver characteristics are not needed here. I don’t think I’ve seen an implementation that had more choices than “on/off” “in/out” in ages. One hopes that hardware implementation work ‘correctly’, and interoperate. SW implementation of HW flow control are almost certain to have problems, and one thing we probably have to admit is that an Uno is never going to do a good job at it. Perhaps an Uno will never even implement the API...

For a chip with some sort of pinmux, it might be possible and desirable to have an option for which pin to use, but … that’s relatively unusual, isn’t it? Most chips might let you select a SET of pins for a particular USART, but once you’re using that set, you probably don’t have any more choices?

For unity with the typical PC communications program, I’d sort-of like to stay away from mentioning RTS or CTS explicitly. How about:

Serial.hwflowin(boolean onoff); // default or fixed pin
Serial.hwflowin(boolean onoff, int pinno); // user choice of pin
Serial.hwflowout(boolean onoff);
Serial.hwflowout(boolean onoff, int pinno);
Serial.hwflow(boolean onoff); // enable/disable both at once.

BillW/WestfW

Andrew Kroll

unread,
Oct 24, 2015, 10:49:39 PM10/24/15
to devel...@arduino.cc

I'd just call it autoflow, because that is what it is... automatic.

Paul Stoffregen

unread,
Oct 24, 2015, 11:28:44 PM10/24/15
to devel...@arduino.cc
On 10/24/2015 07:06 PM, William Westfield wrote:

> and one thing we probably have to admit is that an Uno is never going
> to do a good job at it. Perhaps an Uno will never even implement the
> API...

Indeed, hardware flow control is unlikely to ever be implemented on Uno.

But hopefully Uno would implement the API Cristian proposed, with fixed
return values of false. Third party libraries must be able to depend on
these functions at least compiling correctly. The last thing we want to
do is needlessly force 3rd party libraries to add hardware-specific
#ifdefs only because an API wasn't designed well or implemented fully
for across all boards and variants.


> For a chip with some sort of pinmux, it might be possible and
> desirable to have an option for which pin to use, but … that’s
> relatively unusual, isn’t it?

Actually, pin muxing where you can choose routing of peripheral signal
to pins (usually from a small set of choices per pin), is now the norm.
Nearly all new microcontrollers released in the last few years have this
feature. Arduino Zero and Arduino Due both have it, and Arduino Zero
has an additional layer of flexible signal routing within the serial
port hardware. Microcontrollers from NXP, Freescale and Microchip also
have varying levels of pin muxing. The clear trend is towards
increasing on-chip routing flexibility, where some semiconductor vendors
are differentiating their products with the ability to assign peripheral
signals to nearly any pin.

> SW implementation of HW flow control are almost certain to have problems

Software is actually a very viable option for generating the RTS output,
of course using any digital pin. CTS is much harder to do well with
only software.

William Westfield

unread,
Oct 25, 2015, 6:29:23 AM10/25/15
to devel...@arduino.cc

On Oct 24, 2015, at 8:27 PM, Paul Stoffregen <pa...@pjrc.com> wrote:

>> it might be possible and desirable to have an option for which pin to use, but … that’s relatively unusual, isn’t it?
>
> Actually, pin muxing where you can choose routing of peripheral signal to pins (usually from a small set of choices per pin), is now the norm. Nearly all new microcontrollers released in the last few years have this feature. Arduino Zero and Arduino Due both have it, and Arduino Zero has an additional layer of flexible signal routing within the serial port hardware.

I meant that it’s unusual for RTS/CTS to be independently muxable to more than a very limited set of pins. For example, on the SAMD21 (Arduino 0) SERCOM0 can appear on either of two sets of 4 pins, and the “additional layer” lets you either move RxD/TxD OR enable RTS/CTS. So essentially, once you’ve picked which pin to use for Rx/Tx, you have no choice at all for where RTS/CTS will be. SAM3X (Due) looks to be in worse shape; I don’t see any choices for any of the uart signals - it just uses “pinmux” to connect different peripherals to the same pins.
(I think there are some chips that are more flexible. PIC24/PC32 with their “PPS” (Peripheral Pin Select) feature?)


> Software is actually a very viable option for generating the RTS output, of course using any digital pin. CTS is much harder to do well with only software.

I would have thought the other way around? If you have shallow FIFOs, and assert RTS, there’s no guarantee that you won’t get more than a FIFO worth of data blasted at you (especially if your FIFO is the one byte “holding register”, and you don’t change CTS till you’ve receive the first character.) Whereas you can check CTS input in the Tx ISR… (or are we having DCE/DTE confusing? You said “RTS output”…)

BillW/WestfW

Dennis Lee Bieber

unread,
Oct 25, 2015, 11:12:44 AM10/25/15
to devel...@arduino.cc
On Sat, 24 Oct 2015 19:06:05 -0700, William Westfield
<wes...@mac.com> declaimed the following:

>
>For unity with the typical PC communications program, I’d sort-of like to stay away from mentioning RTS or CTS explicitly. How about:
>
And then there are the remnants of systems that use DSR/DTR signalling
(I think that still shows up in software for amateur radio control; some
gear is wired for DSR/DTR to control the PTT, other user RTS/CTS)

Robert McHugh

unread,
Nov 17, 2015, 4:31:56 AM11/17/15
to Developers, wlf...@ix.netcom.com

I'm not sure that is applicable for the things you are discussing.  The difference between DTR (Data Terminal Ready)/DSR (Data Set Ready) and RTS/CTS systems is that one is considered DTE (Data Terminal Equipment) and the other is considered DCE (Data Carrier Equipment).  The former being "terminals" or things that looked like terminals to the hardware/software, and the later being modems for all intents and purposes.  In the "old" days, DTR was a common control for flow, being both used for terminals and just basic communication circuits, since in those items you weren't too concerned with 100% error free connections.  RTS/CTS normally was used in higher rate communications or when the data needed to be guaranteed complete, or like in a modem - when the endpoints were not specifically defined.  You also had the issue that as someone has mentioned already - baud rates were no higher than 19.2 or 38.4 maximum, and those overruns were handled by 16 byte FIFO chips in hardware, or tight Interrupt service routines, just as bi-sync communications did (this is mid 1980's stuff) at the time.  Obviously contributing to the problem is that the original basis for RS232 was not well defined outside of the basic wiring schematic.  The timing that occurred when using for example Xon/Xoff relied solely on if the receiving end could interrupt or poll fast enough to answer before losing data pouring in.  Ideally, you would handle these high speed communications using a larger ring buffer and a dedicated hardware signal combined with the software buffering, since the return handshake if using Xon/Xoff is most likely is slower than the electrical handshake for those types of physically connected devices.

I can give the history of these having both an intimate electronics knowledge, and software background going back to the "wild west" of things.  As pertaining to the issues at hand, I can only offer this wisdom.  If the slowest rate of communication data at any point in the circuit is "X" then the rest of it should only be required to perform to that specification.  So if you are only sending polled data for a time stamp - it most likely doesn't need to run through a system at a ginormous baud rate, slow works well - even 2400 baud has it's uses - however crude that sounds.

And Dennis, you are quite correct from the standpoint of Ham radio, they have grown rapidly in the last 30 years and now support many types of devices that require data control.  Some are just sheer waiting and re-polling, but I'm not so sure that looking at how some of those devices were implemented wouldn't give new light to the questions on handling data communications within these libraries.

Reply all
Reply to author
Forward
0 new messages