View Course Path

Serial communication with UART in 8051 – Simple in-depth explanation

In this post, we will understand the serial communication circuitry (UART) that is present in the proverbial 8051. We will understand everything about UART, its implementation within the 8051 architecture, and its usage using assembly language programs.

Note that UART is NOT a communication protocol like SPI, I2c etc. It’s just an electronic circuit that receives and transmits data serially in an asynchronous manner. It could, in theory, be used to facilitate the implementation of an asynchronous serial protocol, but it itself isn’t one. Communication protocols generally have some standards to maintain, and these standards are governed by groups or consortiums.

Computers can communicate with one another using serial or parallel transmission of data. In the case of serial communication, one wire is enough to send multiple bits of data in the form of a stream of bits. But for parallel communication individual wires are required for each bit.

So for serial communication, only one wire is used to transfer 8-bit data (bit by bit) whereas parallel communication requires eight wires. When it comes to the speed of data transmission, parallel communication is faster but can cause issues like cross talk and is costlier due to the higher amount of wires it needs. Serial communication, on the other hand, is cheaper as it uses only one wire to transfer data. Their unique pros and cons notwithstanding, the choice of their implementation varies depending on the application. They are both useful in certain specific settings.

Let’s dive into serial communication since that is the prime focus of this post.

Types and modes of serial communication

Serial communication can occur in two ways:

  • Asynchronous communication: As the name suggests, asynchronous communication does not use a synchronizing clock signal when transferring data. It uses special characters known as start and stop bits which help the computers synchronize their communication. These bits also help the communicating devices know when a frame of data is over. This makes it cheap to implement but can be less efficient when compared to synchronous communication.
  • Synchronous communication: In this case, the two devices communicating with another use the same clock signal to synchronize the data transmission. What that means is that we use a clock. The clock is common to both the communicating parties. The reason we use the clock is to tell the devices when the data is being sent and at what speed it needs to sample the data to make sense of it. This makes it faster and more efficient as it does not have any overhead in terms of start and stop bits.

Communication, in general, between any two devices, can follow three different modes of transmission.

  • Simplex– In this mode of communication, there is a one-way movement of data, and only the transmitter can send data.
  • Half-duplex– In this mode, other both the transmitter and receiver can transmit data, but they can’t do it at the same time.
  • Full-duplex– In this mode, both the receiver and the transmitter can communicate at the same time.

These are applicable for serial communication too. Now that we know what serial communication is, what are its two types and what to call it when its in particular directions, let’s zero in on how serial communication takes place in the 8051 microcontroller.

Serial communication in 8051

For serial communication, the 8051 can use either asynchronous or synchronous types. To select which type will be used, we use a combination of registers. We will be talking about them in great detail in the coming section.

Let’s understand this first.

Whenever two computers communicate with one another, there are a few details that need to be known (since they are computers and kind of need to be spoonfed with all necessary information). The computers need to know:

  1. When will the data be sent?
  2. What will be the size of the data?
  3. At what speed the data will be sent?

To answer all these questions, computers either follow protocols when they communicate with one another or use special circuits.

In this case, our 8051 uC IC uses a special circuit that, by design, carries the answers to the above three questions. This special circuitry or module is known as a UART. Its full form is Universal Asynchronous Receiver or Transmitter. Let us look at the UART serial communication method to get insights on how it works.

UART in serial communication

UART (Universal Asynchronous Reciever/Transmitter) as the name suggests is an asynchronous mode of data transmission. In the case of asynchronous data transmission, individual bits of data are transferred in the form of frames. These frames have gaps in between them so that the device can synchronize itself to receive data as it does not use an external clock

