Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Receiving for UART with FreeRTOS

Hello,

I have implemented a somewhat working RX algorithm for receiving data over UART but I'm looking for a good way to make it more robust. I haven't found any good answers to this in either the forum or the infocenter and I get a little bit confused regrading the driver setup.

My implementation:

  • Custom board using nRF52840
  • Using the nRF5 SDK v15.0.0.
  • Using FreeRTOS, built from the "ble_app_hrs_freertos"-example. (At the moment without BLE stack initialization.)
  • Implemented FreeRTOS task for handling UART communication.
  • Have a module connected over UART that use AT-commands.

Assumptions:

  • As it is AT-commands I always know the ending of the message, though I don't know the length as the reply to one command can be different.
    • An example is reading the ID which gives 24 bytes as answer. If there is a failure, the answer is simply "+ERR=x".
    • There is a response to every TX, thus I want to have RX after TX.

So far I have tried mostly with the "Serial port library" and I have tried using blocking-mode similar to:

uint32_t received_bytes = 0;
err = nrf_serial_write(&serial_uart, tx_tmp, tx_size, NULL, NRF_SERIAL_MAX_TIMEOUT);
err = nrf_serial_read(&serial_uart, rx_tmp, 50, &received_bytes, 100);

This does however only work for the first message. The second time I try with nrf_serial_read() I get an error as the handle for the timer is not null, somewhere around line 136 in app_timer_freertos.c (DUH, it is already created). For experimentation I changed to the following in app_timer_create() function in app_timer_freertos.c and now it works (even though I know it is a VERY ugly solution):

    if (pinfo->osHandle == NULL)
    {
        /* New timer is created */
        memset(pinfo, 0, sizeof(app_timer_info_t));

        if (mode == APP_TIMER_MODE_SINGLE_SHOT)
            timer_mode = pdFALSE;
        else
            timer_mode = pdTRUE;

        pinfo->func = timeout_handler;
        pinfo->osHandle = xTimerCreate(" ", 1000, timer_mode, pinfo, app_timer_callback);

        if (pinfo->osHandle == NULL)
            err_code = NRF_ERROR_NULL;
    }
    else
    {
        xTimerReset(pinfo->osHandle, 100);
        /* Timer cannot be reinitialized using FreeRTOS API */
        //return NRF_ERROR_INVALID_STATE;
    }

So is this a bug or am I using it totally wrong? Is the approach of using software timers good, or should I change to some other method?

Regards

Robert

