The Universal Asynchronous Receiver Transmitter circuit or simple UART (pronounced "you art") is one of the more common interfaces used to communicate serially. Some computers, such as the IBM PC, used an integrated circuit called a UART to convert characters to and from asynchronous serial form. By ‘serial’, we mean that data is transmitted one bit at a time.
This circuit was at the base of Recommended Standard - 232 (RS-232 in short) that also defined the physical external COM port in IBM PC. The latest revision to RS-232 is the EIA RS-232 that was done in 1997.
In recent years, the PC has lost the RS-232 port (maybe only externally), replacing it in favor of the USB, but RS-232 is still in wide use including many variants, from RS-485 used in the industry to SpaceWire used in the Space Station. In its simpler forms, it is currently widely adopted in the embedded industry.
To understand a bit more the UART circuit and the RS-232 let’s take a look at the past. Serial transmission is very old, born with the first Teleprinters (TTYs). The teleprinters were in use from 1914 to recent times, with the last company manufacturing TTY closing in 1990, while their last use is reported for air lines bulletin in recent years and most of the terms used come from the TTY world. Mark and Space for example are terms describing logic levels in teleprinter circuits. The baud rate, or Symbol rate, is the speed of a serial connection and is based on multiples of the rates for electromechanical teleprinters.
Today, although it is becoming less common in personal computers, it remains one of the most common peripherals found on micro-controllers (MCU) and is used for communication with external devices and systems, to talk with on-board serial devices or to make links between boards, between boxes or between embedded boards and PCs with an RS-232 port.
RS-232 globally specifies:
- signal voltages
- signal functions
- signal timing
- protocol for information exchange
- UART configuration
With a micro-controller oriented view we will go now to examine all the above points.
The UART is a full duplex communication channel, in asynchronous mode each line is independent of the others in terms of its main function. The RX pin can receive data regardless of the TX pin’s activity and vice versa.
Usually the UART wires from the micro-controller are labeled TX, RX, GND (for transmit, receive and ground) in 3 wire configuration (without flow control implemented) and 5 wires TX, RX, GND, RTS, CTS, with hardware flow control, where RTS stay for Request To Send and CTS for Clear To Send.
The signal is described as a positive voltage to communicate the logic value 0 called a “Mark”, and a negative voltage to communicate the logic value 1, called a “Space”.
This signals are usually too weak in current and voltage as get out from the micro-controller and the standard specify that should be used voltages from ±5V to ±15V with length of wire of 10 meters, so how to interface to the UART lines, that output ±3V with very low current?
The MAX232 chip on the Mizar32's Serial UART add-on board is a TTL to RS-232 level converter which pumps the voltage from ±3V to ±15V.
The signal timing is measured in Baud, where one baud in binary communication corresponds to one bit per second, so at 9600 baud rate, we have 9600 bits per second with a time frame to describe each bit of 104 μs 1/9600. Often the clock will run at 16 times the baud rate to allow the receiver to do centre sampling.
The data exchange protocol is very simple.
The packet begins with start bit, which is a logic 0, being transmitted/received first. In the software side, this bit is important as we can poll the RX pin for this bit to signal that a packet of data is coming. The data bits or the payload may or may not have a parity bit, then the packets end with a logic 1, one or two stop bit.
|XXXXXXX||(7 or 8 bit of data)|
|X||1 optional bit of parity|
|1||one or two stop bits|
Note that the least significant bit of the byte is sent first, whereas we normally write numbers with the LSB on the right, so we should read it from right to left.
Regarding configuration parameters, a common configuration (usually stored in a register) is 9600/8n1 that means for the serial port: 9600 baud, 8 bits data, no parity bit, 1 stop bit. Obviously both UART should be configured in the same way in order to communicate.
The Mizar32 has two serial ports available on the bus connectors, UART0 on the right bus and UART1 on the left bus. UART0 has just data (TXD, RXD) and hardware flow control (CTS, RTS) signals, while UART1 also has modem control signals (DSR, DTR, DCD, RI).
In the Atmel documents, these are called "USART"s because they can also be programmed into synchronous mode to work as additional SPI ports.
|UART0_RX||PA0||BUS4 pin 3|
|UART0_TX||PA1||BUS4 pin 4|
|UART0_RTS||PA3||BUS4 pin 5|
|UART0_CTS||PA4||BUS4 pin 6|
|UART1_RX||PA5||BUS3 pin 3|
|UART1_TX||PA6||BUS3 pin 4|
|UART1_DCD||PB23||BUS3 pin 5|
|UART1_DSR||PB24||BUS3 pin 6|
|UART1_DTR||PB25||BUS3 pin 7|
|UART1_RI||PB26||BUS3 pin 8|
|UART1_CTS||PA9||BUS3 pin 9|
|UART1_RTS||PA8||BUS3 pin 10|
RS232/RS485 serial add-on boardEdit
The add-on serial board has two banks of switches, DIP1 and DIP2 to select between RS232 and RS485 modes.
If all switches of DIP1 are up and all of DIP2 down, it converts the bus signals to RS232 levels on its female DB9 connector J7. This connector is configured as DCE equipment, which is the opposite of a PC serial port, so a cable to communicate with a PC should connect the same pins at each end; a null modem cable is not required. Connecting other DCE equipment to it, like a modem or GPS receiver, requires interchanging of TX and RX, for example with a null modem cable.
Note that in the 1.1.1 version of the serial port the CTS and RTS pins were swapped over by mistake, so to get the right connections here you need to modify either the board or the cable. However, hardware flow control is not yet working in eLua so it makes no difference; see issue #29.
|Signal||Bus pin||UART module
|UART0_RX||P5 pin 3||Pin 3 (input)||Pin 3 (input)|
|UART0_TX||P5 pin 4||Pin 2 (output)||Pin 2 (output)|
|UART0_RTS||P5 pin 5||Pin 8 (output)||Pin 7 (output)|
|UART0_CTS||P5 pin 6||Pin 7 (input)||Pin 8 (input)|
|GND||Various||Pin 5||Pin 5|
If all switches of DIP1 are down and all of DIP2 up, the board's DB9 connector is disabled and RS485 signals appear on the four screw terminals.
This interface allows up to 32 RS485 devices to be connected to the same wires with a cable length of up to 1200m at 100 kbit/sec.
Currently, RS485 mode is not supported in Hempl; see issue #77.
Depending on which firmware you have, UART0 may be used for the PicoLisp console (configured at 115200 baud, 8 data bits, 1 stop bit, no parity) and PicoLisp's default input and output files are the console, so functions like "
prinl" and "
# Greet the user (prinl "What's your name? ") (prinl "Hello, " (setq name (read)) "!")
UART0 can also be accessed (and UART1 must be accessed) using the lower-level
uart Hempl module, which gives a higher degree of control over the UART's behaviour.
The following example sets a different baud rate on UART0 and spits out a prompt character twice a second until a character is received in reply. To do this, it uses the
setup function and the optional timeout parameter of the
# Prompt a 9600 baud serial device until we receive # a character in reply (setq uartid 0 # Which UART should we be talking on? timeout 500000 # Prompt once every half second timerid 0 # Use timer 0 to measure the timeout prompt "U" ) # The prompt character (0x55 : binary 01010101) (de get-char-uart () (uart-getchar uartid timeout timerid) ) # Get a character from UART (setq reply (get-char-uart)) (until (= "" reply) (uart-write uartid prompt) (setq reply (get-char-uart)) )
Please note: You may also download the above code
uart-io.l from our examples repository on github.
Hardware flow controlEdit
Note that enabling hardware flow control with:
(uart-set-flow-control uartid (+ *uart-flow-rts* *uart-flow-cts*) )
does not work yet. See issue #29.
When a UART receives a character it will remember it until you ask for its value using
getchar. However, if a second character arrives before you have read the first one, the first one will be forgotten.
You can get round this by enabling a UART buffer, for example:
(uart-setup 1 115200 8 0 1) (uart-set-buffer 1 1024)
The above configures UART 1 and gives it an input buffer. This will allow the UART to receive up to 1024 characters and remember them all even if you haven't read the first one yet (the 1025th character will provoke an error message and will be forgotten.
UART buffer sizes must be a power of two, i.e. 1, 2, 4, 8, 16 and so on up to a maximum of 32768 characters.
Some firmware uses UART0 as the Alcor6L console. When this is the case, a buffer is always enabled on this UART.
USB CDC serial portEdit
Hempl firmware includes software that emulates another serial port on the USB interface. You connect a USB cable from the Mizar32 to your PC and a new serial port appears on the PC. Under Linux it is called
/dev/ttyACM0 and on Windows it appears as a new "USB serial port".
Usually, this virtual serial port is used as the Hempl console, sending Hempl output and error messages and receiving keyboard input from the user.
It is slightly different from physical serial ports in that:
- It is more than ten times faster than the fastest RS232 serial port;
- It always implements flow control, which ensures you never lose any output or input due to overruns, but if your program produces output and there is no PC attached to the USB port, your program will freeze after a kilobyte or two of output;
- Some settings, like baud rate and stop bits, make no difference because the signals do not travel over RS232 wires;
- I don't know whether Lua interrupts work on the USB serial port or not.
Please note: There is currently no support for interrupt handling in PicoLisp. See issue #2.
Baud rate accuracyEdit
The following table gives the actual baud rates set by Hempl (or eLua) for the most commonly used ones:
Hijacking the serial board's TX LEDEdit
If UART0 is not used, the LED on the serial board can toggled by using
pio.PA_1 as a generic Mizar32/PIO output: a low output value turns this LED off and a high value turns it on.
# Turn the serial board's TX LED on (a low output lights the LED) (setq txled 'PA_1) (pio-pin-setlow txled) # Prepare "off" as the output value (pio-pin-setdir *pio-output* txled) # Make the pin a GPIO output, disabling serial port 0