Framing in UART


  • A UART frame consists of the data to be transmitted (8 bits/9 bits) and Start and Stop bits.
  • In some cases, the number of stop bits can be two, but the start bit is always a single bit.
  • Also, the 8051 supports full-duplex communication which means it can transmit and receive data at the same time. This is made possible by the special design of the SBUF register about which we will talk in the coming sections.
  • The start bit helps in synchronization and is a low signal for a particular duration of time. The stop bit is a high bit and is followed by a space of low signal, which is used to tell the receiver that a particular character has been transmitted.
  • In some cases, a parity bit is also sent with the data to check for transmission errors.
  • Some modern microcontrollers with 8051 IP cores support multiple UARTs in addition to other protocols like the I2C and SPI for easy interfacing with other devices.
The 8051 can also use synchronous communication to transfer data but in this case, the mode of transmission is half-duplex and only one device can communicate at a time.

Serial communication requires a number of registers let us have a look at them to understand serial communication in a better way.

Registers used in serial communication

  • SBUF register – These are two buffer registers that hold the received data and the data to be transmitted.
  • SCON register – The control center for serial communication in 8051.
  • TMOD register – The register that controls the speed of transmission.
  • PCON register – Used to double the speed of transmission.

SBUF register

  • The SBUF register is an 8-bit register which is used only for serial communication.
  • The SBUF register is actually two registers and not one. Before transmitting or receiving any data placing it in the SBUF register is essential.
  • During transmission start and stop bits are added to the data once it is placed in the SBUF register, and then the data is transferred through the TXD pin(Port 3.0).
  • In the case of reception, the data is received through the RXD pin (Port 3.1), placed in the SBUF register, and framing bits are removed.

sbuf_register_in _serial_communication

  • The SBUF register is placed at memory location 99H of the 8051 SFR memory space.
  • It consists of two registers and shift registers to convert the serial data into parallel or visa versa.
  • As the 8051 has two independent registers, it can transmit and receive data at the same time.
  • The shift register in the RXD side acts as a buffer. It allows one byte of data to be received while another is stored in the buffer. If one byte is read by the CPU and the other is not fulls received this mechanism prevents erroneous reception.

SCON register (Serial Control) 

To manage this asynchronous communication between devices, the SCON register is used. The SCON register is placed at the address 98H and is bit addressable. You can think of the SCON register as the main control register for UART.


  • SM2: This bit is used to enable multiprocessor communication in modes 2 and 3. This pin is mostly set to 0
  • REN: The Ren bit enables or disables reception. If Ren bit is high, then the microcontroller can receive data at port3.1, but when it is 0, this port is disabled.
  • TB8: This register stores the 8th bit in mode 2 and 3 during transmission
  • RB8: This register stores the 8th bit in mode 2 and 3 during the reception of data
  • TI: This interrupt is raised by the hardware when the transmission is complete for a block of data.
  • RI: This interrupt is raised by the hardware when a complete block of data is received.
  • SMO and SM1- These bits select the mode of serial communication.


SM0 SM1 Selected operation for Serial communication
0 0 Shift register operation for data transmission
0 1 8-bit UART communication with variable baud rate
1 0 9-bit UART communication with fixed baud rate
1 1 9-bit UART communication with variable baud rate

To summarize the modes:

  • We have a total of four modes.
  • Out of these, three modes enable asynchronous communication (modes 1,2, and 3).
  • Mode 0 enables synchronous shift-register mode.
  • Out of the three modes for asynchronous communication, one has a fixed baud rate (mode 2).
  • Two modes communicate in 9-bit data sequences (modes 2 and 3).
  • And two modes communicate in 8-bit data sequences (modes 0 and 1).
Word of caution: Remembering these modes requires a lot of mental gymnastics and it’s fine if you don’t. In the grand scheme of things, you will always have access to datasheets. It’s just necessary to understand these. Relieve your mind of the pressure of having to remember it. Unless ofcourse, you are preparing for a test.

