USART Tutorial.pdf

(138 KB) Pobierz
316730751 UNPDF
Using the USART – Serial
Communications
Tutorial (c) Dean Camera, 2007.
dean_camera@hotmail.com
This tutorial will focus on setting up the serial USART on the AVR platform. Although other
hardware AVR interfaces (eg, USI) can be configured for limited RS-232 serial transmission and
reception such alternatives will not be covered.
What is the USART?
The vast majority of devices in the current AVR family lineup contain a USART hardware
subsystem. The USART hardware allows the AVR to transmit and receive data serially to and from
other devices - such as a computer or another AVR.
The USART transmission system differs to most other digital busses in that it does not utilize a
separate pin for the serial clock. An agreed clock rate is preset into both devices, which is then used
to sample the Rx/Tx lines at regular intervals. Because of this, the USART requires only three wires
for bi-directional communication (Rx, Tx and GND).
The RS-232 Specification
The serial USART is frequently referred to as RS-232, which is a reference to the RS-232
specification which mandates the logic levels and control signals. While the AVR's normal logic
levels are about 3-5V, RS-232 communication uses a low of +3V to +25V for a digital '0', and -3V
to -25V for a digital '1'.
If the two USART devices are running at AVR logic levels, the Tx and Rx pins can be connected
together directly. If the devices are using RS-232 spec. voltages, a level converter is needed.
This tutorial will assume the user is attempting to interface with a computer RS-232 port, which
uses the proper RS-232 specification logic levels.
First things first - Setting up the Hardware
To connect your AVR to a computer via the USART port, you will need to add a level converter to
your existing circuit. The most common/popular serial level converter is the Maxim MAX232
(datasheet avaliable here ) . This small and relatively inexpensive IC will convert your AVR's 5V
logic levels to 10V RS-232 and vice versa.
Before you can hook up your MAX232, you need to first identify which pins of your AVR are
responsible for serial communication. Looking in your AVR's datasheet , you should be able to
316730751.002.png
identify the two serial port pins (named as alternative pin functions TX and RX).
You should wire up your MAX232 to reflect the schematic here . This will enable your AVR's
USART hardware to interface with the computer's USART hardware.
Deciding on your AVR's system clock frequency
As mentioned above, the USART does not use an external clock. To allow the two interfaced
devices to communicate together, you need to decide on a baud rate for communication. Also due to
the timing-critical nature of the USART, your system's clock should be stable and of a known
frequency. The AVR's system clock frequency is very important. The USART clock is derived from
the system clock, and it is used to sample the Rx line and set the Tx line at precise intervals in order
to maintain the communication.
If the system clock cannot be precisely divided down to a "magic" frequency for perfect
communications, it will have a percentage error (where a byte fails to be read or written inside
designated time frame). System clocks for perfect USART communications should be multiples of
1.8432MHz which when used will give a 0.00% error.
Other frequencies for the main AVR clock can be used, but as the frequency drifts from the
"perfect" multiples of 1.8432MHz, the greater the percentage error will be (and thus the less reliable
the serial communication will be). It is generally accepted that error percentages of less than +/- 2%
are acceptable.
Looking in your AVR's datasheet in the USART section you should see a table of common baud
rates, system clocks and error percentages. You should find a combination which suits your
project's constraints and has the lowest possible error percentage.
I will be basing the rest of this tutorial on an example setup; a MEGA16 running at 7.3728MHz and
a baud rate of 9600bps (bits per second). This combination, because it uses a system clock which is
a multiple of the magic 1.8432MHz, has an error percentage of 0.00%).
Initializing the USART
Now you should have selected a baud rate, system clock and have your AVR set up with a RS-232
level converter - you're all set to go! All you need is the firmware to drive it.
First off, you need to enable both the USART's transmission and reception circuitry. For the
MEGA16, these are bits named RXEN and TXEN, and they are located in the control register
UCSRB. When set, these two bits turn on the serial buffers to allow for serial communications:
Code:
#include <avr/io.h>
int main (void)
{
// Turn on the transmission and reception circuitry:
UCSRB |= (1 << RXEN) | (1 << TXEN);
}
316730751.003.png
Next, we need to tell the AVR what type of serial format we're using. The USART can receive
bytes of various sizes (from 5 to 9 bits) but for simplicity's sake we'll use the standard 8-bits
(normal byte size for the AVR). Looking again at the MEGA16 datasheet, we can see that the bits
responsible for the serial format are named UCSZ0 to UCSZ2, and are located in the USART
control register C named UCSRC.
The datasheet very handily gives us a table showing which bits to set for each format. The standard
8-bit format is chosen by setting the UCSZ0 and UCSZ1 bits. Before we write to the UCSRC
register however, note a curious peculiarity in our chosen AVR, the MEGA16. To save on register
addresses, the UCSRC and UBRRH registers (the latter being explained later in this text) share the
same address. To select between the two, you must also write the URSEL bit when writing to
UCSRC:
Code:
#include <avr/io.h>
int main (void)
{
// Turn on the transmission and reception circuitry:
UCSRB |= (1 << RXEN) | (1 << TXEN);
// Use 8-bit character sizes - URSEL bit set to select
// the UCRSC register:
UCSRC |= (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1);
}
The URSEL bit does not exist in all AVR models; check your chosen AVR's datasheet.
There, we're almost done setting up the USART! The last thing to set for basic serial
communications is the baud rate register. This register sets the clock divider for the USART which
is used as a timebase to sample the incoming data at the correct frequency. It also gives the
timebase for sending data, so it's vital for RS-232 serial communications.
The baud rate register is 16-bit, split into two 8-bit registers as is the case with all 16-bit registers in
the AVR device family. To set our baud rate prescaler value, we first need to determine it. Note that
the baud rate register value is NOT the same as the baud rate you wish to use - this is a common
point of failure amongst those new to the serial subsystem. Instead, the value must be derived from
the following formula:
BaudValue = (((F_CPU / (USART_BAUDRATE * 16))) - 1)
Where F_CPU is your AVR's system clock frequency (in Hz), and USART_BAUDRATE is the
desired communication baud rate.
Given my example project using a system clock of 7372800Hz and a baud rate of 9600, our formula
gives:
BaudValue = (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)
= (7372800 / (9600 * 16) - 1)
316730751.004.png
= (7372800 / 153600 - 1)
= (48 - 1)
= 47
To make our life easier, we can turn this formula into a set of handy macros. F_CPU is
automatically defined in AVR-GCC via your makefile, so all that is needed is the baud rate:
Code:
#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)
This avoids "magic numbers" (unexplained constants) in our source code, and makes changing the
baud rate later on very easy - just change the BAUD_RATE macro value. Now, we need to load this
into the baud rate registers, named UBRRH (for the high byte) and UBRRL (for the low byte). This
is simple via a simple bitshift to grab the upper eight bits of the BAUD_PRESCALE constant:
Code:
// Load lower 8-bits of the baud rate value into the low byte
// of the UBRR register:
UBRRL = BAUD_PRESCALE;
// Load upper 8-bits of the baud rate value into the high byte
//of the UBRR register:
UBRRH = (BAUD_PRESCALE >> 8);
Now we're ready to rock and roll!
Sending and receiving data
Once initialized, we're ready to send and receive data. We do this by the special register named
UDR - short for "USART I/O Data Register". This is special for two reasons; it behaves differently
when it is read or written to, and it is double buffered.
By assigning a byte to the UDR register, that byte is sent out via the AVR's Tx line. This process is
automatic - just assign a value and wait for the transmission to complete. We can tell when the
transmission is complete by looking at the transmission completion flag inside the USART control
registers.
On the MEGA16, the Transmission Complete flag is located in the control register UCSRA, and it
is named TXC. Using this information we can construct a simple wait loop which will prevent data
from being written to the UDR register until the current transmission is complete:
Code:
// Send out the byte value in the variable "ByteToSend":
UDR = ByteToSend;
// Do nothing until transmission complete flag set:
while ((UCSRA & (1 << TXC)) == 0) {};
316730751.005.png
However this is non-optimal. We spend time waiting after each byte which could be better spent
performing other tasks - better to check before a transmission to see if the UDR register is ready for
data. We can do this by checking the USART Data Register Empty flag instead (called UDRE), also
located in the UCSRA control register of the MEGA16:
Code:
// Do nothing until UDR is ready for more data to be written to it:
while ((UCSRA & (1 << UDRE)) == 0) {};
// Send out the byte value in the variable "ByteToSend":
UDR = ByteToSend;
This is much better, as now time is only wasted before a transmission if the UDR register is full.
After a transmission we can immediately continue program execution while the UDR byte is sent,
saving time.
Now we can move on to receiving data. As mentioned before, the UDR register behaves differently
between read and write operations. If data is written to it it is sent out via the AVR's Tx pin,
however when data is received by the RX pin of the AVR, it may be read out of the UDR register.
Before reading the UDR register however, we need to check to see if we have received a byte.
To do this, we can check the USART Receive Complete (RXC) flag to see if it is set. Again, this is
located in the UCSRA control register of the MEGA16:
Code:
// Do nothing until data have been received and is ready to
// be read from the UDR register:
while ((UCSRA & (1 << RXC)) == 0) {};
// Fetch the received byte value into the variable "ReceivedByte":
ReceivedByte = UDR;
Putting it all together
Right! Now it's time to put everything together! Let's make a simple program that echoes the
received characters back to the computer. First, the basic program structure:
Code:
#include <avr/io.h>
int main (void)
{
}
Next, our USART initializing sequence:
Code:
#include <avr/io.h>
316730751.001.png
Zgłoś jeśli naruszono regulamin