Overview
The MCS®-51 family contains a flexible set of microcontrollers. These 8-bit
embedded controllers have different features such as on-chip program memory,
data RAM and some even have integrated A/D converters. One feature that all
of the microcontrollers in the MCS®-51 family have in common is an integrated
UART (Universal Asynchronous Receiver Transmitter).
This guide has been designed so that any programmer with basic microcontroller
experience can learn how to use the general features of the on-chip UART in
a MCS®-51 microcontroller. This document has been created and designed
in response to repeated inquires on the usage of the serial port. Working
examples have been included and explained to ease the learning process.
RCAP2L and RCAP2H are 8-bit registers combined as a 16-bit entity that timer 2 uses as
a reload value. Each time timer 2 overflows (goes one past FFFFH), this 16-bit reload
value is placed back into the timer, and the timer begins to count up from there until
it overflows again. Each time the timer overflows, it signals the processor to send
a data bit out the serial port. The larger the reload value (RCAP2H, RCAP2L),
the more frequently the data bits are transmitted out the serial port. This frequency
of data bits transmitted or received is known as the baud rate.
Similar to timer 2, TH1 is an 8-bit register that timer 1 uses as it's reload value.
The larger the number placed in TH1, the faster the baud rate. SMOD1 is bit position 7
in the PCON register. This bit is called the "Double Baud Rate Bit". When the
serial port is in mode 1, 2 or 3 and timer 1 is being used as the baud rate generator,
the baud rate can be doubled by setting SMOD1. For example; TH1 equals DDH and the
oscillator frequency equals 16Mhz, then the baud rate equals 2400 baud if SMOD1 is
set. If SMOD1 is cleared, for the same example, then the baud rate would be 1200.
Table Two
Baud Rate
Freq (Mhz)
SMOD1
TH1
Baud Rate
Freq (Mhz)
SMOD1
TH1
4,800
16
1
EF
56,800
11.059
1
FF
2,400
16
1
DD
19,200
11.059
1
FD
1,200
16
1
BB
9,600
11.059
1
FA
600
16
1
75
4,800
11.059
1
F4
2,400
16
0
EF
2,400
11.059
1
E8
1,200
16
0
DD
1,200
11.059
1
D0
600
16
0
BB
600
11.059
1
A0
300
16
0
75
300
11.059
1
40
4,800
12
1
F3
9,600
11.059
0
FD
2,400
12
1
E6
4,800
11.059
0
FA
1,200
12
1
CC
2,400
11.059
0
F4
600
12
1
98
1,200
11.059
0
E8
300
12
1
30
600
11.059
0
D0
2,400
12
0
F3
300
11.059
0
A0
1,200
12
0
E6
1,200
6
0
F3
600
12
0
CC
600
6
0
E6
300
12
0
98
300
6
0
CC
110
6
0
72
Baud Rates Missing Why are some baud rates missing from the table?
If you look at the table carefully, you will notice that some common baud rates
are missing in certain scenarios. The reason is, certain microcontroller operating
frequencies will only support specific baud rates. Just because a baud rate reload
value can be calculated by the previous equations, doesn't mean that the
microcontroller can accurately generate that specific baud rate. If you would
like to calculate a baud rate that is not in the previous tables, or if you want
to find out if a specific baud rate can be accurately generated at a specific
operating frequency, follow these steps:
Use the appropriate equation to calculate the reload value.
Round off the calculated reload value to the nearest whole number.
Recalculate the baud rate using the rounded off reload value.
Calculate the percent error between the two baud rates by using the following formula:
If the percent error is less that 2%, then the rounded reload value is adequate to generate
the specified baud rate. If the error is greater than 2%, this means the baud rate generated
by the microcontroller would be different from the baud rate that you expect to be transmitting
and there may be a loss of data in the process.
Common Questions
The intention of this section is to provide quick answers to common problems and questions when trying to set up the serial port in the MCS®-51 family. This has been compiled by Intel employees who technically support the MCS®-51 family of microcontrollers.
1. What is the purpose of using interrupts and/or polling in serial applications?
In serial applications, it is necessary to know when data has completed transmission or
has completed reception. Whenever data has completed transmission or completed reception,
there is a specific bit (flag) that is set when the process has been completed. These
two specific bits are located in the SCON register and determine when an interrupt will
occur or when the polling sequence should be complete. The bits are RI and TI.
RI is the receive interrupt flag. When operating in mode 0 of the UART, this bit is
set by hardware when the 8th bit is received. In all other UART operating modes, the
RI bit is set by hardware upon reception halfway through the stop bit. RI bit must be
cleared by software at the end of the interrupt service routine or at the end of the
polling sequence.
TI is the transmit interrupt flag. This bit operates in the same manner as RI except
it is valid for transmission of data, not reception. By using either interrupts or polling,
it is necessary to check to see if either of the two bits are set.
For the case of transmitting data, it is necessary to "watch" to see if the TI bit is set. A set bit has a logic level of 1 and a cleared bit has a logic level of 0. If you try to transmit more data and your previous data has not yet fully been transmitted, you will overwrite on top of it and have data corruption. Therefore, you must only transmit the next piece of data after the transmission of the current data has been completed.
For the case of receiving data, it is necessary to watch and see if the RI bit
is set. This bit serves a similar purpose as the TI bit. Upon reception of data,
it is necessary to know when data has been completely received so it can be read
before more data comes and overwrites the existing data in the register.
2. How does the serial interrupt and polling work?
A serial interrupt will occur whenever the RI or the TI bit has been set and the serial
interrupts have been enabled in the IE and SCON register. When TI or RI is set, the
processor will vector to location 23H. A common serial interrupt routine would be
the following:
After the processor vectors to 23H, it will then vector off to location label which has a
physical location defined by the assembler. Label is the start of your serial interrupt
subroutine which should do the following:
Find out which bit caused the interrupt RI or TI.
Move data into or out of the SBUF register if necessary.
Clear the corresponding bit that caused the interrupt.
The last line of your serial interrupt subroutine should be RETI. This makes the
processor vector back to the next line of code to be executed before the
processor was interrupted.
Polling is easier to implement than interrupt driven routines. The technique of polling
is simply to continuously check a specified bit without doing anything else. When that bit
changes state, the loop should end. For the case of serial transmission, a section of sample
code would be the following:
...
JNB TI, $ ;this code will jump onto itself until TI is set
CLR TI ;clear the TI bit ...
For receive polling, just replace the TI in the previous code with RI. In either case, make sure that after polling has completed, clear the bit that you were polling.
3. When should I choose polling or interrupts?
Polling is the simplest to use but it has a drawback; high CPU overhead. This means that while the processor is polling, it is not doing anything else, this is a waste of the CPU's time and tends to make programs slow.
Interrupts are a little more complex to use but allows the processor to do other functions.
Thus, serial communication functions are executed only when needed. This makes programs run
faster than programs that use polling.
Common Problems I am viewing data on an oscilloscope and I am not seeing the data I transmitted;
I see other data instead. Why?
You are not waiting for the data to be completely transmitted before you send
more data out. The new data is being written on top of the old data before it
exits to the serial port. See "What is the purpose of using interrupts
and/or polling in serial applications" on page 6.
I am moving data into SBUF, all my registers are configured for serial
communications, and nothing is being transmitted. Why?
Chances are that the timer you chose for your baud rate generator was
never started or "turned on."
All of the registers are set up correctly, but when I receive
data, the microcontroller never vectors to the interrupt routine. Why?
The global interrupt enable bit has not been set or the serial
interrupt bit has not been set. The address of the first line of the serial interrupt routine was not at location 23H.
I am trying to transmit data and all I see on the oscilloscope is a square wave
coming out of the Txd pin. Why?
The microcontroller serial port is in mode 0. In mode 0, the Txd pin outputs
the shift clock (a square wave). Data is actually transmitted and received through
the Rxd pin.
I am receiving data and I move it to another register and read it. The value
that I am reading is not the data that I received. Why?
The data that was received was not moved out of the buffer (SBUF) fast enough
before the new data arrived. Therefore, part of the old data got overwritten
before you transferred it to another register. To avoid this, see "What
is the purpose of using interrupts and/or polling in serial applications?"
on page 6.
Sample Programs
The following programs have been designed to aid in the understanding of the general
setup and transmission of serial applications.