Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of mbed-dev by
targets/TARGET_NORDIC/TARGET_NRF5/serial_api.c
- Committer:
- <>
- Date:
- 2016-10-28
- Revision:
- 149:156823d33999
- Child:
- 150:02e0a0aed4ec
File content as of revision 149:156823d33999:
/* * Copyright (c) 2013 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 or object form under this license must not be reverse * engineered, decompiled, modified and/or disassembled. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 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 "serial_api.h" #if DEVICE_SERIAL #include <string.h> #include "mbed_assert.h" #include "mbed_error.h" #include "nrf_uart.h" #include "nrf_drv_common.h" #include "nrf_drv_config.h" #include "app_util_platform.h" #include "nrf_gpio.h" #define UART_INSTANCE_COUNT 1 #define UART_INSTANCE NRF_UART0 #define UART_IRQn UART0_IRQn #define UART_IRQ_HANDLER UART0_IRQHandler #define UART_INSTANCE_ID 0 #define UART_CB uart_cb[UART_INSTANCE_ID] #define UART_DEFAULT_BAUDRATE UART0_CONFIG_BAUDRATE #define UART_DEFAULT_PARITY UART0_CONFIG_PARITY // expected the macro from mbed configuration system #ifndef MBED_CONF_NORDIC_UART_HWFC #define MBED_CONF_NORDIC_UART_HWFC 1 #warning None of UART flow control configuration (expected macro MBED_CONF_NORDIC_UART_HWFC). The RTSCTS flow control is used by default . #endif #if MBED_CONF_NORDIC_UART_HWFC == 1 #define UART_DEFAULT_HWFC UART0_CONFIG_HWFC #else #define UART_DEFAULT_HWFC NRF_UART_HWFC_DISABLED #endif #define UART_DEFAULT_CTS UART0_CONFIG_PSEL_CTS #define UART_DEFAULT_RTS UART0_CONFIG_PSEL_RTS // Required by "retarget.cpp". int stdio_uart_inited = 0; serial_t stdio_uart; typedef struct { bool initialized; uint32_t irq_context; uart_irq_handler irq_handler; uint32_t pselrxd; uint32_t pseltxd; uint32_t pselcts; uint32_t pselrts; nrf_uart_hwfc_t hwfc; nrf_uart_parity_t parity; nrf_uart_baudrate_t baudrate; #if DEVICE_SERIAL_ASYNCH bool volatile rx_active; uint8_t *rx_buffer; size_t rx_length; size_t rx_pos; void (*rx_asynch_handler)(); uint8_t char_match; bool volatile tx_active; const uint8_t *tx_buffer; size_t tx_length; size_t tx_pos; void (*tx_asynch_handler)(); uint32_t events_wanted; uint32_t events_occured; #define UART_IRQ_TX 1 #define UART_IRQ_RX 2 uint8_t irq_enabled; #endif // DEVICE_SERIAL_ASYNCH } uart_ctlblock_t; static uart_ctlblock_t uart_cb[UART_INSTANCE_COUNT]; #if DEVICE_SERIAL_ASYNCH static void end_asynch_rx(void) { // If RX interrupt is activated for synchronous operations, // don't disable it, just stop handling it here. if (!(UART_CB.irq_enabled & UART_IRQ_RX)) { nrf_uart_int_disable(UART_INSTANCE, NRF_UART_INT_MASK_RXDRDY); } UART_CB.rx_active = false; } static void end_asynch_tx(void) { // If TX interrupt is activated for synchronous operations, // don't disable it, just stop handling it here. if (!(UART_CB.irq_enabled & UART_IRQ_TX)) { nrf_uart_int_disable(UART_INSTANCE, NRF_UART_INT_MASK_TXDRDY); } UART_CB.tx_active = false; } #endif // DEVICE_SERIAL_ASYNCH void UART_IRQ_HANDLER(void) { if (nrf_uart_int_enable_check(UART_INSTANCE, NRF_UART_INT_MASK_RXDRDY) && nrf_uart_event_check(UART_INSTANCE, NRF_UART_EVENT_RXDRDY)) { #if DEVICE_SERIAL_ASYNCH if (UART_CB.rx_active) { nrf_uart_event_clear(UART_INSTANCE, NRF_UART_EVENT_RXDRDY); uint8_t rx_data = nrf_uart_rxd_get(UART_INSTANCE); UART_CB.rx_buffer[UART_CB.rx_pos] = rx_data; bool end_rx = false; // If character matching should be performed, check if the current // data matches the given one. if (UART_CB.char_match != SERIAL_RESERVED_CHAR_MATCH && rx_data == UART_CB.char_match) { // If it does, report the match and abort further receiving. UART_CB.events_occured |= SERIAL_EVENT_RX_CHARACTER_MATCH; if (UART_CB.events_wanted & SERIAL_EVENT_RX_CHARACTER_MATCH) { end_rx = true; } } if (++UART_CB.rx_pos >= UART_CB.rx_length) { UART_CB.events_occured |= SERIAL_EVENT_RX_COMPLETE; end_rx = true; } if (end_rx) { end_asynch_rx(); if (UART_CB.rx_asynch_handler) { // Use local variable to make it possible to start a next // transfer from callback routine. void (*handler)() = UART_CB.rx_asynch_handler; UART_CB.rx_asynch_handler = NULL; handler(); } } } else #endif if (UART_CB.irq_handler) { UART_CB.irq_handler(UART_CB.irq_context, RxIrq); } } if (nrf_uart_int_enable_check(UART_INSTANCE, NRF_UART_INT_MASK_TXDRDY) && nrf_uart_event_check(UART_INSTANCE, NRF_UART_EVENT_TXDRDY)) { #if DEVICE_SERIAL_ASYNCH if (UART_CB.tx_active) { if (++UART_CB.tx_pos <= UART_CB.tx_length) { // When there is still something to send, clear the TXDRDY event // and put next byte to transmitter. nrf_uart_event_clear(UART_INSTANCE, NRF_UART_EVENT_TXDRDY); nrf_uart_txd_set(UART_INSTANCE, UART_CB.tx_buffer[UART_CB.tx_pos]); } else { // When the TXDRDY event is set after the last byte to be sent // has been passed to the transmitter, the job is done and TX // complete can be indicated. // Don't clear the TXDRDY event, it needs to remain set for the // 'serial_writable' function to work properly. end_asynch_tx(); UART_CB.events_occured |= SERIAL_EVENT_TX_COMPLETE; if (UART_CB.tx_asynch_handler) { // Use local variable to make it possible to start a next // transfer from callback routine. void (*handler)() = UART_CB.tx_asynch_handler; UART_CB.tx_asynch_handler = NULL; handler(); } } } else #endif if (UART_CB.irq_handler) { UART_CB.irq_handler(UART_CB.irq_context, TxIrq); } } #if DEVICE_SERIAL_ASYNCH if (nrf_uart_event_check(UART_INSTANCE, NRF_UART_EVENT_ERROR)) { nrf_uart_event_clear(UART_INSTANCE, NRF_UART_EVENT_ERROR); uint8_t errorsrc = nrf_uart_errorsrc_get_and_clear(UART_INSTANCE); if (UART_CB.rx_asynch_handler) { UART_CB.events_occured |= SERIAL_EVENT_ERROR; if (errorsrc & NRF_UART_ERROR_PARITY_MASK) { UART_CB.events_occured |= SERIAL_EVENT_RX_PARITY_ERROR; } if (errorsrc & NRF_UART_ERROR_FRAMING_MASK) { UART_CB.events_occured |= SERIAL_EVENT_RX_FRAMING_ERROR; } if (errorsrc & NRF_UART_ERROR_OVERRUN_MASK) { UART_CB.events_occured |= SERIAL_EVENT_RX_OVERRUN_ERROR; } UART_CB.rx_asynch_handler(); } } #endif // DEVICE_SERIAL_ASYNCH } void serial_init(serial_t *obj, PinName tx, PinName rx) { UART_CB.pseltxd = (tx == NC) ? NRF_UART_PSEL_DISCONNECTED : (uint32_t)tx; UART_CB.pselrxd = (rx == NC) ? NRF_UART_PSEL_DISCONNECTED : (uint32_t)rx; if (UART_CB.pseltxd != NRF_UART_PSEL_DISCONNECTED) { nrf_gpio_pin_set(UART_CB.pseltxd); nrf_gpio_cfg_output(UART_CB.pseltxd); } if (UART_CB.pselrxd != NRF_UART_PSEL_DISCONNECTED) { nrf_gpio_cfg_input(UART_CB.pselrxd, NRF_GPIO_PIN_NOPULL); } if (UART_CB.initialized) { // For already initialized peripheral it is sufficient to reconfigure // RX/TX pins only. // Ensure that there is no unfinished TX transfer. while (!serial_writable(obj)) { } // UART pins can be configured only when the peripheral is disabled. nrf_uart_disable(UART_INSTANCE); nrf_uart_txrx_pins_set(UART_INSTANCE, UART_CB.pseltxd, UART_CB.pselrxd); nrf_uart_enable(UART_INSTANCE); } else { UART_CB.baudrate = UART_DEFAULT_BAUDRATE; UART_CB.parity = UART_DEFAULT_PARITY; UART_CB.hwfc = UART_DEFAULT_HWFC; UART_CB.pselcts = UART_DEFAULT_CTS; UART_CB.pselrts = UART_DEFAULT_RTS; nrf_uart_event_clear(UART_INSTANCE, NRF_UART_EVENT_RXDRDY); nrf_uart_event_clear(UART_INSTANCE, NRF_UART_EVENT_TXDRDY); nrf_uart_task_trigger(UART_INSTANCE, NRF_UART_TASK_STARTRX); nrf_uart_task_trigger(UART_INSTANCE, NRF_UART_TASK_STARTTX); nrf_uart_int_disable(UART_INSTANCE, NRF_UART_INT_MASK_RXDRDY | NRF_UART_INT_MASK_TXDRDY); #if DEVICE_SERIAL_ASYNCH nrf_uart_int_enable(UART_INSTANCE, NRF_UART_INT_MASK_ERROR); #endif nrf_drv_common_irq_enable(UART_IRQn, APP_IRQ_PRIORITY_LOW); // TX interrupt needs to be signaled when transmitter buffer is empty, // so a dummy transmission is needed to get the TXDRDY event initially // set. nrf_uart_configure(UART_INSTANCE, NRF_UART_PARITY_EXCLUDED, NRF_UART_HWFC_DISABLED); // Use maximum baud rate, so this dummy transmission takes as little // time as possible. nrf_uart_baudrate_set(UART_INSTANCE, NRF_UART_BAUDRATE_1000000); // Perform it with disconnected TX pin, so nothing actually comes out // of the device. nrf_uart_txrx_pins_disconnect(UART_INSTANCE); nrf_uart_hwfc_pins_disconnect(UART_INSTANCE); nrf_uart_enable(UART_INSTANCE); nrf_uart_txd_set(UART_INSTANCE, 0); while (!nrf_uart_event_check(UART_INSTANCE, NRF_UART_EVENT_TXDRDY)) { } nrf_uart_disable(UART_INSTANCE); // Now everything is prepared to set the default configuration and // connect the peripheral to actual pins. nrf_uart_txrx_pins_set(UART_INSTANCE, UART_CB.pseltxd, UART_CB.pselrxd); nrf_uart_baudrate_set(UART_INSTANCE, UART_CB.baudrate); nrf_uart_configure(UART_INSTANCE, UART_CB.parity, UART_CB.hwfc); if (UART_CB.hwfc == NRF_UART_HWFC_ENABLED) { serial_set_flow_control(obj, FlowControlRTSCTS, (PinName) UART_CB.pselrts, (PinName) UART_CB.pselcts); } nrf_uart_enable(UART_INSTANCE); UART_CB.initialized = true; } if (tx == STDIO_UART_TX && rx == STDIO_UART_RX) { stdio_uart_inited = 1; memcpy(&stdio_uart, obj, sizeof(serial_t)); } else { stdio_uart_inited = 0; } } void serial_free(serial_t *obj) { (void)obj; if (UART_CB.initialized) { nrf_uart_disable(UART_INSTANCE); nrf_uart_int_disable(UART_INSTANCE, NRF_UART_INT_MASK_RXDRDY | NRF_UART_INT_MASK_TXDRDY | NRF_UART_INT_MASK_ERROR); nrf_drv_common_irq_disable(UART_IRQn); UART_CB.initialized = false; // There is only one UART instance, thus at this point the stdio UART // can no longer be initialized. stdio_uart_inited = 0; } } void serial_baud(serial_t *obj, int baudrate) { // nrf_uart_baudrate_set() is not used here (registers are accessed // directly) to make it possible to set special baud rates like 56000 // or 31250. static uint32_t const acceptedSpeeds[][2] = { { 1200, UART_BAUDRATE_BAUDRATE_Baud1200 }, { 2400, UART_BAUDRATE_BAUDRATE_Baud2400 }, { 4800, UART_BAUDRATE_BAUDRATE_Baud4800 }, { 9600, UART_BAUDRATE_BAUDRATE_Baud9600 }, { 14400, UART_BAUDRATE_BAUDRATE_Baud14400 }, { 19200, UART_BAUDRATE_BAUDRATE_Baud19200 }, { 28800, UART_BAUDRATE_BAUDRATE_Baud28800 }, { 31250, (0x00800000UL) /* 31250 baud */ }, { 38400, UART_BAUDRATE_BAUDRATE_Baud38400 }, { 56000, (0x00E51000UL) /* 56000 baud */ }, { 57600, UART_BAUDRATE_BAUDRATE_Baud57600 }, { 76800, UART_BAUDRATE_BAUDRATE_Baud76800 }, { 115200, UART_BAUDRATE_BAUDRATE_Baud115200 }, { 230400, UART_BAUDRATE_BAUDRATE_Baud230400 }, { 250000, UART_BAUDRATE_BAUDRATE_Baud250000 }, { 460800, UART_BAUDRATE_BAUDRATE_Baud460800 }, { 921600, UART_BAUDRATE_BAUDRATE_Baud921600 }, { 1000000, UART_BAUDRATE_BAUDRATE_Baud1M } }; if (baudrate <= 1200) { UART_INSTANCE->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud1200; return; } int const item_cnt = sizeof(acceptedSpeeds)/sizeof(acceptedSpeeds[0]); for (int i = 1; i < item_cnt; i++) { if ((uint32_t)baudrate < acceptedSpeeds[i][0]) { UART_INSTANCE->BAUDRATE = acceptedSpeeds[i - 1][1]; return; } } UART_INSTANCE->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud1M; } void serial_format(serial_t *obj, int data_bits, SerialParity parity, int stop_bits) { (void)obj; if (data_bits != 8) { error("UART supports only 8 data bits.\r\n"); } if (stop_bits != 1) { error("UART supports only 1 stop bits.\r\n"); } if (parity == ParityNone) { UART_CB.parity = NRF_UART_PARITY_EXCLUDED; } else if (parity == ParityEven) { UART_CB.parity = NRF_UART_PARITY_INCLUDED; } else { error("UART supports only even parity.\r\n"); } // Reconfigure UART peripheral. nrf_uart_configure(UART_INSTANCE, UART_CB.parity, UART_CB.hwfc); } void serial_irq_handler(serial_t *obj, uart_irq_handler handler, uint32_t id) { (void)obj; UART_CB.irq_handler = handler; UART_CB.irq_context = id; } void serial_irq_set(serial_t *obj, SerialIrq irq, uint32_t enable) { (void)obj; if (enable) { switch (irq) { case RxIrq: #if DEVICE_SERIAL_ASYNCH UART_CB.irq_enabled |= UART_IRQ_RX; #endif nrf_uart_int_enable(UART_INSTANCE, NRF_UART_INT_MASK_RXDRDY); break; case TxIrq: #if DEVICE_SERIAL_ASYNCH UART_CB.irq_enabled |= UART_IRQ_TX; #endif nrf_uart_int_enable(UART_INSTANCE, NRF_UART_INT_MASK_TXDRDY); break; } } else { switch (irq) { case RxIrq: #if DEVICE_SERIAL_ASYNCH UART_CB.irq_enabled &= ~UART_IRQ_RX; if (!UART_CB.rx_active) #endif { nrf_uart_int_disable(UART_INSTANCE, NRF_UART_INT_MASK_RXDRDY); } break; case TxIrq: #if DEVICE_SERIAL_ASYNCH UART_CB.irq_enabled &= ~UART_IRQ_TX; if (!UART_CB.tx_active) #endif { nrf_uart_int_disable(UART_INSTANCE, NRF_UART_INT_MASK_TXDRDY); } break; } } } int serial_getc(serial_t *obj) { while (!serial_readable(obj)) { } nrf_uart_event_clear(UART_INSTANCE, NRF_UART_EVENT_RXDRDY); return nrf_uart_rxd_get(UART_INSTANCE); } void serial_putc(serial_t *obj, int c) { while (!serial_writable(obj)) { } nrf_uart_event_clear(UART_INSTANCE, NRF_UART_EVENT_TXDRDY); nrf_uart_txd_set(UART_INSTANCE, (uint8_t)c); } int serial_readable(serial_t *obj) { (void)obj; #if DEVICE_SERIAL_ASYNCH if (UART_CB.rx_active) { return 0; } #endif return (nrf_uart_event_check(UART_INSTANCE, NRF_UART_EVENT_RXDRDY)); } int serial_writable(serial_t *obj) { (void)obj; #if DEVICE_SERIAL_ASYNCH if (UART_CB.tx_active) { return 0; } #endif return (nrf_uart_event_check(UART_INSTANCE, NRF_UART_EVENT_TXDRDY)); } void serial_break_set(serial_t *obj) { (void)obj; nrf_uart_task_trigger(UART_INSTANCE, NRF_UART_TASK_SUSPEND); nrf_uart_txrx_pins_disconnect(UART_INSTANCE); nrf_gpio_pin_clear(UART_CB.pseltxd); } void serial_break_clear(serial_t *obj) { (void)obj; nrf_gpio_pin_set(UART_CB.pseltxd); nrf_uart_txrx_pins_set(UART_INSTANCE, UART_CB.pseltxd, UART_CB.pselrxd); nrf_uart_task_trigger(UART_INSTANCE, NRF_UART_TASK_STARTRX); nrf_uart_task_trigger(UART_INSTANCE, NRF_UART_TASK_STARTTX); } void serial_set_flow_control(serial_t *obj, FlowControl type, PinName rxflow, PinName txflow) { (void)obj; UART_CB.pselrts = (rxflow == NC) ? NRF_UART_PSEL_DISCONNECTED : (uint32_t)rxflow; UART_CB.pselcts = (txflow == NC) ? NRF_UART_PSEL_DISCONNECTED : (uint32_t)txflow; if (UART_CB.pselrts != NRF_UART_PSEL_DISCONNECTED) { nrf_gpio_pin_set(UART_CB.pselrts); nrf_gpio_cfg_output(UART_CB.pselrts); } if (UART_CB.pselcts != NRF_UART_PSEL_DISCONNECTED) { nrf_gpio_cfg_input(UART_CB.pselcts, NRF_GPIO_PIN_NOPULL); } nrf_uart_disable(UART_INSTANCE); nrf_uart_hwfc_pins_set(UART_INSTANCE, UART_CB.pselrts, UART_CB.pselcts); nrf_uart_enable(UART_INSTANCE); } void serial_clear(serial_t *obj) { (void)obj; } #if DEVICE_SERIAL_ASYNCH int serial_tx_asynch(serial_t *obj, const void *tx, size_t tx_length, uint8_t tx_width, uint32_t handler, uint32_t event, DMAUsage hint) { (void)obj; (void)tx_width; (void)hint; if (UART_CB.tx_active || !tx_length) { return 0; } UART_CB.tx_buffer = tx; UART_CB.tx_length = tx_length; UART_CB.tx_pos = 0; UART_CB.tx_asynch_handler = (void(*)())handler; UART_CB.events_wanted &= ~SERIAL_EVENT_TX_ALL; UART_CB.events_wanted |= event; UART_CB.tx_active = true; nrf_uart_int_enable(UART_INSTANCE, NRF_UART_INT_MASK_TXDRDY); return 0; } void serial_rx_asynch(serial_t *obj, void *rx, size_t rx_length, uint8_t rx_width, uint32_t handler, uint32_t event, uint8_t char_match, DMAUsage hint) { (void)obj; (void)rx_width; (void)hint; if (UART_CB.rx_active || !rx_length) { return; } UART_CB.rx_buffer = rx; UART_CB.rx_length = rx_length; UART_CB.rx_pos = 0; UART_CB.rx_asynch_handler = (void(*)())handler; UART_CB.events_wanted &= ~SERIAL_EVENT_RX_ALL; UART_CB.events_wanted |= event; UART_CB.char_match = char_match; UART_CB.rx_active = true; nrf_uart_int_enable(UART_INSTANCE, NRF_UART_INT_MASK_RXDRDY); } uint8_t serial_tx_active(serial_t *obj) { (void)obj; return UART_CB.tx_active; } uint8_t serial_rx_active(serial_t *obj) { (void)obj; return UART_CB.rx_active; } int serial_irq_handler_asynch(serial_t *obj) { (void)obj; uint32_t events_to_report = UART_CB.events_wanted & UART_CB.events_occured; UART_CB.events_occured &= (~events_to_report); return events_to_report; } void serial_tx_abort_asynch(serial_t *obj) { (void)obj; end_asynch_tx(); UART_CB.tx_asynch_handler = NULL; } void serial_rx_abort_asynch(serial_t *obj) { (void)obj; end_asynch_rx(); UART_CB.rx_asynch_handler = NULL; } #endif // DEVICE_SERIAL_ASYNCH #endif // DEVICE_SERIAL