Mode 0

  • During this mode, the transfer of data occurs in a synchronous fashion.
  • The TXD (which originally stands for transmission) pin (Port 3.1) is used to send/receive the synchronizing clock signal.
  • And the RXD (which stands for Receive) pin (Port 3.1) is used for transferring and receiving the data. Reception of data requires REN=1 and RI=0.
  • In this mode, the synchronizing clock signal is the frequency of one machine cycle. In the case of 8051, this is (11.059Mhz/12=921.6Mhz).
  • Just to emphasize, the TXD pin’s function can be a little misleading as it is used here for sending the synchronizing clock signal.
  • In this mode, data is transferred as it would in a shift register—bit by bit with a common synchronizing clock signal.
  • 8-bit data is shifted, LSB first.
  • Transmission starts as soon as data is written into SBUF.
  • Value of SCON for transmitting data in mode 0 = 00H.
  • Value of SCON for receiving data in mode 0 = 10H.



Mode 1

  • Mode 1 is the most commonly used mode for UART based serial communication.
  • You will most likely use this mode for a majority of your simpler UART related applications.
  • In this mode, the UART circuitry is used, and the data can be transferred at variable baud rates which are generated by Timer 1.
  • Here the 8051 follows a full-duplex mode of communication and can transmit and receive data at the same time.
  • For mode 1 in full-duplex, the value 50H should be inserted into SCON register
  • For mode 1 in half-duplex, the value 40H should be inserted into SCON register
Baud rate in serial communication

Baud rate refers to the number of bits transmitted per second. It is very important to decide the number of bits that are transferred per second for proper communication between two devices. Both devices should support a particular baud rate for communication to occur. The 8051 can generate a number of baud rates using timer one in mode 2(8-bit auto-reload) to generate baud rates.


Mode 2

  • This mode also implements UART communication but can be used to transfer/receive 9 bits of data.
  • The baud rate during this mode is fixed to the machine cycle frequency. We’ll look into baud rate generation in the subsequent sections.
  • TB8/RB8 store the 9th bit during the same.
  • The ninth bit is used for sending party bits or is used in multiprocessor communication.
  • the ninth bit is managed by TB8/RB8 in the SCON register.
  • For mode 2 in full-duplex, the value 90H should be inserted into the SCON register.
  • For mode 2 in half-duplex, the value 80H should be inserted into the SCON register.

Mode 3

  • This mode is similar to mode 2 but can transfer data at variable baud rates generated by timer 1.
  • For mode 2 in full-duplex, the value 130H should be inserted into the SCON register.
  • For mode 2 in half-duplex, the value 120H should be inserted into the SCON register.

TI and RI Flag bits 

  • The TI and RI interrupt flags play a very important role in serial communication.
  • Let us say you want to transmit data to an external device and you place it in the SBUF register, but before it is transmitted you send another character to the SBUF register. This could cause issues in overlapping.
MOV SBUF,#23H; writes 23H into the SBUF register to transfer to another device
MOV SBUF,#52H; 52H is written into SBUF before 23H is transmitted and could lead to erroneous data transmission
  • To fix the issue, the TI flag is used.
  • It is a bit in the SCON register which is raised by the microcontroller every time one byte of data is successfully transmitted from the SBUF register.
  • This is how using the TI flag fixes the problem.
MOV SBUF,#23H; writes 23H into the SBUF register to transfer to another device
JNB TI; wait for 23H to be transmitted
CLR TI; clears TI interrupt flag (Note:The hardware sets TI flag but it must be cleared by software)
MOV SBUF,#52H; no errors in data transmission now that the TI flag is set
  • The RI flag performs a similar task during data reception.
  • When the 8051 receives data serially from another device, it places it in the shift register.
  • Once the register is full, it sends the data to the SBUF register and raises the RI, telling the programmer that the complete data has been received.
JNB RI; waits for an entire byte
CLR RI; Note: the receive interrupt flag is set by hardware but must be cleared by software
MOV A, SBUF; moves the data stored in the read-only buffer to the accumulator

TMOD register and Baud rate generation 

Working of TL1 and TH1 bits in 8051 timers

