Initial: MCU_UART hardware UART driver with DMA/ISR TX and ring buffer RX

This commit is contained in:
Mohamed Salem 2026-04-13 03:51:35 +02:00
commit 5313ee98d2
5 changed files with 656 additions and 0 deletions

26
cfg/MCU_UART_cfg.c Normal file
View File

@ -0,0 +1,26 @@
/******************************************************************************
* File: MCU_UART_cfg.c
* Component: MCU_UART
* Description: Configuration array definition for the MCU_UART driver.
*
* Layer: MCU (hardware abstraction) - configuration
*****************************************************************************/
#include "MCU_UART.h"
#include "MCU_UART_cfg.h"
const MCU_UART_tstrConfig MCU_UART_astrConfig[MCU_UART_NUM_INSTANCES] =
{
[MCU_UART_INSTANCE_0] =
{
.u8TxPin = MCU_UART_0_TX_PIN,
.u8RxPin = MCU_UART_0_RX_PIN,
.u32BaudRate = MCU_UART_0_BAUD_RATE,
.enuDataBits = MCU_UART_0_DATA_BITS,
.enuStopBits = MCU_UART_0_STOP_BITS,
.enuParity = MCU_UART_0_PARITY,
.enuTxAsyncMode = MCU_UART_0_TX_ASYNC_MODE,
.pfTxCompleteCallback = MCU_UART_0_TX_COMPLETE_CALLBACK,
.enuRxAsyncMode = MCU_UART_0_RX_ASYNC_MODE,
},
};

51
cfg/MCU_UART_cfg.h Normal file
View File

@ -0,0 +1,51 @@
/******************************************************************************
* File: MCU_UART_cfg.h
* Component: MCU_UART
* Description: Configuration for the MCU_UART driver. Defines instances,
* pin assignments, baud rates, data format, TX/RX async modes,
* and RX buffer sizing.
*
* Layer: MCU (hardware abstraction) - configuration
*****************************************************************************/
#ifndef MCU_UART_CFG_H
#define MCU_UART_CFG_H
#include "STD_TYPES.h"
/* ------------------------------------------------------------------------ */
/* INSTANCE ENUMERATION */
/* ------------------------------------------------------------------------ */
typedef enum
{
MCU_UART_INSTANCE_0 = 0U,
MCU_UART_NUM_INSTANCES
} MCU_UART_tenuInstance;
/* ------------------------------------------------------------------------ */
/* RX RING BUFFER SIZING */
/* ------------------------------------------------------------------------ */
/** @brief Number of address bits for the RX ring buffer (2^N bytes).
* Must be power of 2 for DMA ring wrap.
* 5 = 32, 6 = 64, 7 = 128, 8 = 256. */
#define MCU_UART_RX_BUFFER_SIZE_BITS 6U
#define MCU_UART_RX_BUFFER_SIZE (1U << MCU_UART_RX_BUFFER_SIZE_BITS)
#define MCU_UART_RX_BUFFER_MASK (MCU_UART_RX_BUFFER_SIZE - 1U)
/* ------------------------------------------------------------------------ */
/* INSTANCE 0 CONFIGURATION */
/* ------------------------------------------------------------------------ */
#define MCU_UART_0_TX_PIN 0U
#define MCU_UART_0_RX_PIN 1U
#define MCU_UART_0_BAUD_RATE 115200U
#define MCU_UART_0_DATA_BITS MCU_UART_DATA_BITS_8
#define MCU_UART_0_STOP_BITS MCU_UART_STOP_BITS_1
#define MCU_UART_0_PARITY MCU_UART_PARITY_NONE
#define MCU_UART_0_TX_ASYNC_MODE MCU_UART_ASYNC_DMA
#define MCU_UART_0_TX_COMPLETE_CALLBACK STD_NULL
#define MCU_UART_0_RX_ASYNC_MODE MCU_UART_ASYNC_ISR
#endif /* MCU_UART_CFG_H */

124
inc/MCU_UART.h Normal file
View File

