Sunteți pe pagina 1din 9

LPC1768 UART Programming Tutorial

UART basics revisited


Uart uses TxD(Transmit) Pin for sending Data and RxD(Receive) Pin to get data. UART
sends & receives data in form of chunks or packets. These chunks or packets are also
referred to as ‘Frames’. While sending Data, LSB(i.e. Data Bit 0) is transmitted First and
MSB is transmitted Last. The structure of a UART Packet/Frame using 1 stop bit is as
shown below:

Here is an example of single Packet/Frame using 8 data bits, even parity and 1 stop bit:

LPC1768 UART Block


Now, Lets start with the main Tutorial. ARM Cortex-M3 LPC176x has 4 UART blocks which are UART0 to UART3. UART0/2/3 are
identical. UART1 additionally supports Full modem control handshaking & RS-485. Refer Datasheet for more info. After reset
UART0 & UART1 are enabled by default. TxD and RxD pins for these blocks are mapped on multiple pins.

Pins: TxD RxD


UART0 P0.2 P0.3
UART1 P0.15/P2.0 P0.16/P2.1
UART2 P0.10/P2.8 P0.11/P2.9
UART3 P0.0/P0.25/P4.28 P0.1/P0.26/P4.29

All UART blocks internally have a 16-byte FIFO (First In First Out) structure to hold data. Each byte in this FIFO
represents a
character which was sent or received in order. All blocks also contain 2 registers each, for data
access and assembly as given below:

 Tx has THR(Transmit Holding Register) and TSR(Transmit Shift Register) – When Tx


Data is written to THR, it is then transferred to TSR which assembles the Transmit data.
 Similarly Rx has RSR(Receive Shift Register) and RBR(Receive Buffer Register) –
When Data is Received at the Rx Pin, it is first assembled in RSR and then transferred into
Rx FIFO which can be then accessed using RBR.
UART Pins on LPC176x are 5V tolerant. But I would recommend using 3.3v for UART
communication. Hence, if you have RS232 to serial TTL module make sure it has MAX3232 =OR= if
you are using FTDI’s FT232R based USB to serial TTL module make sure it is configured for 3.3v
I/O.

LPC176x UART Registers used in programming


Before we can use these pins to transfer data, first we need to configure and initialize the UART block in our LPC176x
microcontroller. But before doing that, lets go through some of the important registers:

Data Related Registers


1) UxRBR – Receiver Buffer Register: This register contains the top most byte(8-bit data chunk) in the Rx FIFO i.e the oldest
received data in FIFO. Before reading from UxRBR, the DLAB(Divisor Latch Access) bit in U0LCR register must be 0. As mentioned
in user manual “The right approach for fetching the valid pair of received byte and its status bits is first to read the content of the
U0LSR register, and then to read a byte from the U0RBR.”
2) UxTHR – Transmit Holding Register: U0THR contains the top most byte in Tx FIFO and in this case its the newest(latest)
transmitted data. As in the case with U0RBR , we must set DLAB=0 to access UxTHR for write operation.

Baud Rate Setup related registers


1) UxDLL and UxDLM – Divisor Latch registers: Both of them hold 8-bit values. These register together form a 16-bit divisor
value which is used in baud rate generation. UxDLM holds the upper 8-bits and U0DLL holds the lower 8-bits and the formation is
“[U0DLM:U0DLL]“. Since these form a divisor value and division by zero is invalid, the starting value for U0DLL is 0x01 (and not
0x00). Please keep this in mind while doing baud-rate calculations. In order to access and use these registers properly,
DLAB bit in UxLCR must be first set to 1.
2) UxFDR – Fractional Divider Register : It is used to set the prescale value for baud rate generation. The input clock is the PCLK
and output is the desired clock defined by this register. This register actually holds to different 4-bit values (a divisor and a multiplier)
for prescaling which are:

1. Bit [3 to 0] – DIVADDVAL: This is the prescale divisor value. If this value if 0 then fractional baud rate generator wont
have any effect on Uart Baud rate.
2. Bit [7 to 4] – MULVAL: This is prescale multiplier value. Even if fractional baud rate generator is not used the value in
this register must be more than or equal to 1 else UART0 will not operate properly.
3. Other Bits reserved.
Remark from User-manual:” If the fractional divider is active (DIVADDVAL > 0) and DLM = 0, the value of the DLL register
must be 2 or greater!”