As mentioned in our article on timers and counter the master clock of the microcontroller is divided by 12 to give the machine cycle frequency(11.052Mhz/12=921.6Mhz). In the case of serial communication, this machine cycle frequency is divide by 32 and fed to timer 1. So timer 1 gets a frequency of 28,800 Hz. What this means is that the timer gets 28,800 pulses in a second. Now there are a number of baud rates that can be generated by using timer 1 and this input frequency. The table for the same is given below


Baud rate Value in TH1(Auto reload) Decimal equivalent
9600 Mhz FDH 253 or -3
4800 Mhz FAH 250 or -6
2400 Mhz F4H 244 or -12
1200 Mhz E8H 232 or -24

All this might look a little confusing to you, so let’s try to look at the working of the timer and how all this happens. Let’s take 9600 Mhz as an example. In this case timer 1 is set to mode 2 auto-reload in which every time the timer overflows the value in TH1 is reloaded into TL1, and the timer counts up from the value fed into it. In the case of generating 9600 Mhz baud rate TH1 is fed a value of FDH(253) and as it is an 8 bit up counter, it counts up from 253 to 255 and then overflows. This frequency of overflow is used to generate the baud rate. As mentioned above, the happens every 3 clock pulses making the baud rate 28800/3=9600Mhz.

PCON register for doubling the baud rate 

Bit 7 of the PCON register can be used to double the baud rate. When the SMOD bit is set to 0 the value of the dividing factor changes from 32 to 16 due to which the baud rate doubles.

PCON_register_in _8051


The following formula can be used to find the value to be put in TH1 when SMOD=1

TH1=256 – (Crystal Frequency)/(192 x Baud rate)

when SMOD=0

TH1=256 – (Crystal Frequency)/(384 x Baud rate)

Adding a Parity bit

Parity bits are used in mode 2 and 3. The main reason for using parity bits is to ensure correct data transmission. In some cases, due to noise in the channel data gets corrupted. So to keep erroneous transmission/reception in check parity bits are added. To send a parity bit the particular bit must be put in TB8 register which transmits the data with the 8-bit data in mode 2 or 3. This bit is received and stored in the RB8 register during the reception.

Now that we have an idea of all the components required for serial communication, let us look at a few programs to understand it better.

Using serial communication with polling

In this method, we continuously monitor flags RI and TI. This method does not use CPU resources efficiently; we will look at another method in the next section on how to use interrupts to increase CPU efficiency. Given below are two examples to transmit and receive data using UART.

Transferring data using UART (Sends data “abc” using UART)

MOV SMOD, #50H; mode 1, receiver enabled
MOV TMOD, #20H ;put timer 1 in mode 2
MOV TH1, #253 ;put the reload value in TH1 to generate a baud rate of 9600
SETB TR1 ;start timer 1
MOV 40H, #'a' ; places the ASCII value of 'a' into 40H
MOV 41H, #'b' ; places the ASCII value of 'b' into 41H
MOV 42H, #'c' ; places the ASCII value of 'c' into 42H
MOV R0, #40H ;put the start address in R0
ACALL SENDText; calls SENDText subroutine 

SENDText:MOV A, @R0 ;move data pointed to by R0 into A
         JZ finish ;if contents of A are equal to 0 jump to finish
         MOV SBUF, A ;move contents of A to the serial buffer - this initiates the transmission down the serial line
         INC R0 ;increment R0 to point to next character
         JNB TI, ;wait for entire character to be sent down serial line
         CLR TI ;clear the transmit overflow
         JMP sendText ;go back to send next character

finish:RET; exits SENDText subroutine and terminates sending operation

Receiving data using UART and sends it to port 1

MOV SMOD, #50H; mode 1, receiver enabled
MOV TMOD, #20H ;put timer 1 in mode 2
MOV TH1, #253 ;put the reload value in TH1 to generate a baud rate of 9600
SETB TR1 ;start timer 1
HERE:JNB RI,HERE;checks the RI flag to see if the complete bit is recieved
     MOV A,SBUF ;moves recived data to accumulator
     MOV P1,A ;sends recieved data to port 1
     CLR RI; clears RI flag
     SJMP HERE; repeats task infinitely