@ -0,0 +1,124 @@
/******************************************************************************
* File: MCU_UART.h
* Component: MCU_UART
* Description: Public interface for the MCU UART driver component.
*
* TX: SendByte (blocking), SendBuffer (non-blocking DMA/ISR),
* SendBufferBlocking.
* RX: background ring buffer filled by ISR or DMA.
* ReadByte / ReadBuffer read from the buffer (non-blocking).
*
* Layer: MCU (hardware abstraction)
*****************************************************************************/
#ifndef MCU_UART_H
#define MCU_UART_H
#include "STD_TYPES.h"
/* ------------------------------------------------------------------------ */
/* CONFIGURATION VALUE TYPES */
/* ------------------------------------------------------------------------ */
typedef enum
{
MCU_UART_PARITY_NONE = 0U,
MCU_UART_PARITY_EVEN,
MCU_UART_PARITY_ODD
} MCU_UART_tenuParity;
typedef enum
{
MCU_UART_DATA_BITS_5 = 5U,
MCU_UART_DATA_BITS_6 = 6U,
MCU_UART_DATA_BITS_7 = 7U,
MCU_UART_DATA_BITS_8 = 8U
} MCU_UART_tenuDataBits;
typedef enum
{
MCU_UART_STOP_BITS_1 = 1U,
MCU_UART_STOP_BITS_2 = 2U
} MCU_UART_tenuStopBits;
typedef enum
{
MCU_UART_ASYNC_DMA = 0U,
MCU_UART_ASYNC_ISR
} MCU_UART_tenuAsyncMode;
/* ------------------------------------------------------------------------ */
/* CONFIGURATION STRUCTURE */
/* ------------------------------------------------------------------------ */
typedef struct
{
u8 u8TxPin;
u8 u8RxPin;
u32 u32BaudRate;
MCU_UART_tenuDataBits enuDataBits;
MCU_UART_tenuStopBits enuStopBits;
MCU_UART_tenuParity enuParity;
MCU_UART_tenuAsyncMode enuTxAsyncMode;
STD_tpfCallbackFunc pfTxCompleteCallback;
MCU_UART_tenuAsyncMode enuRxAsyncMode;
} MCU_UART_tstrConfig;
/* ------------------------------------------------------------------------ */
/* TX PUBLIC API */
/* ------------------------------------------------------------------------ */
STD_tenuResult MCU_UART_enuInit(void);
STD_tenuResult MCU_UART_enuSendByte(u8 u8Instance, u8 u8Byte);
STD_tenuResult MCU_UART_enuSendBuffer(u8 u8Instance, const u8 *pu8Data, u16 u16Length);
STD_tenuResult MCU_UART_enuSendBufferBlocking(u8 u8Instance, const u8 *pu8Data, u16 u16Length);
STD_tBool MCU_UART_bIsTxBusy(u8 u8Instance);
/* ------------------------------------------------------------------------ */
/* RX PUBLIC API */
/* ------------------------------------------------------------------------ */
/**
* @brief Read one byte from the RX ring buffer (non-blocking).
*
* The ring buffer is filled in the background by ISR or DMA.
* Returns immediately STD_OK with the byte, or STD_NOK if empty.
*
* @param u8Instance UART instance index.
* @param pu8Byte Pointer to store the received byte.
* @return STD_OK byte read,
* STD_NULL_POINTER_ERROR if pu8Byte is NULL,
* STD_NOK if ring buffer is empty.
*/
STD_tenuResult MCU_UART_enuReadByte(u8 u8Instance, u8 *pu8Byte);
/**
* @brief Read up to u16MaxLength bytes from the RX ring buffer (non-blocking).
*
* Copies as many bytes as are currently available (up to u16MaxLength)
* into pu8Data. Reports the actual number of bytes read via pu16Read.
* Returns immediately even if fewer than u16MaxLength bytes are available.
*
* @param u8Instance UART instance index.
* @param pu8Data Pointer to output buffer.
* @param u16MaxLength Maximum bytes to read.
* @param pu16Read Pointer to store actual bytes read. Must not be NULL.
* @return STD_OK at least one byte read,
* STD_NULL_POINTER_ERROR if pu8Data or pu16Read is NULL,
* STD_NOK if ring buffer is empty (zero bytes read).
*/
STD_tenuResult MCU_UART_enuReadBuffer(u8 u8Instance, u8 *pu8Data, u16 u16MaxLength, u16 *pu16Read);
/**
* @brief Check if the RX ring buffer has data.
*
* @param u8Instance UART instance index.
* @return STD_TRUE if at least one byte available, STD_FALSE if empty.
*/
STD_tBool MCU_UART_bIsRxDataAvailable(u8 u8Instance);
#endif /* MCU_UART_H */

421
prg/MCU_UART_prg.c Normal file
View File

