初始版本

This commit is contained in:
xiaozhengsheng
2025-08-19 09:49:41 +08:00
parent 10f1ddf1c1
commit 6df0f7d96e
2974 changed files with 1712873 additions and 54 deletions

View File

@@ -0,0 +1,679 @@
/**
* Copyright (c) 2019 - 2020, 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 form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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.
*
*/
#include "sdk_config.h"
#include "nrf_libuarte_async.h"
#include "app_error.h"
#include "nrf_balloc.h"
#include "nrfx_timer.h"
#include "nrfx_rtc.h"
#include "nrfx_ppi.h"
#include "nrf_uart.h"
#include "nrf_queue.h"
#define NRF_LOG_MODULE_NAME libUARTE_async
#if NRF_LIBUARTE_CONFIG_LOG_ENABLED
#define NRF_LOG_LEVEL NRF_LIBUARTE_CONFIG_LOG_LEVEL
#define NRF_LOG_INFO_COLOR NRF_LIBUARTE_CONFIG_INFO_COLOR
#define NRF_LOG_DEBUG_COLOR NRF_LIBUARTE_CONFIG_DEBUG_COLOR
#else // NRF_LIBUARTE_CONFIG_LOG_ENABLED
#define NRF_LOG_LEVEL 0
#endif // NRF_LIBUARTE_CONFIG_LOG_ENABLED
#include "nrf_log.h"
NRF_LOG_MODULE_REGISTER();
#if defined(NRFX_RTC_ENABLED) && NRFX_RTC_ENABLED
#define RTC_IN_USE 1
#else
#define RTC_IN_USE 0
#endif
#if defined(NRFX_TIMER_ENABLED) && NRFX_TIMER_ENABLED
#define TIMER_IN_USE 1
#else
#define TIMER_IN_USE 0
#endif
#define FAULT_IRQ_LEVEL 0xFF
/** Macro is setting up PPI channel set which consist of event, task and optional fork.
*
* @param _ch Channel.
* @param _evt Event.
* @param _tsk Task.
* @param _fork Fork. If NULL fork is not configured.
*/
#define PPI_CH_SETUP(_ch, _evt, _tsk, _fork) \
ret = nrfx_ppi_channel_assign(_ch, _evt, _tsk); \
if (ret != NRF_SUCCESS) \
{ \
return NRF_ERROR_INTERNAL; \
} \
if (_fork) \
{ \
ret = nrfx_ppi_channel_fork_assign(_ch, _fork); \
if (ret != NRF_SUCCESS) \
{ \
return NRF_ERROR_INTERNAL; \
} \
}
/* @brief Function returns interrupt level which is the next,lower priority.
*
* If SoftDevice is present then it takes into account which priorities are used
* by the SoftDevice.
*
* @note Caller of this function does not check if error is returned. Error is returned if input
* priority belongs to SoftDevice. In that case SoftDevice will detect attempt to interrupt level
* misuse.
*
* @param prio Interrupt priority.
*
* @return Priority which is one level lower or fault indicator (0xFF).
*/
static uint8_t irq_prio_inc(uint8_t prio)
{
#ifdef SOFTDEVICE_PRESENT
static const uint8_t sd_next_irq_lut[] = {
FAULT_IRQ_LEVEL, /* 0 used by softdevice */
FAULT_IRQ_LEVEL, /* 1 used by softdevice */
APP_IRQ_PRIORITY_MID, /* 2 + 1 = 3 */
APP_IRQ_PRIORITY_LOW_MID, /* 3 + 1 = 5 as 4 is used by softdevice */
FAULT_IRQ_LEVEL, /* 4 used by softdevice */
APP_IRQ_PRIORITY_LOW /* 5 + 1 = 6 */,
APP_IRQ_PRIORITY_LOWEST, /* 6 + 1 = 7 */
};
return sd_next_irq_lut[prio];
#else
return prio + 1;
#endif
}
#if NRF_LIBUARTE_ASYNC_WITH_APP_TIMER
static void app_timer_handler(void * p_context);
#define local_app_timer_cnt_get() app_timer_cnt_get()
#define local_app_timer_start(p_timer, ticks, p_context) app_timer_start(p_timer, ticks, p_context)
#define local_app_timer_stop(p_timer) app_timer_stop(p_timer)
#define local_app_timer_create(p_timer) app_timer_create(p_timer, APP_TIMER_MODE_SINGLE_SHOT, app_timer_handler)
#define local_app_timer_cnt_diff_compute(to, from) app_timer_cnt_diff_compute(to, from)
#else
#ifndef APP_TIMER_CONFIG_RTC_FREQUENCY
#define APP_TIMER_CONFIG_RTC_FREQUENCY 0
#endif
#ifndef APP_TIMER_CLOCK_FREQ
#define APP_TIMER_CLOCK_FREQ 1
#endif
#ifndef APP_TIMER_MIN_TIMEOUT_TICKS
#define APP_TIMER_MIN_TIMEOUT_TICKS 0
#endif
#ifndef APP_TIMER_CONFIG_IRQ_PRIORITY
#define APP_TIMER_CONFIG_IRQ_PRIORITY 1
#endif
static void app_timer_handler(void * p_context) __attribute__((unused));
#define local_app_timer_cnt_get() 0
#define local_app_timer_start(p_timer, ticks, p_context) NRF_SUCCESS
#define local_app_timer_stop(p_timer) NRF_SUCCESS
#define local_app_timer_create(p_timer) NRF_SUCCESS
#define local_app_timer_cnt_diff_compute(to, from) 0
#endif
static uint32_t app_timer_ticks_to_us(uint32_t ticks)
{
return (uint32_t)(((uint64_t)ticks * 1000000 * (APP_TIMER_CONFIG_RTC_FREQUENCY + 1)) /
APP_TIMER_CLOCK_FREQ);
}
static uint32_t app_timer_us_to_ticks(uint32_t us)
{
return (uint32_t)((((uint64_t)APP_TIMER_CLOCK_FREQ/(APP_TIMER_CONFIG_RTC_FREQUENCY + 1)) * us) /
1000000);
}
static bool rx_buffer_schedule(const nrf_libuarte_async_t * p_libuarte)
{
uint8_t * p_data = nrf_balloc_alloc(p_libuarte->p_rx_pool);
if (p_data == NULL)
{
return false;
}
ret_code_t ret = nrf_queue_push(p_libuarte->p_rx_queue, &p_data);
if (ret != NRF_SUCCESS)
{
NRF_LOG_ERROR("RX buffer queue full.");
return false;
}
p_libuarte->p_ctrl_blk->alloc_cnt++;
nrf_libuarte_drv_rx_buf_rsp(p_libuarte->p_libuarte, p_data, p_libuarte->rx_buf_size);
return true;
}
static void uart_evt_handler(void * context, nrf_libuarte_drv_evt_t * p_evt)
{
ret_code_t ret;
const nrf_libuarte_async_t * p_libuarte = (const nrf_libuarte_async_t *)context;
switch (p_evt->type)
{
case NRF_LIBUARTE_DRV_EVT_TX_DONE:
{
NRF_LOG_DEBUG("(evt) TX completed (%d)", p_evt->data.rxtx.length);
nrf_libuarte_async_evt_t evt = {
.type = NRF_LIBUARTE_ASYNC_EVT_TX_DONE,
.data = {
.rxtx = {
.p_data = p_evt->data.rxtx.p_data,
.length = p_evt->data.rxtx.length,
}
}
};
p_libuarte->p_ctrl_blk->evt_handler(p_libuarte->p_ctrl_blk->context, &evt);
break;
}
case NRF_LIBUARTE_DRV_EVT_RX_BUF_REQ:
{
if (p_libuarte->p_ctrl_blk->rx_halted)
{
break;
}
if (rx_buffer_schedule(p_libuarte) == false)
{
if (p_libuarte->p_ctrl_blk->hwfc)
{
p_libuarte->p_ctrl_blk->rx_halted = true;
}
else
{
NRF_LOG_ERROR("(evt) Failed to allocate buffer for RX.");
APP_ERROR_CHECK_BOOL(false);
}
}
break;
}
case NRF_LIBUARTE_DRV_EVT_RX_DATA:
{
uint32_t rx_amount = p_evt->data.rxtx.length - p_libuarte->p_ctrl_blk->sub_rx_count;
if (rx_amount)
{
p_libuarte->p_ctrl_blk->rx_count += rx_amount;
nrf_libuarte_async_evt_t evt = {
.type = NRF_LIBUARTE_ASYNC_EVT_RX_DATA,
.data = {
.rxtx = {
.p_data = &p_evt->data.rxtx.p_data[p_libuarte->p_ctrl_blk->sub_rx_count],
.length = rx_amount,
}
}
};
NRF_LOG_DEBUG("(evt) RX: %d (addr:0x%08X, internal index: %d)",
rx_amount,
p_evt->data.rxtx.p_data,
p_libuarte->p_ctrl_blk->sub_rx_count);
p_libuarte->p_ctrl_blk->sub_rx_count = 0;
if(p_evt->data.rxtx.p_data != p_libuarte->p_ctrl_blk->p_curr_rx_buf)
{
NRF_LOG_ERROR("(evt) RX buffer address mismatch");
}
ret = nrf_queue_pop(p_libuarte->p_rx_queue, &p_libuarte->p_ctrl_blk->p_curr_rx_buf);
if (ret != NRF_SUCCESS)
{
NRF_LOG_ERROR("RX buffer queue empty.");
APP_ERROR_CHECK_BOOL(false);
}
p_libuarte->p_ctrl_blk->evt_handler(p_libuarte->p_ctrl_blk->context, &evt);
}
else
{
NRF_LOG_ERROR("(evt) RX with 0 length: 0x%08X", p_evt->data.rxtx.p_data);
//zero length packet is freed immediately and not forwarded to the application.
APP_ERROR_CHECK_BOOL(false);
}
break;
}
case NRF_LIBUARTE_DRV_EVT_ERROR:
{
nrf_libuarte_async_evt_t evt = {
.type = NRF_LIBUARTE_ASYNC_EVT_ERROR,
.data = {
.errorsrc = p_evt->data.errorsrc
}
};
p_libuarte->p_ctrl_blk->evt_handler(p_libuarte->p_ctrl_blk->context, &evt);
break;
}
case NRF_LIBUARTE_DRV_EVT_OVERRUN_ERROR:
{
NRF_LOG_WARNING("Overrun error - data loss due to UARTE interrupt not handled on time.");
uint32_t rx_amount = p_evt->data.overrun_err.overrun_length - p_libuarte->p_ctrl_blk->sub_rx_count;
p_libuarte->p_ctrl_blk->rx_count += rx_amount;
nrf_libuarte_async_evt_t evt = {
.type = NRF_LIBUARTE_ASYNC_EVT_OVERRUN_ERROR,
.data = {
.overrun_err = { .overrun_length = p_evt->data.overrun_err.overrun_length}
}
};
p_libuarte->p_ctrl_blk->evt_handler(p_libuarte->p_ctrl_blk->context, &evt);
break;
}
default:
APP_ERROR_CHECK_BOOL(false);
break;
}
}
void nrf_libuarte_async_timeout_handler(const nrf_libuarte_async_t * p_libuarte)
{
NRFX_IRQ_DISABLE((IRQn_Type)NRFX_IRQ_NUMBER_GET(p_libuarte->p_libuarte->uarte));
uint32_t capt_rx_count = p_libuarte->p_libuarte->timer.p_reg->CC[3];
if (capt_rx_count > p_libuarte->p_ctrl_blk->rx_count)
{
uint32_t rx_amount = capt_rx_count - p_libuarte->p_ctrl_blk->rx_count;
nrf_libuarte_async_evt_t evt = {
.type = NRF_LIBUARTE_ASYNC_EVT_RX_DATA,
.data = {
.rxtx = {
.p_data = &p_libuarte->p_ctrl_blk->p_curr_rx_buf[p_libuarte->p_ctrl_blk->sub_rx_count],
.length = rx_amount,
}
}
};
NRF_LOG_DEBUG("(tmr evt) RX: %d (addr:0x%08X, internal index: %d)",
rx_amount,
evt.data.rxtx.p_data,
p_libuarte->p_ctrl_blk->sub_rx_count);
p_libuarte->p_ctrl_blk->sub_rx_count += rx_amount;
p_libuarte->p_ctrl_blk->rx_count = capt_rx_count;
p_libuarte->p_ctrl_blk->evt_handler(p_libuarte->p_ctrl_blk->context, &evt);
}
NRFX_IRQ_ENABLE((IRQn_Type)NRFX_IRQ_NUMBER_GET(p_libuarte->p_libuarte->uarte));
}
static void tmr_evt_handler(nrf_timer_event_t event_type, void * p_context)
{
nrf_libuarte_async_timeout_handler((const nrf_libuarte_async_t *)p_context);
}
static void app_timer_handler(void * p_context)
{
const nrf_libuarte_async_t * p_libuarte = p_context;
uint32_t current_rx_count;
uint32_t counter = local_app_timer_cnt_get();
uint32_t ticks = app_timer_us_to_ticks(p_libuarte->p_ctrl_blk->timeout_us)/2;
ticks = MAX(APP_TIMER_MIN_TIMEOUT_TICKS, ticks);
if (p_libuarte->p_ctrl_blk->enabled == false)
{
return;
}
nrf_timer_task_trigger( p_libuarte->p_libuarte->timer.p_reg, NRF_TIMER_TASK_CAPTURE3);
current_rx_count = p_libuarte->p_libuarte->timer.p_reg->CC[3];
UNUSED_RETURN_VALUE(local_app_timer_start(*p_libuarte->p_app_timer, ticks, (void *)p_libuarte));
if (p_libuarte->p_app_timer_ctrl_blk->rx_count != current_rx_count) {
p_libuarte->p_app_timer_ctrl_blk->rx_count = current_rx_count;
/* if number of bytes received changed reset timestamp and activate waiting
* for silent period.
*/
p_libuarte->p_app_timer_ctrl_blk->timestamp = counter;
p_libuarte->p_app_timer_ctrl_blk->activate = true;
} else {
uint32_t diff;
/* In case of detected silent period check if its length exceeds configured
* timeout. If yes trigger timeout handler.
*/
diff = local_app_timer_cnt_diff_compute(counter,
p_libuarte->p_app_timer_ctrl_blk->timestamp);
if (p_libuarte->p_app_timer_ctrl_blk->activate &&
(app_timer_ticks_to_us(diff) > p_libuarte->p_ctrl_blk->timeout_us)) {
p_libuarte->p_app_timer_ctrl_blk->activate = false;
nrf_libuarte_async_timeout_handler(p_libuarte);
}
}
}
ret_code_t nrf_libuarte_async_init(const nrf_libuarte_async_t * const p_libuarte,
nrf_libuarte_async_config_t const * p_config,
nrf_libuarte_async_evt_handler_t evt_handler,
void * context)
{
ret_code_t ret;
if (p_config->int_prio == APP_IRQ_PRIORITY_LOWEST ||
((p_libuarte->p_app_timer && NRF_LIBUARTE_ASYNC_WITH_APP_TIMER) &&
(p_config->int_prio >= APP_TIMER_CONFIG_IRQ_PRIORITY))) {
NRF_LOG_ERROR("Too low priority. Lowest possible priority is %d", APP_IRQ_PRIORITY_LOW);
return NRF_ERROR_INVALID_PARAM;
}
if (p_libuarte->p_ctrl_blk->enabled)
{
return NRF_ERROR_INVALID_STATE;
}
p_libuarte->p_ctrl_blk->evt_handler = evt_handler;
p_libuarte->p_ctrl_blk->rx_count = 0;
p_libuarte->p_ctrl_blk->p_curr_rx_buf = NULL;
p_libuarte->p_ctrl_blk->rx_free_cnt = 0;
p_libuarte->p_ctrl_blk->sub_rx_count = 0;
p_libuarte->p_ctrl_blk->alloc_cnt = 0;
p_libuarte->p_ctrl_blk->context = context;
p_libuarte->p_ctrl_blk->timeout_us = p_config->timeout_us;
p_libuarte->p_ctrl_blk->rx_halted = false;
p_libuarte->p_ctrl_blk->hwfc = (p_config->hwfc == NRF_UARTE_HWFC_ENABLED);
uint32_t i;
uint32_t tmr_start_tsk = 0;
uint32_t tmr_clear_tsk = 0;
uint32_t tmr_stop_tsk = 0;
uint32_t tmr_compare_evt = 0;
if (p_libuarte->p_rtc && RTC_IN_USE)
{
nrfx_rtc_config_t rtc_config = NRFX_RTC_DEFAULT_CONFIG;
rtc_config.interrupt_priority = irq_prio_inc(p_config->int_prio);
rtc_config.prescaler = 0;
ret = nrfx_rtc_init(p_libuarte->p_rtc, &rtc_config, p_libuarte->rtc_handler);
if (ret != NRFX_SUCCESS)
{
return NRF_ERROR_INTERNAL;
}
ret = nrfx_rtc_cc_set(p_libuarte->p_rtc, 0, p_config->timeout_us/32, true);
if (ret != NRFX_SUCCESS)
{
return NRF_ERROR_INTERNAL;
}
tmr_start_tsk = nrfx_rtc_task_address_get(p_libuarte->p_rtc, NRF_RTC_TASK_START);
tmr_clear_tsk = nrfx_rtc_task_address_get(p_libuarte->p_rtc, NRF_RTC_TASK_CLEAR);
tmr_stop_tsk = nrfx_rtc_task_address_get(p_libuarte->p_rtc, NRF_RTC_TASK_STOP);
tmr_compare_evt = nrfx_rtc_event_address_get(p_libuarte->p_rtc, NRF_RTC_EVENT_COMPARE_0);
}
else if (p_libuarte->p_timer && TIMER_IN_USE)
{
nrfx_timer_config_t tmr_config = NRFX_TIMER_DEFAULT_CONFIG;
tmr_config.frequency = NRF_TIMER_FREQ_1MHz;
tmr_config.p_context = (void *)p_libuarte;
tmr_config.interrupt_priority = irq_prio_inc(p_config->int_prio);
ret = nrfx_timer_init(p_libuarte->p_timer, &tmr_config, tmr_evt_handler);
if (ret != NRFX_SUCCESS)
{
return NRF_ERROR_INTERNAL;
}
nrfx_timer_compare(p_libuarte->p_timer, NRF_TIMER_CC_CHANNEL0, p_config->timeout_us, true);
tmr_start_tsk = nrfx_timer_task_address_get(p_libuarte->p_timer, NRF_TIMER_TASK_START);
tmr_clear_tsk = nrfx_timer_task_address_get(p_libuarte->p_timer, NRF_TIMER_TASK_CLEAR);
tmr_stop_tsk = nrfx_timer_task_address_get(p_libuarte->p_timer, NRF_TIMER_TASK_SHUTDOWN);
tmr_compare_evt = nrfx_timer_compare_event_address_get(p_libuarte->p_timer, 0);
}
else if (p_libuarte->p_app_timer && NRF_LIBUARTE_ASYNC_WITH_APP_TIMER) {
/* app_timer in use */
if(!p_libuarte->p_ctrl_blk->app_timer_created)
{
ret = local_app_timer_create(p_libuarte->p_app_timer);
if (ret != NRF_SUCCESS)
{
return ret;
}
p_libuarte->p_ctrl_blk->app_timer_created = true;
}
p_libuarte->p_app_timer_ctrl_blk->activate = false;
p_libuarte->p_app_timer_ctrl_blk->rx_count = 0;
p_libuarte->p_app_timer_ctrl_blk->timestamp = 0;
}
else
{
NRF_LOG_ERROR("No timer or rtc defined");
APP_ERROR_CHECK_BOOL(false);
return NRF_ERROR_INTERNAL;
}
/* if RTC or TIMER is used then PPI channels are allocated. */
if (p_libuarte->p_app_timer == NULL || !NRF_LIBUARTE_ASYNC_WITH_APP_TIMER)
{
for (i = 0; i < NRF_LIBUARTE_ASYNC_PPI_CH_MAX; i++)
{
ret = nrfx_ppi_channel_alloc(&p_libuarte->p_ctrl_blk->ppi_channels[i]);
if (ret != NRFX_SUCCESS)
{
//we don't free already allocated channels, system is wrongly configured.
return NRF_ERROR_INTERNAL;
}
}
/*lint -save -e666 */
PPI_CH_SETUP(p_libuarte->p_ctrl_blk->ppi_channels[NRF_LIBUARTE_ASYNC_PPI_CH_RXRDY_CLEAR],
nrf_uarte_event_address_get(p_libuarte->p_libuarte->uarte, NRF_UARTE_EVENT_RXDRDY),
tmr_start_tsk,
tmr_clear_tsk);
PPI_CH_SETUP(p_libuarte->p_ctrl_blk->ppi_channels[NRF_LIBUARTE_ASYNC_PPI_CH_COMPARE_SHUTDOWN],
tmr_compare_evt,
tmr_stop_tsk,
(uint32_t)&p_libuarte->p_libuarte->timer.p_reg->TASKS_CAPTURE[3]);
/*lint -restore */
}
nrf_libuarte_drv_config_t uart_config = {
.tx_pin = p_config->tx_pin,
.rx_pin = p_config->rx_pin,
.cts_pin = p_config->cts_pin,
.rts_pin = p_config->rts_pin,
.startrx_evt = nrf_uarte_event_address_get(p_libuarte->p_libuarte->uarte, NRF_UARTE_EVENT_ENDRX),
.endrx_evt = 0,
.rxstarted_tsk = 0,
.rxdone_tsk = 0,
.hwfc = p_config->hwfc,
.parity = p_config->parity,
.baudrate = p_config->baudrate,
.irq_priority = p_config->int_prio,
.pullup_rx = p_config->pullup_rx,
};
ret = nrf_libuarte_drv_init(p_libuarte->p_libuarte, &uart_config, uart_evt_handler, (void *)p_libuarte);
if (ret != NRF_SUCCESS)
{
return ret;
}
ret = nrf_balloc_init(p_libuarte->p_rx_pool);
if (ret != NRF_SUCCESS)
{
return ret;
}
nrf_queue_reset(p_libuarte->p_rx_queue);
p_libuarte->p_ctrl_blk->enabled = true;
return ret;
}
void nrf_libuarte_async_uninit(const nrf_libuarte_async_t * const p_libuarte)
{
if (p_libuarte->p_ctrl_blk->enabled == false)
{
return;
}
p_libuarte->p_ctrl_blk->enabled = false;
/* if HW timeout was used */
if (p_libuarte->p_app_timer == NULL || !NRF_LIBUARTE_ASYNC_WITH_APP_TIMER)
{
uint32_t i;
ret_code_t ret;
for (i = 0; i < NRF_LIBUARTE_ASYNC_PPI_CH_MAX; i++)
{
ret = nrfx_ppi_channel_disable(p_libuarte->p_ctrl_blk->ppi_channels[i]);
ASSERT(ret == NRF_SUCCESS)
ret = nrfx_ppi_channel_free(p_libuarte->p_ctrl_blk->ppi_channels[i]);
ASSERT(ret == NRF_SUCCESS)
}
}
if (p_libuarte->p_rtc && RTC_IN_USE)
{
nrfx_rtc_disable(p_libuarte->p_rtc);
nrfx_rtc_uninit(p_libuarte->p_rtc);
}
else if (p_libuarte->p_timer && TIMER_IN_USE)
{
nrfx_timer_disable(p_libuarte->p_timer);
nrfx_timer_uninit(p_libuarte->p_timer);
}
else if (p_libuarte->p_app_timer && NRF_LIBUARTE_ASYNC_WITH_APP_TIMER)
{
UNUSED_RETURN_VALUE(local_app_timer_stop(*p_libuarte->p_app_timer));
}
nrf_libuarte_drv_uninit(p_libuarte->p_libuarte);
}
void nrf_libuarte_async_enable(const nrf_libuarte_async_t * const p_libuarte)
{
uint8_t * p_data;
p_data = nrf_balloc_alloc(p_libuarte->p_rx_pool);
p_libuarte->p_ctrl_blk->alloc_cnt++;
if (p_data == NULL)
{
APP_ERROR_CHECK_BOOL(false);
}
if (p_libuarte->p_rtc && RTC_IN_USE)
{
nrfx_rtc_counter_clear(p_libuarte->p_rtc);
}
else if (p_libuarte->p_timer && TIMER_IN_USE)
{
nrfx_timer_clear(p_libuarte->p_timer);
}
if (!(p_libuarte->p_app_timer && NRF_LIBUARTE_ASYNC_WITH_APP_TIMER))
{
nrfx_err_t err;
err = nrfx_ppi_channel_enable(p_libuarte->p_ctrl_blk->ppi_channels[NRF_LIBUARTE_ASYNC_PPI_CH_RXRDY_CLEAR]);
APP_ERROR_CHECK_BOOL(err == NRFX_SUCCESS);
err = nrfx_ppi_channel_enable(p_libuarte->p_ctrl_blk->ppi_channels[NRF_LIBUARTE_ASYNC_PPI_CH_COMPARE_SHUTDOWN]);
APP_ERROR_CHECK_BOOL(err == NRFX_SUCCESS);
}
p_libuarte->p_ctrl_blk->p_curr_rx_buf = p_data;
ret_code_t ret = nrf_libuarte_drv_rx_start(p_libuarte->p_libuarte, p_data, p_libuarte->rx_buf_size, false);
APP_ERROR_CHECK_BOOL(ret == NRF_SUCCESS);
if (p_libuarte->p_app_timer && NRF_LIBUARTE_ASYNC_WITH_APP_TIMER)
{
uint32_t ticks = app_timer_us_to_ticks(p_libuarte->p_ctrl_blk->timeout_us)/2;
ticks = MAX(APP_TIMER_MIN_TIMEOUT_TICKS, ticks);
UNUSED_RETURN_VALUE(local_app_timer_start(*p_libuarte->p_app_timer, ticks, (void *)p_libuarte));
}
}
ret_code_t nrf_libuarte_async_tx(const nrf_libuarte_async_t * const p_libuarte, uint8_t * p_data, size_t length)
{
return nrf_libuarte_drv_tx(p_libuarte->p_libuarte, p_data, length);
}
void nrf_libuarte_async_rx_free(const nrf_libuarte_async_t * const p_libuarte, uint8_t * p_data, size_t length)
{
p_libuarte->p_ctrl_blk->rx_free_cnt += length;
if (p_libuarte->p_ctrl_blk->rx_free_cnt == p_libuarte->rx_buf_size)
{
p_data -= (p_libuarte->p_ctrl_blk->rx_free_cnt - length);
p_libuarte->p_ctrl_blk->rx_free_cnt = 0;
nrf_balloc_free(p_libuarte->p_rx_pool, p_data);
p_libuarte->p_ctrl_blk->alloc_cnt--;
if (p_libuarte->p_ctrl_blk->alloc_cnt<0)
{
NRF_LOG_ERROR("Freeing more RX buffers than allocated.");
APP_ERROR_CHECK_BOOL(false);
}
NRF_LOG_INFO("Freeing full buffer 0x%08X, %d, (currently allocated:%d).",p_data, length, p_libuarte->p_ctrl_blk->alloc_cnt);
if (p_libuarte->p_ctrl_blk->rx_halted)
{
bool ret = rx_buffer_schedule(p_libuarte);
ASSERT(ret);
p_libuarte->p_ctrl_blk->rx_halted = false;
}
}
else if (p_libuarte->p_ctrl_blk->rx_free_cnt > p_libuarte->rx_buf_size)
{
NRF_LOG_ERROR("Unexpected RX free input parameter.");
APP_ERROR_CHECK_BOOL(false);
}
else
{
NRF_LOG_INFO("Freeing partial buffer: 0x%08X, length:%d", p_data, length);
}
}
void nrf_libuarte_async_rts_clear(const nrf_libuarte_async_t * const p_libuarte)
{
nrf_libuarte_drv_rts_clear(p_libuarte->p_libuarte);
}
void nrf_libuarte_async_rts_set(const nrf_libuarte_async_t * const p_libuarte)
{
nrf_libuarte_drv_rts_set(p_libuarte->p_libuarte);
}

