/****************************************************************************** * 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; }