Eight LEDs controlled through the AT89C52 serial interface, AT89C52 UART, serial LED control, Proteus Virtual Terminal

Serial Communication with 8051 Microcontroller in Proteus

2.5K Views
40
700
60
25
60
PCBWay

Hello friends! Today, we are going to study serial communication with an 8051 microcontroller in Proteus. We will send characters from a Virtual Terminal to an AT89C52, use those characters to control eight LEDs and send a short acknowledgement back to the terminal.

If you have completed our 8051 LED blinking project, you already know how to build the basic circuit and load a HEX file. This tutorial adds a communication channel so the output changes in response to a command instead of a fixed delay.

We will keep the original command scheme and project download, while correcting an important clock detail. The revised program uses an 11.0592 MHz oscillator for 9600 baud. The older screenshots show a 16 MHz crystal, so change that value and the processor clock setting when following the program below.

Eight LEDs controlled through the AT89C52 serial interface, AT89C52 UART, serial LED control, Proteus Virtual Terminal
Figure: Eight LEDs controlled through the AT89C52 serial interface.

What Is UART Serial Communication?

A UART sends data as a sequence of bits over a transmit line and receives bits through a receive line. Unlike a parallel interface, it does not need a separate wire for every bit in a byte. Both ends must nevertheless agree on the timing and frame format.

For this project, we use asynchronous communication with eight data bits, no parity and one stop bit, usually written as 8N1. Each character is preceded by a start bit. The receiver uses that transition to begin sampling the incoming frame at the expected bit intervals.

The line is normally high while idle in the logic-level interface used here. A start bit drives it low, followed by the eight data bits and a high stop bit. There is no separate clock wire between the AT89C52 and the Virtual Terminal.

Asynchronous does not mean that timing is unimportant. It means that each end derives timing locally. If their baud rates differ too much, the receiver samples the wrong parts of the waveform and characters can become corrupted.

TX, RX and the common reference

TX means transmit, while RX means receive. Connect the microcontroller's TX output to the terminal's RX input, and the terminal's TX output to the microcontroller's RX input. Connecting two outputs together is not the same as making a communication link.

In a physical logic-level connection, the devices also need a suitable common reference unless the interface is deliberately isolated. The voltage levels must be compatible. A UART frame format does not make a 5 V logic pin directly compatible with a 3.3 V-only input or an RS-232 connector.

What Does This Project Do?

The AT89C52 controls eight LEDs through Port 1. A command from '1' to '8' turns the corresponding LED on. A lowercase letter from 'a' to 'h' turns the corresponding LED off. Every recognized command receives the character 'k' as an acknowledgement.

LEDOutputTurn onTurn off
1P1.01a
2P1.12b
3P1.23c
4P1.34d
5P1.45e
6P1.56f
7P1.67g
8P1.78h

These are character commands. Typing 1 sends the character code for the digit, not a binary byte whose numerical value is one. This distinction matters when you later replace the terminal with a desktop program or another microcontroller.

The acknowledgement means the firmware accepted the command and updated its output state. It does not prove that a physical LED, relay or appliance actually operated. Confirming a real load would require feedback from that load.

Designing the Proteus Circuit

We need an AT89C52, an oscillator and reset network, eight LEDs with individual resistors, power connections and a Virtual Terminal. Use the component-selection window for the circuit parts and the virtual-instrument tools for the terminal.

Components for the 8051 UART demonstration, AT89C52 UART, serial LED control, Proteus Virtual Terminal
Figure: Components for the 8051 UART demonstration.

The original schematic is shown below. Its numbered terminals connect the eight LED branches to the corresponding Port 1 pins. Labels must match exactly; placing two similar-looking labels does not connect them if their net names differ.

Original UART circuit showing the clock value that needs updating, AT89C52 UART, serial LED control, Proteus Virtual Terminal
Figure: Original UART circuit showing the clock value that needs updating.

