Email: Password: Remember Me | Create Account (Free)

Back to Subject List

Old thread has been locked -- no new posts accepted in this thread
???
02/08/06 16:55
Read: times


 
Msg Score: +4
 +2 Informative
 +2 Good Answer/Helpful
#109529 - Software flow control in depth
Responding to: ???'s previous message
Nick Zlatanov said:
Can anybody help me with some ideas of interrupt-driven "C" routine for serial communication with XON/XOFF control for 8051-series microcontrollers?

I found myself in the exact same situation some time ago. I thought I'd easily find a premade solution, but I didn't have much luck. I've been meaning to write on this for a couple of days now. Sorry for the delay.

Erik Malund said:
For '232/'422 you do not need XON/XOF, for '485 there are better ways.

Thus there is no answer to your question till you get clearer with what you need.

more words does not make you appear stupid, it make you appear clear.

Erik

The question is a little vague. I hope he's thinking about an interrupt-driven ring buffered FIFO for RS-232, because that's what this example is about. :)

Indeed, it's relatively rare that you would need XON/XOFF flow control. Other options exist, such as hardware flow control, or building the necessary flow control into your application-layer protocol. However, doing hardware flow control requires hardware support, both in terms of additional driver channels, and a source for the additional control lines, either in the form of general purpose I/O lines under software control on the 8051 or as part of a full blown hardware UART/FIFO (think: 16550 and friends, although not the ones with the broken FIFO). It is possible that dedication of such hardware resources is not feasible, or the remote system does not support hardware flow control. Then, other alternatives are necessary. If you are developing an entirely new system, application-layer software flow control might be a possibility. However, XON/XOFF flow control is still in wide use, and it is not uncommon to need to be able to interact with it.

Andy Neil said:
Keil's ring-buffered, interrupt-driver serial IO example:
http://www.keil.com/download/docs/200.asp

In the ISR, just add checking for received XOFF, and disabling your transmitter, and sending XOFF if your receiver buffer gets too full.

In theory, yes. But in reality, it's not that simple. Grant addresses the problem precisely.

Grant Beattie said:
Well unless you have some control over the host you are connected to, this may never work. If the other side (host) is a PC, then you can run into a situation where the host has a huge buffer and even if you send XOFF you will get a pile of data before the XOFF request is honoured. You need to use a scope and test to see if the host honors the XOFF immediately or much later. It's it's MUCH later, then you will need a BIG receive buffer.

GB

When I first tried this, I did exactly what Andy suggested, and ran into the exact problem that Grant mentions. Operating at 115200 bps, my 8051's receive buffer would fill, at which point it would send an XOFF character. However, the Windows PC, which was sending to the 8051, continued to send some 100+ bytes following the 8051's transmission of the XOFF character. Note that I didn't find this out on my scope; I don't have one with any nifty RS-232 protocol analysis capabilities, and looking at traces would have been very time consuming. Instead, I used Beyond Logic's RS-232 Protocol Analyzer. It made quick work of understanding the magnitude of the buffer overrun situation. I don't know why Windows took so long to honor the XOFF byte; I assume this was because it was doing quite a bit of advance buffering, but really don't know for sure. The PC application wasn't doing anything fancy, either; I was using Ramon de Klein's CSerial class, which is a pretty thin wrapper around the Win32 API.

In addition to this practical limitation, there are two other very fundamental weaknesses in the XON/XOFF flow control scheme.

1. The XON/XOFF characters become "special" control bytes, and thus cannot be present in the data stream. Thus, sending arbitrary binary data over a serial link with XON/XOFF flow control is impossible, unless the binary data is encoded such that the start and stop bytes are never present in the raw data stream sent over the link. Intel Hex Format and uuencoding come to mind as possible options. However, use of such techniques tends to suggest that an onboard encoder, decoder, or both (depending upon transmission direction) will need to be present in the 8051's firmware. Outside of programming labor, code space, and performance implications, note that some encodings may be significantly larger than their original binary representations. Such is definitely the case with Intel Hex Format, which seems to be oriented more towards human readability than compactness.

