Fading Coder

One Final Commit for the Last Sprint

Home > Notes > Content

Handling Concurrent UART Transmission and Reception on 8051 Using Interrupts

Notes May 7 4

Challenges in Polling-Based UART Communication

In embedded systems based on the 8051 architecture, managing simultaneous serial transmission and reception often presents timing conflicts. Consider a scenario where the microcontroller must periodically transmit status data to a host PC while remaining responsive to incoming control commands. A naive implementation typically relies on polling the receive flag within the main execution loop.

The Latency Problem

When the main loop includes blocking delay functions to regulate transmission intervals, the system cannot check for incoming data during those wait periods. For instance, if the code executes a 1-second delay to pace outgoing messages, any command receivde from the host during this window will not be processed until the delay completes. This results in notiecable lag between issuing a command and observing the physical response, such as toggling an LED.

The root cause lies in the sequential nature of polling. The CPU is occupied with the delay routine and cannot inspect the receive interrupt flag (RI) until it returns to the specific check point in the code. Consequently, real-time responsiveness is compromised.

Leveraging Hardware Interrupts

To resolve this latency, the UART peripheral should be configured to trigger a hardware interrupt upon data reception. This mechanism allows the microcontroller to pause its current task, execute a specific service routine to handle the incoming byte, and then resume the previous task immediately. This ensures that incoming commands are processed regardless of what the main loop is doing.

Configuration Requirements

Enabling UART interrupts involves setting specific bits in the Interrupt Enable register:

  • ES: Enables the serial port interrupt.
  • EA: Enables the global interrupt system.

The 8051 UART shares a single interrupt vector (Vector 4) for both transmission and reception events. Inside the Interrupt Service Routine (ISR), the software must distinguish between the two by checking the TI (Transmit Interrupt) and RI (Receive Interrupt) flags.

Implementation: Interrupt-Driven Reception

The following implementation demonstrates how to maintain periodic status updates while ensuring immediate response to control characters. The LED control logic is moved from the main loop into the ISR.

#include <REGX52.H>
#include <intrins.h>

/* Hardware Definitions */
sfr AUXR = 0x8E;
sbit system_led = P3^7;

/* Function Prototypes */
void delay_ms(unsigned int count);
void uart_init(void);
void uart_send_char(unsigned char data);
void uart_send_string(unsigned char *text);
void serial_isr(void) interrupt 4;

/* Global Variables */
unsigned char rx_byte;

/* Delay Routine */
void delay_ms(unsigned int count) {
    unsigned char i, j, k;
    _nop_();
    /* Approximate delay logic for 11.0592MHz */
    while(count--) {
        i = 2;
        j = 199;
        k = 15;
        do {
            do {
                while (--k);
            } while (--j);
        } while (--i);
    }
}

/* UART Initialization */
void uart_init(void) {
    PCON &= 0x7F;       /* Disable baud rate doubling */
    SCON = 0x50;        /* 8-bit UART, variable baud, enable receiver */
    AUXR = 0x01;        /* Timer 1 clock source */
    TMOD &= 0x0F;       /* Clear Timer 1 mode bits */
    TMOD |= 0x20;       /* Timer 1 in 8-bit auto-reload mode */
    TH1 = 0xFD;         /* Reload value for 9600 baud */
    TL1 = 0xFD;         /* Initial value */
    TR1 = 1;            /* Start Timer 1 */
    
    ES = 1;             /* Enable Serial Interrupt */
    EA = 1;             /* Enable Global Interrupts */
}

/* Single Byte Transmission */
void uart_send_char(unsigned char data) {
    SBUF = data;
    while(!TI);         /* Wait for transmission complete */
    TI = 0;             /* Clear flag */
}

/* String Transmission */
void uart_send_string(unsigned char *text) {
    while(*text != '\0') {
        uart_send_char(*text);
        text++;
    }
}

/* Main Execution Loop */
void main(void) {
    system_led = 1;     /* LED Off */
    uart_init();
    
    while(1) {
        delay_ms(300);  /* Initial stabilization */
        uart_send_string("status_ok\r\n");
        delay_ms(1000); /* Periodic interval */
        /* Note: LED control is now handled in ISR */
    }
}