AT89C52 connectionDestination
P3.0/RXD, pin 10 on the DIP packageVirtual Terminal TXD
P3.1/TXD, pin 11 on the DIP packageVirtual Terminal RXD
P1.0 through P1.7LED branches 1 through 8
EAHigh for this internal-program-memory exercise
RSTReset network that releases the processor after startup
Oscillator11.0592 MHz for the revised UART example

The illustrated LED branches are active high: the port drives each resistor and LED toward ground. The original image labels those resistors as 10 kΩ. Treat that as part of the historical simulation rather than a universal brightness recommendation.

For physical hardware, calculate LED current and check the port's source capability. Classic 8051 pull-ups do not behave like unrestricted power drivers. An active-low sinking arrangement or an external driver may be preferable, with the program's output polarity changed accordingly.

Keep the reset and oscillator wiring from the basic microcontroller circuit, and include appropriate supply decoupling in hardware. Unused Port 0 or Port 2 pins do not need to be toggled to demonstrate UART reception. Avoid unnecessary whole-port writes that could become troublesome when additional devices are connected later.

Why the Oscillator Frequency Matters

The baud rate is generated from a timer clock. Writing a comment saying “9600 baud” does not make an arbitrary timer reload value produce that rate. The oscillator frequency, timer divider, reload value and speed-control settings must agree.

For the classic Timer1 mode-2 arrangement with SMOD cleared, we use:

Baud rate = fosc / (384 × (256 − TH1))

With an 11.0592 MHz oscillator and TH1 = 0xFD = 253, the denominator is 384 × 3 = 1152. Therefore, 11,059,200 / 1152 = 9600. This is why 11.0592 MHz is convenient for this particular example.

If we keep the same reload at 16 MHz, the result becomes approximately 13,888.9 baud. That differs from 9600 by about 44.7%, far beyond a small clock tolerance. The older schematic's 16 MHz label and its 9600-baud comment should not be copied together unchanged.

For other oscillator frequencies or timer arrangements, calculate a suitable configuration. Keil provides an 8051 baud-rate calculator covering common timer choices. This lesson uses the simple exact combination above so we can concentrate on communication behavior.

How long does one character take?

An 8N1 frame contains ten bits: one start bit, eight data bits and one stop bit. At 9600 bits per second, a frame takes approximately 10 / 9600 = 1.0417 ms. The maximum continuous payload rate is therefore about 960 single-byte characters per second before application-level overhead.

Sending one command and waiting for its acknowledgement is a convenient interaction pattern for this demonstration. It avoids assuming that a short polling example can accept an unlimited burst while also transmitting responses.

Configuring the Virtual Terminal

  1. Set the terminal baud rate to 9600.
  2. Select eight data bits, no parity and one stop bit.
  3. Use the normal logic polarity expected by the simulated UART connection.
  4. Connect TX and RX in opposite directions as listed above.
  5. Leave hardware flow-control signals unused for this simple example.
  6. Disable local echo initially so received acknowledgements are easy to identify.

Local echo displays what you type without waiting for the microcontroller. If it is enabled, seeing 1 on the screen does not prove the AT89C52 received it. The returned k is the useful evidence that the command reached the application and the reply came back.

If your terminal sends a carriage return or line feed when you press Enter, the revised program ignores those two characters. Other unsupported characters receive a question mark. That gives a visible distinction between a recognized instruction and a byte outside the command set.

Keil C51 Program

Create an AT89C52 project, add the following C file and build it with the C51 toolchain. The code uses polling to keep the first UART lesson straightforward. It maintains a software copy of the LED states and writes that byte to Port 1.

#include <REG52.H>

unsigned char led_state = 0;

void uart_init(void)
{
    ES = 0;                 /* Polling, no serial interrupt. */
    ET1 = 0;
    TR1 = 0;
    T2CON = 0x00;           /* Timer1 supplies UART timing. */
    PCON &= 0x7F;           /* SMOD = 0. */
    TMOD = (TMOD & 0x0F) | 0x20;
    SCON = 0x50;            /* Mode 1, receiver enabled. */
    TH1 = 0xFD;
    TL1 = 0xFD;
    RI = 0;
    TI = 0;
    P3 |= 0x03;
    TR1 = 1;
}