View File

@@ -0,0 +1,385 @@
/**
* Copyright (c) 2019 - 2020, 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 form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef UART_ASYNC_H
#define UART_ASYNC_H
#include <stdint.h>
#include "sdk_errors.h"
#include "nrf_balloc.h"
#include "nrf_queue.h"
#include "nrfx_ppi.h"
#include "nrfx_timer.h"
#include "nrfx_rtc.h"
#include "nrf_libuarte_drv.h"
#include <hal/nrf_uarte.h>
/**
* @defgroup nrf_libuarte_async libUARTE asynchronous library
* @ingroup app_common
*
* @brief Module for reliable communication over UARTE.
*
* @{
*/
/* Safe guard for sdk_config.h now up to date. */
#ifndef NRF_LIBUARTE_ASYNC_WITH_APP_TIMER
#warning "sdk_config.h is missing NRF_LIBUARTE_ASYNC_WITH_APP_TIMER option"
#define NRF_LIBUARTE_ASYNC_WITH_APP_TIMER 0
#endif
#if NRF_LIBUARTE_ASYNC_WITH_APP_TIMER
#include "app_timer.h"
#endif
/** @brief Types of libuarte driver events. */
typedef enum
{
NRF_LIBUARTE_ASYNC_EVT_RX_DATA, ///< Requested TX transfer completed.
NRF_LIBUARTE_ASYNC_EVT_TX_DONE, ///< Requested RX transfer completed.
NRF_LIBUARTE_ASYNC_EVT_ERROR, ///< Error reported by UARTE peripheral.
NRF_LIBUARTE_ASYNC_EVT_OVERRUN_ERROR ///< Error reported by the driver.
} nrf_libuarte_async_evt_type_t;
typedef enum
{
NRF_LIBUARTE_ASYNC_PPI_CH_RXRDY_CLEAR,
NRF_LIBUARTE_ASYNC_PPI_CH_COMPARE_SHUTDOWN,
NRF_LIBUARTE_ASYNC_PPI_CH_MAX
} nrf_libuarte_async_ppi_channel_t;
/** @brief Structure for libuarte async transfer completion event. */
typedef struct
{
uint8_t * p_data; ///< Pointer to memory used for transfer.
size_t length; ///< Number of bytes transfered.
} nrf_libuarte_async_data_t;
/** @brief Structu for software error event. */
typedef struct
{
uint32_t overrun_length; ///< Number of bytes lost due to overrun.
} nrf_libuarte_async_overrun_err_evt_t;
/** @brief Structure for libuarte error event. */
typedef struct
{
nrf_libuarte_async_evt_type_t type; ///< Event type.
union {
nrf_libuarte_async_data_t rxtx; ///< RXD/TXD data.
uint8_t errorsrc; ///< Error source.
nrf_libuarte_async_overrun_err_evt_t overrun_err; ///< Overrun error data.
} data; ///< Union with data.
} nrf_libuarte_async_evt_t;
/**
* @brief Interrupt event handler.
*
* @param[in] p_evt Pointer to event structure. Event is allocated on the stack so it is available
* only within the context of the event handler.
*/
typedef void (*nrf_libuarte_async_evt_handler_t)(void * context, nrf_libuarte_async_evt_t * p_evt);
/** @brief Structure for libuarte async configuration. */
typedef struct
{
uint32_t rx_pin; ///< RXD pin number.
uint32_t tx_pin; ///< TXD pin number.
uint32_t cts_pin; ///< CTS pin number.
uint32_t rts_pin; ///< RTS pin number.
uint32_t timeout_us; ///< Receiver timeout in us unit.
nrf_uarte_hwfc_t hwfc; ///< Flow control configuration.
nrf_uarte_parity_t parity; ///< Parity configuration.
nrf_uarte_baudrate_t baudrate; ///< Baudrate.
bool pullup_rx; ///< Pull up on RX pin.
uint8_t int_prio; ///< Interrupt priority of UARTE (RTC, TIMER have int_prio - 1)
} nrf_libuarte_async_config_t;
/**
* @brief nrf_libuarte_async control block (placed in RAM).
*/
typedef struct {
nrf_libuarte_async_evt_handler_t evt_handler;
void * context;
nrf_ppi_channel_t ppi_channels[NRF_LIBUARTE_ASYNC_PPI_CH_MAX];
int32_t alloc_cnt;
uint32_t rx_count;
uint32_t sub_rx_count;
uint8_t * p_curr_rx_buf;
uint32_t rx_free_cnt;
uint32_t timeout_us;
bool app_timer_created;
bool hwfc;
bool rx_halted;
bool enabled;
} nrf_libuarte_async_ctrl_blk_t;
typedef struct {
uint32_t rx_count;
uint32_t timestamp;
bool activate;
} nrf_libuarte_app_timer_ctrl_blk_t;
/**
* @brief nrf_libuarte_async instance structure (placed in ROM).
*/
typedef struct {
const nrf_balloc_t * p_rx_pool;
const nrf_queue_t * p_rx_queue;
const nrfx_rtc_t * p_rtc;
const nrfx_timer_t * p_timer;
#if NRF_LIBUARTE_ASYNC_WITH_APP_TIMER
const app_timer_id_t * p_app_timer;
#else
void ** p_app_timer;
#endif
nrf_libuarte_app_timer_ctrl_blk_t * p_app_timer_ctrl_blk;
const nrf_libuarte_drv_t * p_libuarte;
nrf_libuarte_async_ctrl_blk_t * p_ctrl_blk;
nrfx_rtc_handler_t rtc_handler;
uint32_t rx_buf_size;
} nrf_libuarte_async_t;
void nrf_libuarte_async_timeout_handler(const nrf_libuarte_async_t * p_libuarte);
#define NRF_LIBUARTE_PERIPHERAL_NOT_USED 255
#define LIBUARTE_ASYNC_DEBRACKET(...) __VA_ARGS__
#define __LIBUARTE_ASYNC_ARG_2(ignore_this, val, ...) val
#define __LIBUARTE_ASYNC_ARG_2_DEBRACKET(ignore_this, val, ...) LIBUARTE_ASYNC_DEBRACKET val
/* Macro for injecting code based on flag evaluation. If flag exists and equals 1
* then first code is compiled in, else second option. Note that code must be
* in the brackets. Example usage:
* _LIBUARTE_ASYNC_EVAL(MY_FLAG, (foo();), () )
* If MY_FLAG exists and equals 1 then macros resolves to foo(); call, else it resolves to
* empty line.
*
* @param _eval_level Flag to be evaluated. It's positively evaluated if exists and equals 1.
* @param _iftrue Macro is resolved to that code on positive flag evaluation. Code must be
* in the brackets.
* @param _iffalse Macro is resolved to that code on negative flag evaluation. Code must be
* in the brackets.
*/
#define _LIBUARTE_ASYNC_EVAL(_eval_flag, _iftrue, _iffalse) \
_LIBUARTE_ASYNC_EVAL1(_eval_flag, _iftrue, _iffalse)
#define _LIBUARTE_ASYNC_EVAL1(_eval_flag, _iftrue, _iffalse) \
_LIBUARTE_ASYNC_EVAL2(_LIBUARTE_ASYNC_ZZZZ##_eval_flag, _iftrue, _iffalse)
#define _LIBUARTE_ASYNC_ZZZZ1 _LIBUARTE_ASYNC_YYYY,
#define _LIBUARTE_ASYNC_EVAL2(one_or_two_args, _iftrue, _iffalse) \
__LIBUARTE_ASYNC_ARG_2_DEBRACKET(one_or_two_args _iftrue, _iffalse)
/**
* @brief Macro for creating instance of libuarte_async.
*
* Libuarte_async requires one timer-like peripheral (RTC or TIMER) for triggering RX timeout.
* Macro will create instance only for peripheral which is used.
*
* @param _name Instance name.
* @param _uarte_idx UARTE instance used.
* @param _timer0_idx TIMER instance used by libuarte for bytes counting.
* @param _rtc1_idx RTC instance used for timeout. If set to NRF_LIBUARTE_PERIPHERAL_NOT_USED
* then TIMER instance is used or app_timer instance if _timer1_idx is also set
* to NRF_LIBUARTE_PERIPHERAL_NOT_USED.
* @param _timer1_idx TIMER instance used for timeout. If set to NRF_LIBUARTE_PERIPHERAL_NOT_USED
* then RTC instance is used or app_timer instance if _rtc1_idx is also set
* to NRF_LIBUARTE_PERIPHERAL_NOT_USED.
* @param _rx_buf_size Size of single RX buffer. Size impacts accepted latency between buffer
* request and providing next buffer. Next must be provided within before
* _rx_buf_size bytes is received.
* @param _rx_buf_cnt Number of buffers in the RX buffer pool. Size impacts accepted latency
* between NRF_LIBUARTE_ASYNC_EVT_RX_DATA event and
* @ref nrf_libuarte_async_rx_free.
*/
#define NRF_LIBUARTE_ASYNC_DEFINE(_name, _uarte_idx, _timer0_idx,\
_rtc1_idx, _timer1_idx,\
_rx_buf_size, _rx_buf_cnt) \
STATIC_ASSERT(_rx_buf_cnt >= 3, "Wrong number of RX buffers");\
STATIC_ASSERT(!((NRF_LIBUARTE_ASYNC_WITH_APP_TIMER == 0) && \
(_rtc1_idx == NRF_LIBUARTE_PERIPHERAL_NOT_USED) && \
(_timer1_idx == NRF_LIBUARTE_PERIPHERAL_NOT_USED)), \
"App timer support disabled");\
NRF_LIBUARTE_DRV_DEFINE(CONCAT_2(_name, _libuarte), _uarte_idx, _timer0_idx);\
NRF_QUEUE_DEF(uint8_t *, CONCAT_2(_name,_rxdata_queue), _rx_buf_cnt, NRF_QUEUE_MODE_NO_OVERFLOW);\
NRF_BALLOC_DEF(CONCAT_2(_name,_rx_pool), _rx_buf_size, _rx_buf_cnt);\
/* Create TIMER instance only if _timer1_idx != NRF_LIBUARTE_PERIPHERAL_NOT_USED */ \
_LIBUARTE_ASYNC_EVAL(\
NRFX_CONCAT_3(NRFX_TIMER, _timer1_idx, _ENABLED),\
(STATIC_ASSERT((_timer1_idx == NRF_LIBUARTE_PERIPHERAL_NOT_USED) || (CONCAT_3(NRFX_TIMER,_timer1_idx, _ENABLED) == 1), "TIMER instance not enabled");\
static const nrfx_timer_t CONCAT_2(_name, _timer) = NRFX_TIMER_INSTANCE(_timer1_idx);),\
(/* empty */))\
/* Create RTC instance only if _timer1_idx != NRF_LIBUARTE_PERIPHERAL_NOT_USED */ \
_LIBUARTE_ASYNC_EVAL(\
NRFX_CONCAT_3(NRFX_RTC, _rtc1_idx, _ENABLED),\
(STATIC_ASSERT((_rtc1_idx == NRF_LIBUARTE_PERIPHERAL_NOT_USED) || (CONCAT_3(NRFX_RTC,_rtc1_idx, _ENABLED) == 1), "RTC instance not enabled");\
static const nrfx_rtc_t CONCAT_2(_name, _rtc) = NRFX_RTC_INSTANCE(_rtc1_idx);),\
(/* empty */))\
_LIBUARTE_ASYNC_EVAL(NRFX_CONCAT_3(NRFX_TIMER, _timer1_idx, _ENABLED),\
(/* empty */),\
(_LIBUARTE_ASYNC_EVAL(NRFX_CONCAT_3(NRFX_RTC, _rtc1_idx, _ENABLED),(/* empty */), \
(APP_TIMER_DEF(CONCAT_2(_name,_app_timer)); \
nrf_libuarte_app_timer_ctrl_blk_t CONCAT_2(_name,_app_timer_ctrl_blk);))) \
)\
static nrf_libuarte_async_ctrl_blk_t CONCAT_2(_name, ctrl_blk);\
_LIBUARTE_ASYNC_EVAL(\
NRFX_CONCAT_3(NRFX_RTC, _rtc1_idx, _ENABLED), \
(static void CONCAT_2(_name, _rtc_handler)(nrfx_rtc_int_type_t int_type);),\
(/* empty */)) \
\
static const nrf_libuarte_async_t _name = {\
.p_rx_pool = &CONCAT_2(_name,_rx_pool),\
.p_rx_queue = &CONCAT_2(_name,_rxdata_queue),\
/* If p_rtc is not NULL it means that RTC is used for RX timeout */ \
.p_rtc = _LIBUARTE_ASYNC_EVAL(NRFX_CONCAT_3(NRFX_RTC, _rtc1_idx, _ENABLED), (&CONCAT_2(_name, _rtc)), (NULL)),\
/* If p_timer is not NULL it means that RTC is used for RX timeout */ \
.p_timer = _LIBUARTE_ASYNC_EVAL(NRFX_CONCAT_3(NRFX_TIMER, _timer1_idx, _ENABLED), (&CONCAT_2(_name, _timer)), (NULL)),\
/* If p_time and p_rtc is NULL it means that app_timer is used for RX timeout */ \
.p_app_timer = _LIBUARTE_ASYNC_EVAL(NRFX_CONCAT_3(NRFX_TIMER, _timer1_idx, _ENABLED),\
(NULL),\
(_LIBUARTE_ASYNC_EVAL(NRFX_CONCAT_3(NRFX_RTC, _rtc1_idx, _ENABLED),(NULL), \
(&CONCAT_2(_name,_app_timer)))) \
),\
.p_app_timer_ctrl_blk = _LIBUARTE_ASYNC_EVAL(NRFX_CONCAT_3(NRFX_TIMER, _timer1_idx, _ENABLED),\
(NULL),\
(_LIBUARTE_ASYNC_EVAL(NRFX_CONCAT_3(NRFX_RTC, _rtc1_idx, _ENABLED),(NULL), \
(&CONCAT_2(_name,_app_timer_ctrl_blk)))) \
),\
.p_libuarte = &CONCAT_2(_name, _libuarte),\
.p_ctrl_blk = &CONCAT_2(_name, ctrl_blk),\
.rx_buf_size = _rx_buf_size,\
_LIBUARTE_ASYNC_EVAL(\
NRFX_CONCAT_3(NRFX_RTC, _rtc1_idx, _ENABLED),\
(.rtc_handler =CONCAT_2(_name, _rtc_handler)),\
()\
)\
};\
/* RTC compare event is not periodic but need to be enabled again in the callback. */ \
_LIBUARTE_ASYNC_EVAL(\
NRFX_CONCAT_3(NRFX_RTC, _rtc1_idx, _ENABLED),\
(\
static void CONCAT_2(_name, _rtc_handler)(nrfx_rtc_int_type_t int_type)\
{ \
(void)nrfx_rtc_cc_set(_name.p_rtc, 0, _name.p_ctrl_blk->timeout_us/32, true);\
nrf_libuarte_async_timeout_handler(&_name);\
}\
),\
()\
)
/**
* @brief Function for initializing the libuarte async library.
*
* @param[in] p_libuarte Libuarte_async instance.
* @param[in] p_config Pointer to the structure with initial configuration.
* @param[in] evt_handler Event handler provided by the user. Must not be NULL.
* @param[in] context User context passed to the event handler.
*
* @return NRF_SUCCESS when properly initialized. NRF_ERROR_INTERNAL otherwise.
*/
ret_code_t nrf_libuarte_async_init(const nrf_libuarte_async_t * const p_libuarte,
nrf_libuarte_async_config_t const * p_config,
nrf_libuarte_async_evt_handler_t evt_handler,
void * context);
/** @brief Function for uninitializing the libuarte async library.
*
* @param[in] p_libuarte Libuarte_async instance.
*/
void nrf_libuarte_async_uninit(const nrf_libuarte_async_t * const p_libuarte);
/**
* @brief Function for enabling receiver.
*
* @param p_libuarte Libuarte_async instance.
*/
void nrf_libuarte_async_enable(const nrf_libuarte_async_t * const p_libuarte);
/**
* @brief Function for deasserting RTS to pause the transmission.
*
* Flow control must be enabled.
*
* @param p_libuarte Libuarte_async instance.
*/
void nrf_libuarte_async_rts_clear(const nrf_libuarte_async_t * const p_libuarte);
/**
* @brief Function for asserting RTS to restart the transmission.
*
* Flow control must be enabled.
*
* @param p_libuarte Libuarte_async instance.
*/
void nrf_libuarte_async_rts_set(const nrf_libuarte_async_t * const p_libuarte);
/**
* @brief Function for sending data asynchronously over UARTE.
*
* @param[in] p_libuarte Libuarte_async instance.
* @param[in] p_data Pointer to data.
* @param[in] length Number of bytes to send. Maximum possible length is
* dependent on the used SoC (see the MAXCNT register
* description in the Product Specification). The library
* checks it with assertion.
*
* @retval NRF_ERROR_BUSY Data is transferring.
* @retval NRF_ERROR_INTERNAL Error during configuration.
* @retval NRF_SUCCESS Buffer set for sending.
*/
ret_code_t nrf_libuarte_async_tx(const nrf_libuarte_async_t * const p_libuarte,
uint8_t * p_data, size_t length);
/**
* @brief Function for deallocating received buffer data.
*
* @param[in] p_libuarte Libuarte_async instance.
* @param[in] p_data Pointer to data.
* @param[in] length Number of bytes to free.
*/
void nrf_libuarte_async_rx_free(const nrf_libuarte_async_t * const p_libuarte,
uint8_t * p_data, size_t length);
/** @} */
#endif //UART_ASYNC_H