Control and Status Registers


1) UxFCR – FIFO Control Register: Used to control Rx/Tx FIFO operations.

1. Bit 0 – FIFO Enable: 1 to Enable both Rx and Tx FIFOs and 0 to disable.


2. Bit 1 – Rx FIFO Reset: Writing a 1 will clear and reset Rx FIFO.
3. Bit 2 – Tx FIFO Reset: Writing a 1 will clear and reset Tx FIFO.
4. Bits [7 to 6]: Used to determine that how many Rx FIFO characters must be written before an interrupt is activated.
5. Others bits are reserved.
2) UxLCR – Line Control Register : Used to configure the UART block (i.e the data format used in transmission).

1. Bit [1 to 0] – Word Length Select: Used to select the length of an individual data chunk. [00] for 5 bit character length.
Similarly [01] , [10] , [11] for 6 , 7 , 8 bit character lengths respectively.
2. Bit 2 – Stop bit select: 0 for using 1 stop bit and 1 for using 2 stop bits.
3. Bit 3 – Parity Enable: 0 to disabled Partiy generation & checking and 1 to enable it.
4. Bit [5 to 4] – Parity Select: [00] to Odd-parity , [01] for Even-parity , [10] for forced “1”(Mark) parity and [11] for forced
“0”(Space) parity.
5. Bit 6 – Break Control: 0 to disable break transmission and 1 to enable it. TxD pin will be forced to logic 0 when this bit is
1!
6. Bit 7 – Divisior Latch Access bit: 0 to disable access to divisor latches and 1 to enable access.
3) UxLSR – Line Status Register: used to read the status of Rx and Tx blocks. Bits 1 to 4 get cleared after reading UxLSR.

1. Bit 0 – Receiver Data Ready(RDR): 0 means UxRBR is empty(i.e Rx FIFO is empty) and 1 means UxRBR contains valid
data.
2. Bit 1 – Overrun Error(OE): 1 means Overrun has occured, 0 otherwise. Overrun is the condition when RSR(Receive
Shift Register) has new character assembled but the RBR FIFO is full and the new assembled character was eventually
lost since no data is written into FIFO when its full.
3. Bit 2 – Parity Error(PE): 1 means a parity error has occured else not. When the value of the parity bit in the recieved
character is in wrong state then a parity error occurs.
4. Bit 3 – Framing Error(FE): 1 means that a framing error has taken place else not. Framing error occurs when the stop bit
of a received character is zero.
5. Bit 4 – Break Interrupt: 1 means that it has occured else not. A Break Interrupt occurs when the RxD line is pulled low
(i.e all 0s) i.e held in spacing state for 1 full character after which Rx Block goes into Idle state. Rx Block gets back to
active state when RxD pin is pulled high (i.e all 1s) i.e held in marking state for 1 full character.
6. Bit 5 – Transmit Holding Register Empty(THRE): 0 means UxTHR has valid data and 1 means its empty.
7. Bit 6 – Transmitter Empty(TEMT): 0 means UxTHR and/or UxRSR has valid data and 1 means that both UxTHR and
UxRSR are empty.
8. Bit 7 – Error in RX FIFO(RXFE): 0 means that UxRBR has no Rx Errors or Rx FIFO is disabled(i.e 0th bit in U0FCR is 0)
and 1 means that UxRBR has at-least one error. Note: This bit is cleared only if UxLSR is read and there are no other
subsequent errors in Rx FIFO, else this bit will stay 1
4) UxTER – Transmit Enable Register: This register is used to enable UART transmission. When bit-7 (i.e TXEN) is set to 1 Tx
block will be enabled and will keep on transmitting data as soon as its ready. If bit-7 is set to 0 then Tx will stop transmission. Other
bits are reserved.

Interrupt Related Registers


1) UxIER – Interrupt Enable Register: Set a bit to 0 to disable and 1 to enable the corresponding interrupt. Other bits are reserved.

1. Bit 0 – RBR Interrupt Enable


