Using the UART Peripheral in C++
UART (Universal Asynchronous Receiver/Transmitter) is one of the most widely used communication devices in embedded systems.
It is used for various purposes such as debugging, log output, and data exchange with external devices, providing a simple yet reliable communication method.
In this section, we’ll explore how to implement UART in a class-based structure.
Starting from the base class BaseUART, we’ll examine how it can be extended into IntrUART (interrupt-based) and DmaUART (DMA-based) versions depending on performance requirements.
BaseUART: The Base Class
The BaseUART class provides the fundamental features required for UART communication. Its main functions include:
- Transmit/Receive Queues
- Temporary storage for data before and after transmission.
 
- Read/Write Functions
- APIs for controlling UART data transmission and reception.
 
- Configuration Properties
- Management of communication parameters such as baud rate, parity, and stop bits.
 
This class serves as the common foundation for other UART implementations.
IntrUART: Interrupt-Based UART
IntrUART inherits from BaseUART and operates using interrupts.
- Suitable for small data transfers.
- Enables event-driven processing → saves CPU idle time.
- Handles data queues inside the ISR (Interrupt Service Routine).
Whenever a receive event occurs, an interrupt is triggered, the incoming data is stored in the queue, and the application can easily retrieve it later.
DmaUART: DMA-Based UART
DmaUART also inherits from BaseUART but supports high-speed data transfer using DMA.
- Maximizes performance by minimizing CPU involvement.
- Optimized for large or continuous data streams.
- Operates reliably in parallel with other tasks.
It’s particularly effective for applications that require heavy log output or high-bandwidth data exchange.
Code Examples
Example 1 – Using IntrUART
- Include Header and Declare Object
#include "IntrUART.h" ... IntrUART Uart1; ...
- Initialization Function
void InitUarts(void);
...
/*
 *
 */
void InitUarts(void)
{
	Uart1 = IntrUART(USART1, &CTask);
        Uart1.InitInterrupt();
}
- Call Initialization in main.c
... InitUarts(); ...
- Interrupt ISR Handling
#include <IntrUART.h>
...
extern IntrUART Uart1;
/**
  * @brief This function handles USART2 global interrupt.
  */
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
    Uart1.RxInterruptHandler();
    Uart1.TxInterruptHandler();
  /* USER CODE END USART1_IRQn 0 */
  /* USER CODE BEGIN USART1_IRQn 1 */
  /* USER CODE END USART1_IRQn 1 */
}
Example 2 – Using DmaUART
- Include Header and Declare Object
#include "DmaUART.h" ... DmaUART Uart1; ...
- Initialization Function
void InitUarts(void);
...
/*
 *
 */
void InitUarts(void)
{
	Uart1 = DmaUART(USART1, DMA2, LL_DMA_STREAM_7, DMA2, LL_DMA_STREAM_2, &CTask);
        Uart1.DMARxEnable();
}
- Call Initialization in main.c
...
    InitUarts();
...
    while(1){
      ...
    }
- DMA ISR Handling
...
#include <ConsoleTask.h>
...
extern ConsoleTask CTask;
...
/**
  * @brief This function handles USART1 global interrupt.
  */
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
	CTask.uart.IDLEInterruptHandler();
  /* USER CODE END USART1_IRQn 0 */
  /* USER CODE BEGIN USART1_IRQn 1 */
  /* USER CODE END USART1_IRQn 1 */
}
...
/**
  * @brief This function handles DMA2 stream2 global interrupt.
  */
void DMA2_Stream2_IRQHandler(void)
{
  /* USER CODE BEGIN DMA2_Stream2_IRQn 0 */
	CTask.uart.RXDMAInterruptHandler();
  /* USER CODE END DMA2_Stream2_IRQn 0 */
  /* USER CODE BEGIN DMA2_Stream2_IRQn 1 */
  /* USER CODE END DMA2_Stream2_IRQn 1 */
}
...
/**
  * @brief This function handles DMA2 stream7 global interrupt.
  */
void DMA2_Stream7_IRQHandler(void)
{
  /* USER CODE BEGIN DMA2_Stream7_IRQn 0 */
	CTask.uart.TXDMAInterruptHandler();
  /* USER CODE END DMA2_Stream7_IRQn 0 */
  /* USER CODE BEGIN DMA2_Stream7_IRQn 1 */
  /* USER CODE END DMA2_Stream7_IRQn 1 */
}
Troubleshooting & Tips
- No data received → Check if RXNEIE and TXEIE interrupts are enabled.
- Corrupted data → Verify baud rate, parity, and data length settings.
- ISR not triggered → Check NVIC configuration and vector table mapping.
- Slow processing → Consider using DmaUART instead of IntrUART.
Conclusion
In this section, we designed a class hierarchy for UART communication consisting of:
- BaseUART – Common base implementation
- IntrUART – Interrupt-driven UART
- DmaUART – DMA-driven high-speed UART
This layered design allows you to easily switch communication methods at the application level without modifying your main logic.
 English
English				 Korean
Korean					          
0 Comments