In-Depth Analysis of UART Communication: From Low-Level Registers to Interrupt-Driven Processes

Today, while working on an intelligent voice control module, I wanted to reinforce my knowledge of serial communication. Upon reviewing my previous notes, I found them lacking in detail, so I decided to write a new article explaining the principles of serial communication, covering both hardware and software implementations clearly. This will thoroughly clarify the concept of serial communication.In embedded development and IoT applications, serial communication (UART) is a fundamental module that every developer must understand. It is not only widely used, cost-effective, and highly reliable, but it is also the most common way for MCUs to communicate with each other and with peripherals.In-Depth Analysis of UART Communication: From Low-Level Registers to Interrupt-Driven ProcessesSo, what characteristics do we generally use in protocols? I have organized the classification, types, and descriptions of communication protocols below:

Classification Type Description Examples
Clock Synchronization Asynchronous Communication No shared clock line required UART, USB, CAN
Synchronous Communication Requires shared clock line IIC, SPI
Data Transmission Method Serial Communication Transmits one bit at a time, fewer lines UART, SPI, IIC
Parallel Communication Transmits multiple bits at once, more lines, faster 8080, Memory Modules
Communication Direction Full Duplex Can send and receive simultaneously UART, SPI
Half Duplex Can send or receive, but not simultaneously UART (single line), IIC
Simplex Can only send or receive Single Bus
Application Environment Board-Level Bus Internal chip communication, short distance IIC, SPI, 8080
Field Bus Industrial long-distance communication, strong anti-interference RS485, CAN
Connection Method Wired Communication High reliability USB, RS232
Wireless Communication High flexibility WiFi, Bluetooth, 433

Next, I will provide examples of some commonly used serial communication devices/modules:

Device/Module Name Description and Application
WIFI Module (e.g., ESP8266/ESP32-AT) Communicates with the main MCU via AT commands through serial port, enabling networking and HTTP/MQTT communication
Bluetooth Module (e.g., HC-05/HC-06) Communicates with the main board via serial port, enabling mobile Bluetooth control and data transmission
GPS Module (e.g., NEO-6M) Outputs real-time positioning data (latitude, longitude, speed, time, etc.) via serial port
GPRS/4G Module (e.g., SIM800C, SIM7600) Controls sending SMS, making calls, and internet access via serial AT commands
Voice Recognition Module (e.g., LD3320, Anxin Voice Module) Transmits voice recognition results to the main control via serial port, enabling voice control

These are some common devices and modules that utilize serial communication.

1. What is Serial Communication?

Serial communication is a method of communication that sends data bit by bit. Compared to parallel communication, which transmits multiple bits at once, serial communication requires fewer data lines, making it more suitable for long-distance, cost-sensitive device communication.

Serial Asynchronous Full Duplex Communication

2. Core Parameters of UART Communication

UART communication does not require a clock line, but both communicating parties must have the same baud rate and negotiate the data format:

  • Baud Rate: The number of bits transmitted per unit time (e.g., 9600 bps)

  • Data Bits: Typically 8 bits (can be set to 5-9 bits)

  • Stop Bits: Typically 1 or 2 bits

  • Parity Bit: Used for error detection (can be set to none, odd, or even)

The data frame format for serial communication is:

Start Bit (1 bit) + Data Bits (8 bits) + Stop Bit (1 bit)

3. UART Controller Register Analysis (Taking STM32 as an Example)

The following is a structural block diagram of the UART controller, and I will also explain the main registers below.

In-Depth Analysis of UART Communication: From Low-Level Registers to Interrupt-Driven Processes

Detailed Explanation of UART Controller Pin Functions (TX, RX)

1. TX (Transmit): Transmit Pin

  • Definition: The pin used to send serial data.

  • Nature: TX is an output pin (GPIO output mode), actively driven by the MCU to output data.

  • Application: Sends data from the MCU via the serial protocol to the receiving RX pin.

  • Notes:

    • Before sending data, check if the previous transmission is complete (e.g., check the TXE bit or TC bit of USART_SR).

    • TX idle does not mean transmission is complete; distinguish between <span>TXE (Transmit Data Register Empty)</span> and <span>TC (Transmission Complete)</span>.

