/** * 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 #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