421 lines
15 KiB
C
421 lines
15 KiB
C
/******************************************************************************
|
|
* 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;
|
|
} |