Programming the myRIO Universal Asynchronous Receiver/Transmitters (UARTs)
The myRIO C library (see section 0.5) includes facilities for programming UARTs. Unlike other myRIO I/O programming, UART programming does not interact directly with the FPGA. Rather, the low-level UART routines are implemented with the virtual instrument software architecture (VISA) library (Foundation 2020). Using the UART facilities provided in the header file UART.h and the source file UART.c provided in the myRIO C library is the focus of this section.
The UART.h header loads the visa.h header and, in turn, the visatype.h header, which provide prototypes for VISA
functions (such as viWrite()
and viRead()
) and macros
(such as VI_SUCCESS
) used to define
UART functions in UART.c. The UART.c source file defines five functions for
programming UART communication: Uart_Open()
opens a
UART session, Uart_Close()
closes
a session, Uart_Write()
writes
data to a UART session’s port, Uart_Read()
reads
data from a UART session’s port, and Uart_Clear()
clears
a UART session port’s receive buffer. We will briefly consider the
functionality and usage of each function, but first we must understand
the custom UART types used throughout the functions.
UART types
There are several custom types defined in UART.h. The first is
typedef enum {
= 0,
Uart_ParityNone = 1,
Uart_ParityOdd = 2,
Uart_ParityEven = 3,
Uart_ParityMark = 4
Uart_ParitySpace } Uart_Parity;
For instance, one could define a variable that stores a parity
scheme: Uart_Parity parity = Uart_ParityOdd
. A parity
variable like this will be given as
an argument for Uart_Open()
. The
second custom type is
typedef enum {
= 10, // 1.0 stop bits
Uart_StopBits1_0 = 15, // 1.5 stop bits
Uart_StopBits1_5 = 20 // 2.0 stop bits
Uart_StopBits2_0 } Uart_StopBits;
This defines a list of three stop bit configurations. The stop bit can the usual single stop bit with a specific duration. That duration time multiplied by \(1.5\) is the \(1.5\) stop bit, and the duration multiplied by \(2.0\) produces the \(2.0\) stop bit.
The third custom data type is not an enum
but a struct
:
typedef struct {
const char *name; // Resource name on UART port
; // Default resource manager session
ViSession defaultRM; // ViSession reference
ViSession session} MyRio_Uart;
This is our first encounter with a struct
. We can
declare and initialize a MyRio_Uart
structure with
; // declare uart a "MyRio_Uart" struct
MyRio_Uart uart.name = "ASRL1::INSTR"; // define which uart port
uart.defaultRM = 0; // define a default resource manager
uart.session = 0; // define session reference uart
The name "ASRL1::INSTR"
is for the A connector UART, and "ASRL2::INSTR"
is for the B connector UART. Now that we have a handle on the custom
UART types, we are ready to consider the UART functions that use
them.
Opening and configuring a UART session with Uart_Open()
The Uart_Open()
function
can open and configure a UART port. The prototype for Uart_Open()
is
int32_t Uart_Open(
*port, // UART port
MyRio_Uart const uint32_t baud, // bps, e.g., 9600, max 230400
const uint8_t dataBits, // 8 or 7 for a parity bit
const Uart_StopBits stopBits, // Uart_StopBits1_0, etc.
const Uart_Parity parity // Uart_ParityNone, etc.
);
Three of the five arguments are of the custom types just considered.
The exact-width integer types uint32_t
and uint8_t
are familiar
from subsection 2.3.2, and the const
type qualifier
is familiar from subsection 2.3.5. If a parity bit is
used (i.e., if parity
is other than
Uart_ParityNone
), dataBits
should be 7
. If the receiver
needs more time between a single stop bit (Uart_StopBits1_0
) and a new start bit,
choose Uart_StopBits1_5
or Uart_StopBits2_0
for stopBits
. If the UART session is opened and
configured correctly, the function returns VI_SUCCESS
.
Closing a UART session with Uart_Close()
After we have finished using the UART port, the Uart_Close()
function can close the port. The prototype for Uart_Close()
is
simply
int32_t Uart_Close(MyRio_Uart *port);
This port
should be one that has
been opened with Uart_Open()
. If the
port is successfully closed, the function returns VI_SUCCESS
.
Reading data from a UART port with Uart_Read()
The Uart_Read()
function
can read data from an opened UART port’s input register or buffer. The
prototype for the function is
int32_t Uart_Read(MyRio_Uart* port, // UART port
uint8_t* const data, // array for the read data
const size_t nData); // size of data array
Since the data arrive in bytes, each byte fits into a uint8_t
array. This
function waits for the UART input shift register or buffer to fill
completely (or for a 1 second timeout to occur) before reading its data.
When the read is successful, the function returns VI_SUCCESS
.
Writing data to a UART port with Uart_Write()
The Uart_Write()
function can write data to an opened UART port’s output register or
buffer. The prototype for the function is
int32_t Uart_Write(MyRio_Uart* port, // UART port
const uint8_t* const data, // data to write
const size_t nData); // size of data array
As with the read, the data are packed in bytes of type uint8_t
. The
function waits for the output buffer or register to have sufficient
space before writing. When the write is successful, the function returns
VI_SUCCESS
.
Clearing a UART port’s receive buffer with Uart_Clear()
The Uart_Clear()
function can clear an open UART port’s receive buffer or register. The
function’s prototype is
int32_t Uart_Clear(MyRio_Uart* port);
If the clearing of the buffer is successful, the function will
return VI_SUCCESS
.
Configuration for the target display
Now we consider how to write a program that properly configures the myRIO for UART communication with the display.
First, we will create a MyRio_Uart
structure and populate it for the UART on the B connector:
.name = "ASRL2::INSTR"; // use the B connector UART
uart.defaultRM = 0; // default resource manager
uart.session = 0; // session reference uart
The rest of the configuration happens when we open the port with
= Uart_Open(&uart, // port information
status , // baud rate bps
baud_rate8, // no. of data bits
, // 1 stop bit
Uart_StopBits1_0); // No parity Uart_ParityNone
The integer baud rate baud_rate
given in units of bits per second (bps) depends on the specific T1
display device (see section A.2). Now we can write
uint8_t
character codes in an array data
of
size nData
to the target display
with
(&uart, data, nData); // write characters Uart_Write
For instance,
uint8_t data[1] = {'h'}; // casts 'h' to uint8_t
(&uart, data, 1); Uart_Write
The display interprets a received byte with decimal values \([32,127]\) as standard American Standard
Code for Information Exchange (ASCII) characters. The uint8_t
casting of
'h'
gives it a decimal value of 104
, which
corresponds to the decimal ASCII code for “h”.
The display functions of the special character escape sequences '\b'
,
'\f'
,
'\n'
,
and '\v'
have already been introduced (see table 1.12).
shows these escape sequences and the corresponding decimal
code that results from casting the characters to uint8_t
. For
instance, '\b'
casts to the decimal 8
. However, in lab 3, we will interpret these escape sequences into
custom codes that correspond to the functionality of the specific target
display. The escape sequence '\f'
,
which casts to 12
, will clear the
display; '\n'
,
which casts to 10
, should be mapped
to a code
that the display uses to move the cursor to the start of the next line.
In lab 3, we will write the low-level display driver
putchar_lcd()
,
which should translate the escape sequences of table 3.1
into the corresponding target display codes.
Online Resources for Section 3.9
No online resources.