Mizar32/UART

From Wikibooks, open books for an open world
Jump to: navigation, search

Introduction[edit]

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 favour 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:

  • wiring
  • 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 indipendent 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 labelled 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.

0 start)
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.

Hardware view[edit]

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.

Bus pins
Signal GPIO Bus pin eLua name PicoLisp
UART0_RX PA0 BUS4 pin 3 pio.PA_0 'PA_0
UART0_TX PA1 BUS4 pin 4 pio.PA_1 'PA_1
UART0_RTS PA3 BUS4 pin 5 pio.PA_3 'PA_3
UART0_CTS PA4 BUS4 pin 6 pio.PA_4 'PA_4
UART1_RX PA5 BUS3 pin 3 pio.PA_5 'PA_5
UART1_TX PA6 BUS3 pin 4 pio.PA_6 'PA_6
UART1_DCD PB23 BUS3 pin 5 pio.PB_23 'PB_23
UART1_DSR PB24 BUS3 pin 6 pio.PB_24 'PB_24
UART1_DTR PB25 BUS3 pin 7 pio.PB_25 'PB_25
UART1_RI PB26 BUS3 pin 8 pio.PB_26 'PB_26
UART1_CTS PA9 BUS3 pin 9 pio.PA_9 'PA_9
UART1_RTS PA8 BUS3 pin 10 pio.PA_8 'PA_8

RS232/RS485 serial add-on board[edit]

The add-on serial board has two banks of switches, DIP1 and DIP2 to select between RS232 and RS485 modes.

RS232 mode[edit]

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
rev. 1.0
DB-9F pin
UART module
rev. 1.1.1
DB-9F pin
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

RS485 mode[edit]

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 Alcor6L; see issue #77.

Software view[edit]

Simple I/O[edit]

Depending on which firmware you have, UART0 may be used for the Lua console (configured at 115200 baud, 8 data bits, 1 stop bit, no parity) and Lua's default input and output files are the console, so functions like "print()" and "io.write()" can be used to output characters on the serial port (with, and without, a trailing CR-LF newline sequence, respectively)

In eLua:

-- Greet the user
io.write( "What's your name? " )     -- Issue a prompt (with no trailing newline)
name = io.read()                     -- Read a line of input and store it in "name"
print( "Hello, " .. name .. "!" )    -- Salute them

In PicoLisp:

# Greet the user
(prinl "What's your name? ")
(prinl "Hello, " (setq name (read)) "!")

Low-level I/O[edit]

UART0 can also be accessed (and UART1 must be accessed) using the lower-level uart Alcor6L 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 getchar function.

In eLua:

-- Prompt a 9600 baud serial device until we receive a character in reply
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)

uart.setup( uartid, 9600, 8, 0, 1 )    -- Configure the UART
repeat
  uart.write( uartid, prompt )
  reply = uart.getchar( uartid, timeout, timerid )
until reply ~= ""

In PicoLisp:

# 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 control[edit]

Note that enabling hardware flow control with:

Language Code
eLua uart.set_flow_control(uartid, uart.FLOW_RTS + uart.FLOW_CTS)
PicoLisp (uart-set-flow-control uartid (+ *uart-flow-rts* *uart-flow-cts*) )

does not work yet. See issue #29.

Input buffer[edit]

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:

Language Code
eLua uart.setup(1, 115200, 8, 0, 1); uart.set_buffer(1, 1024);
PicoLisp (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 port[edit]

More recent firmware, from the 2013 release, 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 eLua console, sending eLua output and error messages and receiving keyboard input from the user. However, you can send the console output elsewhere by compiling your own firmware at http://builder.simplemachines.it and you can talk to it by specifying serial port number 176 to eLua's low-level uart.*() functions.

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.

Interrupts[edit]

Please note: There is currently no support for interrupt handling in PicoLisp. See issue #12. You can however use interrupts in eLua.

When input buffering is enabled on a UART, an interrupt is generated every time a character is received. This interrupt saves the character in the buffer you asked for, until your program is ready to read it.

If you use firmware with Lua interrupts (included in the Mizar A and B firmware from the 20120123 elua 0.8 release) you can also arrange for your own piece of code to be called every time a character is received.

The following example code quickly flashes the on-board LED every time a character is received:

-- Test UART interrupts handled in Lua.
-- Should flash the onboard LED each time a character is received.

led = pio.PB_29         -- Which PIO pin is the LED connected to?

function uart_handler( resnum )
  -- flash the onboard LED
  pio.pin.setlow( led )
  for i=1,10000 do end  -- for about 1/100th of a second
  pio.pin.sethigh( led )
end

pio.pin.sethigh( led )    -- off
pio.pin.setdir( pio.OUTPUT, led )

uart.setup( 0, 115200, 8, uart.PAR_NONE, 1 )
uart.set_buffer( 0, 1024 )  -- buffer must be enabled for UART IRQs to happen

-- tell eLua which function it should call every time the UART receives
cpu.set_int_handler( cpu.INT_UART_RX, uart_handler )
-- and enable that Lua interrupt
cpu.sei( cpu.INT_UART_RX, 0 )

-- Wait for about ten seconds while the test runs
for i=1,10000000 do end

-- disable the Lua interrupt
cpu.cli( cpu.INT_UART_RX, 0 )
-- and remove our handler function
cpu.set_int_handler( cpu.INT_UART_RX, nil )

The character is received from the UART and placed in the buffer before the Lua interrupt routine is called, so you can read it in your Lua interrupt routine with uart.getchar( 0, 0 ) and act on it immediately.

Baud rate accuracy[edit]

The following table gives the actual baud rates set by eLua for the most commonly used ones:

Want Get Error
300 300 0%
600 600 0%
1200 1200 0%
2400 2400 0%
4800 4799 -0.02%
9600 9604 +0.04%
19200 19186 -0.07%
31250 31250 0%
38400 38372 -0.07%
57600 57692 +0.07%
115200 114583 -0.5%

Hijacking the serial board's TX LED[edit]

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.

In eLua:

-- Turn the serial board's TX LED on (a low output lights the LED)
txled = pio.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

In PicoLisp:

# 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

Further reading[edit]