In this tutorial, I will be discussing basic UART(SERIAL) programming tutorial for ARM Cortex-M0 LPC1343. If you are new to UART, you can go through the basic UART tutorial which I’ve posted @ Basic Uart Tutorial. This tutorial is also applicable to MCUs of same family (LPC13xx)
ARM Cortex-M0 LPC1343 UART Block
ARM Cortex-M0 LPC134x micro-controllers have only 1 UART block. UART block takes it input clock as CCLK since for LPC134x PCLK=CCLK. By default, after reset the input clock for UART is disabled. Hence to use it, we need to enable its input clock. For LPC1311/13/42/43 part numbers the TxD and RxD pins must configured using IOCON register before enabling the UART clock. This sequence is not mandatory for LPC1311/01 and LPC1313/01. Since we will be using LPC1343 MCU, we will have to use the clock enable sequence for UART examples given in the Examples section. Please check the user-manual to verify if the sequence is applicable for your LPC13xx device. Pin mapping for TXD and RXD pins is as given below:
Pins: | RXD | TXD |
---|---|---|
UART0/UART | PIO1_6 | PIO1_7 |
IOCON | LPC_IOCON->PIO1_6 | LPC_IOCON->PIO1_7 |
The UART block internally has 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. It also contains two registers each, for data access and assembly as given below:
- Tx has THR(Transmit Holding Register) and TSR(Transmit Shift Register) – When we write Data to be sent into THR it is then transferred to TSR which assembles the data to be transmitted via Tx Pin.
- Similarly Rx has RSR(Receive Shift Register) and RBR(Receive Buffer Register) – When a valid data is Received at Rx Pin it is first assembled in RSR and then passed in to Rx FIFO which can be then accessed via RBR.
LPC1343 UART Registers
Before we can do any UART/Serial communication, first we need to configure and initialize the UART block in LPC134x microcontroller. But before doing that, lets go through some of the important registers used in LPC134x UART programming:
Data Related Registers
1) RBR – Receiver Buffer Register: Bits[7:0] contains the top most BYTE in the Rx FIFO i.e the oldest received data in FIFO. Before reading from RBR, 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 RBR”.
2) THR – Transmit Holding Register: Bits[7:0] contains the top most BYTE in Tx FIFO and in this case its the newest(latest) transmitted data. DLAB must be 0 to access THR for write operation.
Baud Rate Setup related registers
1) DLL and DLM – Divisor Latch registers: Both of them hold 8-bit values. These registers together form a 16-bit divisor value which is used in baud-rate generation. DLM holds the upper 8-bits(DLMSB) and DLL holds the lower 8-bits(DLLSB) and the formation is “[DLM:DLL]“. Since these form a divisor value and division by zero is invalid, the starting value for DLL 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 must be first set to 1.
2) FDR – Fractional Divider Register : It is used to define the prescale value for baud-rate generation. This register actually holds to different 4-bit values (a divisor and a multiplier) for prescaling which are:
- Bit [3 to 0] – DIVADDVAL: This is the divisor value. If this value if 0 then fractional baud rate generator wont have any effect on Uart Baud rate.
- Bit [7 to 4] – MULVAL: This is the multiplier value. Even if fractional baud rate generator is not used the value in this register must be >= 1.
- 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 3 or greater!”
Control and Status Registers
1) UxFCR – FIFO Control Register: Used to control Rx/Tx FIFO operations.
- Bit 0 – FIFO Enable: 1 to Enable both Rx and Tx FIFOs and 0 to disable.
- Bit 1 – Rx FIFO Reset: Writing a 1 will clear and reset Rx FIFO.
- Bit 2 – Tx FIFO Reset: Writing a 1 will clear and reset Tx FIFO.
- Bits [7 to 6] – Rx Trigger Level: Used to determine that how many Rx FIFO characters must be written before an interrupt is activated. Refer User Manual for information.
- Others bits are reserved.
2) LCR – Line Control Register : Used to configure the UART block (i.e the data format used in transmission).
- 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.
- Bit 2 – Stop bit select: 0 for using 1 stop bit and 1 for using 2 stop bits.
- Bit 3 – Parity Enable: 0 to disabled Parity generation & checking and 1 to enable it.
- 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.
- 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!
- Bit 7 – Divisor Latch Access bit: 0 to disable access to divisor latches and 1 to enable access.
3) LSR – Line Status Register: used to read the status of Rx and Tx blocks.
- Bit 0 – Receiver Data Ready(RDR): 0 = RBR is empty(i.e Rx FIFO is empty) and 1 means RBR contains valid data.
- Bit 1 – Overrun Error(OE): 0 = Overrun hasn’t occurred and 1 = Overrun has occurred. (Note: Reading LSR clears this bit)
- Bit 2 – Parity Error(PE): 0 = no parity error, 1 = parity error. (Note: Reading LSR clears this bit)
- Bit 3 – Framing Error(FE): 0 = no framing error, 1 = framing error detected. (Note: Reading LSR clears this bit)
- Bit 4 – Break Interrupt: 0 = no Break Interrupt occurs and 1 = Break Interrupt has occurred. (Note: Reading LSR clears this bit)
- Bit 5 – Transmit Holding Register Empty(THRE): 0 = THR contains valid data and 1 means its empty.
- Bit 6 – Transmitter Empty (TEMT): 0 = THR and/or RSR contains valid data, 1 = both THR and UxRSR are empty.
- Bit 7 – Error in RX FIFO(RXFE) : 0 = RBR has no Rx Errors or Rx FIFO is disabled(i.e 0th bit in FCR is 0), 1 = RBR has at-least one error. (Note: This bit is cleared only if LSR is read and there are no other subsequent errors in Rx FIFO, else this bit will stay 1)
4) TER – 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) IER – Interrupt Enable Register: Set a bit to 0 to disable and 1 to enable the corresponding interrupt. Other bits are reserved.
- Bit 0 – RBR Interrupt Enable
- Bit 1 – THRE Interrupt Enable
- Bit 2 – RX Line Status Interrupt Enable
- Bit 8 – ATEOInt (End of Auto Baud Interrupt) Enable
- Bit 9 – ATBOInt (Auto Baud Time-Out Interrupt) Enable
2) IIR – Interrupt Identification Register: This register is used to get the status code which give the priority along with sourcing of a pending interrupt. Refer User Manual(Rev 5) Pg. 187 Section 12.6.5 for more information.
LPC134x UART Baud Rate Calculations
The main formula for calculating baud rate is given as:
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 :
X
with following conditions strictly applied:
- 0 < MULVAL <= 15
- 0 <= DIVADDVAL <= 14 {if DIVADDVAL > 0 & DLM = 0 then, DLL must be >= 3}
- DIVADDVAL < MULVAL
Where UART_PCLK is the UART Peripheral Clock (derived from CCLK using UARTCLKDIV register) 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:
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.
Now we know the formula, how do we 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(i.e DIVADDVAL=0). 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 DLL and hence we simplify the equation and solve for UxDLL.
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 DLL might get out of range i.e. > 255 in which case you have to start increasing DLM and recompute a new value for DLL. This method works for 115200 desired baud when UART_PCLK = 72Mhz, 48Mhz and 24Mhz.
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. The Algorithm which I have given below is a basic one suitable for beginners. A more invloved Iterative Algorithm is given on Page 202(Rev 5) under section 12.6.15.1 of the user manual.
When using 72Mhz clock we get DLL = 39.06 with the 1st method, hence we can use DLL = 39 since DLL must be an integer to get Actual Baud = 115384.6(~115384) with an relative error of 0.16% only. Similary when UART_PCLK = 48Mhz we can use DLL = 26 for Baud mentioned above. In both cases we were lucky to get a DLL very close to an integer, but not when we use a clock of 12.5Mhz or change the desired baud to something like 9600.
Example: UART_PCLK = CCLK = 72 Mhz and Required Baud Rate is 9600.
Lets start with DLM = 0 , DIVADDVAL = 0 and MULVAL = 1
We have UART_PCLK = CCLK = 72Mhz = 72 x 106 Hz
So the equation now gets simplified and we can find DLL.
We get DLL = 468.75, which is out of range since DLL must be < 255 as it uses only 8-bits. To bring DLL in range lets use a non-zero DLM value in which case our equation becomes,
Now using DLM=1, we get DLL = 212.75 i.e. = 213 when rounded. With DLL = 213 & DLM = 1 our actual baud rate will be = 9594.8 with a error of 5.2(magnitude) which gives a relative error of 0.05%. Hence, we can use these settings since error is within specification. But if this error would have been larger we would have used the Fraction Part as follows:
- Choose DLL(& DLM if requried) which gives a positive error (i.e. Actual Baud-rate > Desired Baud-Rate, given DLL is >= 3 if DLM = 0) so we can scale it down using Fractional Divider.
- Calculate Required Fraction Part(FP) value given by:
FPrequired = [Desired Baud / Actual Baud Rate] - Choose MULVAL and DIVADDVAL so that the Fraction Part is as close to FPrequired
- Most of the time this will give the actual baud-rate close to required with <1.1% relative error. If not try with different DLL (& or DLM) and repeat.
115200 Baud @ 72Mhz UART_PLCK – Use: DLL=39, DLM=0, MULVAL=1, DIVADDVALL=0
115200 Baud @ 48Mhz UART_PLCK – Use: DLL=26, DLM=0, MULVAL=1, DIVADDVALL=0
9600 Baud @ 72Mhz UART_PLCK – Use: DLL=213, DLM=1, MULVAL=1, DIVADDVALL=0
How to Configure UART in LPC134x
Once you know how UART/Serial communication works, configuring and initializing UART is pretty straight forward. In Source Code Examples for this tutorial we will use the following configuration:
- BaudRate = 115200 (with UART_PCLK=CCLK=72Mhz)
- Data Length = 8 bits
- No Parity Bit
- and 1 Stop Bit
Note: Please make it sure that configuration is same on both the communicating ends.
As seen in example above: in order to get ~115200 bauds at 72Mhz UART_PCLK we must use the following settings for baud generation :
Now, lets write a function “InitUART()” which we can use initialize and configure UART0:
#define MULVAL 1
#define DIVADDVAL 0
#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 initUART(void)
{
/*Assuming CCLK=72Mhz!*/
//UART CLOCK Enable Sequence Step 1 - Select TXD,RXD functions
LPC_IOCON->PIO1_6 = 0x1; //Select RXD for PIO1_6
LPC_IOCON->PIO1_7 = 0x1; //Select RXD for PIO2_6
//UART CLOCK Enable Sequence Step 2 - Set Divider & Enable Clock
LPC_SYSCON->UARTCLKDIV = 1; //Set Divider by 1, so UART_PCLK=CCLK
LPC_SYSCON->SYSAHBCLKCTRL |= (1<<12);//Enable UART clock
LPC_UART->LCR = 3 | DLAB_BIT ; /* 8 bits, no Parity, 1 Stop bit & DLAB set to 1 */
LPC_UART->DLL = 39; /*As per our calculations*/
LPC_UART->DLM = 0;
//LPC_UART0->IER |= ..; //Edit this if want you to use UART interrupts
LPC_UART->FCR |= Ux_FIFO_EN | Rx_FIFO_RST | Tx_FIFO_RST;
LPC_UART->FDR = (MULVAL<<4) | DIVADDVAL; /* MULVAL=15(bits - 7:4) , DIVADDVAL=2(bits - 3:0) */
LPC_UART->LCR &= ~(DLAB_BIT);
//Now since we have applied DLL and DLM we now lock or freeze those values by disabling DLAB i.e DLAB=0
//Baud= ~115200. Now we can perform UART communication!
}
Once this is done you are now ready to transfer data using RBR and THR registers.
Connections between MCU and PC or Laptop
Here we can have 2 possible scenarios.
- Your PC/Laptop(older ones) already has a serial port. In this case you will need a RS232 to TTL converter.
- Your PC/Laptop doesn’t have a serial port and you are using USB to Serial converter(FTDI ones, etc..).
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.
LPC1343 UART Examples with Source Code
Now its time to actually use UART in real life! Lets do some communication between your LPC134x MCU and PC/Laptop. The code is based on CMSIS which is the Base library for ARM Cortex Micrcontrollers.
Example 1: Hello World using UART Example
This demo will print “Hello from LPC1343!” 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 for interfacing LPC134x then only use Line-Feed(LF) character for New-Line. The function uartWrite() is used to transmit data one BYTE at a time. The output of this example is shown below:
Source Code:
/*(C) Umang Gajera - www.ocfreaks.com
More Embedded tutorials @ www.ocfreaks.com/cat/embedded/
LPC1343 UART Example 1 Source Code.
License: GPL.*/
#include <lpc13xx.h>
#define THRE (1<<5) //Transmit Holding Register Empty
#define MULVAL 1
#define DIVADDVAL 0
#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 initUART(void);
void uartWrite(char data);
int main(void)
{
//SystemInit(); //This already gets called by CMSIS Startup Code, sets CCLK=72Mhz
char msg[] = { 'H','e','l','l','o',' ','f','r','o','m',' ','L','P','C','1','3','4','3','\0' };
int count=0;
initUART();
while(1)
{
while( msg[count]!='\0' )
{
uartWrite(msg[count]);
count++;
}
//Send NEW Line Character(s) i.e. "\n"
uartWrite(CARRIAGE_RETURN); //Comment this for Linux or MacOS
uartWrite(LINE_FEED); //Windows uses CR+LF for newline.
count=0; // reset counter
}
//return 0; //This won't execute normally
}
void uartWrite(char txData)
{
while(!(LPC_UART->LSR & THRE)); //wait until THR is empty
//now we can write to Tx FIFO
LPC_UART->THR = txData;
}
void initUART(void)
{
/*Assuming CCLK=UART_PCLK=72Mhz!*/
//UART CLOCK Enable Sequence Step 1 - Select TXD,RXD functions
LPC_IOCON->PIO1_6 = 0x1; //Select RXD for PIO1_6
LPC_IOCON->PIO1_7 = 0x1; //Select RXD for PIO2_6
//UART CLOCK Enable Sequence Step 2 - Set Divider & Enable Clock
LPC_SYSCON->UARTCLKDIV = 1; //Set Divider by 1, so UART_PCLK=CCLK
LPC_SYSCON->SYSAHBCLKCTRL |= (1<<12);//Enable UART clock
LPC_UART->LCR = 3 | DLAB_BIT ; /* 8 bits, no Parity, 1 Stop bit & DLAB set to 1 */
LPC_UART->DLL = 39;
LPC_UART->DLM = 0;
LPC_UART->FCR |= Ux_FIFO_EN | Rx_FIFO_RST | Tx_FIFO_RST;
LPC_UART->FDR = (MULVAL<<4) | DIVADDVAL; /* MULVAL=15(bits - 7:4) , DIVADDVAL=2(bits - 3:0) */
LPC_UART->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. Now we can perform UART communication!
}
Example 2: Echo data in Terminal
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 RXD pin, it will send the same character back to PC via the TXD Pin and it gets displayed in Terminal. Here I’ve defined additional function uartRead() which is used to read the recieved data one BYTE at a time.
LPC134x UART Example 2 output:
Source code:
/*(C) Umang Gajera - www.ocfreaks.com
More Embedded tutorials @ www.ocfreaks.com/cat/embedded/
LPC1343 UART Example 2 Source Code.
License : GPL.*/
#include <lpc13xx.h>
#define RDR (1<<0) //Receiver Data Ready
#define THRE (1<<5) //Transmit Holding Register Empty
#define MULVAL 1
#define DIVADDVAL 0
#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 initUART(void);
void uartWrite(char data);
char uartRead(void);
int main(void)
{
initUART();
char data = 0;
while(1)
{
data = uartRead(); //Read Data from Rx
if(data == ENTER || data == LINE_FEED) //Check if user pressed Enter key
{
//Send NEW Line Character(s) i.e. "\n"
uartWrite(CARRIAGE_RETURN); //Comment this for Linux or MacOS
uartWrite(LINE_FEED); //Windows uses CR+LF for newline.
}
else
{
uartWrite(data); //Tx Read Data back
}
}
//return 0; //Normally this won't execute
}
char uartRead(void)
{
while(!(LPC_UART->LSR & RDR)); //wait until data arrives in Rx FIFO
return LPC_UART->RBR;
}
void uartWrite(char txData)
{
while(!(LPC_UART->LSR & THRE)); //wait until THR is empty
//now we can write to Tx FIFO
LPC_UART->THR = txData;
}
void initUART(void)
{
/*Assuming CCLK=UART_PCLK=72Mhz!*/
//UART CLOCK Enable Sequence Step 1 - Select TXD,RXD functions
LPC_IOCON->PIO1_6 = 0x1; //Select RXD for PIO1_6
LPC_IOCON->PIO1_7 = 0x1; //Select RXD for PIO2_6
//UART CLOCK Enable Sequence Step 2 - Set Divider & Enable Clock
LPC_SYSCON->UARTCLKDIV = 1; //Set Divider by 1, so UART_PCLK=CCLK
LPC_SYSCON->SYSAHBCLKCTRL |= (1<<12);//Enable UART clock
LPC_UART->LCR = 3 | DLAB_BIT ; /* 8 bits, no Parity, 1 Stop bit & DLAB set to 1 */
LPC_UART->DLL = 39;
LPC_UART->DLM = 0;
LPC_UART->FCR |= Ux_FIFO_EN | Rx_FIFO_RST | Tx_FIFO_RST;
LPC_UART->FDR = (MULVAL<<4) | DIVADDVAL; /* MULVAL=15(bits - 7:4) , DIVADDVAL=2(bits - 3:0) */
LPC_UART->LCR &= ~(DLAB_BIT);
//Now since we have applied DLL and DLM we now lock or freeze those values by disabling DLAB i.e DLAB=0
//Baud= ~115200. Now we can perform UART communication!
}