/* * Copyright (c) 2013 Nordic Semiconductor ASA * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list * of conditions and the following disclaimer. * * 2. Redistributions in binary form, except as embedded into a Nordic Semiconductor ASA * integrated circuit in a product or a software update for such product, 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. * * 3. Neither the name of Nordic Semiconductor ASA nor the names of its contributors may be * used to endorse or promote products derived from this software without specific prior * written permission. * * 4. This software, with or without modification, must only be used with a * Nordic Semiconductor ASA integrated circuit. * * 5. Any software provided in binary or object form under this license must not be reverse * engineered, decompiled, modified and/or disassembled. * * 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. * */ #if DEVICE_SERIAL #include "hal/serial_api.h" #include "nrf_uarte.h" #include "nrf_drv_uart.h" #include "nrf_drv_common.h" #include "nrf_atfifo.h" #include "app_util_platform.h" #include "pinmap_ex.h" #include "nrf_drv_ppi.h" #include "nrf_drv_gpiote.h" #include "platform/mbed_critical.h" #if UART0_ENABLED == 0 #error UART0 is disabled. DEVICE_SERIAL must also be disabled to continue. #endif /*** * _____ __ _ _ _ * / ____| / _(_) | | (_) * | | ___ _ __ | |_ _ __ _ _ _ _ __ __ _| |_ _ ___ _ __ * | | / _ \| '_ \| _| |/ _` | | | | '__/ _` | __| |/ _ \| '_ \ * | |___| (_) | | | | | | | (_| | |_| | | | (_| | |_| | (_) | | | | * \_____\___/|_| |_|_| |_|\__, |\__,_|_| \__,_|\__|_|\___/|_| |_| * __/ | * |___/ */ /** * Default FIFO buffer size for UARTE0. */ #ifndef MBED_CONF_NORDIC_UART_0_FIFO_SIZE #define MBED_CONF_NORDIC_UART_0_FIFO_SIZE 32 #endif /** * Default FIFO buffer size for UARTE1. */ #ifndef MBED_CONF_NORDIC_UART_1_FIFO_SIZE #define MBED_CONF_NORDIC_UART_1_FIFO_SIZE 32 #endif /** * Internal short names. */ #define UART0_FIFO_BUFFER_SIZE MBED_CONF_NORDIC_UART_0_FIFO_SIZE #define UART1_FIFO_BUFFER_SIZE MBED_CONF_NORDIC_UART_1_FIFO_SIZE #define DMA_BUFFER_SIZE 1 #define NUMBER_OF_BANKS 2 #define FIFO_MIN 3 /*** * _______ _ __ * |__ __| | | / _| * | |_ _ _ __ ___ __| | ___| |_ ___ * | | | | | '_ \ / _ \/ _` |/ _ \ _/ __| * | | |_| | |_) | __/ (_| | __/ | \__ \ * |_|\__, | .__/ \___|\__,_|\___|_| |___/ * __/ | | * |___/|_| */ /** * Missing event typedefs. */ typedef enum { NRF_UARTE_EVENT_RXDRDY = offsetof(NRF_UARTE_Type, EVENTS_RXDRDY), NRF_UARTE_EVENT_TXDRDY = offsetof(NRF_UARTE_Type, EVENTS_TXDRDY), } nrf_uarte_event_extra_t; /** * Missing interrupt masks. */ typedef enum { NRF_UARTE_INT_RXDRDY_MASK = UARTE_INTENSET_RXDRDY_Msk, NRF_UARTE_INT_TXDRDY_MASK = UARTE_INTENSET_TXDRDY_Msk, } nrf_uarte_int_mask_extra_t; /** * Internal struct for storing each UARTE instance's state: * * owner: pointer to serial object currently using instance. * buffer: buffers assigned to EasyDMA. * tx_data: 1 byte Tx buffer for blocking putc. * tx_in_progress: mutex for atomic Tx. * rx_in_progress: mutex for atomic Rx when using async API. * tx_asynch: set synch or asynch mode for Tx. * rx_asynch: set synch or asynch mode for Rx. * callback_posted: flag for posting only one callback. * active_bank: flag for buffer swapping. * fifo: pointer to the FIFO buffer. */ typedef struct { struct serial_s *owner; uint8_t buffer[NUMBER_OF_BANKS][DMA_BUFFER_SIZE]; uint32_t usage_counter; uint8_t tx_data; volatile uint8_t tx_in_progress; volatile uint8_t rx_in_progress; bool tx_asynch; bool rx_asynch; bool callback_posted; uint8_t active_bank; nrf_atfifo_t *fifo; uint32_t fifo_free_count; nrf_ppi_channel_t ppi_rts; nrf_drv_gpiote_pin_t rts; bool rx_suspended; } nordic_uart_state_t; /** * Turn Mbed HAL IRQ flags into maskable bit masks. */ typedef enum { NORDIC_TX_IRQ = (1 << 0), NORDIC_RX_IRQ = (1 << 1), } nordic_irq_t; /*** * _____ _ _ _ __ __ _ _ _ * / ____| | | | | | \ \ / / (_) | | | | * | | __| | ___ | |__ __ _| | \ \ / /_ _ _ __ _ __ _| |__ | | ___ ___ * | | |_ | |/ _ \| '_ \ / _` | | \ \/ / _` | '__| |/ _` | '_ \| |/ _ \/ __| * | |__| | | (_) | |_) | (_| | | \ / (_| | | | | (_| | |_) | | __/\__ \ * \_____|_|\___/|_.__/ \__,_|_| \/ \__,_|_| |_|\__,_|_.__/|_|\___||___/ * * */ /** * UARTE state. One for each instance. */ static nordic_uart_state_t nordic_nrf5_uart_state[UART_ENABLED_COUNT] = { 0 }; /** * Array with UARTE register pointers for easy access. */ static NRF_UARTE_Type *nordic_nrf5_uart_register[UART_ENABLED_COUNT] = { NRF_UARTE0, #if UART1_ENABLED NRF_UARTE1, #endif }; /** * @brief Create atomic fifo using macro. Macro defines static arrays * for buffer and internal state. */ NRF_ATFIFO_DEF(nordic_nrf5_uart_fifo_0, uint8_t, UART0_FIFO_BUFFER_SIZE); #if UART1_ENABLED NRF_ATFIFO_DEF(nordic_nrf5_uart_fifo_1, uint8_t, UART1_FIFO_BUFFER_SIZE); #endif /** * SWI IRQ mask. */ static uint8_t nordic_nrf5_uart_swi_mask_tx_0 = 0; static uint8_t nordic_nrf5_uart_swi_mask_rx_0 = 0; static uint8_t nordic_nrf5_uart_swi_mask_tx_1 = 0; static uint8_t nordic_nrf5_uart_swi_mask_rx_1 = 0; /** * Global variables expected by mbed_retarget.cpp for STDOUT. */ int stdio_uart_inited = 0; serial_t stdio_uart = { 0 }; /*** * _____ __ _ _____ _ _ * / ____| / _| | |_ _| | | | | * | (___ ___ | |_| |___ ____ _ _ __ ___ | | _ __ | |_ ___ _ __ _ __ _ _ _ __ | |_ * \___ \ / _ \| _| __\ \ /\ / / _` | '__/ _ \ | | | '_ \| __/ _ \ '__| '__| | | | '_ \| __| * ____) | (_) | | | |_ \ V V / (_| | | | __/ _| |_| | | | || __/ | | | | |_| | |_) | |_ * |_____/ \___/|_| \__| \_/\_/ \__,_|_| \___| |_____|_| |_|\__\___|_| |_| \__,_| .__/ \__| * | | * |_| */ /** * @brief SWI interrupt handler for signaling RxIrq handler. * * @param[in] instance The instance */ static void nordic_nrf5_uart_callback_handler(uint32_t instance) { /* Flag that no callback is posted. */ nordic_nrf5_uart_state[instance].callback_posted = false; /* Check if callback handler is set and if event mask match. */ uart_irq_handler callback = (uart_irq_handler) nordic_nrf5_uart_state[instance].owner->handler; uint32_t mask = nordic_nrf5_uart_state[instance].owner->mask; if (callback && (mask & NORDIC_RX_IRQ)) { /* Invoke callback function. */ uint32_t context = nordic_nrf5_uart_state[instance].owner->context; callback(context, RxIrq); } } /** * @brief SWI interrupt handler for when the Tx buffer has been transmitted. * * @param[in] instance The instance */ static void nordic_nrf5_uart_event_handler_endtx(int instance) { /* Release mutex. As the owner this call is safe. */ nordic_nrf5_uart_state[instance].tx_in_progress = 0; /* Check if callback handler and Tx event mask is set. */ uart_irq_handler callback = (uart_irq_handler) nordic_nrf5_uart_state[instance].owner->handler; uint32_t mask = nordic_nrf5_uart_state[instance].owner->mask; if (callback && (mask & NORDIC_TX_IRQ)) { /* Invoke callback function. */ uint32_t context = nordic_nrf5_uart_state[instance].owner->context; callback(context, TxIrq); } } /** * @brief Asynchronous event handler for when Tx DMA buffer has been sent. * * @param[in] instance The instance */ #if DEVICE_SERIAL_ASYNCH static void nordic_nrf5_uart_event_handler_endtx_asynch(int instance) { /* Set Tx done and reset Tx mode to be not asynchronous. */ nordic_nrf5_uart_state[instance].tx_in_progress = 0; nordic_nrf5_uart_state[instance].tx_asynch = false; /* Cast handler to callback function pointer. */ void (*callback)(void) = (void (*)(void)) nordic_nrf5_uart_state[instance].owner->tx_handler; uint32_t mask = nordic_nrf5_uart_state[instance].owner->tx_mask; /* Signal error if event mask matches and event handler is set. */ if (callback && (mask & SERIAL_EVENT_TX_COMPLETE)) { /* Store event value so it can be read back. */ nordic_nrf5_uart_state[instance].owner->tx_event = SERIAL_EVENT_TX_COMPLETE; /* Signal callback handler. */ callback(); } } #endif static void nordic_nrf5_uart_swi0(void) { if (nordic_nrf5_uart_swi_mask_tx_0) { nordic_nrf5_uart_swi_mask_tx_0 = 0; #if DEVICE_SERIAL_ASYNCH if (nordic_nrf5_uart_state[0].tx_asynch) { nordic_nrf5_uart_event_handler_endtx_asynch(0); } else #endif { nordic_nrf5_uart_event_handler_endtx(0); } } if (nordic_nrf5_uart_swi_mask_rx_0) { nordic_nrf5_uart_swi_mask_rx_0 = 0; nordic_nrf5_uart_callback_handler(0); } #if UART1_ENABLED if (nordic_nrf5_uart_swi_mask_tx_1) { nordic_nrf5_uart_swi_mask_tx_1 = 0; #if DEVICE_SERIAL_ASYNCH if (nordic_nrf5_uart_state[1].tx_asynch) { nordic_nrf5_uart_event_handler_endtx_asynch(1); } else #endif { nordic_nrf5_uart_event_handler_endtx(1); } } if (nordic_nrf5_uart_swi_mask_rx_1) { nordic_nrf5_uart_swi_mask_rx_1 = 0; nordic_nrf5_uart_callback_handler(1); } #endif } /** * @brief Trigger Tx SWI. * * @param[in] instance The instance. */ static void nordic_swi_tx_trigger(int instance) { if (instance == 0) { nordic_nrf5_uart_swi_mask_tx_0 = 1; NVIC_SetPendingIRQ(SWI0_EGU0_IRQn); } #if UART1_ENABLED else if (instance == 1) { nordic_nrf5_uart_swi_mask_tx_1 = 1; NVIC_SetPendingIRQ(SWI0_EGU0_IRQn); } #endif } /** * @brief Trigger Rx SWI. * * @param[in] instance The instance */ static void nordic_swi_rx_trigger(int instance) { if (instance == 0) { nordic_nrf5_uart_swi_mask_rx_0 = 1; NVIC_SetPendingIRQ(SWI0_EGU0_IRQn); } #if UART1_ENABLED else if (instance == 1) { nordic_nrf5_uart_swi_mask_rx_1 = 1; NVIC_SetPendingIRQ(SWI0_EGU0_IRQn); } #endif } /*** * _ _ _____ _______ ______ _ _ _ _ _ * | | | | /\ | __ \__ __| | ____| | | | | | | | | | * | | | | / \ | |__) | | | | |____ _____ _ __ | |_ | |__| | __ _ _ __ __| | | ___ _ __ * | | | |/ /\ \ | _ / | | | __\ \ / / _ \ '_ \| __| | __ |/ _` | '_ \ / _` | |/ _ \ '__| * | |__| / ____ \| | \ \ | | | |___\ V / __/ | | | |_ | | | | (_| | | | | (_| | | __/ | * \____/_/ \_\_| \_\ |_| |______\_/ \___|_| |_|\__| |_| |_|\__,_|_| |_|\__,_|_|\___|_| * * */ /** * @brief Event handler for when Rx buffer is full or buffer swap has been * triggered by idle task. * * Copy data from DMA buffer to FIFO buffer. * Post callback if not already posted. * * @param[in] instance The instance */ static void nordic_nrf5_uart_event_handler_endrx(int instance) { /* Read out active bank flag and swap DMA buffers. */ uint8_t active_bank = nordic_nrf5_uart_state[instance].active_bank; nordic_nrf5_uart_state[instance].active_bank = active_bank ^ 0x01; /* Get number of bytes in DMA buffer. */ uint32_t available = nrf_uarte_rx_amount_get(nordic_nrf5_uart_register[instance]); if (available > 0) { /* Copy data from DMA buffer to FIFO buffer. */ for (size_t index = 0; index < available; index++) { /* Atomic FIFO can be used safely without disabling interrutps. */ nrf_atfifo_item_put_t fifo_context; /* Get pointer to available space. */ uint8_t *byte = (uint8_t *) nrf_atfifo_item_alloc(nordic_nrf5_uart_state[instance].fifo, &fifo_context); if (byte != NULL) { /* Copy 1 byte from DMA buffer and commit to FIFO buffer. */ *byte = nordic_nrf5_uart_state[instance].buffer[active_bank][index]; nrf_atfifo_item_put(nordic_nrf5_uart_state[instance].fifo, &fifo_context); core_util_atomic_decr_u32(&nordic_nrf5_uart_state[instance].fifo_free_count, 1); } else { /* Buffer overflow. */ break; } } /* Signal callback through lower priority SWI if not already posted. */ if (nordic_nrf5_uart_state[instance].callback_posted == false) { nordic_nrf5_uart_state[instance].callback_posted = true; nordic_swi_rx_trigger(instance); } } } /** * @brief Event handler for when DMA has been armed with Rx buffer. * * Arm Rx buffer with second buffer for optimal reception. * * @param[in] instance The instance */ static void nordic_nrf5_uart_event_handler_rxstarted(int instance) { uint8_t next_bank = nordic_nrf5_uart_state[instance].active_bank ^ 0x01; nrf_uarte_rx_buffer_set(nordic_nrf5_uart_register[instance], nordic_nrf5_uart_state[instance].buffer[next_bank], DMA_BUFFER_SIZE); if (nordic_nrf5_uart_state[instance].rts != NRF_UART_PSEL_DISCONNECTED) { if (nordic_nrf5_uart_state[instance].fifo_free_count > FIFO_MIN) { /* Clear rts since we are ready to receive the next byte */ nrf_drv_gpiote_clr_task_trigger(nordic_nrf5_uart_state[instance].rts); } else { /* Suspend reception since there isn't enough buffer space. * The function serial_getc will restart reception. */ nordic_nrf5_uart_state[instance].rx_suspended = true; } } } #if DEVICE_SERIAL_ASYNCH /** * @brief Asynchronous event handler for when Rx DMA buffer is full. * * @param[in] instance The instance */ static void nordic_nrf5_uart_event_handler_endrx_asynch(int instance) { /* Set Rx done and reset Rx mode to be not asynchronous. */ nordic_nrf5_uart_state[instance].rx_in_progress = 0; nordic_nrf5_uart_state[instance].rx_asynch = false; /* Cast handler to callback function pointer. */ void (*callback)(void) = (void (*)(void)) nordic_nrf5_uart_state[instance].owner->rx_handler; uint32_t mask = nordic_nrf5_uart_state[instance].owner->rx_mask; /* Signal error if event mask matches and event handler is set. */ if (callback && (mask & SERIAL_EVENT_RX_COMPLETE)) { /* Store event value so it can be read back. */ nordic_nrf5_uart_state[instance].owner->rx_event = SERIAL_EVENT_RX_COMPLETE; /* Signal callback handler. */ callback(); } } #endif /** * @brief UARTE event handler. * * Collect signals from UARTE0 and UARTE1 ISR and translate to instance. * * @param[in] instance The instance */ static void nordic_nrf5_uart_event_handler(int instance) { /* DMA buffer is full or has been swapped out by idle timeout. */ if (nrf_uarte_event_check(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDRX)) { nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDRX); #if DEVICE_SERIAL_ASYNCH /* Call appropriate event handler based on current mode. */ if (nordic_nrf5_uart_state[instance].rx_asynch) { nordic_nrf5_uart_event_handler_endrx_asynch(instance); } else #endif { nordic_nrf5_uart_event_handler_endrx(instance); } } /* Rx DMA buffer has been armed. * * Warning - Do not process NRF_UARTE_EVENT_RXSTARTED if NRF_UARTE_EVENT_ENDRX is pending. * NRF_UARTE_EVENT_RXSTARTED must be processed first or nordic_nrf5_uart_event_handler_rxstarted * will setup the wrong DMA buffer and cause data to be lost. */ if (nrf_uarte_event_check(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_RXSTARTED) && !nrf_uarte_event_check(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDRX)) { nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_RXSTARTED); nordic_nrf5_uart_event_handler_rxstarted(instance); } /* Tx DMA buffer has been sent. */ if (nrf_uarte_event_check(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDTX)) { nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDTX); /* Use SWI to de-prioritize callback. */ nordic_swi_tx_trigger(instance); } } /** * @brief UARTE0 ISR. */ static void nordic_nrf5_uart0_handler(void) { /* Call event handler with instance ID. */ nordic_nrf5_uart_event_handler(0); } #if UART1_ENABLED /** * @brief UARTE1 ISR. */ static void nordic_nrf5_uart1_handler(void) { /* Call event handler with instance ID. */ nordic_nrf5_uart_event_handler(1); } #endif /*** * _____ __ _ _ _ * / ____| / _(_) | | (_) * | | ___ _ __ | |_ _ __ _ _ _ _ __ __ _| |_ _ ___ _ __ * | | / _ \| '_ \| _| |/ _` | | | | '__/ _` | __| |/ _ \| '_ \ * | |___| (_) | | | | | | | (_| | |_| | | | (_| | |_| | (_) | | | | * \_____\___/|_| |_|_| |_|\__, |\__,_|_| \__,_|\__|_|\___/|_| |_| * __/ | * |___/ */ /** * @brief Configure UARTE based on serial object settings. * * Common for both Rx and Tx. * * @param obj The object */ static void nordic_nrf5_uart_configure_object(serial_t *obj) { MBED_ASSERT(obj); #if DEVICE_SERIAL_ASYNCH struct serial_s *uart_object = &obj->serial; #else struct serial_s *uart_object = obj; #endif /* Configure Tx and Rx pins. */ if (uart_object->tx != NRF_UART_PSEL_DISCONNECTED) { nrf_gpio_pin_set(uart_object->tx); nrf_gpio_cfg_output(uart_object->tx); } if (uart_object->rx != NRF_UART_PSEL_DISCONNECTED) { nrf_gpio_cfg_input(uart_object->rx, NRF_GPIO_PIN_NOPULL); } nrf_uarte_txrx_pins_set(nordic_nrf5_uart_register[uart_object->instance], uart_object->tx, uart_object->rx); /* Set hardware flow control pins. */ if (uart_object->hwfc == NRF_UART_HWFC_ENABLED) { /* Check if pin is set before configuring it. */ if (uart_object->cts != NRF_UART_PSEL_DISCONNECTED) { nrf_gpio_cfg_input(uart_object->cts, NRF_GPIO_PIN_NOPULL); } /* Only let UARTE module handle CTS, RTS is handled manually due to buggy UARTE logic. */ nrf_uarte_hwfc_pins_set(nordic_nrf5_uart_register[uart_object->instance], NRF_UART_PSEL_DISCONNECTED, uart_object->cts); } /* Check if the rts pin changed */ if (uart_object->rts != nordic_nrf5_uart_state[uart_object->instance].rts) { uint32_t ret; /* Disable the PPI interconnect */ ret = nrf_drv_ppi_channel_disable(nordic_nrf5_uart_state[uart_object->instance].ppi_rts); MBED_ASSERT(ret == NRF_SUCCESS); /* Free flow control gpiote pin if it was previously set */ if (nordic_nrf5_uart_state[uart_object->instance].rts != NRF_UART_PSEL_DISCONNECTED) { nrf_drv_gpiote_out_uninit((nrf_drv_gpiote_pin_t)uart_object->rts); } /* Allocate and enable flow control gpiote pin if it is being used */ if (uart_object->rts != NRF_UART_PSEL_DISCONNECTED) { static const nrf_drv_gpiote_out_config_t config = { .init_state = NRF_GPIOTE_INITIAL_VALUE_HIGH, .task_pin = true, .action = NRF_GPIOTE_POLARITY_LOTOHI }; /* Allocate gpiote channel */ ret = nrf_drv_gpiote_out_init((nrf_drv_gpiote_pin_t)uart_object->rts, &config); if (ret == NRF_ERROR_INVALID_STATE) { /* Pin was previously set to GPIO so uninitialize it */ nrf_drv_gpiote_out_uninit((nrf_drv_gpiote_pin_t)uart_object->rts); ret = nrf_drv_gpiote_out_init((nrf_drv_gpiote_pin_t)uart_object->rts, &config); } MBED_ASSERT(ret == NRF_SUCCESS); /* Set RTS high on the ENDRX event */ ret = nrf_drv_ppi_channel_assign(nordic_nrf5_uart_state[uart_object->instance].ppi_rts, nrf_uarte_event_address_get(nordic_nrf5_uart_register[uart_object->instance], NRF_UARTE_EVENT_ENDRX), nrf_drv_gpiote_out_task_addr_get(uart_object->rts)); MBED_ASSERT(ret == NRF_SUCCESS); ret = nrf_drv_ppi_channel_enable(nordic_nrf5_uart_state[uart_object->instance].ppi_rts); MBED_ASSERT(ret == NRF_SUCCESS); /* Enable gpiote task - rts pin can no longer be used as GPIO at this point */ nrf_drv_gpiote_out_task_enable((nrf_drv_gpiote_pin_t)uart_object->rts); } nordic_nrf5_uart_state[uart_object->instance].rts = uart_object->rts; } /* Enable flow control and parity. */ nrf_uarte_configure(nordic_nrf5_uart_register[uart_object->instance], uart_object->parity, uart_object->hwfc); /* Set baudrate. */ nrf_uarte_baudrate_set(nordic_nrf5_uart_register[uart_object->instance], uart_object->baudrate); } /** * @brief Setup non-asynchronous reception. * * @param[in] instance The instance */ static void nordic_nrf5_uart_configure_rx(int instance) { /* Disable interrupts during confiration. */ nrf_uarte_int_disable(nordic_nrf5_uart_register[instance], NRF_UARTE_INT_RXSTARTED_MASK | NRF_UARTE_INT_ENDRX_MASK); /* Clear FIFO buffer. */ nrf_atfifo_clear(nordic_nrf5_uart_state[instance].fifo); nordic_nrf5_uart_state[instance].fifo_free_count = UART0_FIFO_BUFFER_SIZE; /* Clear Rx related events. */ nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_RXSTARTED); nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDRX); /* Enable shortcut between buffer full and begin reception on next buffer armed. */ nrf_uarte_shorts_enable(nordic_nrf5_uart_register[instance], NRF_UARTE_SHORT_ENDRX_STARTRX); /* Reset bank flag. */ nordic_nrf5_uart_state[instance].active_bank = 0; /* Arm first DMA buffer. */ nrf_uarte_rx_buffer_set(nordic_nrf5_uart_register[instance], nordic_nrf5_uart_state[instance].buffer[0], DMA_BUFFER_SIZE); /* Set non-asynchronous mode. */ nordic_nrf5_uart_state[instance].rx_asynch = false; /* Clear suspend condition */ nordic_nrf5_uart_state[instance].rx_suspended = false; /* Enable interrupts again. */ nrf_uarte_int_enable(nordic_nrf5_uart_register[instance], NRF_UARTE_INT_RXSTARTED_MASK | NRF_UARTE_INT_ENDRX_MASK); } #if DEVICE_SERIAL_ASYNCH /** * @brief Setup asynchronous reception. * * @param[in] instance The instance */ static void nordic_nrf5_uart_configure_rx_asynch(int instance) { /* Disable Rx related interrupts. */ nrf_uarte_int_disable(nordic_nrf5_uart_register[instance], NRF_UARTE_INT_RXSTARTED_MASK | NRF_UARTE_INT_ENDRX_MASK); /* Clear Rx related events. */ nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_RXSTARTED); nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDRX); /* Disable shortcut. Next Rx buffer must be manually started. */ nrf_uarte_shorts_disable(nordic_nrf5_uart_register[instance], NRF_UARTE_SHORT_ENDRX_STARTRX); /* Set asynchronous mode. */ nordic_nrf5_uart_state[instance].rx_asynch = true; /* Clear suspend condition */ nordic_nrf5_uart_state[instance].rx_suspended = false; /* Enable Rx interrupt. */ nrf_uarte_int_enable(nordic_nrf5_uart_register[instance], NRF_UARTE_INT_ENDRX_MASK); } #endif /** * @brief Main configuration function. * * @param obj The serial object */ static void nordic_nrf5_serial_configure(serial_t *obj) { MBED_ASSERT(obj); #if DEVICE_SERIAL_ASYNCH struct serial_s *uart_object = &obj->serial; #else struct serial_s *uart_object = obj; #endif /* Get object instance. */ int instance = uart_object->instance; /* Only configure if instance owner has changed or an update is forced. */ if ((uart_object != nordic_nrf5_uart_state[instance].owner) || (uart_object->update)) { /* Configure common setting. */ nordic_nrf5_uart_configure_object(obj); /* Set new owner. */ nordic_nrf5_uart_state[instance].owner = uart_object; uart_object->update = false; #if DEVICE_SERIAL_ASYNCH /* Set asynchronous mode. */ if (uart_object->rx_asynch == true) { nordic_nrf5_uart_configure_rx_asynch(instance); } else #endif { /* Set non-asynchronous mode. */ nordic_nrf5_uart_configure_rx(instance); nrf_uarte_task_trigger(nordic_nrf5_uart_register[instance], NRF_UARTE_TASK_STARTRX); } } #if DEVICE_SERIAL_ASYNCH /* Owner hasn't changed but mode has. Reconfigure. */ else if ((uart_object->rx_asynch == false) && (nordic_nrf5_uart_state[instance].rx_asynch == true)) { nordic_nrf5_uart_configure_rx(instance); nrf_uarte_task_trigger(nordic_nrf5_uart_register[instance], NRF_UARTE_TASK_STARTRX); /* Owner hasn't changed but mode has. Reconfigure. */ } else if ((uart_object->rx_asynch == true) && (nordic_nrf5_uart_state[instance].rx_asynch == false)) { nordic_nrf5_uart_configure_rx_asynch(instance); } #endif } /*** * __ __ _ _ _ _ _ _____ _____ * | \/ | | | | | | | | /\ | | /\ | __ \_ _| * | \ / | |__ ___ __| | | |__| | / \ | | / \ | |__) || | * | |\/| | '_ \ / _ \/ _` | | __ | / /\ \ | | / /\ \ | ___/ | | * | | | | |_) | __/ (_| | | | | |/ ____ \| |____ / ____ \| | _| |_ * |_| |_|_.__/ \___|\__,_| |_| |_/_/ \_\______| /_/ \_\_| |_____| * * */ /** Initialize the serial peripheral. It sets the default parameters for serial * peripheral, and configures its specifieds pins. * * Param obj The serial object * Param tx The TX pin name * Param rx The RX pin name */ void serial_init(serial_t *obj, PinName tx, PinName rx) { MBED_ASSERT(obj); #if DEVICE_SERIAL_ASYNCH struct serial_s *uart_object = &obj->serial; #else struct serial_s *uart_object = obj; #endif /* Only initialize on first call. */ static bool first_init = true; if (first_init) { uint32_t ret; first_init = false; /* Initialize components that serial relies on. */ nrf_drv_ppi_init(); if (!nrf_drv_gpiote_is_init()) { nrf_drv_gpiote_init(); } /* Enable interrupts for SWI. */ NVIC_SetVector(SWI0_EGU0_IRQn, (uint32_t) nordic_nrf5_uart_swi0); nrf_drv_common_irq_enable(SWI0_EGU0_IRQn, APP_IRQ_PRIORITY_LOWEST); /* Initialize FIFO buffer for UARTE0. */ NRF_ATFIFO_INIT(nordic_nrf5_uart_fifo_0); nordic_nrf5_uart_state[0].fifo = nordic_nrf5_uart_fifo_0; /* Initialize owner to NULL. */ nordic_nrf5_uart_state[0].owner = NULL; /* Allocate a PPI channel for flow control */ ret = nrf_drv_ppi_channel_alloc(&nordic_nrf5_uart_state[0].ppi_rts); MBED_ASSERT(ret == NRF_SUCCESS); /* Clear RTS */ nordic_nrf5_uart_state[0].rts = NRF_UART_PSEL_DISCONNECTED; /* Clear any old events and enable interrupts for UARTE0. */ nrf_uarte_int_disable(nordic_nrf5_uart_register[0], 0xFFFFFFFF); NVIC_SetVector(UARTE0_UART0_IRQn, (uint32_t) nordic_nrf5_uart0_handler); nrf_drv_common_irq_enable(UARTE0_UART0_IRQn, APP_IRQ_PRIORITY_HIGHEST); #if UART1_ENABLED /* Initialize FIFO buffer for UARTE1. */ NRF_ATFIFO_INIT(nordic_nrf5_uart_fifo_1); nordic_nrf5_uart_state[1].fifo = nordic_nrf5_uart_fifo_1; /* Initialize owner to NULL. */ nordic_nrf5_uart_state[1].owner = NULL; /* Allocate a PPI channel for flow control */ ret = nrf_drv_ppi_channel_alloc(&nordic_nrf5_uart_state[1].ppi_rts); MBED_ASSERT(ret == NRF_SUCCESS); /* Clear RTS */ nordic_nrf5_uart_state[1].rts = NRF_UART_PSEL_DISCONNECTED; /* Clear any old events and enable interrupts for UARTE1. */ nrf_uarte_int_disable(nordic_nrf5_uart_register[1], 0xFFFFFFFF); NVIC_SetVector(UARTE1_IRQn, (uint32_t) nordic_nrf5_uart1_handler); nrf_drv_common_irq_enable(UARTE1_IRQn, APP_IRQ_PRIORITY_HIGHEST); #endif } /* Get instance ID based on provided pins. */ int instance = pin_instance_uart(tx, rx); uart_object->instance = instance; /* Increment usage counter for this instance. */ nordic_nrf5_uart_state[instance].usage_counter++; /* Enable instance on first usage. */ if (nordic_nrf5_uart_state[instance].usage_counter == 1) { nrf_uarte_enable(nordic_nrf5_uart_register[instance]); /* In order to support printing with interrupts disabled serial_putc * must busy wait on NRF_UARTE_EVENT_TXDRDY. This event cannot be set * manually but must be set by the UARTE module after a character has * been sent. * * The following code sends a dummy character into the void so that * NRF_UARTE_EVENT_TXDRDY is correctly set. */ /* Ensure pins are disconnected. */ nrf_uarte_txrx_pins_set(nordic_nrf5_uart_register[instance], NRF_UART_PSEL_DISCONNECTED, NRF_UART_PSEL_DISCONNECTED); /* Set maximum baud rate to minimize waiting. */ nrf_uarte_baudrate_set(nordic_nrf5_uart_register[instance], NRF_UARTE_BAUDRATE_1000000); /* Send character. */ nrf_uarte_tx_buffer_set(nordic_nrf5_uart_register[instance], &nordic_nrf5_uart_state[instance].tx_data, 1); nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDTX); nrf_uarte_task_trigger(nordic_nrf5_uart_register[instance], NRF_UARTE_TASK_STARTTX); /* Wait until NRF_UARTE_EVENT_TXDRDY is set before proceeding. */ bool done = false; do { done = nrf_uarte_event_extra_check(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_TXDRDY); } while(done == false); } /* Store pins in serial object. */ if (tx == NC) { uart_object->tx = NRF_UART_PSEL_DISCONNECTED; } else { uart_object->tx = tx; } if (rx == NC) { uart_object->rx = NRF_UART_PSEL_DISCONNECTED; } else { uart_object->rx = rx; } /* Set default parity, baud rate, and callback handler. */ uart_object->parity = NRF_UART_PARITY_EXCLUDED; uart_object->baudrate = NRF_UART_BAUDRATE_9600; uart_object->cts = NRF_UART_PSEL_DISCONNECTED; uart_object->rts = NRF_UART_PSEL_DISCONNECTED; uart_object->hwfc = NRF_UART_HWFC_DISABLED; uart_object->handler = 0; /* The STDIO object is stored in this file. Set the flag once initialized. */ if (obj == &stdio_uart) { stdio_uart_inited = 1; } /* Take ownership and configure UART. */ uart_object->update = true; nordic_nrf5_serial_configure(obj); } /** Release the serial peripheral, not currently invoked. It requires further * resource management. * * Param obj The serial object */ void serial_free(serial_t *obj) { MBED_ASSERT(obj); #if DEVICE_SERIAL_ASYNCH struct serial_s *uart_object = &obj->serial; #else struct serial_s *uart_object = obj; #endif int instance = uart_object->instance; /* Only consider disabling UARTE if number of users is not zero. */ if (nordic_nrf5_uart_state[instance].usage_counter > 0) { /* Decrement usage counter for this instance. */ nordic_nrf5_uart_state[instance].usage_counter--; /* Disable instance when not in use. */ if (nordic_nrf5_uart_state[instance].usage_counter == 0) { nrf_uarte_disable(nordic_nrf5_uart_register[instance]); } } } /** Configure the baud rate * * Param obj The serial object * Param baudrate The baud rate to be configured */ void serial_baud(serial_t *obj, int baudrate) { MBED_ASSERT(obj); #if DEVICE_SERIAL_ASYNCH struct serial_s *uart_object = &obj->serial; #else struct serial_s *uart_object = obj; #endif nrf_uart_baudrate_t new_rate = NRF_UART_BAUDRATE_9600; /* Round down to nearest supported baud rate. */ if (baudrate < 2400) { new_rate = NRF_UARTE_BAUDRATE_1200; } else if (baudrate < 4800) { new_rate = NRF_UARTE_BAUDRATE_2400; } else if (baudrate < 9600) { new_rate = NRF_UARTE_BAUDRATE_4800; } else if (baudrate < 14400) { new_rate = NRF_UARTE_BAUDRATE_9600; } else if (baudrate < 19200) { new_rate = NRF_UARTE_BAUDRATE_14400; } else if (baudrate < 28800) { new_rate = NRF_UARTE_BAUDRATE_19200; } else if (baudrate < 38400) { new_rate = NRF_UARTE_BAUDRATE_28800; } else if (baudrate < 57600) { new_rate = NRF_UARTE_BAUDRATE_38400; } else if (baudrate < 76800) { new_rate = NRF_UARTE_BAUDRATE_57600; } else if (baudrate < 115200) { new_rate = NRF_UARTE_BAUDRATE_76800; } else if (baudrate < 230400) { new_rate = NRF_UARTE_BAUDRATE_115200; } else if (baudrate < 250000) { new_rate = NRF_UARTE_BAUDRATE_230400; } else if (baudrate < 460800) { new_rate = NRF_UARTE_BAUDRATE_250000; } else if (baudrate < 921600) { new_rate = NRF_UARTE_BAUDRATE_460800; } else if (baudrate < 1000000) { new_rate = NRF_UARTE_BAUDRATE_921600; } else { new_rate = NRF_UARTE_BAUDRATE_1000000; } /* Force reconfiguration next time serial object is owner if baud rate has changed. */ if (uart_object->baudrate != new_rate) { uart_object->baudrate = new_rate; uart_object->update = true; } } /** Configure the format. Set the number of bits, parity and the number of stop bits * * Param obj The serial object * Param data_bits The number of data bits * Param parity The parity * Param stop_bits The number of stop bits */ void serial_format(serial_t *obj, int data_bits, SerialParity parity, int stop_bits) { MBED_ASSERT(obj); /** * Only 8-bit mode, None/Even parity, and 1 stop bit supported by hardware. */ MBED_ASSERT(data_bits == 8); MBED_ASSERT((parity == ParityNone) || (parity == ParityEven)); MBED_ASSERT(stop_bits == 1); #if DEVICE_SERIAL_ASYNCH struct serial_s *uart_object = &obj->serial; #else struct serial_s *uart_object = obj; #endif /** * Only force change if parity has changed. */ if ((uart_object->parity != NRF_UART_PARITY_EXCLUDED) && (parity == ParityNone)) { uart_object->parity = NRF_UART_PARITY_EXCLUDED; uart_object->update = true; } else if ((uart_object->parity != NRF_UART_PARITY_INCLUDED) && (parity == ParityEven)) { uart_object->parity = NRF_UART_PARITY_INCLUDED; uart_object->update = true; } } /** Configure the serial for the flow control. It sets flow control in the hardware * if a serial peripheral supports it, otherwise software emulation is used. * * Param obj The serial object * Param type The type of the flow control. Look at the available FlowControl types. * Param rxflow The TX pin name * Param txflow The RX pin name */ void serial_set_flow_control(serial_t *obj, FlowControl type, PinName rxflow, PinName txflow) { MBED_ASSERT(obj); #if DEVICE_SERIAL_ASYNCH struct serial_s *uart_object = &obj->serial; #else struct serial_s *uart_object = obj; #endif /** * Convert Mbed pin names to Nordic pin names. */ uart_object->cts = ((txflow == NC) || (type == FlowControlRTS)) ? NRF_UART_PSEL_DISCONNECTED : (uint32_t) txflow; uart_object->rts = ((rxflow == NC) || (type == FlowControlCTS)) ? NRF_UART_PSEL_DISCONNECTED : (uint32_t) rxflow; uart_object->hwfc = (type == FlowControlNone) ? NRF_UART_HWFC_DISABLED : NRF_UART_HWFC_ENABLED; /* Force reconfiguration next time object is owner. */ uart_object->update = true; nordic_nrf5_serial_configure(obj); } /** Clear the serial peripheral * * Param obj The serial object */ void serial_clear(serial_t *obj) { MBED_ASSERT(obj); #if DEVICE_SERIAL_ASYNCH struct serial_s *uart_object = &obj->serial; #else struct serial_s *uart_object = obj; #endif /** * Reconfigure UART. */ uart_object->update = true; nordic_nrf5_uart_configure_object(obj); } /** Set the break * * Param obj The serial object */ void serial_break_set(serial_t *obj) { MBED_ASSERT(obj); #if DEVICE_SERIAL_ASYNCH struct serial_s *uart_object = &obj->serial; #else struct serial_s *uart_object = obj; #endif /* Set Tx pin low. */ nrf_gpio_pin_clear(uart_object->tx); } /** Clear the break * * Param obj The serial object */ void serial_break_clear(serial_t *obj) { MBED_ASSERT(obj); #if DEVICE_SERIAL_ASYNCH struct serial_s *uart_object = &obj->serial; #else struct serial_s *uart_object = obj; #endif /* Set Tx pin high (default). */ nrf_gpio_pin_set(uart_object->tx); } /** Configure the TX pin for UART function. * * Param tx The pin name used for TX */ void serial_pinout_tx(PinName tx) { /** * Legacy API. Not used by Mbed. */ MBED_ASSERT(0); } /*** * _____ _ _ _____ _____ * / ____(_) | | /\ | __ \_ _| * | (___ _ _ __ ___ _ __ | | ___ / \ | |__) || | * \___ \| | '_ ` _ \| '_ \| |/ _ \ / /\ \ | ___/ | | * ____) | | | | | | | |_) | | __/ / ____ \| | _| |_ * |_____/|_|_| |_| |_| .__/|_|\___| /_/ \_\_| |_____| * | | * |_| */ /** The serial interrupt handler registration * * Param obj The serial object * Param handler The interrupt handler which will be invoked when the interrupt fires * Param id The SerialBase object */ void serial_irq_handler(serial_t *obj, uart_irq_handler handler, uint32_t id) { MBED_ASSERT(obj); #if DEVICE_SERIAL_ASYNCH struct serial_s *uart_object = &obj->serial; #else struct serial_s *uart_object = obj; #endif /* Store handler and ID in serial object. */ uart_object->handler = (uint32_t) handler; uart_object->context = id; } /** Configure serial interrupt. This function is used for word-approach * * Param obj The serial object * Param irq The serial IRQ type (RX or TX) * Param enable Set to non-zero to enable events, or zero to disable them */ void serial_irq_set(serial_t *obj, SerialIrq irq, uint32_t enable) { MBED_ASSERT(obj); #if DEVICE_SERIAL_ASYNCH struct serial_s *uart_object = &obj->serial; #else struct serial_s *uart_object = obj; #endif /* Convert Mbed type to Nordic IRQ mask. */ uint32_t type = (irq == TxIrq) ? NORDIC_TX_IRQ : NORDIC_RX_IRQ; /* Enable/disable interrupt bit mask. */ if (enable) { uart_object->mask |= type; nordic_nrf5_serial_configure(obj); } else { uart_object->mask &= ~type; } } /** Get character. This is a blocking call, waiting for a character * * Param obj The serial object */ int serial_getc(serial_t *obj) { MBED_ASSERT(obj); #if DEVICE_SERIAL_ASYNCH struct serial_s *uart_object = &obj->serial; uart_object->rx_asynch = false; #else struct serial_s *uart_object = obj; #endif int instance = uart_object->instance; /* Take ownership and configure UART if necessary. */ nordic_nrf5_serial_configure(obj); /** * Use head and tail pointer in FIFO to determine whether there is data available. */ nrf_atfifo_t *fifo = nordic_nrf5_uart_state[instance].fifo; volatile uint16_t *head = &fifo->head.pos.rd; volatile uint16_t *tail = &fifo->tail.pos.rd; /* serial_getc is a blocking call. */ while (*head == *tail); /* Get 1 byte from FIFO buffer. The buffer is atomic * and doesn't need to be protected in a critical section. */ nrf_atfifo_item_get_t context; uint8_t *byte = (uint8_t *) nrf_atfifo_item_get(fifo, &context); nrf_atfifo_item_free(fifo, &context); core_util_atomic_incr_u32(&nordic_nrf5_uart_state[instance].fifo_free_count, 1); if (nordic_nrf5_uart_state[instance].rx_suspended) { nordic_nrf5_uart_state[instance].rx_suspended = false; nrf_drv_gpiote_clr_task_trigger(nordic_nrf5_uart_state[instance].rts); } return *byte; } /** Send a character. This is a blocking call, waiting for a peripheral to be available * for writing * * Param obj The serial object * Param c The character to be sent */ void serial_putc(serial_t *obj, int character) { bool done = false; MBED_ASSERT(obj); #if DEVICE_SERIAL_ASYNCH struct serial_s *uart_object = &obj->serial; #else struct serial_s *uart_object = obj; #endif int instance = uart_object->instance; nordic_nrf5_serial_configure(obj); /* Wait until UART is ready to send next character. */ do { done = nrf_uarte_event_extra_check(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_TXDRDY); } while(done == false); nrf_uarte_event_extra_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_TXDRDY); /* Arm Tx DMA buffer. */ nordic_nrf5_uart_state[instance].tx_data = character; nrf_uarte_tx_buffer_set(nordic_nrf5_uart_register[instance], &nordic_nrf5_uart_state[instance].tx_data, 1); /* Clear Tx event and enable Tx interrupts. */ nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDTX); nrf_uarte_int_enable(nordic_nrf5_uart_register[instance], NRF_UARTE_INT_ENDTX_MASK); /* Start transfer. */ nrf_uarte_task_trigger(nordic_nrf5_uart_register[instance], NRF_UARTE_TASK_STARTTX); } /** Check if the serial peripheral is readable * * Param obj The serial object * Return Non-zero value if a character can be read, 0 if nothing to read */ int serial_readable(serial_t *obj) { MBED_ASSERT(obj); #if DEVICE_SERIAL_ASYNCH struct serial_s *uart_object = &obj->serial; uart_object->rx_asynch = false; #else struct serial_s *uart_object = obj; #endif int instance = uart_object->instance; /* Take ownership and configure UART if necessary. */ nordic_nrf5_serial_configure(obj); /** * Use head and tail pointer in FIFO to determine whether there is data available. */ nrf_atfifo_t *fifo = nordic_nrf5_uart_state[instance].fifo; return (fifo->head.pos.rd != fifo->tail.pos.rd); } /** Check if the serial peripheral is writable * * Param obj The serial object * Return Non-zero value if a character can be written, 0 otherwise. */ int serial_writable(serial_t *obj) { MBED_ASSERT(obj); #if DEVICE_SERIAL_ASYNCH struct serial_s *uart_object = &obj->serial; #else struct serial_s *uart_object = obj; #endif int instance = uart_object->instance; return ((nordic_nrf5_uart_state[instance].tx_in_progress == 0) && (nrf_uarte_event_extra_check(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_TXDRDY))); } /*** * _ _____ _____ * /\ | | /\ | __ \_ _| * / \ ___ _ _ _ __ ___| |__ _ __ ___ _ __ ___ _ _ ___ / \ | |__) || | * / /\ \ / __| | | | '_ \ / __| '_ \| '__/ _ \| '_ \ / _ \| | | / __| / /\ \ | ___/ | | * / ____ \\__ \ |_| | | | | (__| | | | | | (_) | | | | (_) | |_| \__ \ / ____ \| | _| |_ * /_/ \_\___/\__, |_| |_|\___|_| |_|_| \___/|_| |_|\___/ \__,_|___/ /_/ \_\_| |_____| * __/ | * |___/ */ #if DEVICE_SERIAL_ASYNCH /** Begin asynchronous TX transfer. The used buffer is specified in the serial object, * tx_buff * * Param obj The serial object * Param tx The transmit buffer * Param tx_length The number of bytes to transmit * Param tx_width Deprecated argument * Param handler The serial handler * Param event The logical OR of events to be registered * Param hint A suggestion for how to use DMA with this transfer * Return Returns number of data transfered, otherwise returns 0 */ int serial_tx_asynch(serial_t *obj, const void *tx, size_t tx_length, uint8_t tx_width, uint32_t handler, uint32_t mask, DMAUsage hint) { MBED_ASSERT(obj); MBED_ASSERT(tx_width == 8); MBED_ASSERT(tx_length < 256); int instance = obj->serial.instance; /** * tx_in_progress acts like a mutex to ensure only one transmission can be active at a time. * The flag is modified using the atomic compare-and-set function. */ bool mutex = false; do { uint8_t expected = 0; uint8_t desired = 1; mutex = core_util_atomic_cas_u8((uint8_t *) &nordic_nrf5_uart_state[instance].tx_in_progress, &expected, desired); } while (mutex == false); /* State variables. */ int result = 0; bool valid = false; /** * EasyDMA can only access RAM. Check if provided buffer is in RAM or flash. * If the buffer is in flash, check if the FIFO buffer is large enough to store * the Tx data. */ if (instance == 0) { if (nrf_drv_is_in_RAM(tx) || (tx_length <= UART0_FIFO_BUFFER_SIZE)) { valid = true; } } #if UART1_ENABLED else { if (nrf_drv_is_in_RAM(tx) || (tx_length <= UART1_FIFO_BUFFER_SIZE)) { valid = true; } } #endif if (valid) { /* Setup buffers for transfer. */ uint8_t *buffer = NULL; /* Tx buffer is in RAM. */ if (nrf_drv_is_in_RAM(tx)) { buffer = (uint8_t *) tx; } else { /** * Tx buffer is in flash. Copy Tx buffer to FIFO buffer. * NOTE: this prevents simultaneous Rx using non-asynchronous API. */ const uint8_t *pointer = (const uint8_t *) tx; for (size_t index = 0; index < tx_length; index++) { nordic_nrf5_uart_fifo_0_data[index] = pointer[index]; } buffer = (uint8_t *) nordic_nrf5_uart_fifo_0_data; } /* Store callback handler, mask and reset event value. */ obj->serial.tx_handler = handler; obj->serial.tx_mask = mask; obj->serial.tx_event = 0; /* Enable asynchronous mode and configure UART. */ nordic_nrf5_uart_state[instance].tx_asynch = true; nordic_nrf5_serial_configure(obj); /* Clear Tx event and enable Tx interrupts. */ nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDTX); nrf_uarte_int_enable(nordic_nrf5_uart_register[instance], NRF_UARTE_INT_ENDTX_MASK); /* Set Tx DMA buffer. */ nrf_uarte_tx_buffer_set(nordic_nrf5_uart_register[obj->serial.instance], buffer, tx_length); /* Trigger DMA transfer. */ nrf_uarte_task_trigger(nordic_nrf5_uart_register[obj->serial.instance], NRF_UARTE_TASK_STARTTX); /* Setup complete, return length as sign of success. */ result = tx_length; } else { /* Signal error if event mask matches and event handler is set. */ if ((mask & SERIAL_EVENT_ERROR) && handler) { /* Cast handler to callback function pointer. */ void (*callback)(void) = (void (*)(void)) handler; /* Store event value so it can be read back. */ obj->serial.event = SERIAL_EVENT_ERROR; /* Signal callback handler. */ callback(); } } return result; } /** Begin asynchronous RX transfer (enable interrupt for data collecting) * The used buffer is specified in the serial object - rx_buff * * Param obj The serial object * Param rx The receive buffer * Param rx_length The number of bytes to receive * Param rx_width Deprecated argument * Param handler The serial handler * Param event The logical OR of events to be registered * Param handler The serial handler * Param char_match A character in range 0-254 to be matched * Param hint A suggestion for how to use DMA with this transfer */ void serial_rx_asynch(serial_t *obj, void *rx, size_t rx_length, uint8_t rx_width, uint32_t handler, uint32_t mask, uint8_t char_match, DMAUsage hint) { MBED_ASSERT(obj); MBED_ASSERT(rx_width == 8); MBED_ASSERT(rx_length < 256); MBED_ASSERT(char_match == SERIAL_RESERVED_CHAR_MATCH); // EasyDMA based UART handling does not support char_match int instance = obj->serial.instance; /** * rx_in_progress acts like a mutex to ensure only one asynchronous reception can be active at a time. * The flag is modified using the atomic compare-and-set function. */ bool mutex = false; do { uint8_t expected = 0; uint8_t desired = 1; mutex = core_util_atomic_cas_u8((uint8_t *) &nordic_nrf5_uart_state[instance].rx_in_progress, &expected, desired); } while (mutex == false); /* Store callback handler, mask and reset event value. */ obj->serial.rx_handler = handler; obj->serial.rx_mask = mask; obj->serial.rx_event = 0; /* Enable asynchronous mode and configure UART. */ obj->serial.rx_asynch = true; nordic_nrf5_serial_configure(obj); /* Set Rx DMA buffer. */ nrf_uarte_rx_buffer_set(nordic_nrf5_uart_register[instance], (uint8_t *) rx, rx_length); /* Enable reception. */ nrf_uarte_task_trigger(nordic_nrf5_uart_register[instance], NRF_UARTE_TASK_STARTRX); } /** Attempts to determine if the serial peripheral is already in use for TX * * Param obj The serial object * Return Non-zero if the RX transaction is ongoing, 0 otherwise */ uint8_t serial_tx_active(serial_t *obj) { MBED_ASSERT(obj); return nordic_nrf5_uart_state[obj->serial.instance].tx_asynch; } /** Attempts to determine if the serial peripheral is already in use for RX * * Param obj The serial object * Return Non-zero if the RX transaction is ongoing, 0 otherwise */ uint8_t serial_rx_active(serial_t *obj) { MBED_ASSERT(obj); return nordic_nrf5_uart_state[obj->serial.instance].rx_asynch; } /** The asynchronous TX and RX handler. * * Param obj The serial object * Return Returns event flags if an RX transfer termination condition was met; otherwise returns 0 */ int serial_irq_handler_asynch(serial_t *obj) { MBED_ASSERT(obj); return (obj->serial.tx_event | obj->serial.rx_event); } /** Abort the ongoing TX transaction. It disables the enabled interupt for TX and * flushes the TX hardware buffer if TX FIFO is used * * Param obj The serial object */ void serial_tx_abort_asynch(serial_t *obj) { MBED_ASSERT(obj); /* Transmission might be in progress. Disable interrupts to prevent ISR from firing. */ core_util_critical_section_enter(); int instance = obj->serial.instance; /* Disable ENDTX interrupts. */ nrf_uarte_int_disable(nordic_nrf5_uart_register[instance], NRF_UARTE_INT_ENDTX_MASK); /* Clear ENDTX event. */ nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDTX); /* Reset Tx flags. */ nordic_nrf5_uart_state[instance].tx_in_progress = 0; nordic_nrf5_uart_state[instance].tx_asynch = false; /* Force reconfiguration. */ obj->serial.update = true; nordic_nrf5_serial_configure(obj); /* Trigger STOP task. */ nrf_uarte_task_trigger(nordic_nrf5_uart_register[instance], NRF_UARTE_TASK_STOPTX); /* Enable interrupts again. */ core_util_critical_section_exit(); } /** Abort the ongoing RX transaction. It disables the enabled interrupt for RX and * flushes the RX hardware buffer if RX FIFO is used * * Param obj The serial object */ void serial_rx_abort_asynch(serial_t *obj) { MBED_ASSERT(obj); /* Transmission might be in progress. Disable interrupts to prevent ISR from firing. */ core_util_critical_section_enter(); /* Reset Rx flags. */ nordic_nrf5_uart_state[obj->serial.instance].rx_in_progress = 0; nordic_nrf5_uart_state[obj->serial.instance].rx_asynch = false; obj->serial.rx_asynch = false; /* Force reconfiguration. */ obj->serial.update = true; nordic_nrf5_serial_configure(obj); /* Enable interrupts again. */ core_util_critical_section_exit(); } #endif // DEVICE_SERIAL_ASYNCH #endif // DEVICE_SERIAL