Difference between revisions of "Embedded System Tutorial UART"

From Embedded Systems Learning Academy
Jump to: navigation, search
Line 5: Line 5:
  
 
UART is a serial communication, so bits must travel on a single wire.  If you wish to send a '''char''' over UART, the char is enclosed within a '''start''' and a '''stop''' bit, so to send 8-bits of '''char''' data, it would require 2-bit overhead; this 10-bit of information is called a '''UART frame'''.  Let's take a look at how the character 'A' is sent over UART.  In ASCII table, the character 'A' has the value of 65, which in binary is: 0100.0101  If you inform your UART hardware that you wish to send this data at 9600bps, here is how the frame would appear on an oscilloscope :
 
UART is a serial communication, so bits must travel on a single wire.  If you wish to send a '''char''' over UART, the char is enclosed within a '''start''' and a '''stop''' bit, so to send 8-bits of '''char''' data, it would require 2-bit overhead; this 10-bit of information is called a '''UART frame'''.  Let's take a look at how the character 'A' is sent over UART.  In ASCII table, the character 'A' has the value of 65, which in binary is: 0100.0101  If you inform your UART hardware that you wish to send this data at 9600bps, here is how the frame would appear on an oscilloscope :
[[File:uart_tutorial_frame.jpg|right|frame|UART Frame of 'A']]
+
[[File:uart_tutorial_frame.jpg|center|frame|UART Frame of 'A']]
  
 
A micrcontroller can have multiple UARTs in its hardware, and usually UART0 is interfaced to a "USB to serial" converter chip which is then connected to your computer.  In this exercise, you will write a driver for UART-2 and attempt to communicate between two boards.
 
A micrcontroller can have multiple UARTs in its hardware, and usually UART0 is interfaced to a "USB to serial" converter chip which is then connected to your computer.  In this exercise, you will write a driver for UART-2 and attempt to communicate between two boards.
Line 15: Line 15:
 
9600bps means that one bit takes 1/9600 = 104uS (micro-seconds) per bit.  The idea is that we want to divide the peripheral clock to UART hardware by a number that yields roughly 104uS per bit.  The '''Software Driver''' section goes over how to configure your UART driver to divide the clock to yield the desired baud rate.
 
9600bps means that one bit takes 1/9600 = 104uS (micro-seconds) per bit.  The idea is that we want to divide the peripheral clock to UART hardware by a number that yields roughly 104uS per bit.  The '''Software Driver''' section goes over how to configure your UART driver to divide the clock to yield the desired baud rate.
  
[[File:uart_tutorial_clock.jpg|right|frame|Example clock system of LPC17xx]]
+
[[File:uart_tutorial_clock.jpg|center|frame|Example clock system of LPC17xx]]
  
 
== Hardware Design ==
 
== Hardware Design ==
Line 27: Line 27:
  
 
[[File:uart_tutorial_overview.jpg|center|frame|UART chapter summary]]
 
[[File:uart_tutorial_overview.jpg|center|frame|UART chapter summary]]
 +
<BR/>
 +
 +
Writing a uart initialization routine is simple except that some registers require a special setup to access them.  In the sample code below, we power up UART0, and use the PINSEL register to configure the Rx/Tx Uart pins as the pin functionality.  Finally, we just set the divider to achieve 9600bps.  The exception here is that we have to read the datasheet carefully which states that the '''DLM''' and the '''DLL''' registers are only accessible if the '''DLAB''' bit is set at the '''LCR''' register.
 +
 +
<syntaxhighlight lang="c">
 +
void uart0_init(void)
 +
{
 +
    LPC_SC->PCONP |= (1 << 3);      // Enable power to UART0
 +
    LPC_SC->PCLKSEL0 &= ~(3 << 6);
 +
    LPC_SC->PCLKSEL0 |=  (1 << 6);  // Uart clock = CPU / 1
 +
 +
    LPC_PINCON->PINSEL0 &= ~(0xF << 4); // Clear values
 +
    LPC_PINCON->PINSEL0 |= (0x5 << 4);  // Set values for UART0 Rx/Tx
 +
 +
    LPC_UART0->LCR = (1 << 7); // Enable DLAB
 +
    LPC_UART0->DLM = 0;
 +
    LPC_UART0->DLL = CPU_CLOCK / (16 * 9600) + 0.5);
 +
    LPC_UART0->LCR = 3;        // 8-bit data
 +
}
 +
</syntaxhighlight>
 +
[[File:uart_tutorial_dlab.jpg|center|frame|DLAB bit to access registers]]
  
 
== Advanced Design ==
 
== Advanced Design ==
 
What you've done so far is wrote a polling UART driver.  If you used 9600bps, and sent 1000 characters, your processor would basically enter a "busy-wait" loop and spend 1040ms to send 1000 bytes of data.  You can enhance this behavior by allowing your <code>uart_putchar()</code> function to enter data to a queue, and return immediately, and you can use the "THRE" or "Transmitter Holding Register Empty" interrupt indicator to remove your busy-wait loop while you wait for a character to be sent.
 