void uart_send(unsigned char value)
{
    TI = 0;
    SBUF = value;
    while (TI == 0) {
    }
    TI = 0;
}

void process_command(unsigned char command)
{
    unsigned char mask;

    if (command >= '1' && command <= '8') {
        mask = (unsigned char)(1u << (command - '1'));
        led_state |= mask;
    } else if (command >= 'a' && command <= 'h') {
        mask = (unsigned char)(1u << (command - 'a'));
        led_state &= (unsigned char)(~mask);
    } else {
        if (command != '\r' && command != '\n') {
            uart_send('?');
        }
        return;
    }

    P1 = led_state;          /* Active-high LEDs in original circuit. */
    uart_send('k');
}

void main(void)
{
    unsigned char command;

    P1 = 0x00;
    uart_init();
    while (1) {
        if (RI != 0) {
            command = SBUF;
            RI = 0;
            process_command(command);
        }
    }
}

What happens when a character arrives?

The receiver places the incoming byte where software can read it through SBUF and sets RI. The main loop checks RI, copies the byte into command and clears the flag. It then processes that saved byte, so the command logic does not repeatedly read a changing hardware register.

The variable command is used only after reception has been detected. This avoids a problem in the earlier interrupt-style example, where command processing could be reached without a newly received character if a transmit-related event caused the serial interrupt.

For transmission, writing SBUF starts the outgoing character. The example waits for TI before proceeding and clears the flag for the next use. Receive and transmit operations share the register name in software, but they represent different paths inside the UART.

How the LED masks work

Subtracting '1' from a valid on-command produces an index from zero to seven. Shifting a single bit by that index produces the mask for the requested LED. OR sets that bit, while AND with the inverted mask clears it.

The range checks happen before the shift. That matters because shifting by a value derived from an unrestricted character can produce an invalid or unintended operation. We accept only the commands listed in the table.

The software state also makes repeated commands predictable. Sending 1 twice leaves LED 1 on; it does not toggle it back off. This is useful when a sender repeats a command after a missing acknowledgement.

Running the Simulation Step by Step

  1. Build the program and enable HEX-file generation in Keil's target output settings.
  2. Load the resulting HEX file into the AT89C52 in Proteus.
  3. Set the processor clock and oscillator value to 11.0592 MHz.
  4. Check the terminal settings and start the simulation.
  5. Click inside the terminal and type 1.
  6. Confirm that LED 1 changes state and the terminal receives k.
  7. Type a to turn that LED off, then repeat with other outputs.

Original serial simulation with LEDs initially off, AT89C52 UART, serial LED control, Proteus Virtual Terminal
Figure: Original serial simulation with LEDs initially off.

The initial state should have all eight active-high LED outputs low. If the LEDs start on, first determine whether your circuit uses the opposite polarity. Do not change the command mapping until the relationship between a port bit and its LED is clear.

First LED activated by a serial character command, AT89C52 UART, serial LED control, Proteus Virtual Terminal
Figure: First LED activated by a serial character command.

The original single-command screenshot demonstrates the first LED and its acknowledgement. In the revised example, the same user interaction is retained, but the clock and receive handling are explicitly defined.

Eight LED outputs activated with returned acknowledgements, AT89C52 UART, serial LED control, Proteus Virtual Terminal
Figure: Eight LED outputs activated with returned acknowledgements.

Send the commands from 1 through 8 individually to turn all LEDs on. Wait for each acknowledgement before sending the next command during initial testing. Then send lowercase a through h to turn them off again.

Download the Original Simulation

The original attachment is preserved below. It may contain the earlier interrupt-based source and clock settings. Keep a copy of it for reference, then compile the revised code separately and make the clock changes described above before comparing behavior.

Download Proteus Simulation and Code

A successful build does not guarantee that Proteus loaded that particular build. If a code change appears to have no effect, inspect the program-file path. This is especially important when the original archive and a revised project both contain similarly named HEX files.

Polling, Interrupts and Practical Limits