View File

@@ -0,0 +1,868 @@
/**
* Copyright (c) 2019 - 2020, 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 form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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.
*
*/
#include "sdk_config.h"
#include "nrf_libuarte_drv.h"
#include "nrf_uarte.h"
#include "nrf_gpio.h"
#include <nrfx_gpiote.h>
#include <../src/prs/nrfx_prs.h>
#define NRF_LOG_MODULE_NAME libUARTE
#if NRF_LIBUARTE_CONFIG_LOG_ENABLED
#define NRF_LOG_LEVEL NRF_LIBUARTE_CONFIG_LOG_LEVEL
#define NRF_LOG_INFO_COLOR NRF_LIBUARTE_CONFIG_INFO_COLOR
#define NRF_LOG_DEBUG_COLOR NRF_LIBUARTE_CONFIG_DEBUG_COLOR
#else // NRF_LIBUARTE_CONFIG_LOG_ENABLED
#define NRF_LOG_LEVEL 0
#endif // NRF_LIBUARTE_CONFIG_LOG_ENABLED
#include "nrf_log.h"
NRF_LOG_MODULE_REGISTER();
#define MAX_DMA_XFER_LEN ((1UL << UARTE0_EASYDMA_MAXCNT_SIZE) - 1)
#define INTERRUPTS_MASK \
(NRF_UARTE_INT_ENDRX_MASK | NRF_UARTE_INT_RXSTARTED_MASK | NRF_UARTE_INT_ERROR_MASK | \
NRF_UARTE_INT_ENDTX_MASK | NRF_UARTE_INT_TXSTOPPED_MASK)
static const nrf_libuarte_drv_t * m_libuarte_instance[2];
/* if it is defined it means that PRS for uart is not used. */
#ifdef nrfx_uarte_0_irq_handler
#define libuarte_0_irq_handler UARTE0_UART0_IRQHandler
#endif
#if NRFX_CHECK(NRF_LIBUARTE_DRV_UARTE0)
void libuarte_0_irq_handler(void);
#endif
#if NRFX_CHECK(NRF_LIBUARTE_DRV_UARTE1)
void libuarte_1_irq_handler(void);
#endif
#if defined(NRF_LIBUARTE_DRV_HWFC_ENABLED)
#define LIBUARTE_DRV_WITH_HWFC NRF_LIBUARTE_DRV_HWFC_ENABLED
#else
#define LIBUARTE_DRV_WITH_HWFC 1
#endif
#define RTS_PIN_DISABLED 0xff
/** @brief Macro executes given function on every allocated channel in the list between provided
* indexes.
*/
#define PPI_CHANNEL_FOR_M_N(p_libuarte, m, n, func) \
for (int i = m; i < n; i++) \
{ \
if (p_libuarte->ctrl_blk->ppi_channels[i] < PPI_CH_NUM) \
{ func(&p_libuarte->ctrl_blk->ppi_channels[i]); } \
}
/** @brief Macro executes provided function on every allocated PPI channel. */
#define PPI_CHANNEL_FOR_ALL(p_libuarte, func) \
PPI_CHANNEL_FOR_M_N(p_libuarte, 0, NRF_LIBUARTE_DRV_PPI_CH_MAX, func)
/** @brief Macro executes provided function on every allocated group in the list. */
#define PPI_GROUP_FOR_ALL(p_libuarte, func) \
for (int i = 0; i < NRF_LIBUARTE_DRV_PPI_GROUP_MAX; i++) \
{ \
if (p_libuarte->ctrl_blk->ppi_groups[i] < PPI_GROUP_NUM) \
{ func(&p_libuarte->ctrl_blk->ppi_groups[i]); } \
}
/** @brief Allocate and configure PPI channel. Fork is optional and it's not set if NULL.
* Channel parameter is field by the function.
*/
static ret_code_t ppi_channel_configure(nrf_ppi_channel_t * p_ch, uint32_t evt,
uint32_t task, uint32_t fork)
{
nrfx_err_t err;
err = nrfx_ppi_channel_alloc(p_ch);
if (err != NRFX_SUCCESS)
{
return NRF_ERROR_NO_MEM;
}
err = nrfx_ppi_channel_assign(*p_ch, evt, task);
if (err != NRFX_SUCCESS)
{
return NRF_ERROR_INTERNAL;
}
if (fork)
{
err = nrfx_ppi_channel_fork_assign(*p_ch, fork);
if (err != NRFX_SUCCESS)
{
return NRF_ERROR_INTERNAL;
}
}
return NRF_SUCCESS;
}
/** @brief Allocate and configure group with one channel. Fetch addresses of enable/disable tasks.*/
static ret_code_t ppi_group_configure(nrf_ppi_channel_group_t * p_ppi_group, nrf_ppi_channel_t ch,
uint32_t * p_en_task, uint32_t * p_dis_task, bool en)
{
nrfx_err_t err;
err = nrfx_ppi_group_alloc(p_ppi_group);
if (err != NRFX_SUCCESS)
{
return NRF_ERROR_NO_MEM;
}
err = nrfx_ppi_channel_include_in_group(ch, *p_ppi_group);
if (err != NRFX_SUCCESS)
{
return NRF_ERROR_INTERNAL;
}
if (en)
{
err = nrfx_ppi_group_enable(*p_ppi_group);
if (err != NRFX_SUCCESS)
{
return NRF_ERROR_INTERNAL;
}
}
*p_en_task = nrfx_ppi_task_addr_group_enable_get(*p_ppi_group);
*p_dis_task = nrfx_ppi_task_addr_group_disable_get(*p_ppi_group);
return NRF_SUCCESS;
}
/** @brief Disable and free PPI channel. */
static void ppi_ch_free(nrf_ppi_channel_t * p_ch)
{
nrfx_err_t err;
err = nrfx_ppi_channel_disable(*p_ch);
ASSERT(err == NRFX_SUCCESS);
err = nrfx_ppi_channel_free(*p_ch);
ASSERT(err == NRFX_SUCCESS);
*p_ch = (nrf_ppi_channel_t)PPI_CH_NUM;
}
/** @brief Disable and free PPI group. */
static void ppi_group_free(nrf_ppi_channel_group_t * p_group)
{
nrfx_err_t err;
err = nrfx_ppi_group_free(*p_group);
ASSERT(err == NRFX_SUCCESS);
*p_group = (nrf_ppi_channel_group_t)PPI_GROUP_NUM;
}
/** @brief Free all channels. */
static void ppi_free(const nrf_libuarte_drv_t * const p_libuarte)
{
PPI_CHANNEL_FOR_ALL(p_libuarte, ppi_ch_free);
PPI_GROUP_FOR_ALL(p_libuarte, ppi_group_free);
}
/** @brief Enable PPI channel. */
static void ppi_ch_enable(nrf_ppi_channel_t * p_ch)
{
nrfx_err_t err;
err = nrfx_ppi_channel_enable(*p_ch);
ASSERT(err == NRFX_SUCCESS);
}
/** @brief Disable PPI channel. */
static void ppi_ch_disable(nrf_ppi_channel_t * p_ch)
{
nrfx_err_t err;
err = nrfx_ppi_channel_disable(*p_ch);
ASSERT(err == NRFX_SUCCESS);
}
/** @brief Enable PPI channels for RX. */
static void rx_ppi_enable(const nrf_libuarte_drv_t * const p_libuarte)
{
PPI_CHANNEL_FOR_M_N(p_libuarte, 0, NRF_LIBUARTE_DRV_PPI_CH_RX_GROUP_MAX, ppi_ch_enable);
}
/** @brief Disable PPI channels for RX. */
static void rx_ppi_disable(const nrf_libuarte_drv_t * const p_libuarte)
{
PPI_CHANNEL_FOR_M_N(p_libuarte, 0, NRF_LIBUARTE_DRV_PPI_CH_RX_GROUP_MAX, ppi_ch_disable);
}
/** @brief Enable PPI channels for TX. */
static void tx_ppi_enable(const nrf_libuarte_drv_t * const p_libuarte)
{
PPI_CHANNEL_FOR_M_N(p_libuarte, NRF_LIBUARTE_DRV_PPI_CH_RX_GROUP_MAX,
NRF_LIBUARTE_DRV_PPI_CH_MAX, ppi_ch_enable);
}
/** @brief Disable PPI channels for TX. */
static void tx_ppi_disable(const nrf_libuarte_drv_t * const p_libuarte)
{
PPI_CHANNEL_FOR_M_N(p_libuarte, NRF_LIBUARTE_DRV_PPI_CH_RX_GROUP_MAX,
NRF_LIBUARTE_DRV_PPI_CH_MAX, ppi_ch_disable);
}
static ret_code_t ppi_configure(const nrf_libuarte_drv_t * const p_libuarte,
nrf_libuarte_drv_config_t * p_config)
{
ret_code_t ret;
uint32_t gr0_en_task = 0;
uint32_t gr0_dis_task = 0;
uint32_t gr1_en_task = 0;
uint32_t gr1_dis_task = 0;
for (int i = 0; i < NRF_LIBUARTE_DRV_PPI_CH_MAX; i++)
{
/* set to invalid value */
p_libuarte->ctrl_blk->ppi_channels[i] = (nrf_ppi_channel_t)PPI_CH_NUM;
}
for (int i = 0; i < NRF_LIBUARTE_DRV_PPI_GROUP_MAX; i++)
{
/* set to invalid value */
p_libuarte->ctrl_blk->ppi_groups[i] = (nrf_ppi_channel_group_t)PPI_GROUP_NUM;
}
if (MAX_DMA_XFER_LEN < UINT16_MAX)
{
ret = ppi_channel_configure(
&p_libuarte->ctrl_blk->ppi_channels[NRF_LIBUARTE_DRV_PPI_CH_ENDTX_STARTTX],
nrf_uarte_event_address_get(p_libuarte->uarte, NRF_UARTE_EVENT_ENDTX),
nrf_uarte_task_address_get(p_libuarte->uarte, NRF_UARTE_TASK_STARTTX),
0);
if (ret != NRF_SUCCESS)
{
goto complete_config;
}
}
ret = ppi_channel_configure(
&p_libuarte->ctrl_blk->ppi_channels[NRF_LIBUARTE_DRV_PPI_CH_RXRDY_TIMER_COUNT],
nrf_uarte_event_address_get(p_libuarte->uarte, NRF_UARTE_EVENT_RXDRDY),
nrfx_timer_task_address_get(&p_libuarte->timer, NRF_TIMER_TASK_COUNT),
0);
if (ret != NRF_SUCCESS)
{
goto complete_config;
}
ret = ppi_channel_configure(
&p_libuarte->ctrl_blk->ppi_channels[NRF_LIBUARTE_DRV_PPI_CH_ENDRX_STARTRX],
nrf_uarte_event_address_get(p_libuarte->uarte, NRF_UARTE_EVENT_ENDRX),
nrf_uarte_task_address_get(p_libuarte->uarte, NRF_UARTE_TASK_STARTRX),
nrfx_timer_capture_task_address_get(&p_libuarte->timer, 0));
if (ret != NRF_SUCCESS)
{
goto complete_config;
}
if (p_config->endrx_evt && p_config->rxdone_tsk)
{
ret = ppi_channel_configure(
&p_libuarte->ctrl_blk->ppi_channels[NRF_LIBUARTE_DRV_PPI_CH_ENDRX_EXT_TSK],
nrf_uarte_event_address_get(p_libuarte->uarte, NRF_UARTE_EVENT_ENDRX),
nrfx_timer_capture_task_address_get(&p_libuarte->timer, 0),
p_config->rxdone_tsk);
if (ret != NRF_SUCCESS)
{
goto complete_config;
}
ret = ppi_group_configure(&p_libuarte->ctrl_blk->ppi_groups[NRF_LIBUARTE_DRV_PPI_GROUP_ENDRX_STARTRX],
p_libuarte->ctrl_blk->ppi_channels[NRF_LIBUARTE_DRV_PPI_CH_ENDRX_STARTRX],
&gr0_en_task, &gr0_dis_task, true);
if (ret != NRF_SUCCESS)
{
goto complete_config;
}
ret = ppi_group_configure(&p_libuarte->ctrl_blk->ppi_groups[NRF_LIBUARTE_DRV_PPI_GROUP_ENDRX_EXT_RXDONE_TSK],
p_libuarte->ctrl_blk->ppi_channels[NRF_LIBUARTE_DRV_PPI_CH_ENDRX_EXT_TSK],
&gr1_en_task, &gr1_dis_task, false);
if (ret != NRF_SUCCESS)
{
goto complete_config;
}
ret = ppi_channel_configure(
&p_libuarte->ctrl_blk->ppi_channels[NRF_LIBUARTE_DRV_PPI_CH_EXT_STOP_STOPRX],
p_config->endrx_evt,
nrf_uarte_task_address_get(p_libuarte->uarte, NRF_UARTE_TASK_STOPRX),
nrfx_timer_capture_task_address_get(&p_libuarte->timer, 1));
if (ret != NRF_SUCCESS)
{
goto complete_config;
}
ret = ppi_channel_configure(
&p_libuarte->ctrl_blk->ppi_channels[NRF_LIBUARTE_DRV_PPI_CH_EXT_STOP_GROUPS_EN],
p_config->endrx_evt,
gr0_dis_task,
gr1_en_task);
if (ret != NRF_SUCCESS)
{
goto complete_config;
}
}
if (p_config->rxstarted_tsk || gr1_dis_task)
{
ret = ppi_channel_configure(
&p_libuarte->ctrl_blk->ppi_channels[NRF_LIBUARTE_DRV_PPI_CH_RXSTARTED_EXT_TSK],
nrf_uarte_event_address_get(p_libuarte->uarte, NRF_UARTE_EVENT_RXSTARTED),
gr1_dis_task ? gr1_dis_task : p_config->rxstarted_tsk,
gr1_dis_task ? p_config->rxstarted_tsk : 0);
if (ret != NRF_SUCCESS)
{
goto complete_config;
}
}
if (p_config->startrx_evt)
{
ret = ppi_channel_configure(
&p_libuarte->ctrl_blk->ppi_channels[NRF_LIBUARTE_DRV_PPI_CH_EXT_TRIGGER_STARTRX_EN_ENDRX_STARTX],
p_config->startrx_evt,
nrf_uarte_task_address_get(p_libuarte->uarte, NRF_UARTE_TASK_STARTRX),
gr0_en_task);
if (ret != NRF_SUCCESS)
{
goto complete_config;
}
}
if (p_config->endrx_evt)
{
}
if (LIBUARTE_DRV_WITH_HWFC && (p_config->rts_pin != NRF_UARTE_PSEL_DISCONNECTED))
{
ret = ppi_channel_configure(&p_libuarte->ctrl_blk->ppi_channels[NRF_LIBUARTE_DRV_PPI_CH_RTS_PIN],
nrfx_timer_compare_event_address_get(&p_libuarte->timer, 2),
nrfx_gpiote_set_task_addr_get(p_config->rts_pin),
0);
if (ret != NRF_SUCCESS)
{
goto complete_config;
}
}
complete_config:
if (ret == NRF_SUCCESS)
{
return ret;
}
ppi_free(p_libuarte);
return ret;
}
void tmr_evt_handler(nrf_timer_event_t event_type, void * p_context)
{
UNUSED_PARAMETER(event_type);
UNUSED_PARAMETER(p_context);
}
ret_code_t nrf_libuarte_drv_init(const nrf_libuarte_drv_t * const p_libuarte,
nrf_libuarte_drv_config_t * p_config,
nrf_libuarte_drv_evt_handler_t evt_handler,
void * context)
{
ret_code_t ret;
IRQn_Type irqn = nrfx_get_irq_number(p_libuarte->uarte);
if (p_libuarte->ctrl_blk->enabled)
{
return NRF_ERROR_INVALID_STATE;
}
p_libuarte->ctrl_blk->evt_handler = evt_handler;
p_libuarte->ctrl_blk->p_cur_rx = NULL;
p_libuarte->ctrl_blk->p_next_rx = NULL;
p_libuarte->ctrl_blk->p_next_next_rx = NULL;
p_libuarte->ctrl_blk->p_tx = NULL;
p_libuarte->ctrl_blk->context = context;
p_libuarte->ctrl_blk->rts_pin = RTS_PIN_DISABLED;
m_libuarte_instance[p_libuarte->uarte == NRF_UARTE0 ? 0 : 1] = p_libuarte;
//UART init
nrf_gpio_pin_set(p_config->tx_pin);
nrf_gpio_cfg_output(p_config->tx_pin);
nrf_gpio_cfg_input(p_config->rx_pin, p_config->pullup_rx ?
NRF_GPIO_PIN_PULLUP : NRF_GPIO_PIN_NOPULL);
nrf_uarte_baudrate_set(p_libuarte->uarte, p_config->baudrate);
nrf_uarte_configure(p_libuarte->uarte, p_config->parity, p_config->hwfc);
nrf_uarte_txrx_pins_set(p_libuarte->uarte, p_config->tx_pin, p_config->rx_pin);
if (LIBUARTE_DRV_WITH_HWFC && (p_config->hwfc == NRF_UARTE_HWFC_ENABLED))
{
if (p_config->cts_pin != NRF_UARTE_PSEL_DISCONNECTED)
{
nrf_gpio_cfg_input(p_config->cts_pin, NRF_GPIO_PIN_PULLUP);
}
if (p_config->rts_pin != NRF_UARTE_PSEL_DISCONNECTED)
{
nrfx_gpiote_out_config_t out_config = NRFX_GPIOTE_CONFIG_OUT_TASK_TOGGLE(true);
nrfx_err_t err = nrfx_gpiote_init();
if ((err != NRFX_SUCCESS) && (err != NRFX_ERROR_INVALID_STATE))
{
return err;
}
err = nrfx_gpiote_out_init(p_config->rts_pin, &out_config);
if (err != NRFX_SUCCESS)
{
return NRF_ERROR_INTERNAL;
}
nrfx_gpiote_out_task_enable(p_config->rts_pin);
nrf_gpio_cfg_output(p_config->rts_pin);
p_libuarte->ctrl_blk->rts_pin = p_config->rts_pin;
}
nrf_uarte_hwfc_pins_set(p_libuarte->uarte, NRF_UARTE_PSEL_DISCONNECTED, p_config->cts_pin);
}
else if ((p_config->hwfc == NRF_UARTE_HWFC_ENABLED) && !LIBUARTE_DRV_WITH_HWFC)
{
return NRFX_ERROR_INVALID_PARAM;
}
#if NRFX_CHECK(NRFX_PRS_ENABLED) && NRFX_CHECK(NRF_LIBUARTE_DRV_UARTE0)
if (irqn == UARTE0_UART0_IRQn)
{
if (nrfx_prs_acquire(p_libuarte->uarte, libuarte_0_irq_handler) != NRFX_SUCCESS)
{
return NRF_ERROR_BUSY;
}
}
#endif // NRFX_CHECK(NRFX_PRS_ENABLED) && NRFX_CHECK(NRF_LIBUARTE_DRV_UARTE0)
nrf_uarte_int_enable(p_libuarte->uarte, INTERRUPTS_MASK);
NVIC_SetPriority(irqn, p_config->irq_priority);
NVIC_ClearPendingIRQ(irqn);
NVIC_EnableIRQ(irqn);
nrf_uarte_enable(p_libuarte->uarte);
nrfx_timer_config_t tmr_config = NRFX_TIMER_DEFAULT_CONFIG;
tmr_config.mode = NRF_TIMER_MODE_COUNTER;
tmr_config.bit_width = NRF_TIMER_BIT_WIDTH_32;
ret = nrfx_timer_init(&p_libuarte->timer, &tmr_config, tmr_evt_handler);
if (ret != NRFX_SUCCESS)
{
return NRF_ERROR_INTERNAL;
}
ret = ppi_configure(p_libuarte, p_config);
if (ret != NRF_SUCCESS)
{
return NRF_ERROR_INTERNAL;
}
p_libuarte->ctrl_blk->enabled = true;
return NRF_SUCCESS;
}
void nrf_libuarte_drv_uninit(const nrf_libuarte_drv_t * const p_libuarte)
{
IRQn_Type irqn = nrfx_get_irq_number(p_libuarte->uarte);
if (p_libuarte->ctrl_blk->enabled == false)
{
return;
}
p_libuarte->ctrl_blk->enabled = false;
NVIC_DisableIRQ(irqn);
rx_ppi_disable(p_libuarte);
tx_ppi_disable(p_libuarte);
nrf_uarte_int_disable(p_libuarte->uarte, 0xFFFFFFFF);
nrf_uarte_event_clear(p_libuarte->uarte, NRF_UARTE_EVENT_TXSTOPPED);
nrf_uarte_event_clear(p_libuarte->uarte, NRF_UARTE_EVENT_RXTO);
nrf_uarte_task_trigger(p_libuarte->uarte, NRF_UARTE_TASK_STOPTX);
nrf_uarte_task_trigger(p_libuarte->uarte, NRF_UARTE_TASK_STOPRX);
while ( (p_libuarte->ctrl_blk->p_tx && !nrf_uarte_event_check(p_libuarte->uarte, NRF_UARTE_EVENT_TXSTOPPED)) ||
(p_libuarte->ctrl_blk->p_cur_rx && !nrf_uarte_event_check(p_libuarte->uarte, NRF_UARTE_EVENT_RXTO)))
{}
p_libuarte->ctrl_blk->p_tx = NULL;
p_libuarte->ctrl_blk->p_cur_rx = NULL;
nrf_uarte_disable(p_libuarte->uarte);
nrf_uarte_event_clear(p_libuarte->uarte, NRF_UARTE_EVENT_TXSTARTED);
nrf_uarte_event_clear(p_libuarte->uarte, NRF_UARTE_EVENT_TXSTOPPED);
nrf_uarte_event_clear(p_libuarte->uarte, NRF_UARTE_EVENT_ENDTX);
nrf_uarte_event_clear(p_libuarte->uarte, NRF_UARTE_EVENT_ENDRX);
nrf_uarte_event_clear(p_libuarte->uarte, NRF_UARTE_EVENT_RXSTARTED);
nrf_uarte_event_clear(p_libuarte->uarte, NRF_UARTE_EVENT_RXTO);
#if NRFX_CHECK(NRFX_PRS_ENABLED) && NRFX_CHECK(NRF_LIBUARTE_DRV_UARTE0)
if (irqn == UARTE0_UART0_IRQn)
{
nrfx_prs_release(p_libuarte->uarte);
}
#endif // NRFX_CHECK(NRFX_PRS_ENABLED) && NRFX_CHECK(NRF_LIBUARTE_DRV_UARTE0)
nrfx_timer_disable(&p_libuarte->timer);
nrfx_timer_uninit(&p_libuarte->timer);
if (LIBUARTE_DRV_WITH_HWFC && (p_libuarte->ctrl_blk->rts_pin != RTS_PIN_DISABLED))
{
nrfx_gpiote_out_uninit(p_libuarte->ctrl_blk->rts_pin);
}
ppi_free(p_libuarte);
}
ret_code_t nrf_libuarte_drv_tx(const nrf_libuarte_drv_t * const p_libuarte,
uint8_t * p_data, size_t len)
{
if (p_libuarte->ctrl_blk->p_tx)
{
return NRF_ERROR_BUSY;
}
p_libuarte->ctrl_blk->p_tx = p_data;
p_libuarte->ctrl_blk->tx_len = len;
p_libuarte->ctrl_blk->tx_cur_idx = 0;
uint16_t first_chunk;
if ((MAX_DMA_XFER_LEN <= UINT16_MAX) && (len <= MAX_DMA_XFER_LEN))
{
first_chunk = len;
p_libuarte->ctrl_blk->tx_chunk8 = 0;
}
else
{
uint32_t num_of_chunks = CEIL_DIV(len, MAX_DMA_XFER_LEN);
p_libuarte->ctrl_blk->tx_chunk8 = len/num_of_chunks;
first_chunk = p_libuarte->ctrl_blk->tx_chunk8 + len%p_libuarte->ctrl_blk->tx_chunk8;
}
NRF_LOG_WARNING("Started TX total length:%d, first chunk:%d", len, first_chunk);
nrf_uarte_tx_buffer_set(p_libuarte->uarte, p_data, first_chunk);
nrf_uarte_event_clear(p_libuarte->uarte, NRF_UARTE_EVENT_TXSTARTED);
nrf_uarte_task_trigger(p_libuarte->uarte, NRF_UARTE_TASK_STARTTX);
if ((MAX_DMA_XFER_LEN <= UINT16_MAX) && (len > MAX_DMA_XFER_LEN))
{
while(nrf_uarte_event_check(p_libuarte->uarte, NRF_UARTE_EVENT_TXSTARTED) == 0)
{
}
nrf_uarte_event_clear(p_libuarte->uarte, NRF_UARTE_EVENT_TXSTARTED);
tx_ppi_enable(p_libuarte);
nrf_uarte_tx_buffer_set(p_libuarte->uarte, &p_libuarte->ctrl_blk->p_tx[first_chunk], p_libuarte->ctrl_blk->tx_chunk8);
}
return NRF_SUCCESS;
}
ret_code_t nrf_libuarte_drv_rx_start(const nrf_libuarte_drv_t * const p_libuarte,
uint8_t * p_data, size_t len, bool ext_trigger_en)
{
ASSERT(len <= MAX_DMA_XFER_LEN);
if (p_libuarte->ctrl_blk->p_cur_rx)
{
return NRF_ERROR_BUSY;
}
p_libuarte->ctrl_blk->chunk_size = len;
if (p_data)
{
p_libuarte->ctrl_blk->p_cur_rx = p_data;
nrf_uarte_rx_buffer_set(p_libuarte->uarte, p_data, len);
}
/* Reset byte counting */
nrfx_timer_enable(&p_libuarte->timer);
nrfx_timer_clear(&p_libuarte->timer);
p_libuarte->ctrl_blk->last_rx_byte_cnt = 0;
p_libuarte->ctrl_blk->last_pin_rx_byte_cnt = 0;
nrf_uarte_event_clear(p_libuarte->uarte, NRF_UARTE_EVENT_ENDRX);
nrf_uarte_event_clear(p_libuarte->uarte, NRF_UARTE_EVENT_RXSTARTED);
rx_ppi_enable(p_libuarte);
if (LIBUARTE_DRV_WITH_HWFC && (p_libuarte->ctrl_blk->rts_pin != RTS_PIN_DISABLED))
{
uint32_t rx_limit = len - NRF_LIBUARTE_DRV_HWFC_BYTE_LIMIT;
*(uint32_t *)nrfx_gpiote_clr_task_addr_get(p_libuarte->ctrl_blk->rts_pin) = 1;
nrfx_timer_compare(&p_libuarte->timer, NRF_TIMER_CC_CHANNEL2, rx_limit, false);
}
if (!ext_trigger_en)
{
nrf_uarte_task_trigger(p_libuarte->uarte, NRF_UARTE_TASK_STARTRX);
}
NRF_LOG_DEBUG("Start continues RX. Provided buffer:0x%08X", p_data);
return NRF_SUCCESS;
}
void nrf_libuarte_drv_rx_buf_rsp(const nrf_libuarte_drv_t * const p_libuarte,
uint8_t * p_data, size_t len)
{
if (p_libuarte->ctrl_blk->p_next_rx == NULL)
{
p_libuarte->ctrl_blk->p_next_rx = p_data;
NRF_LOG_DEBUG("RX buf response (next). Provided buffer:0x%08X", p_data);
nrf_uarte_rx_buffer_set(p_libuarte->uarte, p_data, len);
}
else
{
NRF_LOG_DEBUG("RX buf response (mp_next_rx not NULL:0x%08X), Provided buffer:0x%08X",
p_libuarte->ctrl_blk->p_next_rx, p_data);
p_libuarte->ctrl_blk->p_next_next_rx = p_data;
}
if (LIBUARTE_DRV_WITH_HWFC && (p_libuarte->ctrl_blk->rts_pin != RTS_PIN_DISABLED))
{
uint32_t rx_limit = nrfx_timer_capture_get(&p_libuarte->timer, NRF_TIMER_CC_CHANNEL0) +
2*len - NRF_LIBUARTE_DRV_HWFC_BYTE_LIMIT;
nrfx_timer_compare(&p_libuarte->timer, NRF_TIMER_CC_CHANNEL2, rx_limit, false);
if (p_libuarte->ctrl_blk->rts_manual == false)
{
*(uint32_t *)nrfx_gpiote_clr_task_addr_get(p_libuarte->ctrl_blk->rts_pin) = 1;
}
}
}
void nrf_libuarte_drv_rx_stop(const nrf_libuarte_drv_t * const p_libuarte)
{
rx_ppi_disable(p_libuarte);
NRF_LOG_DEBUG("RX stopped.");
if (LIBUARTE_DRV_WITH_HWFC && (p_libuarte->ctrl_blk->rts_pin != RTS_PIN_DISABLED))
{
*(uint32_t *)nrfx_gpiote_set_task_addr_get(p_libuarte->ctrl_blk->rts_pin) = 1;
}
p_libuarte->ctrl_blk->p_cur_rx = NULL;
nrf_uarte_task_trigger(p_libuarte->uarte, NRF_UARTE_TASK_STOPRX);
}
void nrf_libuarte_drv_rts_clear(const nrf_libuarte_drv_t * const p_libuarte)
{
if (LIBUARTE_DRV_WITH_HWFC && (p_libuarte->ctrl_blk->rts_pin != RTS_PIN_DISABLED))
{
*(uint32_t *)nrfx_gpiote_clr_task_addr_get(p_libuarte->ctrl_blk->rts_pin) = 1;
p_libuarte->ctrl_blk->rts_manual = false;
}
}
void nrf_libuarte_drv_rts_set(const nrf_libuarte_drv_t * const p_libuarte)
{
if (LIBUARTE_DRV_WITH_HWFC && (p_libuarte->ctrl_blk->rts_pin != RTS_PIN_DISABLED))
{
p_libuarte->ctrl_blk->rts_manual = true;
*(uint32_t *)nrfx_gpiote_set_task_addr_get(p_libuarte->ctrl_blk->rts_pin) = 1;
}
}
static void irq_handler(const nrf_libuarte_drv_t * const p_libuarte)
{
if (nrf_uarte_event_check(p_libuarte->uarte, NRF_UARTE_EVENT_ERROR))
{
nrf_uarte_event_clear(p_libuarte->uarte, NRF_UARTE_EVENT_ERROR);
nrf_libuarte_drv_evt_t evt = {
.type = NRF_LIBUARTE_DRV_EVT_ERROR,
.data = { .errorsrc = nrf_uarte_errorsrc_get_and_clear(p_libuarte->uarte) }
};
p_libuarte->ctrl_blk->evt_handler(p_libuarte->ctrl_blk->context, &evt);
}
if (nrf_uarte_event_check(p_libuarte->uarte, NRF_UARTE_EVENT_RXSTARTED))
{
nrf_uarte_event_clear(p_libuarte->uarte, NRF_UARTE_EVENT_RXSTARTED);
nrf_libuarte_drv_evt_t evt = {
.type = NRF_LIBUARTE_DRV_EVT_RX_BUF_REQ,
};
p_libuarte->ctrl_blk->evt_handler(p_libuarte->ctrl_blk->context, &evt);
}
if (nrf_uarte_event_check(p_libuarte->uarte, NRF_UARTE_EVENT_ENDRX))
{
nrf_uarte_event_clear(p_libuarte->uarte, NRF_UARTE_EVENT_ENDRX);
uint32_t endrx_byte_cnt = nrfx_timer_capture_get(&p_libuarte->timer, NRF_TIMER_CC_CHANNEL0);
uint32_t stop_byte_cnt = nrfx_timer_capture_get(&p_libuarte->timer, NRF_TIMER_CC_CHANNEL1);
uint32_t dma_amount = endrx_byte_cnt - p_libuarte->ctrl_blk->last_rx_byte_cnt;
uint32_t pin_amount = stop_byte_cnt - p_libuarte->ctrl_blk->last_pin_rx_byte_cnt;
NRF_LOG_DEBUG("(evt) RX dma_cnt:%d, endrx_cnt:%d, stop_cnt:%d",
dma_amount,
endrx_byte_cnt,
stop_byte_cnt);
p_libuarte->ctrl_blk->last_rx_byte_cnt = endrx_byte_cnt;
p_libuarte->ctrl_blk->last_pin_rx_byte_cnt = stop_byte_cnt;
if (dma_amount || pin_amount)
{
uint32_t chunk0 = (dma_amount > p_libuarte->ctrl_blk->chunk_size) ?
p_libuarte->ctrl_blk->chunk_size : dma_amount;
uint32_t chunk1 = dma_amount - chunk0;
NRF_LOG_DEBUG("RX END chunk0:%d, chunk1:%d, data[0]=%d %d",
chunk0,
chunk1,
p_libuarte->ctrl_blk->p_cur_rx[0],
p_libuarte->ctrl_blk->p_cur_rx[1]);
nrf_libuarte_drv_evt_t evt = {
.type = NRF_LIBUARTE_DRV_EVT_RX_DATA,
.data = {
.rxtx = {
.p_data = p_libuarte->ctrl_blk->p_cur_rx,
.length = chunk0
}
}
};
p_libuarte->ctrl_blk->p_cur_rx = p_libuarte->ctrl_blk->p_next_rx;
p_libuarte->ctrl_blk->p_next_rx = NULL;
if (p_libuarte->ctrl_blk->p_next_next_rx)
{
p_libuarte->ctrl_blk->p_next_rx = p_libuarte->ctrl_blk->p_next_next_rx;
p_libuarte->ctrl_blk->p_next_next_rx = NULL;
nrf_uarte_rx_buffer_set(p_libuarte->uarte,
p_libuarte->ctrl_blk->p_next_rx,
p_libuarte->ctrl_blk->chunk_size);
}
p_libuarte->ctrl_blk->evt_handler(p_libuarte->ctrl_blk->context, &evt);
if ( chunk1 ||
((dma_amount == p_libuarte->ctrl_blk->chunk_size) && (endrx_byte_cnt == stop_byte_cnt)))
{
NRF_LOG_WARNING("RX END Chunk1:%d", chunk1);
nrf_libuarte_drv_evt_t err_evt = {
.type = NRF_LIBUARTE_DRV_EVT_OVERRUN_ERROR,
.data = {
.overrun_err = {
.overrun_length = chunk1
}
}
};
p_libuarte->ctrl_blk->evt_handler(p_libuarte->ctrl_blk->context, &err_evt);
p_libuarte->ctrl_blk->p_cur_rx = p_libuarte->ctrl_blk->p_next_rx;
p_libuarte->ctrl_blk->p_next_rx = NULL;
}
}
}
if (nrf_uarte_event_check(p_libuarte->uarte, NRF_UARTE_EVENT_TXSTOPPED))
{
nrf_uarte_event_clear(p_libuarte->uarte, NRF_UARTE_EVENT_TXSTOPPED);
nrf_libuarte_drv_evt_t evt = {
.type = NRF_LIBUARTE_DRV_EVT_TX_DONE,
.data = {
.rxtx = {
.p_data = p_libuarte->ctrl_blk->p_tx,
.length = p_libuarte->ctrl_blk->tx_len
}
}
};
p_libuarte->ctrl_blk->p_tx = NULL;
p_libuarte->ctrl_blk->evt_handler(p_libuarte->ctrl_blk->context, &evt);
}
if (nrf_uarte_event_check(p_libuarte->uarte, NRF_UARTE_EVENT_ENDTX))
{
nrf_uarte_event_clear(p_libuarte->uarte, NRF_UARTE_EVENT_ENDTX);
size_t amount = nrf_uarte_tx_amount_get(p_libuarte->uarte);
NRF_LOG_DEBUG("(evt) TX completed (%d)", amount);
p_libuarte->ctrl_blk->tx_cur_idx += amount;
if (p_libuarte->ctrl_blk->tx_cur_idx == p_libuarte->ctrl_blk->tx_len)
{
nrf_uarte_event_clear(p_libuarte->uarte, NRF_UARTE_EVENT_TXSTOPPED);
nrf_uarte_task_trigger(p_libuarte->uarte, NRF_UARTE_TASK_STOPTX);
}
else
{
size_t rem_len = (p_libuarte->ctrl_blk->tx_len - p_libuarte->ctrl_blk->tx_cur_idx);
if ( rem_len <= MAX_DMA_XFER_LEN)
{
tx_ppi_disable(p_libuarte);
}
else
{
uint8_t * p_buffer = &p_libuarte->ctrl_blk->p_tx[
p_libuarte->ctrl_blk->tx_cur_idx +
p_libuarte->ctrl_blk->tx_chunk8];
if (nrf_uarte_event_check(p_libuarte->uarte, NRF_UARTE_EVENT_TXSTARTED) == 0)
{
NRF_LOG_ERROR("Tx not started yet!");
ASSERT(false);
}
nrf_uarte_event_clear(p_libuarte->uarte, NRF_UARTE_EVENT_TXSTARTED);
nrf_uarte_tx_buffer_set(p_libuarte->uarte,
p_buffer,
p_libuarte->ctrl_blk->tx_chunk8);
}
}
}
}
#if NRF_LIBUARTE_DRV_UARTE0
void libuarte_0_irq_handler(void)
{
irq_handler(m_libuarte_instance[0]);
}
#endif
#if NRF_LIBUARTE_DRV_UARTE1
void UARTE1_IRQHandler(void)
{
irq_handler(m_libuarte_instance[1]);
}
#endif

