mcu_uart/prg/MCU_UART_prg.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;
}