What you've done so far is wrote a polling UART driver.  If you used 9600bps, and sent 1000 characters, your processor would basically enter a "busy-wait" loop and spend 1040ms to send 1000 bytes of data.  You can enhance this behavior by allowing your <code>uart_putchar()</code> function to enter data to a queue, and return immediately, and you can use the "THRE" or "Transmitter Holding Register Empty" interrupt indicator to remove your busy-wait loop while you wait for a character to be sent.

Revision as of 22:02, 31 July 2013

This article is under construction

Introduction

UART stands for Universal Asynchronous Receiver Transmitter. There is one wire for transmitting data, an done wire to receive data. A common parameter is the baud rate known as "bps" which stands for bits per second. If a transmitter is configured with 9600bps, then the receiver must be listening on the other end at the same speed.

UART is a serial communication, so bits must travel on a single wire. If you wish to send a char over UART, the char is enclosed within a start and a stop bit, so to send 8-bits of char data, it would require 2-bit overhead; this 10-bit of information is called a UART frame. Let's take a look at how the character 'A' is sent over UART. In ASCII table, the character 'A' has the value of 65, which in binary is: 0100.0101 If you inform your UART hardware that you wish to send this data at 9600bps, here is how the frame would appear on an oscilloscope :

UART Frame of 'A'

A micrcontroller can have multiple UARTs in its hardware, and usually UART0 is interfaced to a "USB to serial" converter chip which is then connected to your computer. In this exercise, you will write a driver for UART-2 and attempt to communicate between two boards.


Clock System & Timing

A crystal drives a processor clock, and it is usually less than 20Mhz. A processor usually uses a "PLL" or "phased-lock-loop" to generate a faster clock than the crystal. So, you could have a 4Mhz clock, and the PLL can be used to internally multiply the clock to provide 48Mhz to the processor. The same 48Mhz is then fed to processor peripherals, and sometimes you have a register that can divide this higher clock to slower peripherals that may not require a high clock rate. Remember that lower clock speed means lower power consumption.

9600bps means that one bit takes 1/9600 = 104uS (micro-seconds) per bit. The idea is that we want to divide the peripheral clock to UART hardware by a number that yields roughly 104uS per bit. The Software Driver section goes over how to configure your UART driver to divide the clock to yield the desired baud rate.

Example clock system of LPC17xx

Hardware Design

There is not much hardware design other than to locate UART-2 pins on your processor board and connecting these wires to the second board. Each pin on a microcontroller may be designed to provide specific feature. So the first thing to do is identify which physical pins can provide UART-2 signals.

After you identify the physical pins, you would connect these pins to the second board. Remember that your TX pin should be connected to second board's RX pin and vice versa. Connecting two TX pins together will damage your processor. After you connect the Rx/Tx pairs together, you also need to connect the ground wire of two boards together. Not connecting the ground reference together is essentially like asking the other board "How far is my hand from the ground" when the ground reference is missing.


Software Driver

The UART chapter on LPC17xx has a really good summary page on how to write a UART driver :

UART chapter summary


Writing a uart initialization routine is simple except that some registers require a special setup to access them. In the sample code below, we power up UART0, and use the PINSEL register to configure the Rx/Tx Uart pins as the pin functionality. Finally, we just set the divider to achieve 9600bps. The exception here is that we have to read the datasheet carefully which states that the DLM and the DLL registers are only accessible if the DLAB bit is set at the LCR register.

void uart0_init(void)
{
    LPC_SC->PCONP |= (1 << 3);       // Enable power to UART0
    LPC_SC->PCLKSEL0 &= ~(3 << 6);
    LPC_SC->PCLKSEL0 |=  (1 << 6);   // Uart clock = CPU / 1

    LPC_PINCON->PINSEL0 &= ~(0xF << 4); // Clear values
    LPC_PINCON->PINSEL0 |= (0x5 << 4);  // Set values for UART0 Rx/Tx

    LPC_UART0->LCR = (1 << 7);	// Enable DLAB
    LPC_UART0->DLM = 0;
    LPC_UART0->DLL = CPU_CLOCK / (16 * 9600) + 0.5);
    LPC_UART0->LCR = 3;         // 8-bit data
}
DLAB bit to access registers

Advanced Design

What you've done so far is wrote a polling UART driver. If you used 9600bps, and sent 1000 characters, your processor would basically enter a "busy-wait" loop and spend 1040ms to send 1000 bytes of data. You can enhance this behavior by allowing your uart_putchar() function to enter data to a queue, and return immediately, and you can use the "THRE" or "Transmitter Holding Register Empty" interrupt indicator to remove your busy-wait loop while you wait for a character to be sent.