Parents
  • Hi,

    I am sorry to see that this has been left unanswered for so long.

    I have tried to look into these issues, but I am afraid I need some help from a colleague who is out-of-office and will be so for the next two weeks. Hopefully I can come back to you before that, but that I cannot guarantee.

    From what I can tell thus far, yes, your proposed workaround in the opening post looks ugly, and I would find it very strange if something like that should be necessary. Are you sure that you have configured the serial port library correctly?

    We might need your full project, at least more code, in order to set up, reproduce and investigate further. You may open a private case in order to do so, if confidential. In that case please refer to this thread.

    Regards,
    Terje

  • Hello,

    Was this issue ever resolve? I am having the same issue with the SDK-15.2.0

  • No, this issue was never resolved. I did continue with my ugly fixes as it was only a technology demonstrator and something very temporary. I have since long stopped working on this project and I'm currently not working with any Nordic MCUs.

    I have received several messages about this so this seems to be an issue for more people. Maybe can help out?

  • Hi!

    This thread is quite old. Please create a new thread with your current issue, and an application engineer will have a look at it from our side.

    Regards,
    Terje

  • The description of the problem is exactly what I am experiencing. Why should we duplicate the information? I can copy and paste the original ticket in a new ticket if it is absolute necessary

  • Hi,

    Since the original issue is still unresolved and if you think your issue matches the exact same description, then we can continue our discussion here.

    There were issues with nrf_serial working with freeRTOS that was mentioned in the known issues list for SDK15.x (check item 40. in the known issues list)

    I see that this was fixed in later SDKs internally but the module was removed for public in latest.

    The problem was that nrf_serial was trying to create too many timers which was turning out to be problematic with FreeRTOS.

    Can you please try to replace the components\libraries\serial\nrf_serial.c file with the attached one and see if it helps.

    Sorry for multiple edits, there is some issue with uploading this file

    /**
     * Copyright (c) 2016 - 2019, 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_common.h"
    #if NRF_MODULE_ENABLED(NRF_SERIAL)
    #include "nrf_serial.h"
    
    #if defined (UART_PRESENT)
    
    typedef struct {
        volatile bool expired;
    } nrf_serial_timeout_ctx_t;
    
    static void event_handler(nrf_serial_t const * p_serial,
                              nrf_serial_event_t event)
    {
        if (p_serial->p_ctx->p_config->ev_handler)
        {
            p_serial->p_ctx->p_config->ev_handler(p_serial, event);
        }
    }
    
    static void sleep_handler(nrf_serial_t const * p_serial)
    {
        if (p_serial->p_ctx->p_config->mode == NRF_SERIAL_MODE_POLLING)
        {
            return;
        }
    
        if (p_serial->p_ctx->p_config->sleep_handler)
        {
            p_serial->p_ctx->p_config->sleep_handler();
        }
    }
    
    static size_t serial_rx(nrf_serial_t const * p_serial,
                            uint8_t * p_buff,
                            size_t length)
    {
        if (p_serial->p_ctx->p_config->mode == NRF_SERIAL_MODE_POLLING)
        {
            size_t rx_len = MIN(length, UINT8_MAX);
            size_t len = rx_len;
    
            while (nrf_drv_uart_rx_ready(&p_serial->instance) && len)
            {
                ret_code_t ret = nrf_drv_uart_rx(&p_serial->instance, p_buff, 1);
                if (ret != NRF_SUCCESS)
                {
                    break;
                }
                p_buff++;
                len--;
            }
    
            return rx_len - len;
        }
    
        nrf_queue_t const * p_rxq = p_serial->p_ctx->p_config->p_queues->p_rxq;
        return nrf_queue_out(p_rxq, p_buff, length);
    }
    
    static size_t serial_tx(nrf_serial_t const * p_serial,
                            uint8_t const * p_buff,
                            size_t length)
    {
        size_t tx_len = 0;
    
        if (p_serial->p_ctx->p_config->mode == NRF_SERIAL_MODE_POLLING)
        {
            tx_len = MIN(length, UINT8_MAX);
            ret_code_t ret = nrf_drv_uart_tx(&p_serial->instance, p_buff, tx_len);
            ASSERT(ret == NRF_SUCCESS)
            return tx_len;
        }
    
        nrf_queue_t const * p_txq = p_serial->p_ctx->p_config->p_queues->p_txq;
        nrf_serial_buffers_t const * p_buffs = p_serial->p_ctx->p_config->p_buffers;
    
        /* Try to enqueue data. */
        size_t queue_in_len = nrf_queue_in(p_txq, p_buff, length);
        if (nrf_drv_uart_tx_in_progress(&p_serial->instance))
        {
            return queue_in_len;
        }
    
        size_t len = nrf_queue_out(p_txq, p_buffs->p_txb, p_buffs->tx_size);
        ASSERT(len > 0);
        ret_code_t ret = nrf_drv_uart_tx(&p_serial->instance, p_buffs->p_txb, len);
        ASSERT(ret == NRF_SUCCESS);
    
        return queue_in_len;
    }
    
    static void uart_event_handler(nrf_drv_uart_event_t * p_event, void * p_context)
    {
        uint32_t ret;
        nrf_serial_t const * p_serial = p_context;
    
        switch (p_event->type)
        {
            case NRF_DRV_UART_EVT_RX_DONE:
            {
                nrf_queue_t const * p_rxq =
                        p_serial->p_ctx->p_config->p_queues->p_rxq;
                size_t len = nrf_queue_in(p_rxq,
                                          p_event->data.rxtx.p_data,
                                          p_event->data.rxtx.bytes);
    
                if (len < p_event->data.rxtx.bytes)
                {
                    event_handler(p_serial, NRF_SERIAL_EVENT_FIFO_ERR);
                    break;
                }
    
                if (p_event->data.rxtx.bytes)
                {
                    event_handler(p_serial, NRF_SERIAL_EVENT_RX_DATA);
                }
                nrf_serial_buffers_t const * p_buffs =
                        p_serial->p_ctx->p_config->p_buffers;
    
                ret = nrf_drv_uart_rx(&p_serial->instance,
                                      p_buffs->p_rxb,
                                      p_buffs->rx_size);
                ASSERT(ret == NRF_SUCCESS);
                break;
            }
            case NRF_DRV_UART_EVT_ERROR:
            {
                event_handler(p_serial, NRF_SERIAL_EVENT_DRV_ERR);
                break;
            }
            case NRF_DRV_UART_EVT_TX_DONE:
            {
                nrf_queue_t const * p_txq =
                        p_serial->p_ctx->p_config->p_queues->p_txq;
                nrf_serial_buffers_t const * p_buffs =
                        p_serial->p_ctx->p_config->p_buffers;
    
                event_handler(p_serial, NRF_SERIAL_EVENT_TX_DONE);
                size_t len = nrf_queue_out(p_txq, p_buffs->p_txb, p_buffs->tx_size);
                if (len == 0)
                {
                    break;
                }
    
                ret = nrf_drv_uart_tx(&p_serial->instance, p_buffs->p_txb, len);
                ASSERT(ret == NRF_SUCCESS);
                break;
            }
            default:
                break;
        }
    }
    
    static void serial_timeout_handler(void * p_context)
    {
        nrf_serial_timeout_ctx_t * p_tout_ctx = p_context;
        p_tout_ctx->expired = true;
    }
    
    ret_code_t nrf_serial_init(nrf_serial_t const * p_serial,
                               nrf_drv_uart_config_t const * p_drv_uart_config,
                               nrf_serial_config_t const * p_config)
    {
        ret_code_t ret;
        ASSERT(p_serial && p_drv_uart_config && p_config);
    
        if (p_serial->p_ctx->p_config)
        {
            /*Already initialized.*/
            return NRF_ERROR_MODULE_ALREADY_INITIALIZED;
        }
    
        if (p_config->mode != NRF_SERIAL_MODE_POLLING)
        {
            ASSERT(p_config->p_queues && p_config->p_buffers);
        }
    
        nrf_drv_uart_config_t drv_config;
        memcpy(&drv_config, p_drv_uart_config, sizeof(nrf_drv_uart_config_t));
        drv_config.p_context = (void *)p_serial;
    #if defined(UARTE_PRESENT) && defined(UART_PRESENT)
        drv_config.use_easy_dma = (p_config->mode == NRF_SERIAL_MODE_DMA);
    #endif
        ret = nrf_drv_uart_init(&p_serial->instance,
                                &drv_config,
                                p_config->mode == NRF_SERIAL_MODE_POLLING ?
                                NULL : uart_event_handler);
        if (ret != NRF_SUCCESS)
        {
            return ret;
        }
    
        p_serial->p_ctx->p_config = p_config;
    
        if (p_serial->p_ctx->p_config->p_queues)
        {
            nrf_queue_reset(p_serial->p_ctx->p_config->p_queues->p_txq);
            nrf_queue_reset(p_serial->p_ctx->p_config->p_queues->p_rxq);
        }
    
        nrf_mtx_init(&p_serial->p_ctx->read_lock);
        nrf_mtx_init(&p_serial->p_ctx->write_lock);
    
        ret = app_timer_create(p_serial->p_tx_timer,
                               APP_TIMER_MODE_SINGLE_SHOT,
                               serial_timeout_handler);
        if (ret != NRF_SUCCESS)
        {
            return ret;
        }
    
        ret = app_timer_create(p_serial->p_rx_timer,
                               APP_TIMER_MODE_SINGLE_SHOT,
                               serial_timeout_handler);
        if (ret != NRF_SUCCESS)
        {
            return ret;
        }
    
        p_serial->p_ctx->flags = NRF_SERIAL_RX_ENABLED_FLAG |
                                 NRF_SERIAL_TX_ENABLED_FLAG;
    
        if (drv_config.pseltxd == NRF_UART_PSEL_DISCONNECTED)
        {
            p_serial->p_ctx->flags &= ~NRF_SERIAL_TX_ENABLED_FLAG;
        }
    
        if (drv_config.pselrxd == NRF_UART_PSEL_DISCONNECTED)
        {
            p_serial->p_ctx->flags &= ~NRF_SERIAL_RX_ENABLED_FLAG;
            return NRF_SUCCESS;
        }
    
        if (p_serial->p_ctx->p_config->mode != NRF_SERIAL_MODE_DMA)
        {
            nrf_drv_uart_rx_enable(&p_serial->instance);
            if (p_serial->p_ctx->p_config->mode == NRF_SERIAL_MODE_POLLING)
            {
                return NRF_SUCCESS;
            }
        }
    
        return nrf_drv_uart_rx(&p_serial->instance,
                               p_serial->p_ctx->p_config->p_buffers->p_rxb,
                               p_serial->p_ctx->p_config->p_buffers->rx_size);
    }
    
    ret_code_t nrf_serial_uninit(nrf_serial_t const * p_serial)
    {
        ASSERT(p_serial);
    
        if (!p_serial->p_ctx->p_config)
        {
            /*Already uninitialized.*/
            return NRF_ERROR_MODULE_NOT_INITIALIZED;
        }
    
        if (!nrf_mtx_trylock(&p_serial->p_ctx->write_lock))
        {
            return NRF_ERROR_BUSY;
        }
        if (!nrf_mtx_trylock(&p_serial->p_ctx->read_lock))
        {
            nrf_mtx_unlock(&p_serial->p_ctx->write_lock);
            return NRF_ERROR_BUSY;
        }
    
        nrf_drv_uart_uninit(&p_serial->instance);
        if (p_serial->p_ctx->p_config->p_queues)
        {
            nrf_queue_reset(p_serial->p_ctx->p_config->p_queues->p_txq);
            nrf_queue_reset(p_serial->p_ctx->p_config->p_queues->p_rxq);
        }
    
        memset(p_serial->p_ctx, 0, sizeof(nrf_serial_ctx_t));
        return NRF_SUCCESS;
    }
    
    static ret_code_t timeout_setup(nrf_serial_t const * p_serial,
                                    app_timer_id_t const * p_timer_id,
                                    uint32_t timeout_ms,
                                    nrf_serial_timeout_ctx_t * p_tout_ctx)
    {
        uint32_t ticks = APP_TIMER_TICKS(timeout_ms);
    
        if (ticks < APP_TIMER_MIN_TIMEOUT_TICKS)
        {
            p_tout_ctx->expired = true;
            return NRF_SUCCESS;
        }
    
        ret_code_t ret = app_timer_stop(*p_timer_id);
        if (ret != NRF_SUCCESS)
        {
            return ret;
        }
    
        return app_timer_start(*p_timer_id, ticks, p_tout_ctx);
    }
    
    ret_code_t nrf_serial_write(nrf_serial_t const * p_serial,
                                void const * p_data,
                                size_t size,
                                size_t * p_written,
                                uint32_t timeout_ms)
    {
        ret_code_t ret;
    
        ASSERT(p_serial);
        if (!p_serial->p_ctx->p_config)
        {
            return NRF_ERROR_MODULE_NOT_INITIALIZED;
        }
    
        if (!(p_serial->p_ctx->flags & NRF_SERIAL_TX_ENABLED_FLAG))
        {
            return NRF_ERROR_INVALID_STATE;
        }
    
        if (size == 0)
        {
            return NRF_SUCCESS;
        }
    
        if (!nrfx_is_in_ram(p_data) &&
             p_serial->p_ctx->p_config->mode == NRF_SERIAL_MODE_DMA)
        {
            return NRF_ERROR_INVALID_ADDR;
        }
    
        if (!nrf_mtx_trylock(&p_serial->p_ctx->write_lock))
        {
            return NRF_ERROR_BUSY;
        }
    
        nrf_serial_timeout_ctx_t tout_ctx = {
                .expired = false,
        };
    
        if (timeout_ms != NRF_SERIAL_MAX_TIMEOUT)
        {
            ret = timeout_setup(p_serial,
                                p_serial->p_tx_timer,
                                timeout_ms,
                                &tout_ctx);
            if (ret != NRF_SUCCESS)
            {
                nrf_mtx_unlock(&p_serial->p_ctx->write_lock);
                return ret;
            }
        }
    
        size_t left = size;
        uint8_t const * p_buff = p_data;
    
        do
        {
            size_t wcnt = serial_tx(p_serial, p_buff, left);
            left -= wcnt;
            p_buff += wcnt;
            if (!left)
            {
                break;
            }
    
            sleep_handler(p_serial);
        } while (!tout_ctx.expired);
    
        if (p_written)
        {
            *p_written = size - left;
        }
    
        if (!tout_ctx.expired && (timeout_ms != NRF_SERIAL_MAX_TIMEOUT))
        {
            (void)app_timer_stop(*p_serial->p_tx_timer);
        }
    
        nrf_mtx_unlock(&p_serial->p_ctx->write_lock);
        if (left && tout_ctx.expired)
        {
            return NRF_ERROR_TIMEOUT;
        }
    
        return NRF_SUCCESS;
    }
    
    ret_code_t nrf_serial_read(nrf_serial_t const * p_serial,
                               void * p_data,
                               size_t size,
                               size_t * p_read,
                               uint32_t timeout_ms)
    {
        ret_code_t ret;
    
        ASSERT(p_serial);
        if (!p_serial->p_ctx->p_config)
        {
            return NRF_ERROR_MODULE_NOT_INITIALIZED;
        }
    
        if (!(p_serial->p_ctx->flags & NRF_SERIAL_RX_ENABLED_FLAG))
        {
            return NRF_ERROR_INVALID_STATE;
        }
    
        if (size == 0)
        {
            return NRF_SUCCESS;
        }
    
        if (!nrf_mtx_trylock(&p_serial->p_ctx->read_lock))
        {
            return NRF_ERROR_BUSY;
        }
    
        nrf_serial_timeout_ctx_t tout_ctx = {
                .expired = false,
        };
    
        if (timeout_ms != NRF_SERIAL_MAX_TIMEOUT)
        {
            ret = timeout_setup(p_serial,
                                p_serial->p_rx_timer,
                                timeout_ms,
                                &tout_ctx);
    
            if (ret != NRF_SUCCESS)
            {
                nrf_mtx_unlock(&p_serial->p_ctx->read_lock);
                return ret;
            }
        }
    
        size_t left = size;
        uint8_t * p_buff = p_data;
        do
        {
            size_t rcnt = serial_rx(p_serial, p_buff, left);
            left -= rcnt;
            p_buff += rcnt;
            if (!left)
            {
                break;
            }
    
            if (tout_ctx.expired)
            {
                if (p_serial->p_ctx->p_config->mode != NRF_SERIAL_MODE_POLLING)
                {
                    nrf_drv_uart_rx_abort(&p_serial->instance);
                }
                break;
            }
    
            sleep_handler(p_serial);
        } while (1);
    
        if (p_read)
        {
            *p_read = size - left;
        }
    
        if (!tout_ctx.expired && (timeout_ms != NRF_SERIAL_MAX_TIMEOUT))
        {
            (void)app_timer_stop(*p_serial->p_rx_timer);
        }
    
        nrf_mtx_unlock(&p_serial->p_ctx->read_lock);
        if (left && tout_ctx.expired)
        {
            return NRF_ERROR_TIMEOUT;
        }
    
        return NRF_SUCCESS;
    }
    
    ret_code_t nrf_serial_flush(nrf_serial_t const * p_serial, uint32_t timeout_ms)
    {
    
        ret_code_t ret;
    
        ASSERT(p_serial);
        if (!p_serial->p_ctx->p_config)
        {
            return NRF_ERROR_MODULE_NOT_INITIALIZED;
        }
    
        if (!(p_serial->p_ctx->flags & NRF_SERIAL_TX_ENABLED_FLAG))
        {
            return NRF_ERROR_INVALID_STATE;
        }
    
        if (p_serial->p_ctx->p_config->mode == NRF_SERIAL_MODE_POLLING)
        {
            return NRF_SUCCESS;
        }
    
        if (!nrf_mtx_trylock(&p_serial->p_ctx->write_lock))
        {
            return NRF_ERROR_BUSY;
        }
    
        nrf_serial_timeout_ctx_t tout_ctx = {
                .expired = false,
        };
    
        if (timeout_ms != NRF_SERIAL_MAX_TIMEOUT)
        {
            ret = timeout_setup(p_serial,
                                p_serial->p_tx_timer,
                                timeout_ms,
                                &tout_ctx);
            if (ret != NRF_SUCCESS)
            {
                nrf_mtx_unlock(&p_serial->p_ctx->write_lock);
                return ret;
            }
        }
    
        bool empty;
        do
        {
            empty = nrf_queue_is_empty(p_serial->p_ctx->p_config->p_queues->p_txq)
                    && !nrf_drv_uart_tx_in_progress(&p_serial->instance);
            if (empty)
            {
                break;
            }
    
            sleep_handler(p_serial);
        } while (!tout_ctx.expired);
    
        if (!tout_ctx.expired && (timeout_ms != NRF_SERIAL_MAX_TIMEOUT))
        {
            (void)app_timer_stop(*p_serial->p_tx_timer);
        }
    
        nrf_mtx_unlock(&p_serial->p_ctx->write_lock);
        if (!empty && tout_ctx.expired)
        {
            return NRF_ERROR_TIMEOUT;
        }
    
        return NRF_SUCCESS;
    }
    
    ret_code_t nrf_serial_tx_abort(nrf_serial_t const * p_serial)
    {
        ASSERT(p_serial);
        if (!p_serial->p_ctx->p_config)
        {
            return NRF_ERROR_MODULE_NOT_INITIALIZED;
        }
    
        if (!(p_serial->p_ctx->flags & NRF_SERIAL_TX_ENABLED_FLAG))
        {
            return NRF_ERROR_INVALID_STATE;
        }
    
        if (!nrf_mtx_trylock(&p_serial->p_ctx->write_lock))
        {
            return NRF_ERROR_BUSY;
        }
    
        nrf_drv_uart_tx_abort(&p_serial->instance);
        if (p_serial->p_ctx->p_config->p_queues->p_txq)
        {
            nrf_queue_reset(p_serial->p_ctx->p_config->p_queues->p_txq);
        }
    
        nrf_mtx_unlock(&p_serial->p_ctx->write_lock);
        return NRF_SUCCESS;
    }
    
    ret_code_t nrf_serial_rx_drain(nrf_serial_t const * p_serial)
    {
        ASSERT(p_serial);
        if (!p_serial->p_ctx->p_config)
        {
            return NRF_ERROR_MODULE_NOT_INITIALIZED;
        }
    
        if (!(p_serial->p_ctx->flags & NRF_SERIAL_RX_ENABLED_FLAG))
        {
            return NRF_ERROR_INVALID_STATE;
        }
    
        if (!nrf_mtx_trylock(&p_serial->p_ctx->read_lock))
        {
            return NRF_ERROR_BUSY;
        }
    
        uint8_t c;
        /*Drain HW FIFO*/
        while (serial_rx(p_serial, &c, sizeof(c)))
        {
    
        }
    
        /*Drain SW FIFO*/
        if (p_serial->p_ctx->p_config->p_queues->p_rxq)
        {
            nrf_queue_reset(p_serial->p_ctx->p_config->p_queues->p_rxq);
        }
        nrf_mtx_unlock(&p_serial->p_ctx->read_lock);
        return NRF_SUCCESS;
    }
    #else
    ret_code_t nrf_serial_init(nrf_serial_t const * p_serial,
                               nrf_drv_uart_config_t const * p_drv_uart_config,
                               nrf_serial_config_t const * p_config)
    {
        return NRF_ERROR_NOT_SUPPORTED;
    }
    
    ret_code_t nrf_serial_uninit(nrf_serial_t const * p_serial)
    {
        return NRF_ERROR_NOT_SUPPORTED;
    }
    ret_code_t nrf_serial_write(nrf_serial_t const * p_serial,
                                void const * p_data,
                                size_t size,
                                size_t * p_written,
                                uint32_t timeout_ms)
    {
        return NRF_ERROR_NOT_SUPPORTED;
    }
    ret_code_t nrf_serial_read(nrf_serial_t const * p_serial,
                               void * p_data,
                               size_t size,
                               size_t * p_read,
                               uint32_t timeout_ms)
    {
        return NRF_ERROR_NOT_SUPPORTED;
    }
    ret_code_t nrf_serial_flush(nrf_serial_t const * p_serial, uint32_t timeout_ms)
    {
        return NRF_ERROR_NOT_SUPPORTED;
    }
    ret_code_t nrf_serial_tx_abort(nrf_serial_t const * p_serial)
    {
        return NRF_ERROR_NOT_SUPPORTED;
    }
    ret_code_t nrf_serial_rx_drain(nrf_serial_t const * p_serial)
    {
        return NRF_ERROR_NOT_SUPPORTED;
    }
    
    #endif // UART_PRESENT
    #endif //NRF_MODULE_ENABLED(NRF_SERIAL)
    

  • /**
     * Copyright (c) 2016 - 2019, 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_common.h"
    #if NRF_MODULE_ENABLED(NRF_SERIAL)
    #include "nrf_serial.h"
    
    #if defined (UART_PRESENT)
    
    typedef struct {
        volatile bool expired;
    } nrf_serial_timeout_ctx_t;
    
    static void event_handler(nrf_serial_t const * p_serial,
                              nrf_serial_event_t event)
    {
        if (p_serial->p_ctx->p_config->ev_handler)
        {
            p_serial->p_ctx->p_config->ev_handler(p_serial, event);
        }
    }
    
    static void sleep_handler(nrf_serial_t const * p_serial)
    {
        if (p_serial->p_ctx->p_config->mode == NRF_SERIAL_MODE_POLLING)
        {
            return;
        }
    
        if (p_serial->p_ctx->p_config->sleep_handler)
        {
            p_serial->p_ctx->p_config->sleep_handler();
        }
    }
    
    static size_t serial_rx(nrf_serial_t const * p_serial,
                            uint8_t * p_buff,
                            size_t length)
    {
        if (p_serial->p_ctx->p_config->mode == NRF_SERIAL_MODE_POLLING)
        {
            size_t rx_len = MIN(length, UINT8_MAX);
            size_t len = rx_len;
    
            while (nrf_drv_uart_rx_ready(&p_serial->instance) && len)
            {
                ret_code_t ret = nrf_drv_uart_rx(&p_serial->instance, p_buff, 1);
                if (ret != NRF_SUCCESS)
                {
                    break;
                }
                p_buff++;
                len--;
            }
    
            return rx_len - len;
        }
    
        nrf_queue_t const * p_rxq = p_serial->p_ctx->p_config->p_queues->p_rxq;
        return nrf_queue_out(p_rxq, p_buff, length);
    }
    
    static size_t serial_tx(nrf_serial_t const * p_serial,
                            uint8_t const * p_buff,
                            size_t length)
    {
        size_t tx_len = 0;
    
        if (p_serial->p_ctx->p_config->mode == NRF_SERIAL_MODE_POLLING)
        {
            tx_len = MIN(length, UINT8_MAX);
            ret_code_t ret = nrf_drv_uart_tx(&p_serial->instance, p_buff, tx_len);
            ASSERT(ret == NRF_SUCCESS)
            return tx_len;
        }
    
        nrf_queue_t const * p_txq = p_serial->p_ctx->p_config->p_queues->p_txq;
        nrf_serial_buffers_t const * p_buffs = p_serial->p_ctx->p_config->p_buffers;
    
        /* Try to enqueue data. */
        size_t queue_in_len = nrf_queue_in(p_txq, p_buff, length);
        if (nrf_drv_uart_tx_in_progress(&p_serial->instance))
        {
            return queue_in_len;
        }
    
        size_t len = nrf_queue_out(p_txq, p_buffs->p_txb, p_buffs->tx_size);
        ASSERT(len > 0);
        ret_code_t ret = nrf_drv_uart_tx(&p_serial->instance, p_buffs->p_txb, len);
        ASSERT(ret == NRF_SUCCESS);
    
        return queue_in_len;
    }
    
    static void uart_event_handler(nrf_drv_uart_event_t * p_event, void * p_context)
    {
        uint32_t ret;
        nrf_serial_t const * p_serial = p_context;
    
        switch (p_event->type)
        {
            case NRF_DRV_UART_EVT_RX_DONE:
            {
                nrf_queue_t const * p_rxq =
                        p_serial->p_ctx->p_config->p_queues->p_rxq;
                size_t len = nrf_queue_in(p_rxq,
                                          p_event->data.rxtx.p_data,
                                          p_event->data.rxtx.bytes);
    
                if (len < p_event->data.rxtx.bytes)
                {
                    event_handler(p_serial, NRF_SERIAL_EVENT_FIFO_ERR);
                    break;
                }
    
                if (p_event->data.rxtx.bytes)
                {
                    event_handler(p_serial, NRF_SERIAL_EVENT_RX_DATA);
                }
                nrf_serial_buffers_t const * p_buffs =
                        p_serial->p_ctx->p_config->p_buffers;
    
                ret = nrf_drv_uart_rx(&p_serial->instance,
                                      p_buffs->p_rxb,
                                      p_buffs->rx_size);
                ASSERT(ret == NRF_SUCCESS);
                break;
            }
            case NRF_DRV_UART_EVT_ERROR:
            {
                event_handler(p_serial, NRF_SERIAL_EVENT_DRV_ERR);
                break;
            }
            case NRF_DRV_UART_EVT_TX_DONE:
            {
                nrf_queue_t const * p_txq =
                        p_serial->p_ctx->p_config->p_queues->p_txq;
                nrf_serial_buffers_t const * p_buffs =
                        p_serial->p_ctx->p_config->p_buffers;
    
                event_handler(p_serial, NRF_SERIAL_EVENT_TX_DONE);
                size_t len = nrf_queue_out(p_txq, p_buffs->p_txb, p_buffs->tx_size);
                if (len == 0)
                {
                    break;
                }
    
                ret = nrf_drv_uart_tx(&p_serial->instance, p_buffs->p_txb, len);
                ASSERT(ret == NRF_SUCCESS);
                break;
            }
            default:
                break;
        }
    }
    
    static void serial_timeout_handler(void * p_context)
    {
        nrf_serial_timeout_ctx_t * p_tout_ctx = p_context;
        p_tout_ctx->expired = true;
    }
    
    ret_code_t nrf_serial_init(nrf_serial_t const * p_serial,
                               nrf_drv_uart_config_t const * p_drv_uart_config,
                               nrf_serial_config_t const * p_config)
    {
        ret_code_t ret;
        ASSERT(p_serial && p_drv_uart_config && p_config);
    
        if (p_serial->p_ctx->p_config)
        {
            /*Already initialized.*/
            return NRF_ERROR_MODULE_ALREADY_INITIALIZED;
        }
    
        if (p_config->mode != NRF_SERIAL_MODE_POLLING)
        {
            ASSERT(p_config->p_queues && p_config->p_buffers);
        }
    
        nrf_drv_uart_config_t drv_config;
        memcpy(&drv_config, p_drv_uart_config, sizeof(nrf_drv_uart_config_t));
        drv_config.p_context = (void *)p_serial;
    #if defined(UARTE_PRESENT) && defined(UART_PRESENT)
        drv_config.use_easy_dma = (p_config->mode == NRF_SERIAL_MODE_DMA);
    #endif
        ret = nrf_drv_uart_init(&p_serial->instance,
                                &drv_config,
                                p_config->mode == NRF_SERIAL_MODE_POLLING ?
                                NULL : uart_event_handler);
        if (ret != NRF_SUCCESS)
        {
            return ret;
        }
    
        p_serial->p_ctx->p_config = p_config;
    
        if (p_serial->p_ctx->p_config->p_queues)
        {
            nrf_queue_reset(p_serial->p_ctx->p_config->p_queues->p_txq);
            nrf_queue_reset(p_serial->p_ctx->p_config->p_queues->p_rxq);
        }
    
        nrf_mtx_init(&p_serial->p_ctx->read_lock);
        nrf_mtx_init(&p_serial->p_ctx->write_lock);
    
        ret = app_timer_create(p_serial->p_tx_timer,
                               APP_TIMER_MODE_SINGLE_SHOT,
                               serial_timeout_handler);
        if (ret != NRF_SUCCESS)
        {
            return ret;
        }
    
        ret = app_timer_create(p_serial->p_rx_timer,
                               APP_TIMER_MODE_SINGLE_SHOT,
                               serial_timeout_handler);
        if (ret != NRF_SUCCESS)
        {
            return ret;
        }
    
        p_serial->p_ctx->flags = NRF_SERIAL_RX_ENABLED_FLAG |
                                 NRF_SERIAL_TX_ENABLED_FLAG;
    
        if (drv_config.pseltxd == NRF_UART_PSEL_DISCONNECTED)
        {
            p_serial->p_ctx->flags &= ~NRF_SERIAL_TX_ENABLED_FLAG;
        }
    
        if (drv_config.pselrxd == NRF_UART_PSEL_DISCONNECTED)
        {
            p_serial->p_ctx->flags &= ~NRF_SERIAL_RX_ENABLED_FLAG;
            return NRF_SUCCESS;
        }
    
        if (p_serial->p_ctx->p_config->mode != NRF_SERIAL_MODE_DMA)
        {
            nrf_drv_uart_rx_enable(&p_serial->instance);
            if (p_serial->p_ctx->p_config->mode == NRF_SERIAL_MODE_POLLING)
            {
                return NRF_SUCCESS;
            }
        }
    
        return nrf_drv_uart_rx(&p_serial->instance,
                               p_serial->p_ctx->p_config->p_buffers->p_rxb,
                               p_serial->p_ctx->p_config->p_buffers->rx_size);
    }
    
    ret_code_t nrf_serial_uninit(nrf_serial_t const * p_serial)
    {
        ASSERT(p_serial);
    
        if (!p_serial->p_ctx->p_config)
        {
            /*Already uninitialized.*/
            return NRF_ERROR_MODULE_NOT_INITIALIZED;
        }
    
        if (!nrf_mtx_trylock(&p_serial->p_ctx->write_lock))
        {
            return NRF_ERROR_BUSY;
        }
        if (!nrf_mtx_trylock(&p_serial->p_ctx->read_lock))
        {
            nrf_mtx_unlock(&p_serial->p_ctx->write_lock);
            return NRF_ERROR_BUSY;
        }
    
        nrf_drv_uart_uninit(&p_serial->instance);
        if (p_serial->p_ctx->p_config->p_queues)
        {
            nrf_queue_reset(p_serial->p_ctx->p_config->p_queues->p_txq);
            nrf_queue_reset(p_serial->p_ctx->p_config->p_queues->p_rxq);
        }
    
        memset(p_serial->p_ctx, 0, sizeof(nrf_serial_ctx_t));
        return NRF_SUCCESS;
    }
    
    static ret_code_t timeout_setup(nrf_serial_t const * p_serial,
                                    app_timer_id_t const * p_timer_id,
                                    uint32_t timeout_ms,
                                    nrf_serial_timeout_ctx_t * p_tout_ctx)
    {
        uint32_t ticks = APP_TIMER_TICKS(timeout_ms);
    
        if (ticks < APP_TIMER_MIN_TIMEOUT_TICKS)
        {
            p_tout_ctx->expired = true;
            return NRF_SUCCESS;
        }
    
        ret_code_t ret = app_timer_stop(*p_timer_id);
        if (ret != NRF_SUCCESS)
        {
            return ret;
        }
    
        return app_timer_start(*p_timer_id, ticks, p_tout_ctx);
    }
    
    ret_code_t nrf_serial_write(nrf_serial_t const * p_serial,
                                void const * p_data,
                                size_t size,
                                size_t * p_written,
                                uint32_t timeout_ms)
    {
        ret_code_t ret;
    
        ASSERT(p_serial);
        if (!p_serial->p_ctx->p_config)
        {
            return NRF_ERROR_MODULE_NOT_INITIALIZED;
        }
    
        if (!(p_serial->p_ctx->flags & NRF_SERIAL_TX_ENABLED_FLAG))
        {
            return NRF_ERROR_INVALID_STATE;
        }
    
        if (size == 0)
        {
            return NRF_SUCCESS;
        }
    
        if (!nrfx_is_in_ram(p_data) &&
             p_serial->p_ctx->p_config->mode == NRF_SERIAL_MODE_DMA)
        {
            return NRF_ERROR_INVALID_ADDR;
        }
    
        if (!nrf_mtx_trylock(&p_serial->p_ctx->write_lock))
        {
            return NRF_ERROR_BUSY;
        }
    
        nrf_serial_timeout_ctx_t tout_ctx = {
                .expired = false,
        };
    
        if (timeout_ms != NRF_SERIAL_MAX_TIMEOUT)
        {
            ret = timeout_setup(p_serial,
                                p_serial->p_tx_timer,
                                timeout_ms,
                                &tout_ctx);
            if (ret != NRF_SUCCESS)
            {
                nrf_mtx_unlock(&p_serial->p_ctx->write_lock);
                return ret;
            }
        }
    
        size_t left = size;
        uint8_t const * p_buff = p_data;
    
        do
        {
            size_t wcnt = serial_tx(p_serial, p_buff, left);
            left -= wcnt;
            p_buff += wcnt;
            if (!left)
            {
                break;
            }
    
            sleep_handler(p_serial);
        } while (!tout_ctx.expired);
    
        if (p_written)
        {
            *p_written = size - left;
        }
    
        if (!tout_ctx.expired && (timeout_ms != NRF_SERIAL_MAX_TIMEOUT))
        {
            (void)app_timer_stop(*p_serial->p_tx_timer);
        }
    
        nrf_mtx_unlock(&p_serial->p_ctx->write_lock);
        if (left && tout_ctx.expired)
        {
            return NRF_ERROR_TIMEOUT;
        }
    
        return NRF_SUCCESS;
    }
    
    ret_code_t nrf_serial_read(nrf_serial_t const * p_serial,
                               void * p_data,
                               size_t size,
                               size_t * p_read,
                               uint32_t timeout_ms)
    {
        ret_code_t ret;
    
        ASSERT(p_serial);
        if (!p_serial->p_ctx->p_config)
        {
            return NRF_ERROR_MODULE_NOT_INITIALIZED;
        }
    
        if (!(p_serial->p_ctx->flags & NRF_SERIAL_RX_ENABLED_FLAG))
        {
            return NRF_ERROR_INVALID_STATE;
        }
    
        if (size == 0)
        {
            return NRF_SUCCESS;
        }
    
        if (!nrf_mtx_trylock(&p_serial->p_ctx->read_lock))
        {
            return NRF_ERROR_BUSY;
        }
    
        nrf_serial_timeout_ctx_t tout_ctx = {
                .expired = false,
        };
    
        if (timeout_ms != NRF_SERIAL_MAX_TIMEOUT)
        {
            ret = timeout_setup(p_serial,
                                p_serial->p_rx_timer,
                                timeout_ms,
                                &tout_ctx);
    
            if (ret != NRF_SUCCESS)
            {
                nrf_mtx_unlock(&p_serial->p_ctx->read_lock);
                return ret;
            }
        }
    
        size_t left = size;
        uint8_t * p_buff = p_data;
        do
        {
            size_t rcnt = serial_rx(p_serial, p_buff, left);
            left -= rcnt;
            p_buff += rcnt;
            if (!left)
            {
                break;
            }
    
            if (tout_ctx.expired)
            {
                if (p_serial->p_ctx->p_config->mode != NRF_SERIAL_MODE_POLLING)
                {
                    nrf_drv_uart_rx_abort(&p_serial->instance);
                }
                break;
            }
    
            sleep_handler(p_serial);
        } while (1);
    
        if (p_read)
        {
            *p_read = size - left;
        }
    
        if (!tout_ctx.expired && (timeout_ms != NRF_SERIAL_MAX_TIMEOUT))
        {
            (void)app_timer_stop(*p_serial->p_rx_timer);
        }
    
        nrf_mtx_unlock(&p_serial->p_ctx->read_lock);
        if (left && tout_ctx.expired)
        {
            return NRF_ERROR_TIMEOUT;
        }
    
        return NRF_SUCCESS;
    }
    
    ret_code_t nrf_serial_flush(nrf_serial_t const * p_serial, uint32_t timeout_ms)
    {
    
        ret_code_t ret;
    
        ASSERT(p_serial);
        if (!p_serial->p_ctx->p_config)
        {
            return NRF_ERROR_MODULE_NOT_INITIALIZED;
        }
    
        if (!(p_serial->p_ctx->flags & NRF_SERIAL_TX_ENABLED_FLAG))
        {
            return NRF_ERROR_INVALID_STATE;
        }
    
        if (p_serial->p_ctx->p_config->mode == NRF_SERIAL_MODE_POLLING)
        {
            return NRF_SUCCESS;
        }
    
        if (!nrf_mtx_trylock(&p_serial->p_ctx->write_lock))
        {
            return NRF_ERROR_BUSY;
        }
    
        nrf_serial_timeout_ctx_t tout_ctx = {
                .expired = false,
        };
    
        if (timeout_ms != NRF_SERIAL_MAX_TIMEOUT)
        {
            ret = timeout_setup(p_serial,
                                p_serial->p_tx_timer,
                                timeout_ms,
                                &tout_ctx);
            if (ret != NRF_SUCCESS)
            {
                nrf_mtx_unlock(&p_serial->p_ctx->write_lock);
                return ret;
            }
        }
    
        bool empty;
        do
        {
            empty = nrf_queue_is_empty(p_serial->p_ctx->p_config->p_queues->p_txq)
                    && !nrf_drv_uart_tx_in_progress(&p_serial->instance);
            if (empty)
            {
                break;
            }
    
            sleep_handler(p_serial);
        } while (!tout_ctx.expired);
    
        if (!tout_ctx.expired && (timeout_ms != NRF_SERIAL_MAX_TIMEOUT))
        {
            (void)app_timer_stop(*p_serial->p_tx_timer);
        }
    
        nrf_mtx_unlock(&p_serial->p_ctx->write_lock);
        if (!empty && tout_ctx.expired)
        {
            return NRF_ERROR_TIMEOUT;
        }
    
        return NRF_SUCCESS;
    }
    
    ret_code_t nrf_serial_tx_abort(nrf_serial_t const * p_serial)
    {
        ASSERT(p_serial);
        if (!p_serial->p_ctx->p_config)
        {
            return NRF_ERROR_MODULE_NOT_INITIALIZED;
        }
    
        if (!(p_serial->p_ctx->flags & NRF_SERIAL_TX_ENABLED_FLAG))
        {
            return NRF_ERROR_INVALID_STATE;
        }
    
        if (!nrf_mtx_trylock(&p_serial->p_ctx->write_lock))
        {
            return NRF_ERROR_BUSY;
        }
    
        nrf_drv_uart_tx_abort(&p_serial->instance);
        if (p_serial->p_ctx->p_config->p_queues->p_txq)
        {
            nrf_queue_reset(p_serial->p_ctx->p_config->p_queues->p_txq);
        }
    
        nrf_mtx_unlock(&p_serial->p_ctx->write_lock);
        return NRF_SUCCESS;
    }
    
    ret_code_t nrf_serial_rx_drain(nrf_serial_t const * p_serial)
    {
        ASSERT(p_serial);
        if (!p_serial->p_ctx->p_config)
        {
            return NRF_ERROR_MODULE_NOT_INITIALIZED;
        }
    
        if (!(p_serial->p_ctx->flags & NRF_SERIAL_RX_ENABLED_FLAG))
        {
            return NRF_ERROR_INVALID_STATE;
        }
    
        if (!nrf_mtx_trylock(&p_serial->p_ctx->read_lock))
        {
            return NRF_ERROR_BUSY;
        }
    
        uint8_t c;
        /*Drain HW FIFO*/
        while (serial_rx(p_serial, &c, sizeof(c)))
        {
    
        }
    
        /*Drain SW FIFO*/
        if (p_serial->p_ctx->p_config->p_queues->p_rxq)
        {
            nrf_queue_reset(p_serial->p_ctx->p_config->p_queues->p_rxq);
        }
        nrf_mtx_unlock(&p_serial->p_ctx->read_lock);
        return NRF_SUCCESS;
    }
    #else
    ret_code_t nrf_serial_init(nrf_serial_t const * p_serial,
                               nrf_drv_uart_config_t const * p_drv_uart_config,
                               nrf_serial_config_t const * p_config)
    {
        return NRF_ERROR_NOT_SUPPORTED;
    }
    
    ret_code_t nrf_serial_uninit(nrf_serial_t const * p_serial)
    {
        return NRF_ERROR_NOT_SUPPORTED;
    }
    ret_code_t nrf_serial_write(nrf_serial_t const * p_serial,
                                void const * p_data,
                                size_t size,
                                size_t * p_written,
                                uint32_t timeout_ms)
    {
        return NRF_ERROR_NOT_SUPPORTED;
    }
    ret_code_t nrf_serial_read(nrf_serial_t const * p_serial,
                               void * p_data,
                               size_t size,
                               size_t * p_read,
                               uint32_t timeout_ms)
    {
        return NRF_ERROR_NOT_SUPPORTED;
    }
    ret_code_t nrf_serial_flush(nrf_serial_t const * p_serial, uint32_t timeout_ms)
    {
        return NRF_ERROR_NOT_SUPPORTED;
    }
    ret_code_t nrf_serial_tx_abort(nrf_serial_t const * p_serial)
    {
        return NRF_ERROR_NOT_SUPPORTED;
    }
    ret_code_t nrf_serial_rx_drain(nrf_serial_t const * p_serial)
    {
        return NRF_ERROR_NOT_SUPPORTED;
    }
    
    #endif // UART_PRESENT
    #endif //NRF_MODULE_ENABLED(NRF_SERIAL)
    