@ -0,0 +1,421 @@
/******************************************************************************
* File: MCU_UART_prg.c
* Component: MCU_UART
* Description: TX: blocking + non-blocking (DMA or ISR) with callback.
* RX: ISR or DMA fills ring buffer in background. ReadByte
* and ReadBuffer read from the buffer non-blocking.
*
* Layer: MCU (hardware abstraction)
*****************************************************************************/
#include "MCU_UART.h"
#include "MCU_UART_priv.h"
#include "MCU_UART_cfg.h"
#include "hardware/uart.h"
#include "hardware/gpio.h"
#include "hardware/dma.h"
#include "hardware/irq.h"
/* ------------------------------------------------------------------------ */
/* INSTANCE LOOKUP TABLE */
/* ------------------------------------------------------------------------ */
static uart_inst_t * const apstrInstances[] = { uart0, uart1 };
/* ------------------------------------------------------------------------ */
/* RUNTIME STATE */
/* ------------------------------------------------------------------------ */
static MCU_UART_tstrControl strControl;
/* ------------------------------------------------------------------------ */
/* INTERNAL HELPERS */
/* ------------------------------------------------------------------------ */
static void vCallTxCallback(u8 u8Instance)
{
STD_tpfCallbackFunc pfCbLoc = MCU_UART_astrConfig[u8Instance].pfTxCompleteCallback;
if (pfCbLoc != STD_NULL)
{
pfCbLoc();
}
}
/** @brief Get current RX head. DMA mode derives from hw write pointer. */
static u16 u16GetRxHead(u8 u8Instance)
{
u16 u16HeadLoc;
if (MCU_UART_astrConfig[u8Instance].enuRxAsyncMode == MCU_UART_ASYNC_DMA)
{
s8 s8ChLoc = strControl.as8RxDmaChannel[u8Instance];
u32 u32WrLoc = (u32)dma_channel_hw_addr((u32)s8ChLoc)->write_addr;
u32 u32BaseLoc = (u32)(&strControl.aau8RxBuffer[u8Instance][0]);
u16HeadLoc = (u16)((u32WrLoc - u32BaseLoc) & MCU_UART_RX_BUFFER_MASK);
}
else
{
u16HeadLoc = strControl.au16RxHead[u8Instance];
}
return u16HeadLoc;
}
/* ------------------------------------------------------------------------ */
/* TX DMA IRQ HANDLER (INSTANCE 0) */
/* ------------------------------------------------------------------------ */
static void vTxDmaIrqHandler0(void)
{
s8 s8ChLoc = strControl.as8TxDmaChannel[MCU_UART_INSTANCE_0];
u32 u32StatusLoc = dma_channel_get_irq0_status((u32)s8ChLoc);
if (u32StatusLoc != 0U)
{
dma_irqn_acknowledge_channel(0, (u32)s8ChLoc);
strControl.abTxBusy[MCU_UART_INSTANCE_0] = STD_FALSE;
vCallTxCallback((u8)MCU_UART_INSTANCE_0);
}
}
/* ------------------------------------------------------------------------ */
/* UART ISR HANDLER (RX + TX) */
/* ------------------------------------------------------------------------ */
static void vUartIsrHandler(u8 u8Instance)
{
uart_inst_t *pstrUartLoc = apstrInstances[u8Instance];
/* --- RX: drain FIFO into ring buffer --- */
STD_tBool bReadableLoc = (uart_is_readable(pstrUartLoc) != 0) ? STD_TRUE : STD_FALSE;
while (bReadableLoc == STD_TRUE)
{
u8 u8ByteLoc = (u8)uart_getc(pstrUartLoc);
u16 u16HeadLoc = strControl.au16RxHead[u8Instance];
strControl.aau8RxBuffer[u8Instance][u16HeadLoc] = u8ByteLoc;
strControl.au16RxHead[u8Instance] = (u16HeadLoc + 1U) & MCU_UART_RX_BUFFER_MASK;
bReadableLoc = (uart_is_readable(pstrUartLoc) != 0) ? STD_TRUE : STD_FALSE;
}
/* --- TX: fill FIFO from buffer (if TX ISR active) --- */
STD_tBool bTxBusyLoc = strControl.abTxBusy[u8Instance];
if (bTxBusyLoc == STD_TRUE)
{
STD_tBool bFifoReady = (uart_is_writable(pstrUartLoc) != 0) ? STD_TRUE : STD_FALSE;
STD_tBool bDataLeft = (strControl.au16TxIndex[u8Instance] < strControl.au16TxLength[u8Instance]) ? STD_TRUE : STD_FALSE;
while ((bFifoReady == STD_TRUE) && (bDataLeft == STD_TRUE))
{
uart_putc_raw(pstrUartLoc,
strControl.apu8TxBuffer[u8Instance][strControl.au16TxIndex[u8Instance]]);
strControl.au16TxIndex[u8Instance]++;
bFifoReady = (uart_is_writable(pstrUartLoc) != 0) ? STD_TRUE : STD_FALSE;
bDataLeft = (strControl.au16TxIndex[u8Instance] < strControl.au16TxLength[u8Instance]) ? STD_TRUE : STD_FALSE;
}
if (bDataLeft == STD_FALSE)
{
uart_set_irqs_enabled(pstrUartLoc, false, true);
strControl.abTxBusy[u8Instance] = STD_FALSE;
vCallTxCallback(u8Instance);
}
}
}
static void vUart0IrqHandler(void)
{
vUartIsrHandler((u8)MCU_UART_INSTANCE_0);
}
/* ========================================================================= */
/* INIT */
/* ========================================================================= */
STD_tenuResult MCU_UART_enuInit(void)
{
STD_tenuResult enuResultLoc = STD_OK;
u8 u8IndexLoc;
for (u8IndexLoc = 0U; u8IndexLoc < (u8)MCU_UART_NUM_INSTANCES; u8IndexLoc++)
{
strControl.apu8TxBuffer[u8IndexLoc] = STD_NULL;
strControl.au16TxLength[u8IndexLoc] = 0U;
strControl.au16TxIndex[u8IndexLoc] = 0U;
strControl.abTxBusy[u8IndexLoc] = STD_FALSE;
strControl.as8TxDmaChannel[u8IndexLoc] = -1;
strControl.au16RxHead[u8IndexLoc] = 0U;
strControl.au16RxTail[u8IndexLoc] = 0U;
strControl.as8RxDmaChannel[u8IndexLoc] = -1;
}
for (u8IndexLoc = 0U; u8IndexLoc < (u8)MCU_UART_NUM_INSTANCES; u8IndexLoc++)
{
const MCU_UART_tstrConfig *pstrCfgLoc = &MCU_UART_astrConfig[u8IndexLoc];
uart_inst_t *pstrUartLoc = apstrInstances[u8IndexLoc];
uart_init(pstrUartLoc, pstrCfgLoc->u32BaudRate);
gpio_set_function(pstrCfgLoc->u8TxPin, GPIO_FUNC_UART);
gpio_set_function(pstrCfgLoc->u8RxPin, GPIO_FUNC_UART);
uart_set_format(pstrUartLoc,
pstrCfgLoc->enuDataBits,
pstrCfgLoc->enuStopBits,
pstrCfgLoc->enuParity);
/* TX async setup */
if (pstrCfgLoc->enuTxAsyncMode == MCU_UART_ASYNC_DMA)
{
s8 s8ChLoc = (s8)dma_claim_unused_channel(true);
strControl.as8TxDmaChannel[u8IndexLoc] = s8ChLoc;
dma_channel_set_irq0_enabled((u32)s8ChLoc, true);
if (u8IndexLoc == (u8)MCU_UART_INSTANCE_0)
{
irq_set_exclusive_handler(DMA_IRQ_0, vTxDmaIrqHandler0);
irq_set_enabled(DMA_IRQ_0, true);
}
}
/* RX async setup */
if (pstrCfgLoc->enuRxAsyncMode == MCU_UART_ASYNC_DMA)
{
s8 s8RxChLoc = (s8)dma_claim_unused_channel(true);
strControl.as8RxDmaChannel[u8IndexLoc] = s8RxChLoc;
dma_channel_config strRxCfgLoc = dma_channel_get_default_config((u32)s8RxChLoc);
channel_config_set_transfer_data_size(&strRxCfgLoc, DMA_SIZE_8);
channel_config_set_read_increment(&strRxCfgLoc, false);
channel_config_set_write_increment(&strRxCfgLoc, true);
channel_config_set_dreq(&strRxCfgLoc, uart_get_dreq(pstrUartLoc, false));
channel_config_set_ring(&strRxCfgLoc, true, MCU_UART_RX_BUFFER_SIZE_BITS);
dma_channel_configure(
(u32)s8RxChLoc,
&strRxCfgLoc,
&strControl.aau8RxBuffer[u8IndexLoc][0],
&uart_get_hw(pstrUartLoc)->dr,
0xFFFFFFFFU,
true
);
}
else
{
if (u8IndexLoc == (u8)MCU_UART_INSTANCE_0)
{
irq_set_exclusive_handler(UART0_IRQ, vUart0IrqHandler);
irq_set_enabled(UART0_IRQ, true);
}
uart_set_irqs_enabled(pstrUartLoc, false, true);
}
}
return enuResultLoc;
}
/* ========================================================================= */
/* SEND BYTE (BLOCKING) */
/* ========================================================================= */
STD_tenuResult MCU_UART_enuSendByte(u8 u8Instance, u8 u8Byte)
{
STD_tenuResult enuResultLoc = STD_OK;
uart_putc_raw(apstrInstances[u8Instance], u8Byte);
return enuResultLoc;
}
/* ========================================================================= */
/* SEND BUFFER (NON-BLOCKING) */
/* ========================================================================= */
STD_tenuResult MCU_UART_enuSendBuffer(u8 u8Instance, const u8 *pu8Data, u16 u16Length)
{
STD_tenuResult enuResultLoc = STD_OK;
STD_tBool bBusyLoc = strControl.abTxBusy[u8Instance];
MCU_UART_tenuAsyncMode enuModeLoc = MCU_UART_astrConfig[u8Instance].enuTxAsyncMode;
if (pu8Data == STD_NULL)
{
enuResultLoc = STD_NULL_POINTER_ERROR;
}
else if (bBusyLoc == STD_TRUE)
{
enuResultLoc = STD_NOK;
}
else
{
strControl.apu8TxBuffer[u8Instance] = pu8Data;
strControl.au16TxLength[u8Instance] = u16Length;
strControl.au16TxIndex[u8Instance] = 0U;
strControl.abTxBusy[u8Instance] = STD_TRUE;
if (enuModeLoc == MCU_UART_ASYNC_DMA)
{
s8 s8ChLoc = strControl.as8TxDmaChannel[u8Instance];
uart_inst_t *pstrUartLoc = apstrInstances[u8Instance];
dma_channel_config strCfgLoc = dma_channel_get_default_config((u32)s8ChLoc);
channel_config_set_transfer_data_size(&strCfgLoc, DMA_SIZE_8);
channel_config_set_read_increment(&strCfgLoc, true);
channel_config_set_write_increment(&strCfgLoc, false);
channel_config_set_dreq(&strCfgLoc, uart_get_dreq(pstrUartLoc, true));
dma_channel_configure(
(u32)s8ChLoc, &strCfgLoc,
&uart_get_hw(pstrUartLoc)->dr, pu8Data, u16Length, true);
}
else
{
uart_inst_t *pstrUartLoc = apstrInstances[u8Instance];
STD_tBool bFifoReady = (uart_is_writable(pstrUartLoc) != 0) ? STD_TRUE : STD_FALSE;
STD_tBool bDataLeft = (strControl.au16TxIndex[u8Instance] < u16Length) ? STD_TRUE : STD_FALSE;
while ((bFifoReady == STD_TRUE) && (bDataLeft == STD_TRUE))
{
uart_putc_raw(pstrUartLoc, pu8Data[strControl.au16TxIndex[u8Instance]]);
strControl.au16TxIndex[u8Instance]++;
bFifoReady = (uart_is_writable(pstrUartLoc) != 0) ? STD_TRUE : STD_FALSE;
bDataLeft = (strControl.au16TxIndex[u8Instance] < u16Length) ? STD_TRUE : STD_FALSE;
}
if (bDataLeft == STD_FALSE)
{
strControl.abTxBusy[u8Instance] = STD_FALSE;
vCallTxCallback(u8Instance);
}
else
{
uart_set_irqs_enabled(pstrUartLoc, true, true);
}
}
}
return enuResultLoc;
}
/* ========================================================================= */
/* SEND BUFFER (BLOCKING) */
/* ========================================================================= */
STD_tenuResult MCU_UART_enuSendBufferBlocking(u8 u8Instance, const u8 *pu8Data, u16 u16Length)
{
STD_tenuResult enuResultLoc = STD_OK;
u16 u16IndexLoc;
if (pu8Data == STD_NULL)
{
enuResultLoc = STD_NULL_POINTER_ERROR;
}
else
{
for (u16IndexLoc = 0U; u16IndexLoc < u16Length; u16IndexLoc++)
{
uart_putc_raw(apstrInstances[u8Instance], pu8Data[u16IndexLoc]);
}
vCallTxCallback(u8Instance);
}
return enuResultLoc;
}
/* ========================================================================= */
/* TX BUSY CHECK */
/* ========================================================================= */
STD_tBool MCU_UART_bIsTxBusy(u8 u8Instance)
{
return strControl.abTxBusy[u8Instance];
}
/* ========================================================================= */
/* READ BYTE (NON-BLOCKING) */
/* ========================================================================= */
STD_tenuResult MCU_UART_enuReadByte(u8 u8Instance, u8 *pu8Byte)
{
STD_tenuResult enuResultLoc = STD_OK;
if (pu8Byte == STD_NULL)
{
enuResultLoc = STD_NULL_POINTER_ERROR;
}
else
{
u16 u16HeadLoc = u16GetRxHead(u8Instance);
u16 u16TailLoc = strControl.au16RxTail[u8Instance];
if (u16HeadLoc == u16TailLoc)
{
enuResultLoc = STD_NOK;
}
else
{
*pu8Byte = strControl.aau8RxBuffer[u8Instance][u16TailLoc];
strControl.au16RxTail[u8Instance] = (u16TailLoc + 1U) & MCU_UART_RX_BUFFER_MASK;
}
}
return enuResultLoc;
}
/* ========================================================================= */
/* READ BUFFER (NON-BLOCKING) */
/* ========================================================================= */
STD_tenuResult MCU_UART_enuReadBuffer(u8 u8Instance, u8 *pu8Data, u16 u16MaxLength, u16 *pu16Read)
{
STD_tenuResult enuResultLoc = STD_OK;
if ((pu8Data == STD_NULL) || (pu16Read == STD_NULL))
{
enuResultLoc = STD_NULL_POINTER_ERROR;
}
else
{
u16 u16HeadLoc = u16GetRxHead(u8Instance);
u16 u16TailLoc = strControl.au16RxTail[u8Instance];
u16 u16CountLoc = 0U;
while ((u16HeadLoc != u16TailLoc) && (u16CountLoc < u16MaxLength))
{
pu8Data[u16CountLoc] = strControl.aau8RxBuffer[u8Instance][u16TailLoc];
u16TailLoc = (u16TailLoc + 1U) & MCU_UART_RX_BUFFER_MASK;
u16CountLoc++;
}
strControl.au16RxTail[u8Instance] = u16TailLoc;
*pu16Read = u16CountLoc;
if (u16CountLoc == 0U)
{
enuResultLoc = STD_NOK;
}
}
return enuResultLoc;
}
/* ========================================================================= */
/* RX DATA AVAILABLE CHECK */
/* ========================================================================= */
STD_tBool MCU_UART_bIsRxDataAvailable(u8 u8Instance)
{
STD_tBool bResultLoc = STD_FALSE;
u16 u16HeadLoc = u16GetRxHead(u8Instance);
u16 u16TailLoc = strControl.au16RxTail[u8Instance];
if (u16HeadLoc != u16TailLoc)
{
bResultLoc = STD_TRUE;
}
return bResultLoc;
}