2. Bit 1 – THRE Interrupt Enable
3. Bit 2 – RX Line Status Interrupt Enable
4. Bit 8 – ATEOInt (End of Auto Baud Interrupt) Enable
5. Bit 9 – ATBOInt (Auto Baud Time-Out Interrupt) Enable
2) UxIIR – Interrupt Identification Register: This register is organized as follows:

1. Bit 0 – Interrupt Pending : 0 means at-least one interrupt is pending, 1 means no interrupts are pending. Note: This bit is
ACTIVE LOW!
2. Bits [3 to 1] – Interrupt Identification : [011] implies Receive Line Status(RLS) , [010] implies Receive Data
Available(RDA) , 110 implies Character Time-out Indicator(CTI) , [001] implies THRE Interrupt.
3. Bits [7 to 6] – FIFO Enable.
4. Bit 8 – ABEOInt : 1 means Auto Baud Interrupt has successfully ended and 0 otherwise.
5. Bit 9 – ABTOInt : 1 means Auto Baud Interrupt has Timed-out.
6. All others bits are reserved.

UART Baud Rate Calculations


The main formula for calculating baud rate is given as:
Baud =

PCLK in Hertz 16 x (256xDLM + DLL) x (1 + DIVADDVAL/MULVAL)

Where DIVADDVAL & MULVAL are part of “Fractional Rate Divider” or “Baud-Prescaler” which is used in Baud-Rate generation.
This “Fractional Divider” is only active when DIVADDVAL > 0. This formula is pretty common for LPC ARM micro-controllers.
which can be further simplified to :
Baud =

MULVAL MULVAL + DIVADDVAL


X

PCLK in Hertz 16 x (256xDLM + DLL)

with following conditions strictly applied:

 0 < MULVAL <= 15


 0 <= DIVADDVAL <= 14 - if DIVADDVAL > 0 & DLM = 0 then, DLL must be >= 2
 DIVADDVAL < MULVAL
Where PCLK is the Peripheral Clock value in Hz , DLM and DLL are the divisor registers which we saw earlier and finally
DIVADDVAL and MULVAL are part of the Fractional baudrate generator register.
As it can been seen this formula has 2 prominent parts which are: A Base value and a Fraction Part(Prescaler) i.e:
BaudRate = [ Fraction Part (Prescaler) ] x [ Base ]

This Fraction Part i.e. the Fractional Divider or the “Baud Rate Prescaler” can be used to scale down or keep the base value as it is
(when disabled). Hence, its very useful for fine-tuning and getting the baudrate as accurate as possible.
Note: In real world there are very less chances that you will get the actual baudrate same as the desired baudrate. In most cases
the actual baudrate will drift a little above or below the desired baud and also, as the desired baudrate increases this drift or error
will also increase – this is because of the equation itself and the limitations on MULVAL , DIVADDVAL! For e.g. if the desired baud
rate is 115200 and depends on the variables you may get a bauds like 114300, 115470, 116050 .. and so on. But in almost all cases
it will work as required if the error is not significant. A small amount of error in actual baudrate is generally tolerable in most systems
which must be <1.1% (relative error).

Now we know the formula, how we do actually start ?

1) The Dirty and Simplest Method:


The quickest and also the dirtiest(accuracy wise) method without using any algorithm or fine-tuning is to set DLM=0 and disable the
Fractional Divider. In this case MULVAL=1 and DIVADDVAL=0 which makes the Fraction Part(FP) = 1. Now we are left with only 1
unknown in the equation which is UxDLL and hence we simplify the equation and solve for UxDLL.
UxDLL =

PCLK in Hertz 16 x Desired-BaudRate

In my opinion, if possible, you must stay away from above method as it works only for particular bauds & specific PCLK value and
moreover computed UxDLL might get out of range i.e. > 255 in which case you have to start increasing DLM and recompute a new
value for DLL.

2) A better Method/Algorithm (Recommended):


In these method we again start with DLM=0 , DIVADDVAL=0(i.e. Fractional divider disabled) and MULVAL=1 and get an initial value
for DLM. If you are lucky you will get a very close baudrate to the desired one. If not then we perform some fine-tuning using DLM,
MULVAL and DIVADDVAL and get a new value for DLM. There is on one single method to perform fine-tuning and one can also
make an algorithm for computing the best match given the desired baudrate and PCLK. The fine-tuning method or Algorithm which I
have given below is a basic one suitable for beginners(in my opinion though). A more invloved Iterative Algorithm is given on Page
324(Rev 4.1) of the user manual.