Reply
  • /**
     * Copyright (c) 2016 - 2019, 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_common.h"
    #if NRF_MODULE_ENABLED(NRF_SERIAL)
    #include "nrf_serial.h"
    
    #if defined (UART_PRESENT)
    
    typedef struct {
        volatile bool expired;
    } nrf_serial_timeout_ctx_t;
    
    static void event_handler(nrf_serial_t const * p_serial,
                              nrf_serial_event_t event)
    {
        if (p_serial->p_ctx->p_config->ev_handler)
        {
            p_serial->p_ctx->p_config->ev_handler(p_serial, event);
        }
    }
    
    static void sleep_handler(nrf_serial_t const * p_serial)
    {
        if (p_serial->p_ctx->p_config->mode == NRF_SERIAL_MODE_POLLING)
        {
            return;
        }
    
        if (p_serial->p_ctx->p_config->sleep_handler)
        {
            p_serial->p_ctx->p_config->sleep_handler();
        }
    }
    
    static size_t serial_rx(nrf_serial_t const * p_serial,
                            uint8_t * p_buff,
                            size_t length)
    {
        if (p_serial->p_ctx->p_config->mode == NRF_SERIAL_MODE_POLLING)
        {
            size_t rx_len = MIN(length, UINT8_MAX);
            size_t len = rx_len;
    
            while (nrf_drv_uart_rx_ready(&p_serial->instance) && len)
            {
                ret_code_t ret = nrf_drv_uart_rx(&p_serial->instance, p_buff, 1);
                if (ret != NRF_SUCCESS)
                {
                    break;
                }
                p_buff++;
                len--;
            }
    
            return rx_len - len;
        }
    
        nrf_queue_t const * p_rxq = p_serial->p_ctx->p_config->p_queues->p_rxq;
        return nrf_queue_out(p_rxq, p_buff, length);
    }
    
    static size_t serial_tx(nrf_serial_t const * p_serial,
                            uint8_t const * p_buff,
                            size_t length)
    {
        size_t tx_len = 0;
    
        if (p_serial->p_ctx->p_config->mode == NRF_SERIAL_MODE_POLLING)
        {
            tx_len = MIN(length, UINT8_MAX);
            ret_code_t ret = nrf_drv_uart_tx(&p_serial->instance, p_buff, tx_len);
            ASSERT(ret == NRF_SUCCESS)
            return tx_len;
        }
    
        nrf_queue_t const * p_txq = p_serial->p_ctx->p_config->p_queues->p_txq;
        nrf_serial_buffers_t const * p_buffs = p_serial->p_ctx->p_config->p_buffers;
    
        /* Try to enqueue data. */
        size_t queue_in_len = nrf_queue_in(p_txq, p_buff, length);
        if (nrf_drv_uart_tx_in_progress(&p_serial->instance))
        {
            return queue_in_len;
        }
    
        size_t len = nrf_queue_out(p_txq, p_buffs->p_txb, p_buffs->tx_size);
        ASSERT(len > 0);
        ret_code_t ret = nrf_drv_uart_tx(&p_serial->instance, p_buffs->p_txb, len);
        ASSERT(ret == NRF_SUCCESS);
    
        return queue_in_len;
    }
    
    static void uart_event_handler(nrf_drv_uart_event_t * p_event, void * p_context)
    {
        uint32_t ret;
        nrf_serial_t const * p_serial = p_context;
    
        switch (p_event->type)
        {
            case NRF_DRV_UART_EVT_RX_DONE:
            {
                nrf_queue_t const * p_rxq =
                        p_serial->p_ctx->p_config->p_queues->p_rxq;
                size_t len = nrf_queue_in(p_rxq,
                                          p_event->data.rxtx.p_data,
                                          p_event->data.rxtx.bytes);
    
                if (len < p_event->data.rxtx.bytes)
                {
                    event_handler(p_serial, NRF_SERIAL_EVENT_FIFO_ERR);
                    break;
                }
    
                if (p_event->data.rxtx.bytes)
                {
                    event_handler(p_serial, NRF_SERIAL_EVENT_RX_DATA);
                }
                nrf_serial_buffers_t const * p_buffs =
                        p_serial->p_ctx->p_config->p_buffers;
    
                ret = nrf_drv_uart_rx(&p_serial->instance,
                                      p_buffs->p_rxb,
                                      p_buffs->rx_size);
                ASSERT(ret == NRF_SUCCESS);
                break;
            }
            case NRF_DRV_UART_EVT_ERROR:
            {
                event_handler(p_serial, NRF_SERIAL_EVENT_DRV_ERR);
                break;
            }
            case NRF_DRV_UART_EVT_TX_DONE:
            {
                nrf_queue_t const * p_txq =
                        p_serial->p_ctx->p_config->p_queues->p_txq;
                nrf_serial_buffers_t const * p_buffs =
                        p_serial->p_ctx->p_config->p_buffers;
    
                event_handler(p_serial, NRF_SERIAL_EVENT_TX_DONE);
                size_t len = nrf_queue_out(p_txq, p_buffs->p_txb, p_buffs->tx_size);
                if (len == 0)
                {
                    break;
                }
    
                ret = nrf_drv_uart_tx(&p_serial->instance, p_buffs->p_txb, len);
                ASSERT(ret == NRF_SUCCESS);
                break;
            }
            default:
                break;
        }
    }
    
    static void serial_timeout_handler(void * p_context)
    {
        nrf_serial_timeout_ctx_t * p_tout_ctx = p_context;
        p_tout_ctx->expired = true;
    }
    
    ret_code_t nrf_serial_init(nrf_serial_t const * p_serial,
                               nrf_drv_uart_config_t const * p_drv_uart_config,
                               nrf_serial_config_t const * p_config)
    {
        ret_code_t ret;
        ASSERT(p_serial && p_drv_uart_config && p_config);
    
        if (p_serial->p_ctx->p_config)
        {
            /*Already initialized.*/
            return NRF_ERROR_MODULE_ALREADY_INITIALIZED;
        }
    
        if (p_config->mode != NRF_SERIAL_MODE_POLLING)
        {
            ASSERT(p_config->p_queues && p_config->p_buffers);
        }
    
        nrf_drv_uart_config_t drv_config;
        memcpy(&drv_config, p_drv_uart_config, sizeof(nrf_drv_uart_config_t));
        drv_config.p_context = (void *)p_serial;
    #if defined(UARTE_PRESENT) && defined(UART_PRESENT)
        drv_config.use_easy_dma = (p_config->mode == NRF_SERIAL_MODE_DMA);
    #endif
        ret = nrf_drv_uart_init(&p_serial->instance,
                                &drv_config,
                                p_config->mode == NRF_SERIAL_MODE_POLLING ?
                                NULL : uart_event_handler);
        if (ret != NRF_SUCCESS)
        {
            return ret;
        }
    
        p_serial->p_ctx->p_config = p_config;
    
        if (p_serial->p_ctx->p_config->p_queues)
        {
            nrf_queue_reset(p_serial->p_ctx->p_config->p_queues->p_txq);
            nrf_queue_reset(p_serial->p_ctx->p_config->p_queues->p_rxq);
        }
    
        nrf_mtx_init(&p_serial->p_ctx->read_lock);
        nrf_mtx_init(&p_serial->p_ctx->write_lock);
    
        ret = app_timer_create(p_serial->p_tx_timer,
                               APP_TIMER_MODE_SINGLE_SHOT,
                               serial_timeout_handler);
        if (ret != NRF_SUCCESS)
        {
            return ret;
        }
    
        ret = app_timer_create(p_serial->p_rx_timer,
                               APP_TIMER_MODE_SINGLE_SHOT,
                               serial_timeout_handler);
        if (ret != NRF_SUCCESS)
        {
            return ret;
        }
    
        p_serial->p_ctx->flags = NRF_SERIAL_RX_ENABLED_FLAG |
                                 NRF_SERIAL_TX_ENABLED_FLAG;
    
        if (drv_config.pseltxd == NRF_UART_PSEL_DISCONNECTED)
        {
            p_serial->p_ctx->flags &= ~NRF_SERIAL_TX_ENABLED_FLAG;
        }
    
        if (drv_config.pselrxd == NRF_UART_PSEL_DISCONNECTED)
        {
            p_serial->p_ctx->flags &= ~NRF_SERIAL_RX_ENABLED_FLAG;
            return NRF_SUCCESS;
        }
    
        if (p_serial->p_ctx->p_config->mode != NRF_SERIAL_MODE_DMA)
        {
            nrf_drv_uart_rx_enable(&p_serial->instance);
            if (p_serial->p_ctx->p_config->mode == NRF_SERIAL_MODE_POLLING)
            {
                return NRF_SUCCESS;
            }
        }
    
        return nrf_drv_uart_rx(&p_serial->instance,
                               p_serial->p_ctx->p_config->p_buffers->p_rxb,
                               p_serial->p_ctx->p_config->p_buffers->rx_size);
    }
    
    ret_code_t nrf_serial_uninit(nrf_serial_t const * p_serial)
    {
        ASSERT(p_serial);
    
        if (!p_serial->p_ctx->p_config)
        {
            /*Already uninitialized.*/
            return NRF_ERROR_MODULE_NOT_INITIALIZED;
        }
    
        if (!nrf_mtx_trylock(&p_serial->p_ctx->write_lock))
        {
            return NRF_ERROR_BUSY;
        }
        if (!nrf_mtx_trylock(&p_serial->p_ctx->read_lock))
        {
            nrf_mtx_unlock(&p_serial->p_ctx->write_lock);
            return NRF_ERROR_BUSY;
        }
    
        nrf_drv_uart_uninit(&p_serial->instance);
        if (p_serial->p_ctx->p_config->p_queues)
        {
            nrf_queue_reset(p_serial->p_ctx->p_config->p_queues->p_txq);
            nrf_queue_reset(p_serial->p_ctx->p_config->p_queues->p_rxq);
        }
    
        memset(p_serial->p_ctx, 0, sizeof(nrf_serial_ctx_t));
        return NRF_SUCCESS;
    }
    
    static ret_code_t timeout_setup(nrf_serial_t const * p_serial,
                                    app_timer_id_t const * p_timer_id,
                                    uint32_t timeout_ms,
                                    nrf_serial_timeout_ctx_t * p_tout_ctx)
    {
        uint32_t ticks = APP_TIMER_TICKS(timeout_ms);
    
        if (ticks < APP_TIMER_MIN_TIMEOUT_TICKS)
        {
            p_tout_ctx->expired = true;
            return NRF_SUCCESS;
        }
    
        ret_code_t ret = app_timer_stop(*p_timer_id);
        if (ret != NRF_SUCCESS)
        {
            return ret;
        }
    
        return app_timer_start(*p_timer_id, ticks, p_tout_ctx);
    }
    
    ret_code_t nrf_serial_write(nrf_serial_t const * p_serial,
                                void const * p_data,
                                size_t size,
                                size_t * p_written,
                                uint32_t timeout_ms)
    {
        ret_code_t ret;
    
        ASSERT(p_serial);
        if (!p_serial->p_ctx->p_config)
        {
            return NRF_ERROR_MODULE_NOT_INITIALIZED;
        }
    
        if (!(p_serial->p_ctx->flags & NRF_SERIAL_TX_ENABLED_FLAG))
        {
            return NRF_ERROR_INVALID_STATE;
        }
    
        if (size == 0)
        {
            return NRF_SUCCESS;
        }
    
        if (!nrfx_is_in_ram(p_data) &&
             p_serial->p_ctx->p_config->mode == NRF_SERIAL_MODE_DMA)
        {
            return NRF_ERROR_INVALID_ADDR;
        }
    
        if (!nrf_mtx_trylock(&p_serial->p_ctx->write_lock))
        {
            return NRF_ERROR_BUSY;
        }
    
        nrf_serial_timeout_ctx_t tout_ctx = {
                .expired = false,
        };
    
        if (timeout_ms != NRF_SERIAL_MAX_TIMEOUT)
        {
            ret = timeout_setup(p_serial,
                                p_serial->p_tx_timer,
                                timeout_ms,
                                &tout_ctx);
            if (ret != NRF_SUCCESS)
            {
                nrf_mtx_unlock(&p_serial->p_ctx->write_lock);
                return ret;
            }
        }
    
        size_t left = size;
        uint8_t const * p_buff = p_data;
    
        do
        {
            size_t wcnt = serial_tx(p_serial, p_buff, left);
            left -= wcnt;
            p_buff += wcnt;
            if (!left)
            {
                break;
            }
    
            sleep_handler(p_serial);
        } while (!tout_ctx.expired);
    
        if (p_written)
        {
            *p_written = size - left;
        }
    
        if (!tout_ctx.expired && (timeout_ms != NRF_SERIAL_MAX_TIMEOUT))
        {
            (void)app_timer_stop(*p_serial->p_tx_timer);
        }
    
        nrf_mtx_unlock(&p_serial->p_ctx->write_lock);
        if (left && tout_ctx.expired)
        {
            return NRF_ERROR_TIMEOUT;
        }
    
        return NRF_SUCCESS;
    }
    
    ret_code_t nrf_serial_read(nrf_serial_t const * p_serial,
                               void * p_data,
                               size_t size,
                               size_t * p_read,
                               uint32_t timeout_ms)
    {
        ret_code_t ret;
    
        ASSERT(p_serial);
        if (!p_serial->p_ctx->p_config)
        {
            return NRF_ERROR_MODULE_NOT_INITIALIZED;
        }
    
        if (!(p_serial->p_ctx->flags & NRF_SERIAL_RX_ENABLED_FLAG))
        {
            return NRF_ERROR_INVALID_STATE;
        }
    
        if (size == 0)
        {
            return NRF_SUCCESS;
        }
    
        if (!nrf_mtx_trylock(&p_serial->p_ctx->read_lock))
        {
            return NRF_ERROR_BUSY;
        }
    
        nrf_serial_timeout_ctx_t tout_ctx = {
                .expired = false,
        };
    
        if (timeout_ms != NRF_SERIAL_MAX_TIMEOUT)
        {
            ret = timeout_setup(p_serial,
                                p_serial->p_rx_timer,
                                timeout_ms,
                                &tout_ctx);
    
            if (ret != NRF_SUCCESS)
            {
                nrf_mtx_unlock(&p_serial->p_ctx->read_lock);
                return ret;
            }
        }
    
        size_t left = size;
        uint8_t * p_buff = p_data;
        do
        {
            size_t rcnt = serial_rx(p_serial, p_buff, left);
            left -= rcnt;
            p_buff += rcnt;
            if (!left)
            {
                break;
            }
    
            if (tout_ctx.expired)
            {
                if (p_serial->p_ctx->p_config->mode != NRF_SERIAL_MODE_POLLING)
                {
                    nrf_drv_uart_rx_abort(&p_serial->instance);
                }
                break;
            }
    
            sleep_handler(p_serial);
        } while (1);
    
        if (p_read)
        {
            *p_read = size - left;
        }
    
        if (!tout_ctx.expired && (timeout_ms != NRF_SERIAL_MAX_TIMEOUT))
        {
            (void)app_timer_stop(*p_serial->p_rx_timer);
        }
    
        nrf_mtx_unlock(&p_serial->p_ctx->read_lock);
        if (left && tout_ctx.expired)
        {
            return NRF_ERROR_TIMEOUT;
        }
    
        return NRF_SUCCESS;
    }
    
    ret_code_t nrf_serial_flush(nrf_serial_t const * p_serial, uint32_t timeout_ms)
    {
    
        ret_code_t ret;
    
        ASSERT(p_serial);
        if (!p_serial->p_ctx->p_config)
        {
            return NRF_ERROR_MODULE_NOT_INITIALIZED;
        }
    
        if (!(p_serial->p_ctx->flags & NRF_SERIAL_TX_ENABLED_FLAG))
        {
            return NRF_ERROR_INVALID_STATE;
        }
    
        if (p_serial->p_ctx->p_config->mode == NRF_SERIAL_MODE_POLLING)
        {
            return NRF_SUCCESS;
        }
    
        if (!nrf_mtx_trylock(&p_serial->p_ctx->write_lock))
        {
            return NRF_ERROR_BUSY;
        }
    
        nrf_serial_timeout_ctx_t tout_ctx = {
                .expired = false,
        };
    
        if (timeout_ms != NRF_SERIAL_MAX_TIMEOUT)
        {
            ret = timeout_setup(p_serial,
                                p_serial->p_tx_timer,
                                timeout_ms,
                                &tout_ctx);
            if (ret != NRF_SUCCESS)
            {
                nrf_mtx_unlock(&p_serial->p_ctx->write_lock);
                return ret;
            }
        }
    
        bool empty;
        do
        {
            empty = nrf_queue_is_empty(p_serial->p_ctx->p_config->p_queues->p_txq)
                    && !nrf_drv_uart_tx_in_progress(&p_serial->instance);
            if (empty)
            {
                break;
            }
    
            sleep_handler(p_serial);
        } while (!tout_ctx.expired);
    
        if (!tout_ctx.expired && (timeout_ms != NRF_SERIAL_MAX_TIMEOUT))
        {
            (void)app_timer_stop(*p_serial->p_tx_timer);
        }
    
        nrf_mtx_unlock(&p_serial->p_ctx->write_lock);
        if (!empty && tout_ctx.expired)
        {
            return NRF_ERROR_TIMEOUT;
        }
    
        return NRF_SUCCESS;
    }
    
    ret_code_t nrf_serial_tx_abort(nrf_serial_t const * p_serial)
    {
        ASSERT(p_serial);
        if (!p_serial->p_ctx->p_config)
        {
            return NRF_ERROR_MODULE_NOT_INITIALIZED;
        }
    
        if (!(p_serial->p_ctx->flags & NRF_SERIAL_TX_ENABLED_FLAG))
        {
            return NRF_ERROR_INVALID_STATE;
        }
    
        if (!nrf_mtx_trylock(&p_serial->p_ctx->write_lock))
        {
            return NRF_ERROR_BUSY;
        }
    
        nrf_drv_uart_tx_abort(&p_serial->instance);
        if (p_serial->p_ctx->p_config->p_queues->p_txq)
        {
            nrf_queue_reset(p_serial->p_ctx->p_config->p_queues->p_txq);
        }
    
        nrf_mtx_unlock(&p_serial->p_ctx->write_lock);
        return NRF_SUCCESS;
    }
    
    ret_code_t nrf_serial_rx_drain(nrf_serial_t const * p_serial)
    {
        ASSERT(p_serial);
        if (!p_serial->p_ctx->p_config)
        {
            return NRF_ERROR_MODULE_NOT_INITIALIZED;
        }
    
        if (!(p_serial->p_ctx->flags & NRF_SERIAL_RX_ENABLED_FLAG))
        {
            return NRF_ERROR_INVALID_STATE;
        }
    
        if (!nrf_mtx_trylock(&p_serial->p_ctx->read_lock))
        {
            return NRF_ERROR_BUSY;
        }
    
        uint8_t c;
        /*Drain HW FIFO*/
        while (serial_rx(p_serial, &c, sizeof(c)))
        {
    
        }
    
        /*Drain SW FIFO*/
        if (p_serial->p_ctx->p_config->p_queues->p_rxq)
        {
            nrf_queue_reset(p_serial->p_ctx->p_config->p_queues->p_rxq);
        }
        nrf_mtx_unlock(&p_serial->p_ctx->read_lock);
        return NRF_SUCCESS;
    }
    #else
    ret_code_t nrf_serial_init(nrf_serial_t const * p_serial,
                               nrf_drv_uart_config_t const * p_drv_uart_config,
                               nrf_serial_config_t const * p_config)
    {
        return NRF_ERROR_NOT_SUPPORTED;
    }
    
    ret_code_t nrf_serial_uninit(nrf_serial_t const * p_serial)
    {
        return NRF_ERROR_NOT_SUPPORTED;
    }
    ret_code_t nrf_serial_write(nrf_serial_t const * p_serial,
                                void const * p_data,
                                size_t size,
                                size_t * p_written,
                                uint32_t timeout_ms)
    {
        return NRF_ERROR_NOT_SUPPORTED;
    }
    ret_code_t nrf_serial_read(nrf_serial_t const * p_serial,
                               void * p_data,
                               size_t size,
                               size_t * p_read,
                               uint32_t timeout_ms)
    {
        return NRF_ERROR_NOT_SUPPORTED;
    }
    ret_code_t nrf_serial_flush(nrf_serial_t const * p_serial, uint32_t timeout_ms)
    {
        return NRF_ERROR_NOT_SUPPORTED;
    }
    ret_code_t nrf_serial_tx_abort(nrf_serial_t const * p_serial)
    {
        return NRF_ERROR_NOT_SUPPORTED;
    }
    ret_code_t nrf_serial_rx_drain(nrf_serial_t const * p_serial)
    {
        return NRF_ERROR_NOT_SUPPORTED;
    }
    
    #endif // UART_PRESENT
    #endif //NRF_MODULE_ENABLED(NRF_SERIAL)
    

Children
No Data
Related