2. RX (Receive): Receive Pin

  • Definition: The pin used to receive serial data.

  • Nature: RX is an input pin (GPIO input mode), receiving data sent from external devices.

  • Application: Used to receive data sent from other devices via serial, connected to the TX pin of the other party.

  • Notes:

    • Before reading data, ensure that the data reception is complete (e.g., RXNE bit of USART_SR is 1).

    • After reading the receive register (e.g., USART_DR), RXNE is automatically cleared, indicating that the data has been read.

  1. Status Register (USART_SR)

  • TC (Bit 6): Transmission Complete Flag

  • RXNE (Bit 5): Receive Data Ready Flag

  • Data Register (USART_DR)

    • Read and write to the same register; writing sends, reading receives

  • Baud Rate Register (USART_BRR)

    • Sets the communication rate, e.g., <span>9600bps</span>

    • Calculation formula (16x oversampling):<span>USARTDIV = fCK / (16 × Baud Rate)</span>

  • Control Register 1 (USART_CR1)

    • UE: UART Enable

    • TE: Transmit Enable

    • RE: Receive Enable

    • M: Word Length Setting (default 8 bits)

  • Control Register 2 (USART_CR2)

    • STOP: Stop Bit Configuration (1 bit, 2 bits)

    4. Software Design Framework for Serial Communication, including Interrupt Configuration

    Initialization Function

    void USART_Init(void){// 1. Enable GPIO and USART clock// 2. Configure GPIO for alternate function (TX, RX)// 3. Configure UART Baud Rate Register USART_BRR// 4. Configure Control Register USART_CR1, enable transmit and receive}
    // Initialize USART1void init(u32 baud){ // Initialize GPIO // Enable GPIOB clock RCC->AHB1ENR |= (1<<0); // Configure IO mode GPIOA->MODER &= ~(3<<18); GPIOA->MODER &= ~(3<<20); GPIOA->MODER |= (2<<18); GPIOA->MODER |= (2<<20); // Configure IO output type GPIOA->OTYPER &= ~(1<<9); // Configure IO output speed GPIOA->OTYPER &= (3<<18); // Configure GPIO pull-up/pull-down GPIOA->PUPDR &= (3<<18); GPIOA->PUPDR &= (3<<20); // Configure port reset GPIOA->AFR[1] &= ~(0xf<<4); GPIOA->AFR[1] &= ~(0xf<<8); GPIOA->AFR[1] |= (7<<4); GPIOA->AFR[1] |= (7<<8); // Configure UART initialization // Enable UART clock RCC->APB2ENR |= (1<<4); // Initialize Baud Rate USART1->BRR = 84000000 / baud; // Initialize UART Control Register CR1 USART1->CR1 &= ~(1<<15); // Initialize Oversampling Mode USART1->CR1 |= (1<<12); // Initialize Word Length USART1->CR1 |= (1<<3); // Transmitter Enable USART1->CR1 |= (1<<2); // Receiver Enable // Initialize UART Control Register CR2 USART1->CR2 &= (3<<12); // CR2 Stop Bits // Generally, this is in Control Register CR1 bit 13, enabling USART, placed at the end of UART initialization USART1->CR1 |= (1<<13);}

    Function to Send a Byte via UART

    void USART_SendByte(uint8_t data){while (!(USARTx->SR &amp; (1 << 6))); // Wait for the previous transmission to complete USARTx->DR = data;}

    void send_Uart(u8 data){ while(!(USART1->SR &amp; (1<< 6))); USART1->DR = data;}

    Function to Receive a Byte via UART

    uint8_t USART_ReceiveByte(void){while (!(USARTx->SR &amp; (1 << 5))); // Wait for reception to complete return USARTx->DR;}
    u8 rece_Uart(void){ u8 datab; while(!(USART1->SR &amp; (1<< 5))); datab = USART1->DR; return datab;}

    Detailed Explanation of UART Interrupt Usage

    1. Overview of UART Interrupts

    1. UART Receive Interrupt Mechanism

    • When the UART controllerreceives a byte of data, the data enters the Receive Data Register (DR).

    • At this point, the status register’s RXNE (Receive Not Empty) bit is automatically set to 1.

    • If the receive interrupt is enabled (RXNEIE = 1 in USART_CR1), the UART controller will send an interrupt request to the NVIC controller.

    • After NVIC responds, the CPU executes the UART Interrupt Service Routine.

    2. UART Idle Interrupt Mechanism

    • After the UART continuously receives multiple bytes of data, if no new data is received for a period, it indicates that the current data frame has been completely received.

    • At this point, the status register’s IDLE (Idle) flag is automatically set to 1.

    • If the idle interrupt is enabled (IDLEIE = 1 in USART_CR1), an idle interrupt will be triggered, and the CPU will also be scheduled by NVIC to enter the interrupt service function.

    Example: Controlling a Buzzer via SerialHere we will define a structure

    typedef struct usart1{ u8 usart1_buff[50];  // Receive buffer, can store up to 50 bytes of string data u32 len;             // Current number of received bytes (valid data length) u8 usart1_flag;      // Flag indicating whether reception is complete (set to 1 after idle interrupt triggers)} USART1VAL_t; void USART1_IRQHandler(void){ // Check if receive interrupt signal is triggered if(USART1->SR &amp; (1<<5)){ // Clear flag // Urgent event usart1_val.usart1_buff[usart1_val.len] = USART1->DR; usart1_val.len++; } // Check if idle interrupt signal is triggered if(USART1->SR &amp; (1<<4)){ // Clear flag USART1->SR; USART1->DR; // Urgent event usart1_val.usart1_buff[usart1_val.len] = '\0';      // Manually add end character to form complete string usart1_val.len = 0; usart1_val.usart1_flag = 1;            // String reception complete }} while(1){ if(usart1_val.usart1_flag){ usart1_val.usart1_flag = 0; if(strcmp((char *)usart1_val.usart1_buff,"open") == 0){ BEEP_ON; } else if(strcmp((char *)usart1_val.usart1_buff,"close") == 0){ BEEP_OFF; } } led_flash(5); } return 0;}

    Leave a Comment