2. Software flow control is truly in-band; it reduces the amount of serial bandwidth available for your application. The big implication here is that without appropriate amounts of hysteresis in the software flow control state machine, it is possible to overwhelm the serial channel with XON/XOFF control bytes to the point where there are more XON/XOFF bytes present on the wire than actual data bytes. Such XON/XOFF thrashing is largely analagous to the disk thrashing you get when a PC application's working set does not fit in physical memory and constant disk swapping is necessary.

Andy Neil said:
The standard Keil implementation of putchar() includes XON/XOFF handling. The source code is provided - see http://www.keil.com/support/m...utchar.htm

Indeed, it does. But there's some irony in handling received XON/XOFF characters on the 8051 but not transmitting them. After all, if the 8051 is connected to a PC or other faster device, it is likely that the 8051 will experience the buffer overrun rather than the other device. Not to poke too much at the default Keil implemention, but their default implementation also uses polled I/O, which as others have commented, is practical in only the most trivial of applications.

All things considered, if there was ever a time to make a pitch for hardware flow control instead of XON/XOFF software flow control, this would be it. However, assuming you must do XON/XOFF flow control, keep reading.

*   *   *   *   *

Having finally realized all of the problems I was facing, I grabbed a copy of serial.c from Keil's traffic light example. It was the closest thing to what I wanted, and included an efficiently written ring buffer. The code uses RTX-51 Tiny; however, much of the design is applicable regardless of whether RTX-51 Tiny is used or not. It is neither my point nor intention to promote the use of RTX-51 Tiny or other RTOSes on 8051-class devices. The code is also, perhaps not surprisingly, intended to compile under Keil's compiler. Of course, not much should stop it from being ported to SDCC or your favorite 8051 C toolchain.

As you can see from my port of Keil's code (serial.c, serial.h), the only major departure I have made from Keil's implementation is making the receiver a state machine with four states. According to the comments, these are the states:

NORMAL: Receive buffer is empty or filling, but is not full. Reception should proceed normally.

FULL: Receive buffer has been marked full. We need to send an XOFF character so that the sender allows us to relieve our buffer.

EMPTYING: Receive buffer is full or emptying, but is not empty. An XOFF character has already been sent, so reception should be suspended until the buffer is empty. If we permitted reception prior to completely emptying the buffer, we would put ourselves in a situation where it is very likely that the buffer would soon fill up again. This would be inefficient, as we wish to keep the XON/XOFF : data byte ratio very low. This hysteresis helps to achieve that goal.

EMPTY: Receive buffer has been marked empty. We need to send an XON character so that the sender begins sending us data again.

In short, in my code, I send an XOFF byte after 8 data bytes have accumulated in my input buffer, but can continue to receive another 120 data bytes before experiencing a buffer overflow condition. I then wait until my receive buffer is empty before sending the XON byte. These values were experimentally determined to be practical for my system, which happened to be running on a DS89C450, where I was readily able to scrounge up the 128 bytes of XDATA space required for my input buffer. Using this scheme, I successfully transfered multimegabyte data streams without overruns or excessive XON/XOFF activity. Your mileage, however, is likely to vary depending on a variety of application-specific factors.

There are a couple of additional tweaks present in my code, but they are well commented and do not require further discussion.

As one final note, my code does not include any of the traditional serial port initialization that must be done prior to using an 8051 serial port (setting serial port mode, timer, etc.)

--Sasha Jevtic

List of 17 messages in thread
TopicAuthorDate
XON/XOFF control            01/01/70 00:00      
   what do you need to know?            01/01/70 00:00      
   XON/XOFF control            01/01/70 00:00      
   What's the problem            01/01/70 00:00      
      Buffer overflow            01/01/70 00:00      
   Standard Keil implementation!            01/01/70 00:00      
   Software flow control in depth            01/01/70 00:00      
      nice!            01/01/70 00:00      
         Bad 16550s            01/01/70 00:00      
            re: Bad 16550s            01/01/70 00:00      
      XOFF Clarification            01/01/70 00:00      
         Software flow control in hardware            01/01/70 00:00      
            Neat            01/01/70 00:00      
               Opposite of SMS?            01/01/70 00:00      
         Pesky hardware FIFOs            01/01/70 00:00      
            Bad 16550's and Control            01/01/70 00:00      
   then join me and score it            01/01/70 00:00      

Back to Subject List