680 lines
24 KiB
C
680 lines
24 KiB
C
/**
|
|
* 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);
|
|
}
|