HL-PDJ-1/components/libraries/libuarte/nrf_libuarte_async.h

386 lines
16 KiB
C
Raw Permalink Normal View History

2025-08-19 09:49:41 +08:00
/**
* 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