mirror of https://github.com/ARMmbed/mbed-os.git
1824 lines
56 KiB
C
1824 lines
56 KiB
C
/*
|
|
* 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 "nrf_rtc.h"
|
|
#include "pinmap_ex.h"
|
|
|
|
#include "platform/mbed_critical.h"
|
|
|
|
#if UART0_ENABLED == 0
|
|
#error UART0 is disabled. DEVICE_SERIAL must also be disabled to continue.
|
|
#endif
|
|
|
|
|
|
/***
|
|
* _____ __ _ _ _
|
|
* / ____| / _(_) | | (_)
|
|
* | | ___ _ __ | |_ _ __ _ _ _ _ __ __ _| |_ _ ___ _ __
|
|
* | | / _ \| '_ \| _| |/ _` | | | | '__/ _` | __| |/ _ \| '_ \
|
|
* | |___| (_) | | | | | | | (_| | |_| | | | (_| | |_| | (_) | | | |
|
|
* \_____\___/|_| |_|_| |_|\__, |\__,_|_| \__,_|\__|_|\___/|_| |_|
|
|
* __/ |
|
|
* |___/
|
|
*/
|
|
|
|
/**
|
|
* Idle timeout between characters before DMA buffer is flushed.
|
|
*/
|
|
#ifndef MBED_CONF_NORDIC_UART_TIMEOUT_US
|
|
#define MBED_CONF_NORDIC_UART_TIMEOUT_US 2000
|
|
#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
|
|
|
|
/**
|
|
* Default DMA buffer size. Each instance has two DMA buffers.
|
|
*/
|
|
#ifndef MBED_CONF_NORDIC_UART_DMA_SIZE
|
|
#define MBED_CONF_NORDIC_UART_DMA_SIZE 8
|
|
#else
|
|
#if MBED_CONF_NORDIC_UART_DMA_SIZE < 5
|
|
#error MBED_CONF_NORDIC_UART_DMA_SIZE must be at least 5 bytes
|
|
#endif
|
|
#if MBED_CONF_NORDIC_UART_DMA_SIZE > 255
|
|
#error MBED_CONF_NORDIC_UART_DMA_SIZE must be less than 256 bytes
|
|
#endif
|
|
#endif
|
|
|
|
/**
|
|
* Internal short names.
|
|
*/
|
|
#define IDLE_TIMEOUT_US MBED_CONF_NORDIC_UART_TIMEOUT_US
|
|
#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 MBED_CONF_NORDIC_UART_DMA_SIZE
|
|
#define NUMBER_OF_BANKS 2
|
|
|
|
/**
|
|
* Use RTC2 for idle timeouts.
|
|
* Each channel is dedicated to one particular task.
|
|
*/
|
|
#define UARTE0_RTC_TIMEOUT_CHANNEL 0
|
|
#define UARTE1_RTC_TIMEOUT_CHANNEL 1
|
|
|
|
/**
|
|
* RTC frequency.
|
|
*/
|
|
#define RTC_FREQUENCY 32768
|
|
|
|
/***
|
|
* _______ _ __
|
|
* |__ __| | | / _|
|
|
* | |_ _ _ __ ___ __| | ___| |_ ___
|
|
* | | | | | '_ \ / _ \/ _` |/ _ \ _/ __|
|
|
* | | |_| | |_) | __/ (_| | __/ | \__ \
|
|
* |_|\__, | .__/ \___|\__,_|\___|_| |___/
|
|
* __/ | |
|
|
* |___/|_|
|
|
*/
|
|
|
|
/**
|
|
* 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.
|
|
* rxdrdy_counter: count received characters for idle detection.
|
|
* endrx_counter: count filled DMA buffers for idle detection.
|
|
* 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.
|
|
* ticker_is_running: flag for enabling/disabling idle timer.
|
|
* 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 rxdrdy_counter;
|
|
uint32_t endrx_counter;
|
|
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 ticker_is_running;
|
|
bool callback_posted;
|
|
uint8_t active_bank;
|
|
nrf_atfifo_t *fifo;
|
|
} 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 };
|
|
|
|
|
|
/***
|
|
* _____ _ _______ _
|
|
* / ____| | | |__ __(_)
|
|
* | | _ _ ___| |_ ___ _ __ ___ | | _ _ __ ___ ___ _ __
|
|
* | | | | | / __| __/ _ \| '_ ` _ \ | | | | '_ ` _ \ / _ \ '__|
|
|
* | |___| |_| \__ \ || (_) | | | | | | | | | | | | | | | __/ |
|
|
* \_____\__,_|___/\__\___/|_| |_| |_| |_| |_|_| |_| |_|\___|_|
|
|
*
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* @brief Set timout for a particular channel in RTC2.
|
|
*
|
|
* @param[in] timeout The timeout
|
|
* @param[in] channel The channel
|
|
*/
|
|
static void nordic_custom_ticker_set(uint32_t timeout, int channel)
|
|
{
|
|
/**
|
|
* Add timeout to current time and set as compare value for channel.
|
|
*/
|
|
uint32_t now = nrf_rtc_counter_get(NRF_RTC2);
|
|
uint32_t ticksout = (timeout * RTC_FREQUENCY) / (1000 * 1000);
|
|
nrf_rtc_cc_set(NRF_RTC2, channel, ticksout + now);
|
|
|
|
/**
|
|
* Enable interrupt for channel.
|
|
*/
|
|
uint32_t mask = nrf_rtc_int_get(NRF_RTC2);
|
|
nrf_rtc_int_enable(NRF_RTC2, mask | RTC_CHANNEL_INT_MASK(channel));
|
|
}
|
|
|
|
/**
|
|
* @brief Set idle timeout for particular instance.
|
|
* This function translates instance number to RTC channel.
|
|
*
|
|
* @param[in] instance The instance
|
|
*/
|
|
static void nordic_custom_ticker_set_timeout(int instance)
|
|
{
|
|
if (instance == 0) {
|
|
|
|
nordic_custom_ticker_set(IDLE_TIMEOUT_US, UARTE0_RTC_TIMEOUT_CHANNEL);
|
|
}
|
|
else if (instance == 1) {
|
|
|
|
nordic_custom_ticker_set(IDLE_TIMEOUT_US, UARTE1_RTC_TIMEOUT_CHANNEL);
|
|
}
|
|
}
|
|
|
|
/***
|
|
* _______ _ _ _ _ _
|
|
* |__ __(_) | | | | | | |
|
|
* | | _ _ __ ___ ___ _ __ | |__| | __ _ _ __ __| | | ___ _ __
|
|
* | | | | '_ ` _ \ / _ \ '__| | __ |/ _` | '_ \ / _` | |/ _ \ '__|
|
|
* | | | | | | | | | __/ | | | | | (_| | | | | (_| | | __/ |
|
|
* |_| |_|_| |_| |_|\___|_| |_| |_|\__,_|_| |_|\__,_|_|\___|_|
|
|
*
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* @brief Interrupt handler for idle timeouts.
|
|
* This function fans out interrupts from ISR and
|
|
* translates channel to instance.
|
|
*
|
|
* @param[in] instance The instance
|
|
*/
|
|
static void nordic_nrf5_uart_timeout_handler(uint32_t instance)
|
|
{
|
|
/**
|
|
* Check if any characters have been received or buffers been flushed
|
|
* since the last idle timeout.
|
|
*/
|
|
if ((nordic_nrf5_uart_state[instance].rxdrdy_counter > 0) ||
|
|
(nordic_nrf5_uart_state[instance].endrx_counter > 0)) {
|
|
|
|
/* Activity detected, reset timeout. */
|
|
nordic_custom_ticker_set_timeout(instance);
|
|
|
|
} else {
|
|
|
|
/* No activity detected, no timeout set. */
|
|
nordic_nrf5_uart_state[instance].ticker_is_running = false;
|
|
|
|
/**
|
|
* Stop Rx, this triggers a buffer swap and copies data from
|
|
* DMA buffer to FIFO buffer.
|
|
*/
|
|
nrf_uarte_task_trigger(nordic_nrf5_uart_register[instance],
|
|
NRF_UARTE_TASK_STOPRX);
|
|
}
|
|
|
|
/* reset activity counters. */
|
|
nordic_nrf5_uart_state[instance].rxdrdy_counter = 0;
|
|
nordic_nrf5_uart_state[instance].endrx_counter = 0;
|
|
}
|
|
|
|
/**
|
|
* @brief RTC2 ISR. Used for timeouts and scheduled callbacks.
|
|
*/
|
|
static void nordic_nrf5_rtc2_handler(void)
|
|
{
|
|
/* Channel 0 */
|
|
if (nrf_rtc_event_pending(NRF_RTC2, NRF_RTC_EVENT_COMPARE_0)) {
|
|
|
|
/* Clear event and disable interrupt for channel. */
|
|
nrf_rtc_event_clear(NRF_RTC2, NRF_RTC_EVENT_COMPARE_0);
|
|
|
|
uint32_t mask = nrf_rtc_int_get(NRF_RTC2);
|
|
nrf_rtc_int_enable(NRF_RTC2, mask & ~NRF_RTC_INT_COMPARE0_MASK);
|
|
|
|
/* Call timeout handler with instance ID. */
|
|
nordic_nrf5_uart_timeout_handler(0);
|
|
}
|
|
|
|
#if UART1_ENABLED
|
|
/* Channel 1 */
|
|
if (nrf_rtc_event_pending(NRF_RTC2, NRF_RTC_EVENT_COMPARE_1)) {
|
|
|
|
/* Clear event and disable interrupt for channel. */
|
|
nrf_rtc_event_clear(NRF_RTC2, NRF_RTC_EVENT_COMPARE_1);
|
|
|
|
uint32_t mask = nrf_rtc_int_get(NRF_RTC2);
|
|
nrf_rtc_int_enable(NRF_RTC2, mask & ~NRF_RTC_INT_COMPARE1_MASK);
|
|
|
|
/* Call timeout handler with instance ID. */
|
|
nordic_nrf5_uart_timeout_handler(1);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/***
|
|
* _____ __ _ _____ _ _
|
|
* / ____| / _| | |_ _| | | | |
|
|
* | (___ ___ | |_| |___ ____ _ _ __ ___ | | _ __ | |_ ___ _ __ _ __ _ _ _ __ | |_
|
|
* \___ \ / _ \| _| __\ \ /\ / / _` | '__/ _ \ | | | '_ \| __/ _ \ '__| '__| | | | '_ \| __|
|
|
* ____) | (_) | | | |_ \ 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)
|
|
{
|
|
/* Disable ENDTX event again. */
|
|
nrf_uarte_int_disable(nordic_nrf5_uart_register[instance], NRF_UARTE_INT_ENDTX_MASK);
|
|
|
|
/* 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)
|
|
{
|
|
/* Disable ENDTX interrupt. */
|
|
nrf_uarte_int_disable(nordic_nrf5_uart_register[instance], NRF_UARTE_INT_ENDTX_MASK);
|
|
|
|
/* 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)
|
|
{
|
|
/* Increment idle counter. */
|
|
core_util_atomic_incr_u32(&nordic_nrf5_uart_state[instance].endrx_counter, 1);
|
|
|
|
/* 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);
|
|
|
|
} 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);
|
|
}
|
|
|
|
/**
|
|
* @brief Event handler for when a character has been received in DMA buffer.
|
|
*
|
|
* Increment idle counter and set idle timeout if not already set.
|
|
*
|
|
* @param[in] instance The instance
|
|
*/
|
|
static void nordic_nrf5_uart_event_handler_rxdrdy(int instance)
|
|
{
|
|
/* Increment idle counter. */
|
|
core_util_atomic_incr_u32(&nordic_nrf5_uart_state[instance].rxdrdy_counter, 1);
|
|
|
|
/* Set idle timeout if not already set. */
|
|
if (nordic_nrf5_uart_state[instance].ticker_is_running == false) {
|
|
|
|
nordic_nrf5_uart_state[instance].ticker_is_running = true;
|
|
|
|
nordic_custom_ticker_set_timeout(instance);
|
|
}
|
|
}
|
|
|
|
#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. */
|
|
if (nrf_uarte_event_check(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_RXSTARTED))
|
|
{
|
|
nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_RXSTARTED);
|
|
|
|
nordic_nrf5_uart_event_handler_rxstarted(instance);
|
|
}
|
|
|
|
/* Single character has been put in DMA buffer. */
|
|
if (nrf_uarte_event_check(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_RXDRDY))
|
|
{
|
|
nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_RXDRDY);
|
|
|
|
nordic_nrf5_uart_event_handler_rxdrdy(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->rts != NRF_UART_PSEL_DISCONNECTED) {
|
|
|
|
nrf_gpio_cfg_output(uart_object->rts);
|
|
}
|
|
|
|
/* 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);
|
|
}
|
|
|
|
nrf_uarte_hwfc_pins_set(nordic_nrf5_uart_register[uart_object->instance],
|
|
uart_object->rts,
|
|
uart_object->cts);
|
|
}
|
|
|
|
/* 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 |
|
|
NRF_UARTE_INT_RXDRDY_MASK);
|
|
|
|
/* Clear FIFO buffer. */
|
|
nrf_atfifo_clear(nordic_nrf5_uart_state[instance].fifo);
|
|
|
|
/* 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);
|
|
nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_RXDRDY);
|
|
|
|
/* 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;
|
|
|
|
/* Enable interrupts again. */
|
|
nrf_uarte_int_enable(nordic_nrf5_uart_register[instance], NRF_UARTE_INT_RXSTARTED_MASK |
|
|
NRF_UARTE_INT_ENDRX_MASK |
|
|
NRF_UARTE_INT_RXDRDY_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 |
|
|
NRF_UARTE_INT_RXDRDY_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);
|
|
nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_RXDRDY);
|
|
|
|
/* 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;
|
|
|
|
/* 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) {
|
|
first_init = false;
|
|
|
|
/* Register RTC2 ISR. */
|
|
NVIC_SetVector(RTC2_IRQn, (uint32_t) nordic_nrf5_rtc2_handler);
|
|
|
|
/* Clear RTC2 channel events. */
|
|
nrf_rtc_event_clear(NRF_RTC2, NRF_RTC_EVENT_COMPARE_0);
|
|
nrf_rtc_event_clear(NRF_RTC2, NRF_RTC_EVENT_COMPARE_1);
|
|
|
|
/* Enable interrupts for all four RTC2 channels. */
|
|
nrf_rtc_event_enable(NRF_RTC2,
|
|
NRF_RTC_INT_COMPARE0_MASK |
|
|
NRF_RTC_INT_COMPARE1_MASK);
|
|
|
|
/* Enable RTC2 IRQ. Priority is set to highest so that the UARTE ISR can't interrupt it. */
|
|
nrf_drv_common_irq_enable(RTC2_IRQn, APP_IRQ_PRIORITY_HIGHEST);
|
|
|
|
/* Start RTC2. According to the datasheet the added power consumption is neglible so
|
|
* the RTC2 will run forever.
|
|
*/
|
|
nrf_rtc_task_trigger(NRF_RTC2, NRF_RTC_TASK_START);
|
|
|
|
/* 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;
|
|
|
|
/* Clear any old events and enable interrupts for UARTE0. */
|
|
nrf_uarte_int_disable(nordic_nrf5_uart_register[0], NRF_UARTE_INT_RXSTARTED_MASK |
|
|
NRF_UARTE_INT_ENDRX_MASK |
|
|
NRF_UARTE_INT_RXDRDY_MASK);
|
|
|
|
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;
|
|
|
|
/* Clear any old events and enable interrupts for UARTE1. */
|
|
nrf_uarte_int_disable(nordic_nrf5_uart_register[1], NRF_UARTE_INT_RXSTARTED_MASK |
|
|
NRF_UARTE_INT_ENDRX_MASK |
|
|
NRF_UARTE_INT_RXDRDY_MASK);
|
|
|
|
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]);
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/** 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;
|
|
|
|
} 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);
|
|
|
|
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)
|
|
{
|
|
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;
|
|
|
|
/**
|
|
* 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);
|
|
|
|
/* Take ownership and configure UART if necessary. */
|
|
nordic_nrf5_serial_configure(obj);
|
|
|
|
/* 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 ENDTX event and enable 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);
|
|
|
|
/* Trigger DMA 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);
|
|
}
|
|
|
|
/***
|
|
* _ _____ _____
|
|
* /\ | | /\ | __ \_ _|
|
|
* / \ ___ _ _ _ __ ___| |__ _ __ ___ _ __ ___ _ _ ___ / \ | |__) || |
|
|
* / /\ \ / __| | | | '_ \ / __| '_ \| '__/ _ \| '_ \ / _ \| | | / __| / /\ \ | ___/ | |
|
|
* / ____ \\__ \ |_| | | | | (__| | | | | | (_) | | | | (_) | |_| \__ \ / ____ \| | _| |_
|
|
* /_/ \_\___/\__, |_| |_|\___|_| |_|_| \___/|_| |_|\___/ \__,_|___/ /_/ \_\_| |_____|
|
|
* __/ |
|
|
* |___/
|
|
*/
|
|
|
|
#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
|