Polling is appropriate for this small interactive demonstration because the main loop has little other work to do. The transmit function nevertheless blocks briefly while sending an acknowledgement. Do not assume the program can safely receive an arbitrary continuous stream under every timing condition.

For a larger application, use a receive buffer and an interrupt routine that promptly captures incoming bytes. Process commands outside the interrupt, and handle transmit events separately. Keil's 8051 serial I/O example provides additional toolchain context for using the serial port from C.

On the classic serial interrupt arrangement, receive and transmit flags share an interrupt source. An interrupt handler must inspect the relevant flags rather than assuming every invocation means a new received character. Blocking a long operation inside that handler can also delay other work.

If you extend the command language, define message boundaries, invalid-command behavior and timeouts. A two-character command such as 10 is currently interpreted as a valid 1 followed by an invalid 0; it is not automatically the number ten.

Checking the command behavior systematically

Begin with all outputs off and send 1, then 8, waiting for a reply after each. Only the first and last LEDs should be on. Sending a should clear the first LED while leaving the eighth unchanged. This checks that one command does not accidentally overwrite another output.

Next, repeat 8 and confirm that the last LED remains on. Send an unsupported character such as x; the output state should remain unchanged and the reply should be a question mark. Finally, send h and confirm that all LEDs are off again.

Record the command, expected state and actual response when debugging. A short sequence like this exercises independent bit control, repeated commands and rejection of invalid input. It is more informative than sending a long random string and trying to infer what should have happened afterward.

Troubleshooting Serial Communication

SymptomCheck first
No LED response and no replyRX/TX crossing, receiver enable, program execution and baud rate
Unreadable charactersActual oscillator, TH1, SMOD and terminal frame settings
LED changes but no acknowledgementMicrocontroller TX path and terminal RX connection
Typed characters appear but nothing happensWhether local echo is displaying them locally
Only some LEDs respondNet labels, bit mapping and individual LED branches
Unexpected question marksUppercase letters, spaces or unsupported terminal characters

When checking the waveform, a 9600-baud bit lasts approximately 104 microseconds. A scope or logic analyzer can therefore distinguish a real baud-rate mismatch from an application parsing problem. Measure the electrical timing before repeatedly changing unrelated C statements.

For a physical PC connection, choose a USB-to-UART adapter with compatible logic levels. A traditional RS-232 port requires an appropriate transceiver; its signaling is not the direct logic-level connection represented by the Virtual Terminal.

Project Review

This project demonstrates communication in both directions with an easily observed output. The command table is small, the LED state is explicit and the acknowledgement makes it possible to test a complete round trip. Correct clock selection is the essential foundation beneath all of those features.

The LEDs stand in for controlled outputs, but they do not justify connecting real appliances directly to GPIO pins. Any later driver stage needs its own electrical design, and applications with important state changes should include feedback and more informative responses.

Frequently Asked Questions

Why use 11.0592 MHz instead of the pictured 16 MHz?

With the chosen classic Timer1 settings and TH1 equal to 0xFD, 11.0592 MHz produces 9600 baud exactly in the ideal calculation. The same settings at 16 MHz do not.

Do I need to press Enter after each digit?

No. Each received character is a complete command. Carriage return and line feed are ignored so terminal line-ending settings do not create unwanted actions.

Why does the program send k?

It preserves the original tutorial's acknowledgement convention. You could choose a different response, but both the sender and receiver should agree on what that response means.

Will uppercase A turn the first LED off?

No. This command set uses lowercase a through h. Uppercase characters are unsupported and receive a question mark in the revised example.

Can I use interrupts instead of polling?

Yes. Use separate handling for receive and transmit flags, keep interrupt work short and add buffering when the application must accept data while performing other tasks.


Comments

5

Join the conversation

Reply3

serial rxtx.c(1): error C141: syntax error near '&'
serial rxtx.c(1): error C129: missing ';' before '.'
serial rxtx.c(1): error C309: bad or missing filename
serial rxtx.c(1): warning C318: can't open file ''
serial rxtx.c - 3 Error(s), 1 Warning(s).

i am using keil uv5