Example: PCLK = 25 Mhz and Required Baud Rate is 115200 bauds.


Lets start with DLM = 0 , DIVADDVAL = 0 and MULVAL = 1
We have PCLK = 25 Mhz = 25 x 106 Hz
So the equation now gets simplified and we can find DLL.
We get UxDLL = 13.56, since it must be an integer we round it to 14. With UxDLL = 14 our actual baud rate will be =
111607.14 with a error of 3592.86(magnitude) which gives a relative error of 3.11%. Since its greater than 1.1% error specification
we cannot use this. Now all we have to do is to get this error as low as possible. This can be done by multiplying it by a suitable
fraction defined using MULVAL and DIVADDVAL – as given in equation. But since MULVAL & DIVADDVAL can be maximum 15 &
14 receptively, we don’t have much control over the Fraction Part(FP) value.
First lets compute the Required Fraction Part(FP) value given by FPrequired = [Desired Baud / Actual Baud Rate]. In our case
its FPrequired = 1.032. Note that this is the required “Baud Prescaler” value that needs to be multiplied by Base value we got above to
bring back it to ~115200. Now we have a problem – the max value for the Fractional part is 1(When Fractional Divider is disabled),
but in this case we need an upscaling value which is not possible. Only downscaling is possible(when Fractional Divider is Enabled).
So what do we do now? Well, a few approaches can be used. One of the simple approach is to decrease the computed DLL value
so that our final baudrate overshoots i.e. we get a positive error. So, then we can try to scale it down using Fractional Part (i.e.
MULVAL and DIVADDVAL) to get near to desired baud rate.
Lets set UxDLL = 12. We now get new baud rate = 130208.33 with error = +15008.33 from 115200. Now, we have FPrequired =
0.8847. We must try to get Fractional Part of equation as close as possible to 0.8847. The closest possible Fraction to 0.8847 is FP
= 0.8823 which is when MULVAL=15 and DIVADDVAL=2. So, multiplying that with computed baud we get: [Base] x [Fraction
Part(FP)] = 130208.33 x 0.8823 = 114882.8 ~ 114882 which is pretty bang on with an error of 318(magnitude) i.e. 0.27% Relative
Error that is well with in 1.1% spec. Hence, we can use the following settings:
PCLK = 25 MHz
U0DLL = 12
U0DLM = 0
MULVAL = 15
DIVADDVAL = 2
Some of the standard Baud rates that can be used are: 2400, 4800, 7200, 9600, 14400, 19200, 28800, 38400, 57600, 115200,
230400, etc..

How to Configure and Initialize UART


Once you know how UART communication works, configuring and initializing UART is pretty straight forward. In Source Code
Examples for this tutorial we will use UART0 with following configuration:

 BaudRate = 115200 (with PCLK=25Mhz)


 Data Length = 8 bits
 No Parity Bit
 and 1 Stop Bit
Note: Its your responsibility to make it sure that configuration is same on both the communicating ends.
As seen in example above: in order to get 115200(114882 actually) bauds at 25Mhz PCLK we must use the following settings for
baud generation :
U0DLL = 12 ; U0DLM = 0 ; MULVAL = 15 ; DIVADDVAL = 2

Now, lets write a function “InitUART0()” which we can use initialize and configure UART0:

#define MULVAL 14
#define DIVADDVAL 2
#define Ux_FIFO_EN (1<<0)
#define Rx_FIFO_RST (1<<1)
#define Tx_FIFO_RST (1<<2)
#define DLAB_BIT (1<<7)
void InitUART0(void)
{
LPC_PINCON->PINSEL0 |= (1<<4) | (1<<6); //Select TXD0 and RXD0 function for P0.2 & P0.3!
//LPC_SC->PCONP |= 1<<3; //Power up UART0 block. By Default it is enabled after RESET.

LPC_UART0->LCR = 3 | DLAB_BIT ; /* 8 bits, no Parity, 1 Stop bit & DLAB set to 1 */


LPC_UART0->DLL = 12;
LPC_UART0->DLM = 0;

//LPC_UART0->IER |= ..; //Edit this if want you to use UART interrupts


LPC_UART0->FCR |= Ux_FIFO_EN | Rx_FIFO_RST | Tx_FIFO_RST;
LPC_UART0->FDR = (MULVAL<<4) | DIVADDVAL; /* MULVAL=15(bits - 7:4) , DIVADDVAL=2(bits - 3:0)
*/
LPC_UART0->LCR &= ~(DLAB_BIT);
//Now since we have applied DLL and DLM we now lock or freeze those valuse by diabling DLAB i.e
DLAB=0
//Baud= ~115200(114882). Now we can perform UART communication!
}
Once this is done you are now ready to transfer data using U0RBR and U0THR registers.
Connections between MCU and PC or Laptop
Here we can have 2 possible scenarios.

1. Your PC/Laptop(older ones) already has a serial port. In this case you will need a RS232 to TTL converter.
2. Your PC/Laptop doesn’t have a serial port and you are using USB to Serial converter(FTDI ones, etc..).
If you are using a separate converter then in both cases the TTL side of the converter have to use a minimum of 3 pins for basic
UART communication which are: TxD, RxD and GND. Connect your MCU RxD to TxD of converter and MCU TxD to RxD of
converter. Finally connect GND on both sides. Make sure you module supports 3.3v voltage-levels.
Please refer to the connection diagrams & terminal software configuration(and COM ports) as given in Basic Uart Tutorial.
Here is a configuration screenshot for terminal software PuTTYtel which we will be using for examples :

Note: You can get PuTTYtel from Here. Direct download link: Here. You can also use other similar terminal software as well.

LPC1768 UART Programming Examples


Now its time to actually use UART in real life! Lets do some communication between your LPC1768(or similar MCU like LPC1769)
MCU and PC/Laptop. Before we get into actual examples for LPC1768, first lets define 2 functions which will be used to Read and
Write Data from UART block. The code is based on CMSIS which is the Base library for ARM Cortex Micrcontrollers.

U0Read() – Read Data from UART0:

#define RDR (1<<0) //Receiver Data Ready


char U0Read(void)
{
while(!(LPC_UART0->LSR & RDR)); //wait until any data arrives in Rx FIFO
return LPC_UART0->RBR;
}

U0Write() – Write Data to UART0:

#define THRE (1<<5) //Transmit Holding Register Empty


void U0Write(char data)
{
while(!(LPC_UART0->LSR & THRE)); //wait till the THR is empty
//now we can write to the Tx FIFO
LPC_UART0->THR = data;
}

LPC1768 UART Example 1


This demo will print “Hello from LPC1768!” on a new line in Serial Terminal repeatedly. The code given below uses Carriage-Return
+ Line-Feed (i.e. CR+LF) as New-Line(Enter or “\n”) character. If you are using Terminal on Linux or MacOS to interfacing LPC176x
then only use Line-Feed(LF) character for New-Line.

/*(C) Umang Gajera - www.ocfreaks.com


More Embedded tutorials @ www.ocfreaks.com/cat/embedded
LPC1768 Basic UART Example 1 Source Code.
License : GPL.*/

#include <lpc17xx.h>

#define THRE (1<<5) //Transmit Holding Register Empty


#define MULVAL 15
#define DIVADDVAL 2
#define Ux_FIFO_EN (1<<0)
#define Rx_FIFO_RST (1<<1)
#define Tx_FIFO_RST (1<<2)
#define DLAB_BIT (1<<7)
#define LINE_FEED 0x0A //LF, For Linux, MAC and Windows Terminals
#define CARRIAGE_RETURN 0x0D //CR, For Windows Terminals (CR+LF).

void initUART0(void);
void U0Write(char data);

int main(void)
{
//SystemInit(); //This already gets called by CMSIS Startup Code.
char msg[] = { 'H','e','l','l','o',' ','f','r','o','m',' ','L','P','C','1','7','6','8','\0' };
int count=0;

initUART0();

while(1)
{
while( msg[count]!='\0' )
{
U0Write(msg[count]);
count++;
}
//Send NEW Line Character(s) i.e. "\n"
U0Write(CARRIAGE_RETURN); //Comment this for Linux or MacOS
U0Write(LINE_FEED); //Windows uses CR+LF for newline.
count=0; // reset counter
}
//return 0; //This won't execute normally
}