/* Interrupt Service Routine */
void serial_isr(void) interrupt 4 {
    if(RI == 1) {
        RI = 0;                 /* Clear receive flag */
        rx_byte = SBUF;         /* Read data */
        
        /* Command Processing */
        if(rx_byte == 'o') {
            system_led = 0;     /* LED On */
        }
        else if(rx_byte == 'c') {
            system_led = 1;     /* LED Off */
        }
    }
}

Advanced Handling: String Command Buffering

For more complex control schemes, single-character commands may be insufficient. Implementing a buffer within the ISR allows the system to recognize multi-character sequences. The following example accumulates incoming data into an array and scans for specific command strings like "op" (open) or "cl" (close).

#include <REGX52.H>
#include <intrins.h>
#include <string.h>

/* Hardware Definitions */
sfr AUXR = 0x8E;
sbit system_led = P3^7;

/* Constants */
#define BUF_SIZE 12

/* Global Variables */
unsigned char cmd_buffer[BUF_SIZE];
unsigned char buf_index = 0;

/* Delay and UART Helper Functions */
void delay_ms(unsigned int count);
void uart_init(void);
void uart_send_string(unsigned char *text);

/* Delay Implementation */
void delay_ms(unsigned int count) {
    unsigned char i, j, k;
    _nop_();
    while(count--) {
        i = 2; j = 199; k = 15;
        do { do { while(--k); } while(--j); } while(--i);
    }
}

/* UART Setup */
void uart_init(void) {
    PCON &= 0x7F;
    SCON = 0x50;
    AUXR = 0x01;
    TMOD &= 0x0F;
    TMOD |= 0x20;
    TH1 = 0xFD;
    TL1 = 0xFD;
    TR1 = 1;
    ES = 1;
    EA = 1;
}

/* Transmission */
void uart_send_string(unsigned char *text) {
    while(*text) {
        SBUF = *text++;
        while(!TI);
        TI = 0;
    }
}

/* Main Loop */
void main(void) {
    system_led = 1;
    uart_init();
    memset(cmd_buffer, 0, BUF_SIZE);
    
    while(1) {
        delay_ms(300);
        uart_send_string("system_ready\r\n");
        delay_ms(1000);
    }
}

/* ISR with Buffering */
void serial_isr(void) interrupt 4 {
    if(RI == 1) {
        RI = 0;
        cmd_buffer[buf_index] = SBUF;
        buf_index++;
        
        /* Circular Buffer Logic */
        if(buf_index >= BUF_SIZE) {
            buf_index = 0;
        }
        
        /* Command Detection */
        if(strstr((char*)cmd_buffer, "op")) {
            system_led = 0;
            buf_index = 0;
            memset(cmd_buffer, 0, BUF_SIZE);
        }
        else if(strstr((char*)cmd_buffer, "cl")) {
            system_led = 1;
            buf_index = 0;
            memset(cmd_buffer, 0, BUF_SIZE);
        }
    }
}

In this buffered approach, every received byte triggers the interrupt service routine. The data is stored sequentially in the array. The system checks the buffer content after each insertion. Once a matching command sequence is identified, the corresponding action is executed, and the buffer is cleared to prepare for the next command. This ensures that multi-byte instructions are recognized correctly without blocking the main program flow.

Related Articles

Designing Alertmanager Templates for Prometheus Notifications

How to craft Alertmanager templates to format alert messages, improving clarity and presentation. Alertmanager uses Go’s text/template engine with additional helper functions. Alerting rules referenc...

Deploying a Maven Web Application to Tomcat 9 Using the Tomcat Manager

Tomcat 9 does not provide a dedicated Maven plugin. The Tomcat Manager interface, however, is backward-compatible, so the Tomcat 7 Maven Plugin can be used to deploy to Tomcat 9. This guide shows two...

Skipping Errors in MySQL Asynchronous Replication

When a replica halts because the SQL thread encounters an error, you can resume replication by skipping the problematic event(s). Two common approaches are available. Methods to Skip Errors 1) Skip a...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.