From 551bc8a1f54f32b980b6702f482a127596ef1525 Mon Sep 17 00:00:00 2001 From: Michal Paszta Date: Mon, 9 Sep 2019 08:03:26 +0300 Subject: [PATCH] Enable async serial for K66F Added DMA definitions and usage for UART, based on K64F's code. --- .../TARGET_K66F/dma_reqs.h | 62 +++ .../TARGET_K66F/serial_api.c | 504 ++++++++++++++++-- targets/targets.json | 1 + 3 files changed, 524 insertions(+), 43 deletions(-) create mode 100644 targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/TARGET_K66F/dma_reqs.h diff --git a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/TARGET_K66F/dma_reqs.h b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/TARGET_K66F/dma_reqs.h new file mode 100644 index 00000000000..b48de64f366 --- /dev/null +++ b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/TARGET_K66F/dma_reqs.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2016, Freescale Semiconductor, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * o Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * o Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * o Neither the name of Freescale Semiconductor, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FSL_DMA_REQS_H_ +#define _FSL_DMA_REQS_H_ + +#include "fsl_common.h" + +/* Array for DSPI DMA TX requests */ +#define SPI_DMA_TX_REQUEST_NUMBERS \ + { \ + kDmaRequestMux0SPI0Tx, kDmaRequestMux0SPI1Tx \ + } + +/* Array for DSPI DMA RX requests */ +#define SPI_DMA_RX_REQUEST_NUMBERS \ + { \ + kDmaRequestMux0SPI0Rx, kDmaRequestMux0SPI1Rx \ + } + +/* Array for UART DMA TX requests */ +#define UART_DMA_TX_REQUEST_NUMBERS \ + { \ + kDmaRequestMux0UART0Tx, kDmaRequestMux0UART1Tx, kDmaRequestMux0UART2Tx, \ + kDmaRequestMux0UART3Tx, kDmaRequestMux0UART4 \ + } + +/* Array for UART DMA RX requests */ +#define UART_DMA_RX_REQUEST_NUMBERS \ + { \ + kDmaRequestMux0UART0Rx, kDmaRequestMux0UART1Rx, kDmaRequestMux0UART2Rx, \ + kDmaRequestMux0UART3Rx, kDmaRequestMux0UART4 \ + } + +#endif /* _FSL_DMA_REQS_H_ */ diff --git a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/TARGET_K66F/serial_api.c b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/TARGET_K66F/serial_api.c index d1435ab40e6..0981d560584 100644 --- a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/TARGET_K66F/serial_api.c +++ b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/TARGET_K66F/serial_api.c @@ -24,10 +24,12 @@ #include #include "cmsis.h" +#include "mbed_power_mgmt.h" #include "pinmap.h" #include "fsl_uart.h" #include "peripheral_clock_defines.h" #include "PeripheralPins.h" +#include "dma_reqs.h" #include "fsl_clock_config.h" static uint32_t serial_irq_ids[FSL_FEATURE_SOC_UART_COUNT] = {0}; @@ -37,6 +39,12 @@ static UART_Type *const uart_addrs[] = UART_BASE_PTRS; /* Array of UART bus clock frequencies */ static clock_name_t const uart_clocks[] = UART_CLOCK_FREQS; +/* UART transfer states */ +#define kUART_TxIdle 0 +#define kUART_TxBusy 1 +#define kUART_RxIdle 2 +#define kUART_RxBusy 3 + int stdio_uart_inited = 0; serial_t stdio_uart; @@ -44,8 +52,8 @@ void serial_init(serial_t *obj, PinName tx, PinName rx) { uint32_t uart_tx = pinmap_peripheral(tx, PinMap_UART_TX); uint32_t uart_rx = pinmap_peripheral(rx, PinMap_UART_RX); - obj->index = pinmap_merge(uart_tx, uart_rx); - MBED_ASSERT((int)obj->index != NC); + obj->serial.index = pinmap_merge(uart_tx, uart_rx); + MBED_ASSERT((int)obj->serial.index != NC); uart_config_t config; @@ -54,40 +62,47 @@ void serial_init(serial_t *obj, PinName tx, PinName rx) config.enableTx = false; config.enableRx = false; - UART_Init(uart_addrs[obj->index], &config, CLOCK_GetFreq(uart_clocks[obj->index])); + UART_Init(uart_addrs[obj->serial.index], &config, CLOCK_GetFreq(uart_clocks[obj->serial.index])); pinmap_pinout(tx, PinMap_UART_TX); pinmap_pinout(rx, PinMap_UART_RX); if (tx != NC) { - UART_EnableTx(uart_addrs[obj->index], true); + UART_EnableTx(uart_addrs[obj->serial.index], true); pin_mode(tx, PullUp); } if (rx != NC) { - UART_EnableRx(uart_addrs[obj->index], true); + UART_EnableRx(uart_addrs[obj->serial.index], true); pin_mode(rx, PullUp); } - if (obj->index == STDIO_UART) { + if (obj->serial.index == STDIO_UART) { stdio_uart_inited = 1; memcpy(&stdio_uart, obj, sizeof(serial_t)); } + + obj->serial.uartDmaRx.dmaUsageState = DMA_USAGE_OPPORTUNISTIC;; + obj->serial.txstate = kUART_TxIdle; + obj->serial.rxstate = kUART_RxIdle; + + /* Zero the handle. */ + memset(&(obj->serial.uart_transfer_handle), 0, sizeof(obj->serial.uart_transfer_handle)); } void serial_free(serial_t *obj) { - UART_Deinit(uart_addrs[obj->index]); - serial_irq_ids[obj->index] = 0; + UART_Deinit(uart_addrs[obj->serial.index]); + serial_irq_ids[obj->serial.index] = 0; } void serial_baud(serial_t *obj, int baudrate) { - UART_SetBaudRate(uart_addrs[obj->index], (uint32_t)baudrate, CLOCK_GetFreq(uart_clocks[obj->index])); + UART_SetBaudRate(uart_addrs[obj->serial.index], (uint32_t)baudrate, CLOCK_GetFreq(uart_clocks[obj->serial.index])); } void serial_format(serial_t *obj, int data_bits, SerialParity parity, int stop_bits) { - UART_Type *base = uart_addrs[obj->index]; + UART_Type *base = uart_addrs[obj->serial.index]; uint8_t temp; /* Set bit count and parity mode. */ temp = base->C1 & ~(UART_C1_PE_MASK | UART_C1_PT_MASK | UART_C1_M_MASK); @@ -163,10 +178,11 @@ void uart4_irq() uint32_t status_flags = UART4->S1; uart_irq((status_flags & UART_S1_TDRE_MASK), (status_flags & UART_S1_RDRF_MASK), 4); } + void serial_irq_handler(serial_t *obj, uart_irq_handler handler, uint32_t id) { irq_handler = handler; - serial_irq_ids[obj->index] = id; + serial_irq_ids[obj->serial.index] = id; } void serial_irq_set(serial_t *obj, SerialIrq irq, uint32_t enable) @@ -174,7 +190,7 @@ void serial_irq_set(serial_t *obj, SerialIrq irq, uint32_t enable) IRQn_Type uart_irqs[] = UART_RX_TX_IRQS; uint32_t vector = 0; - switch (obj->index) { + switch (obj->serial.index) { case 0: vector = (uint32_t)&uart0_irq; break; @@ -197,42 +213,42 @@ void serial_irq_set(serial_t *obj, SerialIrq irq, uint32_t enable) if (enable) { switch (irq) { case RxIrq: - UART_EnableInterrupts(uart_addrs[obj->index], kUART_RxDataRegFullInterruptEnable); + UART_EnableInterrupts(uart_addrs[obj->serial.index], kUART_RxDataRegFullInterruptEnable); break; case TxIrq: - UART_EnableInterrupts(uart_addrs[obj->index], kUART_TxDataRegEmptyInterruptEnable); + UART_EnableInterrupts(uart_addrs[obj->serial.index], kUART_TxDataRegEmptyInterruptEnable); break; default: break; } - NVIC_SetVector(uart_irqs[obj->index], vector); - NVIC_EnableIRQ(uart_irqs[obj->index]); + NVIC_SetVector(uart_irqs[obj->serial.index], vector); + NVIC_EnableIRQ(uart_irqs[obj->serial.index]); } else { // disable int all_disabled = 0; SerialIrq other_irq = (irq == RxIrq) ? (TxIrq) : (RxIrq); switch (irq) { case RxIrq: - UART_DisableInterrupts(uart_addrs[obj->index], kUART_RxDataRegFullInterruptEnable); + UART_DisableInterrupts(uart_addrs[obj->serial.index], kUART_RxDataRegFullInterruptEnable); break; case TxIrq: - UART_DisableInterrupts(uart_addrs[obj->index], kUART_TxDataRegEmptyInterruptEnable); + UART_DisableInterrupts(uart_addrs[obj->serial.index], kUART_TxDataRegEmptyInterruptEnable); break; default: break; } switch (other_irq) { case RxIrq: - all_disabled = ((UART_GetEnabledInterrupts(uart_addrs[obj->index]) & kUART_RxDataRegFullInterruptEnable) == 0); + all_disabled = ((UART_GetEnabledInterrupts(uart_addrs[obj->serial.index]) & kUART_RxDataRegFullInterruptEnable) == 0); break; case TxIrq: - all_disabled = ((UART_GetEnabledInterrupts(uart_addrs[obj->index]) & kUART_TxDataRegEmptyInterruptEnable) == 0); + all_disabled = ((UART_GetEnabledInterrupts(uart_addrs[obj->serial.index]) & kUART_TxDataRegEmptyInterruptEnable) == 0); break; default: break; } if (all_disabled) - NVIC_DisableIRQ(uart_irqs[obj->index]); + NVIC_DisableIRQ(uart_irqs[obj->serial.index]); } } @@ -240,7 +256,7 @@ int serial_getc(serial_t *obj) { while (!serial_readable(obj)); uint8_t data; - data = UART_ReadByte(uart_addrs[obj->index]); + data = UART_ReadByte(uart_addrs[obj->serial.index]); return data; } @@ -248,22 +264,22 @@ int serial_getc(serial_t *obj) void serial_putc(serial_t *obj, int c) { while (!serial_writable(obj)); - UART_WriteByte(uart_addrs[obj->index], (uint8_t)c); + UART_WriteByte(uart_addrs[obj->serial.index], (uint8_t)c); } int serial_readable(serial_t *obj) { - uint32_t status_flags = UART_GetStatusFlags(uart_addrs[obj->index]); + uint32_t status_flags = UART_GetStatusFlags(uart_addrs[obj->serial.index]); if (status_flags & kUART_RxOverrunFlag) - UART_ClearStatusFlags(uart_addrs[obj->index], kUART_RxOverrunFlag); + UART_ClearStatusFlags(uart_addrs[obj->serial.index], kUART_RxOverrunFlag); return (status_flags & kUART_RxDataRegFullFlag); } int serial_writable(serial_t *obj) { - uint32_t status_flags = UART_GetStatusFlags(uart_addrs[obj->index]); + uint32_t status_flags = UART_GetStatusFlags(uart_addrs[obj->serial.index]); if (status_flags & kUART_RxOverrunFlag) - UART_ClearStatusFlags(uart_addrs[obj->index], kUART_RxOverrunFlag); + UART_ClearStatusFlags(uart_addrs[obj->serial.index], kUART_RxOverrunFlag); return (status_flags & kUART_TxDataRegEmptyFlag); } @@ -278,14 +294,36 @@ void serial_pinout_tx(PinName tx) void serial_break_set(serial_t *obj) { - uart_addrs[obj->index]->C2 |= UART_C2_SBK_MASK; + uart_addrs[obj->serial.index]->C2 |= UART_C2_SBK_MASK; } void serial_break_clear(serial_t *obj) { - uart_addrs[obj->index]->C2 &= ~UART_C2_SBK_MASK; + uart_addrs[obj->serial.index]->C2 &= ~UART_C2_SBK_MASK; +} + +const PinMap *serial_tx_pinmap() +{ + return PinMap_UART_TX; +} + +const PinMap *serial_rx_pinmap() +{ + return PinMap_UART_RX; +} + +const PinMap *serial_cts_pinmap() +{ + return PinMap_UART_CTS; +} + +const PinMap *serial_rts_pinmap() +{ + return PinMap_UART_RTS; } +#if DEVICE_SERIAL_FC + /* * Only hardware flow control is implemented in this API. */ @@ -294,24 +332,24 @@ void serial_set_flow_control(serial_t *obj, FlowControl type, PinName rxflow, Pi switch(type) { case FlowControlRTS: pinmap_pinout(rxflow, PinMap_UART_RTS); - uart_addrs[obj->index]->MODEM &= ~UART_MODEM_TXCTSE_MASK; - uart_addrs[obj->index]->MODEM |= UART_MODEM_RXRTSE_MASK; + uart_addrs[obj->serial.index]->MODEM &= ~UART_MODEM_TXCTSE_MASK; + uart_addrs[obj->serial.index]->MODEM |= UART_MODEM_RXRTSE_MASK; break; case FlowControlCTS: pinmap_pinout(txflow, PinMap_UART_CTS); - uart_addrs[obj->index]->MODEM &= ~UART_MODEM_RXRTSE_MASK; - uart_addrs[obj->index]->MODEM |= UART_MODEM_TXCTSE_MASK; + uart_addrs[obj->serial.index]->MODEM &= ~UART_MODEM_RXRTSE_MASK; + uart_addrs[obj->serial.index]->MODEM |= UART_MODEM_TXCTSE_MASK; break; case FlowControlRTSCTS: pinmap_pinout(rxflow, PinMap_UART_RTS); pinmap_pinout(txflow, PinMap_UART_CTS); - uart_addrs[obj->index]->MODEM |= UART_MODEM_TXCTSE_MASK | UART_MODEM_RXRTSE_MASK; + uart_addrs[obj->serial.index]->MODEM |= UART_MODEM_TXCTSE_MASK | UART_MODEM_RXRTSE_MASK; break; case FlowControlNone: - uart_addrs[obj->index]->MODEM &= ~(UART_MODEM_TXCTSE_MASK | UART_MODEM_RXRTSE_MASK); + uart_addrs[obj->serial.index]->MODEM &= ~(UART_MODEM_TXCTSE_MASK | UART_MODEM_RXRTSE_MASK); break; default: @@ -319,24 +357,404 @@ void serial_set_flow_control(serial_t *obj, FlowControl type, PinName rxflow, Pi } } -const PinMap *serial_tx_pinmap() +#endif + +static void serial_send_asynch(serial_t *obj) { - return PinMap_UART_TX; + uart_transfer_t sendXfer; + + /*Setup send transfer*/ + sendXfer.data = obj->tx_buff.buffer; + sendXfer.dataSize = obj->tx_buff.length; + + if (obj->serial.uartDmaRx.dmaUsageState == DMA_USAGE_ALLOCATED || + obj->serial.uartDmaRx.dmaUsageState == DMA_USAGE_TEMPORARY_ALLOCATED) { + UART_SendEDMA(uart_addrs[obj->serial.index], &obj->serial.uart_dma_handle, &sendXfer); + } else { + UART_TransferSendNonBlocking(uart_addrs[obj->serial.index], &obj->serial.uart_transfer_handle, &sendXfer); + } } -const PinMap *serial_rx_pinmap() +static void serial_receive_asynch(serial_t *obj) { - return PinMap_UART_RX; + uart_transfer_t receiveXfer; + + /*Setup send transfer*/ + receiveXfer.data = obj->rx_buff.buffer; + receiveXfer.dataSize = obj->rx_buff.length; + + if (obj->serial.uartDmaRx.dmaUsageState == DMA_USAGE_ALLOCATED || + obj->serial.uartDmaRx.dmaUsageState == DMA_USAGE_TEMPORARY_ALLOCATED) { + UART_ReceiveEDMA(uart_addrs[obj->serial.index], &obj->serial.uart_dma_handle, &receiveXfer); + } else { + UART_TransferReceiveNonBlocking(uart_addrs[obj->serial.index], &obj->serial.uart_transfer_handle, &receiveXfer, NULL); + } } -const PinMap *serial_cts_pinmap() +static bool serial_allocate_dma(serial_t *obj, uint32_t handler) { - return PinMap_UART_CTS; + dma_request_source_t dma_rx_requests[] = UART_DMA_RX_REQUEST_NUMBERS; + dma_request_source_t dma_tx_requests[] = UART_DMA_TX_REQUEST_NUMBERS; + edma_config_t userConfig; + + /* Allocate the UART RX DMA channel */ + obj->serial.uartDmaRx.dmaChannel = dma_channel_allocate(dma_rx_requests[obj->serial.index]); + if (obj->serial.uartDmaRx.dmaChannel == DMA_ERROR_OUT_OF_CHANNELS) { + return false; + } + + /* Allocate the UART TX DMA channel */ + obj->serial.uartDmaTx.dmaChannel = dma_channel_allocate(dma_tx_requests[obj->serial.index]); + if (obj->serial.uartDmaTx.dmaChannel == DMA_ERROR_OUT_OF_CHANNELS) { + dma_channel_free(obj->serial.uartDmaRx.dmaChannel); + return false; + } + + /* EDMA init*/ + /* + * userConfig.enableRoundRobinArbitration = false; + * userConfig.enableHaltOnError = true; + * userConfig.enableContinuousLinkMode = false; + * userConfig.enableDebugMode = false; + */ + EDMA_GetDefaultConfig(&userConfig); + + EDMA_Init(DMA0, &userConfig); + + memset(&(obj->serial.uartDmaTx.handle), 0, sizeof(obj->serial.uartDmaTx.handle)); + memset(&(obj->serial.uartDmaRx.handle), 0, sizeof(obj->serial.uartDmaRx.handle)); + + EDMA_CreateHandle(&(obj->serial.uartDmaRx.handle), DMA0, obj->serial.uartDmaRx.dmaChannel); + EDMA_CreateHandle(&(obj->serial.uartDmaTx.handle), DMA0, obj->serial.uartDmaTx.dmaChannel); + + UART_TransferCreateHandleEDMA(uart_addrs[obj->serial.index], &obj->serial.uart_dma_handle, (uart_edma_transfer_callback_t)handler, + NULL, &obj->serial.uartDmaTx.handle, &obj->serial.uartDmaRx.handle); + + return true; } -const PinMap *serial_rts_pinmap() +void serial_enable_dma(serial_t *obj, uint32_t handler, DMAUsage state) { - return PinMap_UART_RTS; + dma_init(); + + if (state == DMA_USAGE_ALWAYS && obj->serial.uartDmaRx.dmaUsageState != DMA_USAGE_ALLOCATED) { + /* Try to allocate channels */ + if (serial_allocate_dma(obj, handler)) { + obj->serial.uartDmaRx.dmaUsageState = DMA_USAGE_ALLOCATED; + } else { + obj->serial.uartDmaRx.dmaUsageState = state; + } + } else if (state == DMA_USAGE_OPPORTUNISTIC) { + if (obj->serial.uartDmaRx.dmaUsageState == DMA_USAGE_ALLOCATED) { + /* Channels have already been allocated previously by an ALWAYS state, so after this transfer, we will release them */ + obj->serial.uartDmaRx.dmaUsageState = DMA_USAGE_TEMPORARY_ALLOCATED; + } else { + /* Try to allocate channels */ + if (serial_allocate_dma(obj, handler)) { + obj->serial.uartDmaRx.dmaUsageState = DMA_USAGE_TEMPORARY_ALLOCATED; + } else { + obj->serial.uartDmaRx.dmaUsageState = state; + } + } + } else if (state == DMA_USAGE_NEVER) { + /* If channels are allocated, get rid of them */ + if (obj->serial.uartDmaRx.dmaUsageState == DMA_USAGE_ALLOCATED) { + dma_channel_free(obj->serial.uartDmaRx.dmaChannel); + dma_channel_free(obj->serial.uartDmaRx.dmaChannel); + } + obj->serial.uartDmaRx.dmaUsageState = DMA_USAGE_NEVER; + } +} + +void serial_enable_event(serial_t *obj, int event, uint8_t enable) +{ + // Keep track of the requested events. + if (enable) { + obj->serial.events |= event; + } else { + obj->serial.events &= ~event; + } +} + +static void serial_tx_buffer_set(serial_t *obj, void *tx, int tx_length, uint8_t width) { + (void)width; + + // Exit if a transmit is already on-going + if (serial_tx_active(obj)) { + return; + } + + obj->tx_buff.buffer = tx; + obj->tx_buff.length = tx_length; + obj->tx_buff.pos = 0; +} + +int serial_tx_asynch(serial_t *obj, const void *tx, size_t tx_length, uint8_t tx_width, uint32_t handler, uint32_t event, DMAUsage hint) +{ + // Check that a buffer has indeed been set up + MBED_ASSERT(tx != (void*)0); + + if (tx_length == 0) return 0; + + if (serial_tx_active(obj)) { + return 0; + } + + // Set up buffer + serial_tx_buffer_set(obj, (void *)tx, tx_length, tx_width); + + // Set up events + serial_enable_event(obj, SERIAL_EVENT_TX_ALL, false); + serial_enable_event(obj, event, true); + + /* If using DMA, allocate channels only if they have not already been allocated */ + if (hint != DMA_USAGE_NEVER) { + /* User requested to transfer using DMA */ + serial_enable_dma(obj, handler, hint); + + /* Check if DMA setup was successful */ + if (obj->serial.uartDmaRx.dmaUsageState != DMA_USAGE_ALLOCATED && obj->serial.uartDmaRx.dmaUsageState != DMA_USAGE_TEMPORARY_ALLOCATED) { + /* Set up an interrupt transfer as DMA is unavailable */ + if (obj->serial.uart_transfer_handle.callback == 0) { + UART_TransferCreateHandle(uart_addrs[obj->serial.index], &obj->serial.uart_transfer_handle, (uart_transfer_callback_t)handler, NULL); + } + } + } else { + /* User requested to transfer using interrupts */ + /* Disable the DMA */ + serial_enable_dma(obj, handler, hint); + + /* Set up the interrupt transfer */ + if (obj->serial.uart_transfer_handle.callback == 0) { + UART_TransferCreateHandle(uart_addrs[obj->serial.index], &obj->serial.uart_transfer_handle, (uart_transfer_callback_t)handler, NULL); + } + } + + obj->serial.txstate = kUART_TxBusy; + + /* Start the transfer */ + serial_send_asynch(obj); + + /* Can't enter deep sleep as long as UART transmit is active */ + sleep_manager_lock_deep_sleep(); + + return 0; +} + +void serial_rx_buffer_set(serial_t *obj, void *rx, int rx_length, uint8_t width) +{ + // We only support byte buffers for now + MBED_ASSERT(width == 8); + + if (serial_rx_active(obj)) return; + + obj->rx_buff.buffer = rx; + obj->rx_buff.length = rx_length; + obj->rx_buff.pos = 0; + + return; +} + +/* Character match is currently not supported */ +void serial_rx_asynch(serial_t *obj, void *rx, size_t rx_length, uint8_t rx_width, uint32_t handler, uint32_t event, uint8_t char_match, DMAUsage hint) +{ + // Check that a buffer has indeed been set up + MBED_ASSERT(rx != (void*)0); + if (rx_length == 0) return; + + if (serial_rx_active(obj)) { + return; + } + + // Set up buffer + serial_rx_buffer_set(obj,(void*) rx, rx_length, rx_width); + + // Set up events + serial_enable_event(obj, SERIAL_EVENT_RX_ALL, false); + serial_enable_event(obj, event, true); + + //obj->char_match = char_match; + + /* If using DMA, allocate channels only if they have not already been allocated */ + if (hint != DMA_USAGE_NEVER) { + /* User requested to transfer using DMA */ + serial_enable_dma(obj, handler, hint); + + /* Check if DMA setup was successful */ + if (obj->serial.uartDmaRx.dmaUsageState != DMA_USAGE_ALLOCATED && obj->serial.uartDmaRx.dmaUsageState != DMA_USAGE_TEMPORARY_ALLOCATED) { + /* Set up an interrupt transfer as DMA is unavailable */ + if (obj->serial.uart_transfer_handle.callback == 0) { + UART_TransferCreateHandle(uart_addrs[obj->serial.index], &obj->serial.uart_transfer_handle, (uart_transfer_callback_t)handler, NULL); + } + } + + } else { + /* User requested to transfer using interrupts */ + /* Disable the DMA */ + serial_enable_dma(obj, handler, hint); + + /* Set up the interrupt transfer */ + if (obj->serial.uart_transfer_handle.callback == 0) { + UART_TransferCreateHandle(uart_addrs[obj->serial.index], &obj->serial.uart_transfer_handle, (uart_transfer_callback_t)handler, NULL); + } + } + + obj->serial.rxstate = kUART_RxBusy; + + /* Start the transfer */ + serial_receive_asynch(obj); + + /* Can't enter deep sleep as long as UART transfer is active */ + sleep_manager_lock_deep_sleep(); +} + +uint8_t serial_tx_active(serial_t *obj) +{ + if (obj->serial.txstate == kUART_TxIdle) { + return 0; + } + + return 1; +} + +uint8_t serial_rx_active(serial_t *obj) +{ + if (obj->serial.rxstate == kUART_RxIdle) { + return 0; + } + + return 1; +} + +int serial_irq_handler_asynch(serial_t *obj) +{ + int status = 0; + //uint8_t *buf = (uint8_t*)obj->rx_buff.buffer; + uint32_t status_flags = UART_GetStatusFlags(uart_addrs[obj->serial.index]); + + /* Determine whether the current scenario is DMA or IRQ, and act accordingly */ + if (obj->serial.uartDmaRx.dmaUsageState == DMA_USAGE_ALLOCATED || obj->serial.uartDmaRx.dmaUsageState == DMA_USAGE_TEMPORARY_ALLOCATED) { + /* DMA implementation */ + if ((obj->serial.txstate != kUART_TxIdle) && (obj->serial.uart_dma_handle.txState == kUART_TxIdle)) { + obj->serial.txstate = kUART_TxIdle; + status |= SERIAL_EVENT_TX_COMPLETE; + /* Transmit is complete, re-enable entry to deep sleep mode */ + sleep_manager_unlock_deep_sleep(); + } + + if ((obj->serial.rxstate != kUART_RxIdle) && (obj->serial.uart_dma_handle.rxState == kUART_RxIdle)) { + obj->serial.rxstate = kUART_RxIdle; + status |= SERIAL_EVENT_RX_COMPLETE; + /* Receive is complete, re-enable entry to deep sleep mode */ + sleep_manager_unlock_deep_sleep(); + } + + /* Release the dma channels if they were opportunistically allocated */ + if (obj->serial.uartDmaRx.dmaUsageState == DMA_USAGE_TEMPORARY_ALLOCATED) { + /* Ensure both TX and RX channels are idle before freeing them */ + if ((obj->serial.uart_dma_handle.txState == kUART_TxIdle) && (obj->serial.uart_dma_handle.rxState == kUART_RxIdle)) { + dma_channel_free(obj->serial.uartDmaRx.dmaChannel); + dma_channel_free(obj->serial.uartDmaTx.dmaChannel); + obj->serial.uartDmaRx.dmaUsageState = DMA_USAGE_OPPORTUNISTIC; + } + } + } else { + /* Interrupt implementation */ + if ((obj->serial.txstate != kUART_TxIdle) && (obj->serial.uart_transfer_handle.txState == kUART_TxIdle)) { + obj->serial.txstate = kUART_TxIdle; + status |= SERIAL_EVENT_TX_COMPLETE; + /* Transmit is complete, re-enable entry to deep sleep mode */ + sleep_manager_unlock_deep_sleep(); + } + + if ((obj->serial.rxstate != kUART_RxIdle) && (obj->serial.uart_transfer_handle.rxState == kUART_RxIdle)) { + obj->serial.rxstate = kUART_RxIdle; + status |= SERIAL_EVENT_RX_COMPLETE; + /* Receive is complete, re-enable entry to deep sleep mode */ + sleep_manager_unlock_deep_sleep(); + } + } +#if 0 + if (obj->char_match != SERIAL_RESERVED_CHAR_MATCH){ + /* Check for character match event */ + if (buf[obj->rx_buff.length - 1] == obj->char_match) { + status |= SERIAL_EVENT_RX_CHARACTER_MATCH; + } + } +#endif + + if (status_flags & kUART_RxOverrunFlag) { + UART_ClearStatusFlags(uart_addrs[obj->serial.index], kUART_RxOverrunFlag); + status |= SERIAL_EVENT_RX_OVERRUN_ERROR; + } + + if (status_flags & kUART_FramingErrorFlag) { + UART_ClearStatusFlags(uart_addrs[obj->serial.index], kUART_FramingErrorFlag); + status |= SERIAL_EVENT_RX_FRAMING_ERROR; + } + + if (status_flags & kUART_ParityErrorFlag) { + UART_ClearStatusFlags(uart_addrs[obj->serial.index], kUART_ParityErrorFlag); + status |= SERIAL_EVENT_RX_PARITY_ERROR; + } + + return status & obj->serial.events; +} + +void serial_tx_abort_asynch(serial_t *obj) +{ + // If we're not currently transferring, then there's nothing to do here + if (serial_tx_active(obj) == 0) { + return; + } + + if (obj->serial.uartDmaRx.dmaUsageState == DMA_USAGE_ALLOCATED || obj->serial.uartDmaRx.dmaUsageState == DMA_USAGE_TEMPORARY_ALLOCATED) { + UART_TransferAbortSendEDMA(uart_addrs[obj->serial.index], &obj->serial.uart_dma_handle); + /* Release the dma channels if they were opportunistically allocated */ + if (obj->serial.uartDmaRx.dmaUsageState == DMA_USAGE_TEMPORARY_ALLOCATED) { + /* Ensure both TX and RX channels are idle before freeing them */ + if ((obj->serial.uart_dma_handle.txState == kUART_TxIdle) && (obj->serial.uart_dma_handle.rxState == kUART_RxIdle)) { + dma_channel_free(obj->serial.uartDmaRx.dmaChannel); + dma_channel_free(obj->serial.uartDmaTx.dmaChannel); + obj->serial.uartDmaRx.dmaUsageState = DMA_USAGE_OPPORTUNISTIC; + } + } + } else { + UART_TransferAbortSend(uart_addrs[obj->serial.index], &obj->serial.uart_transfer_handle); + } + + obj->serial.txstate = kUART_TxIdle; + + /* Re-enable entry to deep sleep mode */ + sleep_manager_unlock_deep_sleep(); +} + +void serial_rx_abort_asynch(serial_t *obj) +{ + // If we're not currently transferring, then there's nothing to do here + if (serial_rx_active(obj) == 0) { + return; + } + + if (obj->serial.uartDmaRx.dmaUsageState == DMA_USAGE_ALLOCATED || obj->serial.uartDmaRx.dmaUsageState == DMA_USAGE_TEMPORARY_ALLOCATED) { + UART_TransferAbortReceiveEDMA(uart_addrs[obj->serial.index], &obj->serial.uart_dma_handle); + /* Release the dma channels if they were opportunistically allocated */ + if (obj->serial.uartDmaRx.dmaUsageState == DMA_USAGE_TEMPORARY_ALLOCATED) { + /* Ensure both TX and RX channels are idle before freeing them */ + if ((obj->serial.uart_dma_handle.txState == kUART_TxIdle) && (obj->serial.uart_dma_handle.rxState == kUART_RxIdle)) { + dma_channel_free(obj->serial.uartDmaRx.dmaChannel); + dma_channel_free(obj->serial.uartDmaTx.dmaChannel); + obj->serial.uartDmaRx.dmaUsageState = DMA_USAGE_OPPORTUNISTIC; + } + } + } else { + UART_TransferAbortReceive(uart_addrs[obj->serial.index], &obj->serial.uart_transfer_handle); + } + + obj->serial.rxstate = kUART_RxIdle; + + /* Re-enable entry to deep sleep mode */ + sleep_manager_unlock_deep_sleep(); } static int serial_is_enabled(uint32_t uart_index) diff --git a/targets/targets.json b/targets/targets.json index a7d0018a80c..8b1b21f4fa3 100644 --- a/targets/targets.json +++ b/targets/targets.json @@ -1817,6 +1817,7 @@ "PWMOUT", "SERIAL", "SERIAL_FC", + "SERIAL_ASYNCH", "SLEEP", "SPI", "SPISLAVE",