void U0Write(char txData)


{
while(!(LPC_UART0->LSR & THRE)); //wait until THR is empty
//now we can write to Tx FIFO
LPC_UART0->THR = txData;
}

void initUART0(void)
{
/*Assuming CCLK = 100Mhz and PCLK = 25Mhz!*/
LPC_PINCON->PINSEL0 |= (1<<4) | (1<<6); //Select TXD0 and RXD0 function for P0.2 & P0.3!
//LPC_SC->PCONP |= 1<<3; //Power up UART0 block. By Default it is enabled after RESET.

LPC_UART0->LCR = 3 | DLAB_BIT ; /* 8 bits, no Parity, 1 Stop bit & DLAB set to 1 */


LPC_UART0->DLL = 12;
LPC_UART0->DLM = 0;
//LPC_UART0->IER |= ..; //Edit this if want you to use UART interrupts
LPC_UART0->FCR |= Ux_FIFO_EN | Rx_FIFO_RST | Tx_FIFO_RST;
LPC_UART0->FDR = (MULVAL<<4) | DIVADDVAL; /* MULVAL=15(bits - 7:4) , DIVADDVAL=2(bits - 3:0)
*/
LPC_UART0->LCR &= ~(DLAB_BIT);
//Now since we have applied DLL and DLM we now lock or freeze those valuse by diabling DLAB i.e
DLAB=0
//Baud= ~115200(114882). Now we can perform UART communication!
}
KEIL ARM uV5 Project for Example #1 on GitHub @ Example 1 [Successfully tested on Keil uV5.23], Download Project
Zip @ Example_1.Zip. You can find the HEX file inside objects folder.

LPC1768 UART Example 2


In this example you will see the characters that you type on your keyboard i.e. we echo back the characters(data) received by MCU.
Serial Terminals only displays the character that it receives via serial Port. When you type a character it is directly sent to the other
side via serial port. Hence, in order for us to see the character which we typed, we need to send back the same character. This is
what the program below does. Whenever MCU receives a character from the RXD0 pin, it will send the same character back to PC
via the TXD0 Pin and it gets displayed in Terminal.
Here is a snippet of Example 2: (Full source code given is in KEIL ARM project files – attached below)

/*(C) Umang Gajera - www.ocfreaks.com


More Embedded tutorials @ www.ocfreaks.com/cat/embedded/
LPC1768 Basic UART Example 2 Source Code.
License: GPL.*/

#include <lpc17xx.h>

#define RDR (1<<0) //Receiver Data Ready


#define THRE (1<<5) //Transmit Holding Register Empty
#define MULVAL 15
#define DIVADDVAL 2
#define Ux_FIFO_EN (1<<0)
#define Rx_FIFO_RST (1<<1)
#define Tx_FIFO_RST (1<<2)
#define DLAB_BIT (1<<7)
#define LINE_FEED 0x0A //LF, For Linux, MAC and Windows Terminals
#define CARRIAGE_RETURN 0x0D //CR, For Windows Terminals (CR+LF).
#define ENTER CARRIAGE_RETURN //Ascii value/code for Enter is 0x0D i.e. CR

void initUART0(void);
char U0Read(void);
void U0Write(char data);

int main(void)
{
initUART0();
char data = 0;

while(1)
{
data = U0Read(); //Read Data from Rx
if(data == ENTER) //Check if user pressed Enter key
{
//Send NEW Line Character(s) i.e. "\n"
U0Write(CARRIAGE_RETURN); //Comment this for Linux or MacOS
U0Write(LINE_FEED); //Windows uses CR+LF for newline.
}
else
{
U0Write(data); //Tx Read Data back
}
}
//return 0; //Normally this won't execute
}
char U0Read(void)
{
while(!(LPC_UART0->LSR & RDR)); //wait until data arrives in Rx FIFO
return LPC_UART0->RBR;
}
KEIL ARM uV5 Project for Example #2 on GitHub @ Example 1 [Successfully tested on Keil uV5.23], Download Project
Zip @ Example_2.Zip. You can find the HEX file inside objects folder.

S-ar putea să vă placă și