Raspberry Pi 3 Hardware Flow Control


Getting the UART (serial port) going on a Raspberry Pi is generally fairly straightforward as long as you use a device that can handle converting the 3.3V TTL outputs to proper serial levels (or else just use a USB serial adapter that handles this).

By using the RX/TX/GND pins on the GPIO header you can get a standard serial port with software flow control (meaning you can use ^S and ^Q to stop and start the flow of characters).

Serial ports traditionally also supported various types of hardware flow control, where extra wires are used to start and stop the flow (especially useful if your program [e.g. EMACS] wants to use every possible control character). The Raspberry Pi machines support hardware flow control but it can be tricky to set up.

I started looking into this due to JWZ's troubles getting his old AAA60 terminal working with a Pi 3 (link1, link2).

Since I teach a course where we do low level work on the Pi serial port and write low-level UART drivers I thought I could maybe help out and perhaps accomplish the impossible task of giving a useful reply to a lazy-web request. As with all embedded system/Linux tasks it turned out to be impossibly complex, especially when trying to diagnose locally.

How to

  1. First get serial working with software flow control on your pi. I won't go into too many details on this, as by default if you're running Raspbian it's usually set up and working. For testing you can install apt-get minicom to give your serial port experience the old 1980s BBS feel that you've been missing.

  2. Note that if you have a Pi3, the "good" serial port (ttyAMA0) driven by drivers/tty/serial/amba-pl011.c has been taken away and given to the bluetooth interface, leaving only the simpler/more-troublesome "mini-uart" instead. You will probably want to switch it back. You can do this by editing /boot/config.txt and adding the line dtoverlay=pi3-miniuart-bt
  3. The hardware flow control pins RTS/CTS are not enabled by default. You will need a program that enables them. On older Pis the pins are on a non-soldered header and you will have to do some extra work to get access to them. On the newer boards with 40-pin headers (b+, 2b, 3b) the pins are available.

    To enable RTS/CTS you will need to run a program that toggles the proper GPIO output multiplexing bits to switch them to be RTS/CTS. On Pi3 this means GPIO16 (header pin 36) becomes CTS and GPIO17 (header pin 11) becomes RTS.

    There are various tools floating around that do this for you, but most don't handle the Pi3 properly (the Pi2 and Pi3 have more RAM so the MMIO offset is at a different location than on earlier boards). I have some code that will do this in my github repository: https://github.org/deater/uarch-configure/rasp-pi-serial Run rpirtscts on.

  4. Once you have RTS/CTS enabled, in theory the Linux kernel driver will automatically use it. You can use the pulse_rts and toggle_rts programs in the aforementioned repository to test this, either with a multimeter or by putting a resistor and LED on the proper pin.

  5. Now that flow control is hooked up, you'll want to run things through a proper serial level line driver. You can get these, the ones using a 3232 style chip are common. Be sure to get one that not only drives RX/TX but can do RTS/CTS as well. The one I am using is the one described here: rs232 to ttl converter

    Be sure to follow any directions on setting up your converter, including jumpering the pins properly.

    I used a null-modem cable, and the following connections:
    Pi3rs232 converter
    RX (pin10) GPIO15RX
    TX (pin8) GPIO14TX
    RTS (pin11) GPIO17RTS
    CTS (pin36) CPIO16CTS

  6. Now it's time to test if the HW flow control actually works.

    Our test case is the rpi3 as the main system with a terminal hooked up to it (in this case an x86 machine running minicom).

    You need to get agetty running on the pi over ttyAMA0 using flow control (that way you can login from the terminal). On sane Linux systems this is as simple as modifying /etc/inittab. If you're cursed with systemd then it is much more of a pain, involving editing files in /etc/systemd/syste/getty.starget.wants/ I never figured out how to get that to work so I was running agetty by hand with the -h 115200 ttyAMA0 options.

    Now configure your terminal to do HW flow control. You can do this in a menu from minicom.

    Now send a lot of data across! Run cmtarix. Or cat /boot/kernel.img | uudecode -m -. This doesn't seem to be enough to toggle the RTS line, but if you bring up a menu on minicom (^AO) it stops reading data out of the port and this causes the pi3 side to see CTS=1,RTS=0 and things stop. If you close out the menu the pi sees CTS=0,RTS=0 and resumes transmitting.

Nowhere near as pretty as JWZ's terminal.

Back to VMW Productions page