Using serial communication with interrupts

In our post on interrupts in 8051, we had mentioned that we would be covering serial interrupts (RI and TI) in this post.

In the case of serial communication, the interrupts behave a little differently from what we had seen in the post on interrupts. Both RI and TI interrupt flags have the same ISR and whenever any one of these flags are raised the control is transferred to the serial interrupt’s ISR. Also, the RETI command does not clear the RI and TI flag bits and needs to be cleared by software.

The advantage of using interrupts in serial communication is that while data is being transmitted or received, the microcontroller can perform any other task. Once any of these tasks complete the RI/TI flags are raised which interrupts the execution of the microcontroller taking it to the ISR. This saves a lot of CPU resources that were being wasted in the polling approach.

The code in the ISR should be written with the understanding that both RI and TI can interrupt the microcontroller and have the same ISR.

In the example given below data received at port 1 is transferred to port 2. This data is also transferred serially using UART, and if and data is received from the serial port it is also sent to port 2

ORG 0000H; location where execution of the program starts from
LJMP MAIN; LJMP used to bypass the ISR

ORG 0023H; location for ISR for both TI and RI
LJMP SERIAL; the control is shifted to the serial subroutine

ORG 30H; location of the starting address of the program code 
MAIN: MOV P1,#0FFH; makes P1 input port
      MOV TMOD, #20H; selects timer 1 mode 2 for generating baud rate
      MOV TH1, #0FDH; generates a baud rate of 9600
      MOV SCON, #50H; selects mode 1 with the receiver enabled
      MOV IE, #10010000B; enables serial interrupt which can be caused by both TI/RI
      SETB TR1; starts timer1
BACK: MOV A, P1; reads data from port1
      MOV SBUF, A ; moves received data to SBUF
      MOV P2, A ; sends data to P2
SJMP BACK; continuously receives data from port 1 and send it to port 2.

ORG 100H; location where the serial subroutine is stored
SERIAL: JB TI, TRANS; if the interrupt is caused by T1 control is transferred to trans as the old data has been transferred and new data can be sent to the SBUF
        MOV A, SBUF; otherwise the interrupt was caused due to RI and received data is put into the accumulator.
        CLR RI; clears RI flag
        RETI; transfers control to main
TRANS: CLR TI;  clears TI flag
       RETI;  transfers control to main
Note that in case of other interrupts the interrupt flags are reset when the RETI instruction executes but in the case of serial communication this has to be done by the programmer

We hope that this article helped you understand all the concepts related to serial communication in 8051. If you have any doubts, feel free to contact us through the comments section, and we will try to help clear your doubt!

2 thoughts on “Serial communication with UART in 8051 – Simple in-depth explanation

  1. The post was very interesting especially with your easy language making it simple to learn. One small doubt, during the serial communication you mentioned that clock signal must be divided by 32, and during timer, it must be divided by 12. Why are we dividing by these numbers only? Are they default numbers or any other reason? Waiting for your valuable reply.

    1. Hi Niharika! Good question! These two numbers are not arbitrary. And more importantly, not related to each other at all. In the post on 8051 architecture, under the subheading Crystal Oscillator, we studied that since the AT89C51 (the version of the 8051 we are using) requires 12 clock cycles to complete the execution of an instruction, the external oscillator’s frequency is divided by 12. 8051’s (However, modern 8051 variants use only 1 clock cycle thus we can drive them at a faster rate using external crystals). Whereas the 32 value of the divider for serial communication comes from the SMOD bit of the PCON register. When SMOD is 0, the dividing value is 16, when 1, it’s 32. To summarize, the 12 is because the AT89C51 variant takes 12 cycles to completely execute an instruction. The 32 is because of the SMOD bit in the PCON register. I know it’s confusing but if you read it a couple of times and draw it all out on a paper, it becomes easy. Good luck!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.