View File

@@ -0,0 +1,279 @@
/**
* Copyright (c) 2018 - 2020, 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 form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef NRF_LIBUARTE_DRV_H
#define NRF_LIBUARTE_DRV_H
#include "sdk_errors.h"
#include "nrf_uarte.h"
#include "nrfx_ppi.h"
#include "nrfx_timer.h"
#include <stdint.h>
#include <stdbool.h>
/**
* @defgroup nrf_libuarte_drv libUARTE driver
* @ingroup app_common
*
* @brief Module for reliable communication over UARTE.
*
* @{
*/
/* Number of bytes available in the buffer when RTS line is set. */
#define NRF_LIBUARTE_DRV_HWFC_BYTE_LIMIT 4
typedef enum
{
NRF_LIBUARTE_DRV_EVT_RX_DATA, ///< Data received.
NRF_LIBUARTE_DRV_EVT_RX_BUF_REQ, ///< Requesting new buffer for receiving data.
NRF_LIBUARTE_DRV_EVT_TX_DONE, ///< Requested TX transfer completed.
NRF_LIBUARTE_DRV_EVT_ERROR, ///< Error reported by the UARTE peripheral.
NRF_LIBUARTE_DRV_EVT_OVERRUN_ERROR ///< Error reported by the driver.
} nrf_libuarte_drv_evt_type_t;
/**
* @brief PPI channels used by libuarte
*/
typedef enum
{
NRF_LIBUARTE_DRV_PPI_CH_EXT_TRIGGER_STARTRX_EN_ENDRX_STARTX,
NRF_LIBUARTE_DRV_PPI_CH_RXSTARTED_EXT_TSK,
NRF_LIBUARTE_DRV_PPI_CH_EXT_STOP_STOPRX,
NRF_LIBUARTE_DRV_PPI_CH_EXT_STOP_GROUPS_EN,
NRF_LIBUARTE_DRV_PPI_CH_RXRDY_TIMER_COUNT,
NRF_LIBUARTE_DRV_PPI_CH_ENDRX_STARTRX,
NRF_LIBUARTE_DRV_PPI_CH_ENDRX_EXT_TSK,
NRF_LIBUARTE_DRV_PPI_CH_RTS_PIN,
NRF_LIBUARTE_DRV_PPI_CH_RX_MAX,
NRF_LIBUARTE_DRV_PPI_CH_RX_GROUP_MAX = NRF_LIBUARTE_DRV_PPI_CH_RX_MAX,
NRF_LIBUARTE_DRV_PPI_CH_ENDTX_STARTTX = NRF_LIBUARTE_DRV_PPI_CH_RX_GROUP_MAX,
NRF_LIBUARTE_DRV_PPI_CH_MAX
} nrf_libuarte_drv_ppi_channel_t;
/**
* @brief PPI groups used by libuarte
*/
typedef enum
{
NRF_LIBUARTE_DRV_PPI_GROUP_ENDRX_STARTRX, ///< Group used for controlling PPI connection between ENDRX and STARTRX
NRF_LIBUARTE_DRV_PPI_GROUP_ENDRX_EXT_RXDONE_TSK, ///< Group used for controlling PPI connection between ENDRX and RXDONE
NRF_LIBUARTE_DRV_PPI_GROUP_MAX
} nrf_libuarte_drv_ppi_group_t;
typedef struct
{
uint8_t * p_data; ///< Pointer to the data to be sent or received.
size_t length; ///< Length of the data.
} nrf_libuarte_drv_data_t;
typedef struct
{
uint32_t overrun_length;
} nrf_libuarte_drv_overrun_err_evt_t;
typedef struct
{
nrf_libuarte_drv_evt_type_t type; ///< Event type.
union {
nrf_libuarte_drv_data_t rxtx; ///< Data provided for transfer completion events.
uint8_t errorsrc; ///< Error source flags.
nrf_libuarte_drv_overrun_err_evt_t overrun_err; ///< SW Error structure.
} data;
} nrf_libuarte_drv_evt_t;
typedef struct {
uint32_t tx_pin; ///< TXD pin number.
uint32_t rx_pin; ///< RXD pin number.
uint32_t cts_pin; ///< CTS pin number.
uint32_t rts_pin; ///< RTS pin number.
uint32_t startrx_evt; ///< Event to trigger STARTRX task in UARTE.
uint32_t endrx_evt; ///< Event to trigger STOPRX task in UARTE.
uint32_t rxstarted_tsk; ///< Task to be triggered when RXSTARTED UARTE event occurs.
uint32_t rxdone_tsk; ///< Task to be triggered when ENDRX UARTE event occurs.
nrf_uarte_hwfc_t hwfc; ///< Flow control configuration.
nrf_uarte_parity_t parity; ///< Parity configuration.
nrf_uarte_baudrate_t baudrate; ///< Baud rate.
uint8_t irq_priority; ///< Interrupt priority.
bool pullup_rx; ///< Pull up on RX pin.
} nrf_libuarte_drv_config_t;
typedef void (*nrf_libuarte_drv_evt_handler_t)(void * context,
nrf_libuarte_drv_evt_t * p_evt);
extern const IRQn_Type libuarte_irqn[];
typedef struct {
nrf_ppi_channel_t ppi_channels[NRF_LIBUARTE_DRV_PPI_CH_MAX];
nrf_ppi_channel_group_t ppi_groups[NRF_LIBUARTE_DRV_PPI_GROUP_MAX];
uint8_t * p_tx;
size_t tx_len;
size_t tx_cur_idx;
uint8_t * p_cur_rx;
uint8_t * p_next_rx;
uint8_t * p_next_next_rx;
nrf_libuarte_drv_evt_handler_t evt_handler;
uint32_t last_rx_byte_cnt;
uint32_t last_pin_rx_byte_cnt;
uint32_t chunk_size;
void * context;
uint16_t tx_chunk8;
uint8_t rts_pin;
bool rts_manual;
bool enabled;
} nrf_libuarte_drv_ctrl_blk_t;
typedef struct {
nrf_libuarte_drv_ctrl_blk_t * ctrl_blk;
nrfx_timer_t timer;
NRF_UARTE_Type * uarte;
} nrf_libuarte_drv_t;
#define NRF_LIBUARTE_DRV_DEFINE(_name, _uarte_idx, _timer_idx) \
STATIC_ASSERT(_uarte_idx < UARTE_COUNT, "UARTE instance not present");\
STATIC_ASSERT(CONCAT_2(NRF_LIBUARTE_DRV_UARTE,_uarte_idx) == 1, "UARTE instance not enabled");\
STATIC_ASSERT(CONCAT_3(NRFX_TIMER,_timer_idx, _ENABLED) == 1, "Timer instance not enabled");\
static nrf_libuarte_drv_ctrl_blk_t CONCAT_2(_name, ctrl_blk); \
static const nrf_libuarte_drv_t _name = { \
.ctrl_blk = &CONCAT_2(_name, ctrl_blk), \
.timer = NRFX_TIMER_INSTANCE(_timer_idx), \
.uarte = CONCAT_2(NRF_UARTE, _uarte_idx),\
}
/**
* @brief Function for initializing the libUARTE library.
*
* @param[in] p_libuarte Pointer to libuarte instance.
* @param[in] p_config Pointer to the structure with initial configuration.
* @param[in] evt_handler Event handler provided by the user. Must not be NULL.
* @param[in] context User context passed in the callback.
*
* @return NRF_SUCCESS when properly initialized. NRF_ERROR_INTERNAL otherwise.
*/
ret_code_t nrf_libuarte_drv_init(const nrf_libuarte_drv_t * const p_libuarte,
nrf_libuarte_drv_config_t * p_config,
nrf_libuarte_drv_evt_handler_t evt_handler, void * context);
/**
* @brief Function for uninitializing the libUARTE library.
*
* @param[in] p_libuarte Pointer to libuarte instance.
*/
void nrf_libuarte_drv_uninit(const nrf_libuarte_drv_t * const p_libuarte);
/**
* @brief Function for sending data over UARTE using EasyDMA.
*
* @param[in] p_libuarte Pointer to libuarte instance.
* @param[in] p_data Pointer to data.
* @param[in] len Number of bytes to send.
*
* @retval NRF_ERROR_BUSY Data is transferring.
* @retval NRF_ERROR_INTERNAL Error during PPI channel configuration.
* @retval NRF_SUCCESS Buffer set for sending.
*/
ret_code_t nrf_libuarte_drv_tx(const nrf_libuarte_drv_t * const p_libuarte,
uint8_t * p_data, size_t len);
/**
* @brief Function for starting receiving data with additional configuration of external
* trigger to start receiving.
*
* @param p_libuarte Pointer to libuarte instance.
* @param p_data Pointer to data.
* @param len Number of bytes to receive. Maximum possible length is
* dependent on the used SoC (see the MAXCNT register
* description in the Product Specification). The library
* checks it with an assertion.
* @param ext_trigger_en True to disable immediate start.
*
* @retval NRF_ERROR_INTERNAL Error during PPI channel configuration.
* @retval NRF_SUCCESS Buffer set for receiving.
*/
ret_code_t nrf_libuarte_drv_rx_start(const nrf_libuarte_drv_t * const p_libuarte,
uint8_t * p_data, size_t len, bool ext_trigger_en);
/**
* @brief Function for setting a buffer for data that will be later received in UARTE.
*
* @param p_libuarte Pointer to libuarte instance.
* @param p_data Pointer to data.
* @param len Number of bytes to receive. Maximum possible length is
* dependent on the used SoC (see the MAXCNT register
* description in the Product Specification). The library
* checks it with an assertion.
*/
void nrf_libuarte_drv_rx_buf_rsp(const nrf_libuarte_drv_t * const p_libuarte,
uint8_t * p_data, size_t len);
/**
* @brief Function for stopping receiving data over UARTE.
*
* @param p_libuarte Pointer to libuarte instance.
*/
void nrf_libuarte_drv_rx_stop(const nrf_libuarte_drv_t * const p_libuarte);
/**
* @brief Function for deasserting RTS to pause the transmission.
*
* Flow control must be enabled.
*
* @param p_libuarte Pointer to libuarte instance.
*/
void nrf_libuarte_drv_rts_clear(const nrf_libuarte_drv_t * const p_libuarte);
/**
* @brief Function for asserting RTS to restart the transmission.
*
* Flow control must be enabled.
*
* @param p_libuarte Pointer to libuarte instance.
*/
void nrf_libuarte_drv_rts_set(const nrf_libuarte_drv_t * const p_libuarte);
/** @} */
#endif //NRF_LIBUARTE_DRV_H