34
prg/MCU_UART_priv.h Normal file
View File

@ -0,0 +1,34 @@
/******************************************************************************
* File: MCU_UART_priv.h
* Component: MCU_UART
* Description: Private header control struct and extern config array.
*
* Layer: MCU (hardware abstraction) - internal use only
*****************************************************************************/
#ifndef MCU_UART_PRIV_H
#define MCU_UART_PRIV_H
#include "MCU_UART.h"
#include "MCU_UART_cfg.h"
extern const MCU_UART_tstrConfig MCU_UART_astrConfig[MCU_UART_NUM_INSTANCES];
typedef struct
{
/* TX state */
const u8 *apu8TxBuffer[MCU_UART_NUM_INSTANCES];
u16 au16TxLength[MCU_UART_NUM_INSTANCES];
u16 au16TxIndex[MCU_UART_NUM_INSTANCES];
STD_tBool abTxBusy[MCU_UART_NUM_INSTANCES];
s8 as8TxDmaChannel[MCU_UART_NUM_INSTANCES];
/* RX ring buffer — filled by ISR or DMA in the background */
u8 aau8RxBuffer[MCU_UART_NUM_INSTANCES][MCU_UART_RX_BUFFER_SIZE]
__attribute__((aligned(MCU_UART_RX_BUFFER_SIZE)));
u16 au16RxHead[MCU_UART_NUM_INSTANCES];
u16 au16RxTail[MCU_UART_NUM_INSTANCES];
s8 as8RxDmaChannel[MCU_UART_NUM_INSTANCES];
} MCU_UART_tstrControl;
#endif /* MCU_UART_PRIV_H */