From ce0315b5a882349a0408738c56cc7d17d4e73ee8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eivind=20J=C3=B8lsgard?= Date: Thu, 10 Jul 2025 13:29:31 +0200 Subject: [PATCH 1/3] samples: bluetooth: ble_nus: use board-config for uarte pin and inst cfg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use board-config.h for uarte pin and instance config. Signed-off-by: Eivind Jølsgard --- samples/bluetooth/ble_nus/Kconfig | 24 ------------------ samples/bluetooth/ble_nus/src/main.c | 37 ++++++++-------------------- 2 files changed, 10 insertions(+), 51 deletions(-) diff --git a/samples/bluetooth/ble_nus/Kconfig b/samples/bluetooth/ble_nus/Kconfig index bcf4dab53c..79171b01a0 100644 --- a/samples/bluetooth/ble_nus/Kconfig +++ b/samples/bluetooth/ble_nus/Kconfig @@ -10,36 +10,12 @@ config BLE_UART_IRQ_PRIO int "BLE UART IRQ priority" default 3 -config BLE_UART_PIN_TX - int "BLE UART TX pin" - default 6 if SOC_SERIES_NRF52X - default 0 - -config BLE_UART_PIN_RX - int "BLE UART RX pin" - default 8 if SOC_SERIES_NRF52X - default 1 - config BLE_UART_PARITY bool "BLE UART use parity" config BLE_UART_HWFC bool "BLE UART use HWFC" -if BLE_UART_HWFC - -config BLE_UART_PIN_CTS - int "BLE UART CTS pin" - default 7 if SOC_SERIES_NRF52X - default 3 - -config BLE_UART_PIN_RTS - int "BLE UART RTS pin" - default 5 if SOC_SERIES_NRF52X - default 2 - -endif #BLE_UART_HWFC - module=BLE_NUS_SAMPLE module-dep=LOG module-str=BLE NUS Sample diff --git a/samples/bluetooth/ble_nus/src/main.c b/samples/bluetooth/ble_nus/src/main.c index 1a4a122242..f38ed31c43 100644 --- a/samples/bluetooth/ble_nus/src/main.c +++ b/samples/bluetooth/ble_nus/src/main.c @@ -19,6 +19,8 @@ #include #include +#include + LOG_MODULE_REGISTER(app, CONFIG_BLE_NUS_SAMPLE_LOG_LEVEL); BLE_ADV_DEF(ble_adv); /* BLE advertising instance */ @@ -29,11 +31,7 @@ BLE_QWR_DEF(ble_qwr); /* BLE QWR instance */ static uint16_t conn_handle = BLE_CONN_HANDLE_INVALID; /** NUS UARTE instance */ -#if defined(CONFIG_SOC_SERIES_NRF52X) -static const nrfx_uarte_t uarte_inst = NRFX_UARTE_INSTANCE(0); -#elif defined(CONFIG_SOC_SERIES_NRF54LX) -static const nrfx_uarte_t uarte_inst = NRFX_UARTE_INSTANCE(30); -#endif +static const nrfx_uarte_t uarte_inst = NRF_UARTE_INST_GET(BOARD_APP_UARTE_INST); /* Maximum length of data (in bytes) that can be transmitted to the peer by the * Nordic UART service module. @@ -290,13 +288,13 @@ static int uarte_init(void) { int err; - nrfx_uarte_config_t uarte_config = NRFX_UARTE_DEFAULT_CONFIG(CONFIG_BLE_UART_PIN_TX, - CONFIG_BLE_UART_PIN_RX); + nrfx_uarte_config_t uarte_config = NRFX_UARTE_DEFAULT_CONFIG(BOARD_APP_UARTE_PIN_TX, + BOARD_APP_UARTE_PIN_RX); #if defined(CONFIG_BLE_UART_HWFC) uarte_config.config.hwfc = NRF_UARTE_HWFC_ENABLED; - uarte_config.cts_pin = CONFIG_BLE_UART_PIN_CTS; - uarte_config.rts_pin = CONFIG_BLE_UART_PIN_RTS; + uarte_config.cts_pin = BOARD_APP_UARTE_PIN_CTS; + uarte_config.rts_pin = BOARD_APP_UARTE_PIN_RTS; #endif #if defined(CONFIG_BLE_UART_PARITY) @@ -306,17 +304,10 @@ static int uarte_init(void) uarte_config.interrupt_priority = CONFIG_BLE_UART_IRQ_PRIO; /** We need to connect the IRQ ourselves. */ -#if defined(CONFIG_SOC_SERIES_NRF52X) - IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_UARTE_INST_GET(0)), CONFIG_BLE_UART_IRQ_PRIO, - NRFX_UARTE_INST_HANDLER_GET(0), 0, 0); - - irq_enable(NRFX_IRQ_NUMBER_GET(NRF_UARTE_INST_GET(0))); -#elif defined(CONFIG_SOC_SERIES_NRF54LX) - IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_UARTE_INST_GET(30)), CONFIG_BLE_UART_IRQ_PRIO, - NRFX_UARTE_INST_HANDLER_GET(30), 0, 0); + IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_UARTE_INST_GET(BOARD_APP_UARTE_INST)), CONFIG_BLE_UART_IRQ_PRIO, + NRFX_UARTE_INST_HANDLER_GET(BOARD_APP_UARTE_INST), 0, 0); - irq_enable(NRFX_IRQ_NUMBER_GET(NRF_UARTE_INST_GET(30))); -#endif + irq_enable(NRFX_IRQ_NUMBER_GET(NRF_UARTE_INST_GET(BOARD_APP_UARTE_INST))); err = nrfx_uarte_init(&uarte_inst, &uarte_config, uarte_evt_handler); if (err != NRFX_SUCCESS) { @@ -324,14 +315,6 @@ static int uarte_init(void) return err; } - /* optional: enable pull-up on RX pin in case pin may become floating. - * Induced noise on a floating RX input may lead to an UARTE error condition - */ -#if defined(CONFIG_SOC_SERIES_NRF52X) - NRF_GPIO->PIN_CNF[uarte_config.rxd_pin] |= - (GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos); -#endif - return 0; } From 9b02cfd7f7053e47dbccb3db5df80114853b80e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eivind=20J=C3=B8lsgard?= Date: Fri, 11 Jul 2025 11:10:43 +0200 Subject: [PATCH 2/3] drivers: add lpuarte driver and sample MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add low power uarte driver and sample. Based on NCS LPUARTE driver and sample, nrfconnect/sdk-nrf/commit/d33871cc92bc50034a6d9b7bdcac94c0ff358390 Co-authored-by: Andreas Moltumyr Signed-off-by: Eivind Jølsgard --- CODEOWNERS | 1 + .../bm_nrf54l15dk/include/board-config.h | 18 + doc/nrf-bm/drivers.rst | 17 + doc/nrf-bm/drivers/lpuarte.rst | 55 ++ doc/nrf-bm/index.rst | 1 + doc/nrf-bm/links.txt | 1 + drivers/CMakeLists.txt | 1 + drivers/Kconfig | 1 + drivers/lpuarte/CMakeLists.txt | 8 + drivers/lpuarte/Kconfig | 20 + drivers/lpuarte/lpuarte.c | 631 ++++++++++++++++++ include/bm_lpuarte.h | 199 ++++++ samples/peripherals/lpuarte/CMakeLists.txt | 12 + samples/peripherals/lpuarte/Kconfig | 35 + samples/peripherals/lpuarte/README.rst | 75 +++ samples/peripherals/lpuarte/prj.conf | 11 + samples/peripherals/lpuarte/sample.yaml | 18 + samples/peripherals/lpuarte/src/main.c | 155 +++++ 18 files changed, 1259 insertions(+) create mode 100644 doc/nrf-bm/drivers.rst create mode 100644 doc/nrf-bm/drivers/lpuarte.rst create mode 100644 drivers/lpuarte/CMakeLists.txt create mode 100644 drivers/lpuarte/Kconfig create mode 100644 drivers/lpuarte/lpuarte.c create mode 100644 include/bm_lpuarte.h create mode 100644 samples/peripherals/lpuarte/CMakeLists.txt create mode 100644 samples/peripherals/lpuarte/Kconfig create mode 100644 samples/peripherals/lpuarte/README.rst create mode 100644 samples/peripherals/lpuarte/prj.conf create mode 100644 samples/peripherals/lpuarte/sample.yaml create mode 100644 samples/peripherals/lpuarte/src/main.c diff --git a/CODEOWNERS b/CODEOWNERS index 618a174541..40a4d3fed4 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -34,6 +34,7 @@ # Drivers /drivers/clock_control/ @nrfconnect/ncs-bm /drivers/console/ @nrfconnect/ncs-bm +/drivers/lpuarte/ @nrfconnect/ncs-bm # Include /include/*.h @nrfconnect/ncs-bm diff --git a/boards/nordic/bm_nrf54l15dk/include/board-config.h b/boards/nordic/bm_nrf54l15dk/include/board-config.h index 2ef67f752f..90071099ae 100644 --- a/boards/nordic/bm_nrf54l15dk/include/board-config.h +++ b/boards/nordic/bm_nrf54l15dk/include/board-config.h @@ -81,6 +81,24 @@ extern "C" { #define BOARD_APP_UARTE_PIN_CTS NRF_PIN_PORT_TO_PIN_NUMBER(3, 0) #endif +/* Application LPUART configuration */ +#ifndef BOARD_APP_LPUARTE_INST +#define BOARD_APP_LPUARTE_INST 21 +#endif + +#ifndef BOARD_APP_LPUARTE_PIN_TX +#define BOARD_APP_LPUARTE_PIN_TX NRF_PIN_PORT_TO_PIN_NUMBER(11, 1) +#endif +#ifndef BOARD_APP_LPUARTE_PIN_RX +#define BOARD_APP_LPUARTE_PIN_RX NRF_PIN_PORT_TO_PIN_NUMBER(10, 1) +#endif +#ifndef BOARD_APP_LPUARTE_PIN_REQ +#define BOARD_APP_LPUARTE_PIN_REQ NRF_PIN_PORT_TO_PIN_NUMBER(8, 1) +#endif +#ifndef BOARD_APP_LPUARTE_PIN_RDY +#define BOARD_APP_LPUARTE_PIN_RDY NRF_PIN_PORT_TO_PIN_NUMBER(9, 1) +#endif + #ifdef __cplusplus } #endif diff --git a/doc/nrf-bm/drivers.rst b/doc/nrf-bm/drivers.rst new file mode 100644 index 0000000000..9a07169114 --- /dev/null +++ b/doc/nrf-bm/drivers.rst @@ -0,0 +1,17 @@ +.. _drivers: + +Drivers +####### + +In supplement to nrfx drivers, |BMlong| provides drivers for typical use cases with Nordic Semiconductor devices. +If the driver you are looking for is not available below, please check the `nrfx documentation`_. + +Only nrfx drivers and the drivers located under :file:`nrf-bm/drivers` are in the scope of the |BMshort| option. +All other drivers that are included in the distribution must be ignored when working with the |BMshort| option. + +.. toctree:: + :maxdepth: 1 + :glob: + :caption: Subpages: + + drivers/* diff --git a/doc/nrf-bm/drivers/lpuarte.rst b/doc/nrf-bm/drivers/lpuarte.rst new file mode 100644 index 0000000000..02724fe473 --- /dev/null +++ b/doc/nrf-bm/drivers/lpuarte.rst @@ -0,0 +1,55 @@ +.. _driver_lpuarte: + +Low Power UART with EasyDMA (LPUARTE) +##################################### + +The Low Power UART with EasyDMA (LPUARTE) driver implements the standard UART with EasyDMA and two extra control lines for low power capabilities. +The control lines allow for disabling the UART receiver during the idle period. +This results in low power consumption, as you can shut down the high-frequency clock when UART is in idle state. + +Control protocol +**************** + +You can use the protocol for duplex transmission between two devices. + +The control protocol uses two additional lines alongside the standard TX and RX lines: + +* the *Request* line (REQ), +* the *Ready* line (RDY). + +The REQ line of the first device is connected with the RDY line of the second device. +It is configured as an output and set to low. + +The RDY line is configured as input without pull-up and the interrupt is configured to detect the transition from low to high state. + +Implementation +************** + +The driver implements the control protocol in the following way: + +#. The transmitter initiates the transmission by calling :c:func:`bm_lpuarte_tx`. +#. The driver reconfigures the REQ line to input with pull-up and enables the detection of high to low transition. +#. The line is set to high due to the pull-up register, and that triggers an interrupt on the RDY line on the receiver device. +#. On that interrupt, the driver starts the UART receiver. +#. When the receiver is ready, the driver reconfigures the RDY line to output low. +#. Then, the driver reconfigures the RDY line to input with pull-up and enables the interrupt on detection of high to low transition. +#. This sequence results in a short pulse on the line, as the line goes low and high. +#. The initiator detects the pulse and starts the standard UART transfer. +#. When the transfer is completed, the transmitter driver reconfigures the REQ line to the initial state: output set to low. + This results in a line state change. +#. As the line goes low, the receiver detects the change. + This indicates that the UARTE receiver can be stopped. + +Once the receiver acknowledges the transfer, it must be capable of receiving the whole transfer until the REQ line goes down. + +This requirement can be fulfilled in two ways: + +* By providing a buffer of the size of the maximum packet length. +* By continuously responding to the RX buffer request event. + The latency of the event handling must be taken into account in that case. + For example, a flash page erase on some devices might have a significant impact. + +Sample usage +************ + +See the :ref:`bm_lpuarte_sample` sample for the implementation of this driver. diff --git a/doc/nrf-bm/index.rst b/doc/nrf-bm/index.rst index 828e2dfe57..f93189efba 100644 --- a/doc/nrf-bm/index.rst +++ b/doc/nrf-bm/index.rst @@ -35,6 +35,7 @@ The |BMlong| is a distinct repository that incorporates elements from the existi :caption: Contents install_nrf_bm.rst + drivers.rst libraries/index.rst samples.rst ug_dfu.rst diff --git a/doc/nrf-bm/links.txt b/doc/nrf-bm/links.txt index 3076ac8fcb..31ec44e217 100644 --- a/doc/nrf-bm/links.txt +++ b/doc/nrf-bm/links.txt @@ -67,3 +67,4 @@ .. _`nRF54L15`: https://docs.nordicsemi.com/category/nRF54L15-category .. _`nRF54L10`: https://docs.nordicsemi.com/category/nRF54L10-category .. _`nRF54L05`: https://docs.nordicsemi.com/category/nRF54L05-category +.. _`nrfx documentation`: https://docs.nordicsemi.com/bundle/nrfx-apis-latest/page/index.html diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index 5d0adf8fa8..32a536ae19 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -10,3 +10,4 @@ add_compile_options($) add_subdirectory_ifdef(CONFIG_CONSOLE console) add_subdirectory_ifdef(CONFIG_CLOCK_CONTROL clock_control) add_subdirectory_ifdef(CONFIG_FLASH flash) +add_subdirectory_ifdef(CONFIG_BM_SW_LPUARTE lpuarte) diff --git a/drivers/Kconfig b/drivers/Kconfig index 6ab8b89fbb..f2dfb2561b 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -8,6 +8,7 @@ menu "Device Drivers" # zephyr-keep-sorted-start rsource "console/Kconfig" rsource "flash/Kconfig" +rsource "lpuarte/Kconfig" # zephyr-keep-sorted-stop endmenu diff --git a/drivers/lpuarte/CMakeLists.txt b/drivers/lpuarte/CMakeLists.txt new file mode 100644 index 0000000000..358495bd27 --- /dev/null +++ b/drivers/lpuarte/CMakeLists.txt @@ -0,0 +1,8 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# +zephyr_library() + +zephyr_library_sources(lpuarte.c) diff --git a/drivers/lpuarte/Kconfig b/drivers/lpuarte/Kconfig new file mode 100644 index 0000000000..ef713901cf --- /dev/null +++ b/drivers/lpuarte/Kconfig @@ -0,0 +1,20 @@ +# +# Copyright (c) 2025 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +config BM_SW_LPUARTE + bool "Low Power UARTE using REQ/RDY lines" + depends on BM_TIMER + help + Low Power UARTE implements UARTE API and extends standard UARTE + communication with a 2-pin protocol for receiver wake-up and flow control. + +if BM_SW_LPUARTE + +module = BM_SW_LPUARTE +module-str = Low Power UARTE +source "subsys/logging/Kconfig.template.log_config" + +endif # BM_SW_LPUARTE diff --git a/drivers/lpuarte/lpuarte.c b/drivers/lpuarte/lpuarte.c new file mode 100644 index 0000000000..1d4a3bbc0b --- /dev/null +++ b/drivers/lpuarte/lpuarte.c @@ -0,0 +1,631 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#include + +LOG_MODULE_REGISTER(lpuarte, CONFIG_BM_SW_LPUARTE_LOG_LEVEL); + +static const nrfx_gpiote_t gpiote20_instance = NRFX_GPIOTE_INSTANCE(20); +static const nrfx_gpiote_t gpiote30_instance = NRFX_GPIOTE_INSTANCE(30); + +static void req_pin_handler(nrfx_gpiote_pin_t pin, + nrfx_gpiote_trigger_t trigger, + void *context); + +static void rdy_pin_handler(nrfx_gpiote_pin_t pin, + nrfx_gpiote_trigger_t trigger, + void *context); + +static inline const nrfx_gpiote_t *gpiote_get(uint32_t pin) +{ + switch (NRF_PIN_NUMBER_TO_PORT(pin)) { + case 0: + return &gpiote30_instance; + case 1: + return &gpiote20_instance; + default: + return NULL; + } +} + +/* Called when UARTE transfer is finished to indicate to the receiver that it can be closed. */ +static void req_pin_idle(struct bm_lpuarte *lpu) +{ + nrf_gpio_cfg(lpu->req_pin, + NRF_GPIO_PIN_DIR_OUTPUT, + NRF_GPIO_PIN_INPUT_DISCONNECT, + NRF_GPIO_PIN_NOPULL, + NRF_GPIO_PIN_S0S1, + NRF_GPIO_PIN_NOSENSE); +} + +static void pend_req_pin_idle(struct bm_lpuarte *lpu) +{ + /* Wait until the pin is high */ + while (!nrfx_gpiote_in_is_set(lpu->req_pin)) { + } +} + +/* Force pin assertion. Pin is kept high during uarte transfer. */ +static void req_pin_set(struct bm_lpuarte *lpu) +{ + const nrf_gpio_pin_dir_t dir = NRF_GPIO_PIN_DIR_INPUT; + const nrf_gpio_pin_input_t input = NRF_GPIO_PIN_INPUT_CONNECT; + + nrf_gpio_reconfigure(lpu->req_pin, &dir, &input, NULL, NULL, NULL); + + nrfx_gpiote_trigger_disable(gpiote_get(lpu->req_pin), lpu->req_pin); +} + +/* Reconfigure pin to input with pull up and high->low state detection. + * Receiver will pull pin down for a moment when ready, which means that the transfer can start. + */ +static void req_pin_arm(struct bm_lpuarte *lpu) +{ + const nrf_gpio_pin_pull_t pull = NRF_GPIO_PIN_PULLUP; + + /* Add pull up before reconfiguring to input. */ + nrf_gpio_reconfigure(lpu->req_pin, NULL, NULL, &pull, NULL, NULL); + + nrfx_gpiote_trigger_enable(gpiote_get(lpu->req_pin), lpu->req_pin, true); +} + +static nrfx_err_t req_pin_init(struct bm_lpuarte *lpu, nrfx_gpiote_pin_t pin) +{ + uint8_t ch; + nrfx_err_t err; + nrf_gpio_pin_pull_t pull_config = NRF_GPIO_PIN_PULLDOWN; + nrfx_gpiote_trigger_config_t trigger_config = { + .trigger = NRFX_GPIOTE_TRIGGER_HITOLO, + .p_in_channel = &ch, + }; + nrfx_gpiote_handler_config_t handler_config = { + .handler = req_pin_handler, + .p_context = lpu, + }; + nrfx_gpiote_input_pin_config_t input_config = { + .p_pull_config = &pull_config, + .p_trigger_config = &trigger_config, + .p_handler_config = &handler_config + }; + + err = nrfx_gpiote_channel_alloc(gpiote_get(pin), &ch); + if (err != NRFX_SUCCESS) { + return err; + } + + err = nrfx_gpiote_input_configure(gpiote_get(pin), pin, &input_config); + if (err != NRFX_SUCCESS) { + return err; + } + + lpu->req_pin = pin; + + /* Set the request pin in idle state to indicate to the receiver that there is no pending + * transfer. + */ + req_pin_idle(lpu); + + return NRFX_SUCCESS; +} + +static void req_pin_uninit(struct bm_lpuarte *lpu, nrfx_gpiote_pin_t pin) +{ + (void)nrfx_gpiote_pin_uninit(gpiote_get(pin), pin); +} + +static void rdy_pin_suspend(struct bm_lpuarte *lpu) +{ + nrfx_gpiote_trigger_disable(gpiote_get(lpu->rdy_pin), lpu->rdy_pin); +} + +static nrfx_err_t rdy_pin_init(struct bm_lpuarte *lpu, nrfx_gpiote_pin_t pin) +{ + nrfx_err_t err; + nrf_gpio_pin_pull_t pull_config = NRF_GPIO_PIN_NOPULL; + nrfx_gpiote_handler_config_t handler_config = { + .handler = rdy_pin_handler, + .p_context = lpu + }; + nrfx_gpiote_input_pin_config_t input_config = { + .p_pull_config = &pull_config, + .p_trigger_config = NULL, + .p_handler_config = &handler_config + }; + + err = nrfx_gpiote_channel_alloc(gpiote_get(pin), &lpu->rdy_ch); + if (err != NRFX_SUCCESS) { + return err; + } + + err = nrfx_gpiote_input_configure(gpiote_get(pin), pin, &input_config); + if (err != NRFX_SUCCESS) { + return err; + } + + lpu->rdy_pin = pin; + nrf_gpio_pin_clear(pin); + + return NRFX_SUCCESS; +} + +static void rdy_pin_uninit(struct bm_lpuarte *lpu, nrfx_gpiote_pin_t pin) +{ + (void)nrfx_gpiote_pin_uninit(gpiote_get(pin), pin); +} + +/* Pin activated to detect high state (using SENSE). */ +static void rdy_pin_idle(struct bm_lpuarte *lpu) +{ + nrfx_err_t err; + nrfx_gpiote_trigger_config_t trigger_config = { + .trigger = NRFX_GPIOTE_TRIGGER_HIGH + }; + nrfx_gpiote_input_pin_config_t input_config = { + .p_pull_config = NULL, + .p_trigger_config = &trigger_config, + .p_handler_config = NULL + }; + const nrfx_gpiote_t *gpiote = gpiote_get(lpu->rdy_pin); + + err = nrfx_gpiote_input_configure(gpiote, lpu->rdy_pin, &input_config); + __ASSERT(err == NRFX_SUCCESS, "Unexpected nrfx_err %#x", err); + + nrfx_gpiote_trigger_enable(gpiote, lpu->rdy_pin, true); +} + +/* Indicate to the transmitter that receiver is ready by pulling pin down for + * a moment, then reconfiguring it back to input with low state detection to detect when + * transmission is complete. + * + * Function checks if transmitter has request pin in the expected state and if not + * false is returned. + */ +static bool rdy_pin_blink(struct bm_lpuarte *lpu) +{ + nrfx_err_t err; + nrfx_gpiote_trigger_config_t trigger_config = { + .trigger = NRFX_GPIOTE_TRIGGER_HITOLO, + .p_in_channel = &lpu->rdy_ch + }; + nrfx_gpiote_input_pin_config_t input_config = { + .p_pull_config = NULL, + .p_trigger_config = &trigger_config, + .p_handler_config = NULL + }; + const nrf_gpio_pin_dir_t dir_in = NRF_GPIO_PIN_DIR_INPUT; + const nrf_gpio_pin_dir_t dir_out = NRF_GPIO_PIN_DIR_OUTPUT; + const nrfx_gpiote_t *gpiote = gpiote_get(lpu->rdy_pin); + bool ret; + + /* Drive low for a moment */ + nrf_gpio_reconfigure(lpu->rdy_pin, &dir_out, NULL, NULL, NULL, NULL); + + err = nrfx_gpiote_input_configure(gpiote, lpu->rdy_pin, &input_config); + __ASSERT(err == NRFX_SUCCESS, "Unexpected nrfx_err %#x", err); + + nrfx_gpiote_trigger_enable(gpiote, lpu->rdy_pin, true); + + NRFX_CRITICAL_SECTION_ENTER(); + + nrf_gpiote_event_t event = nrf_gpiote_in_event_get(lpu->rdy_ch); + + nrf_gpio_reconfigure(lpu->rdy_pin, &dir_in, NULL, NULL, NULL, NULL); + + /* Wait a bit. After switching to input the transmitter pin pullup should drive + * this pin high. + */ + k_busy_wait(1); + if (nrf_gpio_pin_read(lpu->rdy_pin) == 0 && + !nrf_gpiote_event_check(gpiote->p_reg, event)) { + /* Suspicious pin state (low). It might be that context was preempted + * for long enough and transfer ended (in that case event will be set) + * or the transmitter is working abnormally or pin is just floating. + */ + ret = false; + LOG_WRN("req pin low when expected high"); + } else { + ret = true; + } + NRFX_CRITICAL_SECTION_EXIT(); + + return ret; +} + +/* Set response pin to idle and disable RX. */ +static void deactivate_rx(struct bm_lpuarte *lpu) +{ + nrfx_err_t err; + + /* abort rx */ + LOG_DBG("RX: Deactivate"); + lpu->rx_state = RX_TO_IDLE; + err = nrfx_uarte_rx_abort(&lpu->uarte_inst, true, false); + if (err != NRFX_SUCCESS) { + LOG_ERR("RX: Failed to disable, nrfx_err %#x", err); + } + + rdy_pin_idle(lpu); +} + +/* Enable RX and inform the transmitter that it is ready to receive by + * clearing the RDY pin (configure to output, low level). RDY pin is then reconfigured to input with + * pullup and high to low detection to detect end of transfer. + */ +static void activate_rx(struct bm_lpuarte *lpu) +{ + nrfx_err_t err; + + LOG_DBG("Activating uarte RX"); + + err = nrfx_uarte_rx_enable(&lpu->uarte_inst, + NRFX_UARTE_RX_ENABLE_CONT | NRFX_UARTE_RX_ENABLE_STOP_ON_END); + if (err != NRFX_SUCCESS) { + LOG_ERR("lpuarte rx enable failed, nrfx_err %#x", err); + } + + lpu->rx_state = RX_ACTIVE; + + /* Ready. Confirm by toggling the pin. */ + if (!rdy_pin_blink(lpu)) { + /* If tranmitter behaves abnormally deactivate RX. */ + rdy_pin_suspend(lpu); + deactivate_rx(lpu); + return; + } + + LOG_DBG("RX activated"); +} + +static void start_rx_activation(struct bm_lpuarte *lpu) +{ + lpu->rx_state = RX_PREPARE; + activate_rx(lpu); +} + +static void tx_complete(struct bm_lpuarte *lpu) +{ + LOG_DBG("TX completed, pin idle"); + if (lpu->tx_active) { + pend_req_pin_idle(lpu); + } else { + req_pin_set(lpu); + } + + req_pin_idle(lpu); + lpu->tx_buf = NULL; + lpu->tx_active = false; +} + +/* Called when the REQ pin transition to low state is detected, which indicates + * that the receiver is ready for the transfer. + */ +static void req_pin_handler(nrfx_gpiote_pin_t pin, nrfx_gpiote_trigger_t trigger, void *context) +{ + ARG_UNUSED(trigger); + ARG_UNUSED(pin); + const uint8_t *buf; + size_t len; + nrfx_err_t err; + struct bm_lpuarte *lpu = context; + + LOG_DBG("req_pin_evt"); + + if (lpu->tx_buf == NULL) { + LOG_WRN("TX: request confirmed but no data to send"); + tx_complete(lpu); + /* aborted */ + return; + } + + LOG_DBG("TX: Confirmed, starting."); + + req_pin_set(lpu); + bm_timer_stop(&lpu->tx_timer); + + NRFX_CRITICAL_SECTION_ENTER(); + lpu->tx_active = true; + buf = lpu->tx_buf; + len = lpu->tx_len; + NRFX_CRITICAL_SECTION_EXIT(); + err = nrfx_uarte_tx(&lpu->uarte_inst, buf, len, 0); + if (err != NRFX_SUCCESS) { + LOG_ERR("TX: Not started, nrfx_err %#x", err); + tx_complete(lpu); + + const nrfx_uarte_event_t tx_done_aborted_evt = { + .type = NRFX_UARTE_EVT_TX_DONE, + .data.tx = { + .p_buffer = buf, + .length = 0, + .flags = NRFX_UARTE_TX_DONE_ABORTED, + }, + }; + + lpu->callback(&tx_done_aborted_evt, lpu); + } +} + +/* RDY pin handler is called in two cases: + * - high state detection. Receiver is idle and new transfer request is received. + * - high->low state. Receiver is active and receiving a packet. Transmitter indicates + * end of the packet. + */ +static void rdy_pin_handler(nrfx_gpiote_pin_t pin, nrfx_gpiote_trigger_t trigger, void *context) +{ + struct bm_lpuarte *lpu = context; + + rdy_pin_suspend(lpu); + if (trigger == NRFX_GPIOTE_TRIGGER_HIGH) { + __ASSERT_NO_MSG(lpu->rx_state != RX_ACTIVE); + + LOG_DBG("RX: Request detected. RX state %d", lpu->rx_state); + if (lpu->rx_state == RX_IDLE || lpu->rx_state == RX_TO_IDLE) { + start_rx_activation(lpu); + } + } else { /* HITOLO */ + if (lpu->rx_state != RX_ACTIVE) { + LOG_WRN("RX: End detected at unexpected state (%d).", lpu->rx_state); + lpu->rx_state = RX_IDLE; + rdy_pin_idle(lpu); + return; + } + + LOG_DBG("RX: End detected."); + deactivate_rx(lpu); + } +} + +static void tx_timeout(void *context) +{ + nrfx_err_t err; + struct bm_lpuarte *lpu = context; + const uint8_t *buf = lpu->tx_buf; + + LOG_WRN("TX abort timeout"); + if (lpu->tx_active) { + err = nrfx_uarte_tx_abort(&lpu->uarte_inst, true); + if (err == NRFX_ERROR_INVALID_STATE) { + LOG_DBG("No active transfer. Already finished?"); + } else if (err != NRFX_SUCCESS) { + __ASSERT(0, "Unexpected tx_abort, nrfx_err %#x", err); + } + return; + } + + tx_complete(lpu); + + const nrfx_uarte_event_t tx_done_aborted_evt = { + .type = NRFX_UARTE_EVT_TX_DONE, + .data.tx = { + .p_buffer = buf, + .length = 0, + .flags = NRFX_UARTE_TX_DONE_ABORTED, + }, + }; + + lpu->callback(&tx_done_aborted_evt, lpu); +} + +static void nrfx_uarte_evt_handler(nrfx_uarte_event_t const *event, void *ctx) +{ + struct bm_lpuarte *lpu = ctx; + + switch (event->type) { + case NRFX_UARTE_EVT_TX_DONE: + LOG_DBG("TX complete event, %d, %x", event->data.tx.length, event->data.tx.flags); + tx_complete(ctx); + break; + case NRFX_UARTE_EVT_RX_DONE: + if (lpu->rx_state == RX_TO_OFF) { + lpu->rx_state = RX_OFF; + rdy_pin_idle(lpu); + } + break; + case NRFX_UARTE_EVT_RX_DISABLED: + /* UARTE receiver is disabled, we go to rx idle to allow for new RX initiation. */ + lpu->rx_state = RX_IDLE; + + rdy_pin_idle(lpu); + break; + case NRFX_UARTE_EVT_ERROR: + LOG_ERR("UARTE error event, %#x", event->data.error.error_mask); + break; + default: + break; + } + + lpu->callback(event, ctx); +} + +nrfx_err_t bm_lpuarte_init(struct bm_lpuarte *lpu, + struct bm_lpuarte_config *lpu_cfg, + nrfx_uarte_event_handler_t event_handler) +{ + nrfx_err_t err; + + /* We use the uarte context for storing the pointer to the lpu instance */ + lpu_cfg->uarte_cfg.p_context = lpu; + + memcpy(&lpu->uarte_inst, &lpu_cfg->uarte_inst, sizeof(nrfx_uarte_t)); + lpu->req_pin = lpu_cfg->req_pin; + lpu->rdy_pin = lpu_cfg->rdy_pin; + lpu->rx_state = RX_OFF; + + lpu->callback = event_handler; + + if (!nrfx_gpiote_init_check(&gpiote20_instance)) { + err = nrfx_gpiote_init(&gpiote20_instance, 0); + if (err != NRFX_SUCCESS) { + LOG_ERR("Failed to initialize gpiote20, nrfx_err: %#x", err); + return NRFX_ERROR_INVALID_STATE; + } + } + + if (!nrfx_gpiote_init_check(&gpiote30_instance)) { + err = nrfx_gpiote_init(&gpiote30_instance, 0); + if (err != NRFX_SUCCESS) { + LOG_ERR("Failed to initialize gpiote30, nrfx_err: %#x", err); + return NRFX_ERROR_INVALID_STATE; + } + } + + err = req_pin_init(lpu, lpu_cfg->req_pin); + if (err < 0) { + LOG_ERR("req pin init failed, nrfx_err %#x", err); + return err; + } + + err = rdy_pin_init(lpu, lpu_cfg->rdy_pin); + if (err < 0) { + LOG_ERR("rdy pin init failed, nrfx_err %#x", err); + return err; + } + + bm_timer_init(&lpu->tx_timer, BM_TIMER_MODE_SINGLE_SHOT, tx_timeout); + + err = nrfx_uarte_init(&lpu->uarte_inst, &lpu_cfg->uarte_cfg, nrfx_uarte_evt_handler); + if (err != NRFX_SUCCESS) { + LOG_ERR("Failed to initialize UARTE, nrfx_err %#x", err); + return err; + } + + return err; +} + +void bm_lpuarte_uninit(struct bm_lpuarte *lpu) +{ + if (lpu->rx_state != RX_OFF) { + (void)bm_lpuarte_rx_abort(lpu, true); + } + if (lpu->tx_buf) { + (void)bm_lpuarte_tx_abort(lpu, true); + } + + nrfx_uarte_uninit(&lpu->uarte_inst); + req_pin_uninit(lpu, lpu->req_pin); + rdy_pin_uninit(lpu, lpu->rdy_pin); + + /* Don't uninitialize gpiote instances as they can be used by other drivers and libraries */ +} + +nrfx_err_t bm_lpuarte_tx(struct bm_lpuarte *lpu, uint8_t const *data, size_t len, + int32_t timeout) +{ + if (!lpu || !data) { + return NRFX_ERROR_NULL; + } + if (!len) { + return NRFX_ERROR_INVALID_LENGTH; + } + if (!atomic_ptr_cas((atomic_ptr_t *)&lpu->tx_buf, NULL, (void *)data)) { + return NRFX_ERROR_BUSY; + } + + lpu->tx_len = len; + bm_timer_start(&lpu->tx_timer, BM_TIMER_MS_TO_TICKS(timeout), lpu); + + /* Enable interrupt on pin going low. */ + req_pin_arm(lpu); + + return NRFX_SUCCESS; +} + +bool bm_lpuarte_tx_in_progress(struct bm_lpuarte *lpu) +{ + return (lpu->tx_buf != NULL); +} + +nrfx_err_t bm_lpuarte_tx_abort(struct bm_lpuarte *lpu, bool sync) +{ + nrfx_err_t err; + const uint8_t *buf = lpu->tx_buf; + + if (!lpu) { + return NRFX_ERROR_NULL; + } + if (!lpu->tx_buf) { + return NRFX_ERROR_INVALID_STATE; + } + + bm_timer_stop(&lpu->tx_timer); + NRFX_CRITICAL_SECTION_ENTER(); + tx_complete(lpu); + NRFX_CRITICAL_SECTION_EXIT(); + + err = nrfx_uarte_tx_abort(&lpu->uarte_inst, sync); + if (err == NRFX_ERROR_INVALID_STATE && !sync) { + /* If abort is before TX is started we report ABORT from here. */ + err = NRFX_SUCCESS; + + const nrfx_uarte_event_t tx_done_aborted_evt = { + .type = NRFX_UARTE_EVT_TX_DONE, + .data.tx = { + .p_buffer = buf, + .length = 0, + .flags = NRFX_UARTE_TX_DONE_ABORTED, + }, + }; + + lpu->callback(&tx_done_aborted_evt, lpu); + } + + return err; +} + +nrfx_err_t bm_lpuarte_rx_enable(struct bm_lpuarte *lpu) +{ + if (!atomic_cas((atomic_t *)&lpu->rx_state, (atomic_val_t)RX_OFF, (atomic_val_t)RX_IDLE)) { + return NRFX_ERROR_BUSY; + } + + rdy_pin_idle(lpu); + + return NRFX_SUCCESS; +} + +nrfx_err_t bm_lpuarte_rx_buffer_set(struct bm_lpuarte *lpu, + uint8_t *data, + size_t length) +{ + return nrfx_uarte_rx_buffer_set(&lpu->uarte_inst, data, length); +} + +nrfx_err_t bm_lpuarte_rx_abort(struct bm_lpuarte *lpu, bool sync) +{ + nrfx_err_t err; + + if (lpu->rx_state == RX_OFF) { + return NRFX_ERROR_INVALID_STATE; + } + + lpu->rx_state = RX_TO_OFF; + err = nrfx_uarte_rx_abort(&lpu->uarte_inst, true, sync); + if (err == NRFX_ERROR_INVALID_STATE || sync) { + lpu->rx_state = RX_OFF; + rdy_pin_idle(lpu); + + if (!sync) { + /* RX not started, report empty RX done ourselves without buffer as none is + * yet provided. + */ + const nrfx_uarte_event_t rx_done_aborted_evt = { + .type = NRFX_UARTE_EVT_RX_DONE, + .data.rx = { + .p_buffer = NULL, + .length = 0, + }, + }; + + lpu->callback(&rx_done_aborted_evt, lpu); + } + } + + return err; +} diff --git a/include/bm_lpuarte.h b/include/bm_lpuarte.h new file mode 100644 index 0000000000..34737bc2f4 --- /dev/null +++ b/include/bm_lpuarte.h @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/** @file + * + * @defgroup bm_lpuarte NCS Bare Metal Low Power UART with EasyDMA driver + * @{ + */ + +#ifndef LPUARTE_H__ +#define LPUARTE_H__ + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* States. */ +enum bm_lpuarte_rx_state { + /* RX is disabled. */ + RX_OFF, + /* RX is in low power, idle state with pin detection armed. */ + RX_IDLE, + /* RX request is pending, receiver is in preparation. */ + RX_PREPARE, + /* RX is in active state, receiver is running. */ + RX_ACTIVE, + /* RX is transitioning to from active idle state. */ + RX_TO_IDLE, + /* RX is transitioning to off state. */ + RX_TO_OFF, +}; + +/* Low power uart structure. */ +struct bm_lpuarte { + /* Physical UART device instance */ + nrfx_uarte_t uarte_inst; + /* Request pin. */ + nrfx_gpiote_pin_t req_pin; + /* Response pin. */ + nrfx_gpiote_pin_t rdy_pin; + /* GPIOTE channel used by rdy pin. */ + uint8_t rdy_ch; + /* Timer used for TX timeout. */ + struct bm_timer tx_timer; + /* Current TX buffer. */ + const uint8_t *tx_buf; + /* Length of TX data. */ + size_t tx_len; + /* Set to true if physical transfer is started. */ + bool tx_active; + /* Application callback. */ + nrfx_uarte_event_handler_t callback; + /* RX state */ + enum bm_lpuarte_rx_state rx_state; +}; + +/* Configuration structured. */ +struct bm_lpuarte_config { + /* Uarte instance. */ + nrfx_uarte_t uarte_inst; + /* Uarte instance configuration. */ + nrfx_uarte_config_t uarte_cfg; + /* Request pin number. */ + nrfx_gpiote_pin_t req_pin; + /* Ready pin number. */ + nrfx_gpiote_pin_t rdy_pin; +}; + +/** + * @brief Initialize LPUARTE driver instance. + * + * @param[in] lpu Low Power UARTE driver instance structure. + * @param[in] lpu_cfg Low Power UARTE driver instance configuration structure. + * @param[in] event_handler Event handler provided by the user. + */ +nrfx_err_t bm_lpuarte_init(struct bm_lpuarte *lpu, + struct bm_lpuarte_config *lpu_cfg, + nrfx_uarte_event_handler_t event_handler); + +/** + * @brief Deinitialize LPUARTE driver instance. + * + * @param[in] lpu Low Power UARTE driver instance structure. + */ +void bm_lpuarte_uninit(struct bm_lpuarte *lpu); + +/** + * @brief Send data over LPUARTE. + * + * @param[in] lpu Low Power UARTE driver instance structure. + * @param[in] data Data to transfer. + * @param[in] length Size of data to transfer. + * @param[in] timeout TX timeout in milliseconds. + * + * @retval NRFX_SUCCESS Initialization of transmission was successful. + * @retval NRFX_ERROR_BUSY When transfer is already in progress. + * @retval NRFX_ERROR_NULL @p lpu or @p data is NULL. + * @retval NRFX_ERROR_INVALID_LENGTH length is zero. + */ +nrfx_err_t bm_lpuarte_tx(struct bm_lpuarte *lpu, uint8_t const *data, size_t length, + int32_t timeout); + +/** + * @brief Check if TX is in progress. + * + * @retval true if transfer in progress. + * @retval false if no transfer in progress. + */ +bool bm_lpuarte_tx_in_progress(struct bm_lpuarte *lpu); + +/** + * @brief Abort transmission. + * + * @param[in] lpu Low Power UARTE driver instance structure. + * @param[in] sync If true, transmition is aborted synchronously. + * + * @retval NRFX_SUCCESS Successfully initiated abort. + * @retval NRFX_ERROR_NULL if @p lpu is NULL. + * @retval NRFX_ERROR_INVALID_STATE if there is no pending transfer. + */ +nrfx_err_t bm_lpuarte_tx_abort(struct bm_lpuarte *lpu, bool sync); + +/** + * @brief Enable the receiver. + * + * The event handler will be called from the caller context with + * the @ref NRFX_UARTE_EVT_RX_BUF_REQUEST event. The user may respond and provide a buffer + * using @ref bm_lpuarte_rx_buffer_set. An error is returned if buffer is not provided. After that, + * the receiver is started and another @ref NRFX_UARTE_EVT_RX_BUF_REQUEST is generated. + * If a new buffer is not provided, then the receiver is disabled once the first buffer + * becomes full. If a new buffer is provided, then the receiver will seamlessly switch to + * a new buffer (using a hardware shortcut). + * + * @param[in] lpu Low Power UARTE driver instance structure. + * + * @retval NRFX_SUCCESS Receiver successfully enabled. + * @retval NRFX_ERROR_BUSY When receiver is already enabled. + */ +nrfx_err_t bm_lpuarte_rx_enable(struct bm_lpuarte *lpu); + +/** + * @brief Provide reception buffer. + * + * The function should be called as a response to the @ref NRFX_UARTE_EVT_RX_BUF_REQUEST event. + * If the function is called before enabling the receiver, the first buffer is configured. + * If the function is called and there is no active buffer but the receiver is enabled + * but not started, it starts reception. + * + * @param[in] lpu Low Power UARTE driver instance structure. + * @param[in] data Pointer to a buffer. + * @param[in] length Buffer length. + * + * @retval NRFX_SUCCESS Buffer successfully set. + * @retval NRFX_ERROR_INVALID_STATE Buffer provided without pending request. + * @retval NRFX_ERROR_TIMEOUT Buffer provided too late. Receiver is being disabled. + */ +nrfx_err_t bm_lpuarte_rx_buffer_set(struct bm_lpuarte *lpu, uint8_t *data, size_t length); + +/** + * @brief Abort any ongoing reception. + * + * @note @ref NRFX_UARTE_EVT_RX_DONE event will be generated in non-blocking mode. + * It will contain number of bytes received until the abort was called. The event + * handler will be called from the UARTE interrupt context. + * + * @warning When the double-buffering feature is used and the UARTE interrupt + * is processed with a delay (for example, due to a higher priority + * interrupt) long enough for the first buffer to be filled completely, + * the event handler will be supplied with the pointer to the first + * buffer and the number of bytes received in the second buffer. + * This is because from hardware perspective it is impossible to deduce + * the reception of which buffer has been aborted. + * To prevent this from happening, keep the UARTE interrupt latency low + * or use large enough reception buffers. + * + * @param[in] lpu Low Power UARTE driver instance structure. + * @param[in] sync If true, receiver is disabled synchronously. + * + * @retval NRFX_SUCCESS Successfully initiate disabling or disabled (synchronous mode). + * @retval NRFX_ERROR_INVALID_STATE Receiver was not enabled. + */ +nrfx_err_t bm_lpuarte_rx_abort(struct bm_lpuarte *lpu, bool sync); + +#ifdef __cplusplus +} +#endif + +#endif /* LPUARTE_H__ */ + +/** @} */ diff --git a/samples/peripherals/lpuarte/CMakeLists.txt b/samples/peripherals/lpuarte/CMakeLists.txt new file mode 100644 index 0000000000..913cd39bb6 --- /dev/null +++ b/samples/peripherals/lpuarte/CMakeLists.txt @@ -0,0 +1,12 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(lpuarte) + +target_sources(app PRIVATE src/main.c) diff --git a/samples/peripherals/lpuarte/Kconfig b/samples/peripherals/lpuarte/Kconfig new file mode 100644 index 0000000000..3b81e36990 --- /dev/null +++ b/samples/peripherals/lpuarte/Kconfig @@ -0,0 +1,35 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +menu "LPUARTE sample" + +config LPUARTE_DATA_LEN_MAX + int "Maximum UARTE data length" + default 128 + help + Defines the maximum length the application can store in the receive buffer. + +config LPUARTE_PARITY + bool "UARTE parity" + help + Enable parity on the UARTE peripheral used by the application. + +config LPUARTE_IRQ_PRIO + int "LPUARTE IRQ priority" + default 5 + +config LPUARTE_GPIOTE_IRQ_PRIO + int "GPIOTE IRQ priority" + default 3 + +module=LPUARTE_SAMPLE +module-dep=LOG +module-str=Low Power UARTE Sample +source "$(ZEPHYR_BASE)/subsys/logging/Kconfig.template.log_config" + +endmenu # "LPUARTE sample" + +source "Kconfig.zephyr" diff --git a/samples/peripherals/lpuarte/README.rst b/samples/peripherals/lpuarte/README.rst new file mode 100644 index 0000000000..443cdaa2de --- /dev/null +++ b/samples/peripherals/lpuarte/README.rst @@ -0,0 +1,75 @@ +.. _bm_lpuarte_sample: + +LPUARTE +####### + +.. contents:: + :local: + :depth: 2 + +The LPUARTE sample demonstrates how to configure and use the LPUARTE driver as a low power alternative to regular UART. + +Requirements +************ + +The sample supports the following development kits: + +.. list-table:: + :header-rows: 1 + + * - Hardware platform + - PCA + - Board target + * - `nRF54L15 DK`_ + - PCA10156 + - bm_nrf54l15dk/nrf54l15/cpuapp/s115_softdevice + * - `nRF54L15 DK`_ (emulating nRF54L10) + - PCA10156 + - bm_nrf54l15dk/nrf54l10/cpuapp/s115_softdevice + * - `nRF54L15 DK`_ (emulating nRF54L05) + - PCA10156 + - bm_nrf54l15dk/nrf54l05/cpuapp/s115_softdevice + +The sample also requires the following pins to be shorted: + + .. list-table:: Pin connections. + :widths: auto + :header-rows: 1 + + * - Development Kit + - nRF54L15 DK pins + * - Request-Response Pins + - P1.08-P1.09 + * - UART RX-TX Pins + - P1.10-P1.11 + +Additionally, it requires a logic analyzer. + +Overview +******** + +The sample initializes the application LPUARTE instance, specified in the :file:`board-config.h` file in the board. +It then implements a simple loopback using a single LPUARTE instance. +By default, the console and logging are disabled to demonstrate low power consumption when UART is active. + +Building and running +******************** + +This sample can be found under :file:`samples/peripherals/lpuarte/` in the |BMshort| folder structure. + +.. include:: /includes/create_sample.txt + +.. include:: /includes/configure_and_build_sample.txt + +.. include:: /includes/program_sample.txt + +Testing +======= + +You can test this sample by performing the following steps: + +1. Compile and program the application. +2. Connect the logic analyzer to the shorted pins, to confirm UARTE activity. +3. Measure the current to confirm that the power consumption indicates that high-frequency clock is disabled during the idle stage. + +During the idle stage, the UARTE receiver is ready to start reception, as the request pin wakes it up. diff --git a/samples/peripherals/lpuarte/prj.conf b/samples/peripherals/lpuarte/prj.conf new file mode 100644 index 0000000000..324c0a77d0 --- /dev/null +++ b/samples/peripherals/lpuarte/prj.conf @@ -0,0 +1,11 @@ +# Disable logging and console for power efficiency +CONFIG_LOG=n +CONFIG_LOG_BACKEND_BM_UARTE=n +CONFIG_CONSOLE=n +CONFIG_BM_UARTE_CONSOLE=n + +CONFIG_BM_TIMER=y +CONFIG_CLOCK_CONTROL=y +CONFIG_BM_SW_LPUARTE=y +# Set number of event handlers used by the LPUART driver +CONFIG_NRFX_GPIOTE_NUM_OF_EVT_HANDLERS=2 diff --git a/samples/peripherals/lpuarte/sample.yaml b/samples/peripherals/lpuarte/sample.yaml new file mode 100644 index 0000000000..d7ca5c4aa3 --- /dev/null +++ b/samples/peripherals/lpuarte/sample.yaml @@ -0,0 +1,18 @@ +sample: + name: LPUARTE Sample +tests: + sample.lpuarte: + sysbuild: true + build_only: true + integration_platforms: + - bm_nrf54l15dk/nrf54l05/cpuapp/s115_softdevice/mcuboot + - bm_nrf54l15dk/nrf54l10/cpuapp/s115_softdevice + - bm_nrf54l15dk/nrf54l15/cpuapp/s115_softdevice + platform_allow: + - bm_nrf54l15dk/nrf54l05/cpuapp/s115_softdevice + - bm_nrf54l15dk/nrf54l10/cpuapp/s115_softdevice + - bm_nrf54l15dk/nrf54l15/cpuapp/s115_softdevice + - bm_nrf54l15dk/nrf54l05/cpuapp/s115_softdevice/mcuboot + - bm_nrf54l15dk/nrf54l10/cpuapp/s115_softdevice/mcuboot + - bm_nrf54l15dk/nrf54l15/cpuapp/s115_softdevice/mcuboot + tags: ci_build diff --git a/samples/peripherals/lpuarte/src/main.c b/samples/peripherals/lpuarte/src/main.c new file mode 100644 index 0000000000..fbb484fcb2 --- /dev/null +++ b/samples/peripherals/lpuarte/src/main.c @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#include + +#include +#include + +LOG_MODULE_REGISTER(app, CONFIG_LPUARTE_SAMPLE_LOG_LEVEL); + +/** Application Low Power UARTE instance */ +struct bm_lpuarte lpu; + +/* Receive buffer used in UARTE ISR callback */ +static uint8_t uarte_rx_buf[256]; +static int buf_idx; + +/* Handle data received from UARTE. */ +static void uarte_rx_handler(char *data, size_t data_len) +{ + LOG_HEXDUMP_INF(data, data_len, "Received data from UARTE:"); +} + +/* UARTE event handler */ +static void lpuarte_event_handler(nrfx_uarte_event_t const *event, void *ctx) +{ + uint32_t err; + struct bm_lpuarte *lpu = ctx; + + switch (event->type) { + case NRFX_UARTE_EVT_RX_DONE: + if (event->data.rx.length > 0) { + uarte_rx_handler(event->data.rx.p_buffer, event->data.rx.length); + } + break; + case NRFX_UARTE_EVT_RX_BUF_REQUEST: + err = bm_lpuarte_rx_buffer_set(lpu, &uarte_rx_buf[buf_idx * 128], 128); + buf_idx++; + buf_idx = (buf_idx < 2) ? buf_idx : 0; + break; + case NRFX_UARTE_EVT_ERROR: + LOG_ERR("UARTE error event, %#x", event->data.error.error_mask); + break; + default: + break; + } +} + +/* Initialize UARTE driver. */ +static uint32_t uarte_init(void) +{ + uint32_t err; + + struct bm_lpuarte_config lpu_cfg = { + .uarte_inst = NRFX_UARTE_INSTANCE(BOARD_APP_LPUARTE_INST), + .uarte_cfg = NRFX_UARTE_DEFAULT_CONFIG(BOARD_APP_LPUARTE_PIN_TX, + BOARD_APP_LPUARTE_PIN_RX), + .req_pin = BOARD_APP_LPUARTE_PIN_REQ, + .rdy_pin = BOARD_APP_LPUARTE_PIN_RDY, + }; + +#if defined(CONFIG_LPUARTE_PARITY) + lpu_cfg.uarte_cfg.config.parity = NRF_UARTE_PARITY_INCLUDED; +#endif + + lpu_cfg.uarte_cfg.interrupt_priority = CONFIG_LPUARTE_IRQ_PRIO; + + IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_GPIOTE_INST_GET(20)) + NRF_GPIOTE_IRQ_GROUP, + CONFIG_LPUARTE_GPIOTE_IRQ_PRIO, NRFX_GPIOTE_INST_HANDLER_GET(20), 0, 0); + + IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_GPIOTE_INST_GET(30)) + NRF_GPIOTE_IRQ_GROUP, + CONFIG_LPUARTE_GPIOTE_IRQ_PRIO, NRFX_GPIOTE_INST_HANDLER_GET(30), 0, 0); + + IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_UARTE_INST_GET(BOARD_APP_LPUARTE_INST)), + CONFIG_LPUARTE_IRQ_PRIO, + NRFX_UARTE_INST_HANDLER_GET(BOARD_APP_LPUARTE_INST), 0, 0); + + irq_enable(NRFX_IRQ_NUMBER_GET(NRF_UARTE_INST_GET(BOARD_APP_LPUARTE_INST))); + + err = bm_lpuarte_init(&lpu, &lpu_cfg, lpuarte_event_handler); + if (err != NRFX_SUCCESS) { + LOG_ERR("Failed to initialize UARTE, nrfx_err %d", err); + return err; + } + + return 0; +} + +static struct bm_timer tx_timer; +static uint8_t out[] = {1, 2, 3, 4, 5}; + +static void tx_timeout(void *context) +{ + uint32_t err; + + err = bm_lpuarte_tx(&lpu, out, sizeof(out), 3000); + if (err != NRFX_SUCCESS) { + LOG_ERR("UARTE TX failed, nrfx err %#x", err); + return; + } +} + +int main(void) +{ + uint32_t err; + + LOG_INF("LPUARTE sample started"); + LOG_INF("Disable console and logging for minimal power consumption"); + + err = uarte_init(); + if (err) { + LOG_ERR("Failed to enable UARTE, err %#x", err); + goto idle; + } + + /* Start reception */ + err = bm_lpuarte_rx_enable(&lpu); + if (err != NRFX_SUCCESS) { + LOG_ERR("UARTE RX failed, nrfx_err %#x", err); + goto idle; + } + + err = bm_timer_init(&tx_timer, BM_TIMER_MODE_REPEATED, tx_timeout); + if (err) { + LOG_ERR("bm_timer_init failed, err %d", err); + goto idle; + } + + err = bm_timer_start(&tx_timer, BM_TIMER_MS_TO_TICKS(5000), NULL); + if (err) { + LOG_ERR("bm_timer_start failed, err %d", err); + } + +idle: + while (true) { + while (LOG_PROCESS()) { + } + + /* Wait for an event. */ + __WFE(); + + /* Clear Event Register */ + __SEV(); + __WFE(); + } + + /* Unreachable */ + return 0; +} From 80ff0d60905c9956cf10b6bcb7ff173d65c89ff6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eivind=20J=C3=B8lsgard?= Date: Tue, 22 Jul 2025 08:12:16 +0200 Subject: [PATCH 3/3] samples: bluetooth: ble_nus: add LPUARTE support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for LPUARTE to NUS sample. Co-authored-by: Andreas Moltumyr Signed-off-by: Eivind Jølsgard --- samples/bluetooth/ble_nus/Kconfig | 30 ++++- samples/bluetooth/ble_nus/README.rst | 12 ++ samples/bluetooth/ble_nus/lpuarte.conf | 8 ++ samples/bluetooth/ble_nus/prj.conf | 1 - samples/bluetooth/ble_nus/sample.yaml | 12 ++ samples/bluetooth/ble_nus/src/main.c | 164 ++++++++++++++++++++----- 6 files changed, 189 insertions(+), 38 deletions(-) create mode 100644 samples/bluetooth/ble_nus/lpuarte.conf diff --git a/samples/bluetooth/ble_nus/Kconfig b/samples/bluetooth/ble_nus/Kconfig index 79171b01a0..c298f714ef 100644 --- a/samples/bluetooth/ble_nus/Kconfig +++ b/samples/bluetooth/ble_nus/Kconfig @@ -6,15 +6,35 @@ menu "BLE NUS sample" -config BLE_UART_IRQ_PRIO - int "BLE UART IRQ priority" +config NUS_UART_IRQ_PRIO + int "NUS UART IRQ priority" default 3 -config BLE_UART_PARITY +config NUS_UART_PARITY bool "BLE UART use parity" -config BLE_UART_HWFC - bool "BLE UART use HWFC" +config NUS_LPUARTE + bool "NUS Low Power UARTE" + +if NUS_LPUARTE + +config GPIOTE_IRQ_PRIO + int "GPIOTE IRQ priority" + default 3 + +endif # NUS_LPUARTE + +config NUS_UART_HWFC + bool "NUS UART use HWFC" + depends on !NUS_LPUARTE + default y + +config NUS_UART_RX_BUF_SIZE + int "NUS UART receive buffer size" + default 128 if NUS_LPUARTE + default 1 + help + UART RX is double buffered, so the actual buffer will be twice as large. module=BLE_NUS_SAMPLE module-dep=LOG diff --git a/samples/bluetooth/ble_nus/README.rst b/samples/bluetooth/ble_nus/README.rst index 470e535e69..d2fe07932d 100644 --- a/samples/bluetooth/ble_nus/README.rst +++ b/samples/bluetooth/ble_nus/README.rst @@ -60,12 +60,24 @@ This sample can be found under :file:`samples/bluetooth/ble_nus/` in the |BMshor .. include:: /includes/program_sample.txt +Building and running with LPUARTE +********************************* + +The :file:`lpuarte.conf` file configures the sample to use the :ref:`LPUARTE ` driver for the NUS Service. +This is useful for reducing the power consumption. +The file must be added to the build configuration is VS Code or as an extra argument to west: ``-DEXTRA_CONF_FILE="lpuarte.conf"``. + +To test the NUS sample with the :ref:`LPUARTE ` driver in loopback mode, connect pin ``P1.08`` (REQ) with ``P1.09`` (RDY) and ``P1.10`` (RX) with ``P1.11`` (TX) on the nRF54L15DK, as given in the :file:`board-config.h`. +It is also possible to test between two devices running NUS with LPUART by connecting the above mentioned pins and ``GND`` between the devices. +Make sure the ``REQ`` pin on one board is connected to the ``RDY`` on the other board, and vice versa. + Testing ======= 1. Compile and program the application. #. Connect the device to the computer to access UART 0 and UART 1. If you use a development kit, UART 0 and 1 are forwarded as COM ports (Windows) or ttyACM devices (Linux) after you connect the development kit over USB. + One instance is used for logging (if enabled), the other for the NUS service. #. Connect to the kit with a terminal emulator (for example, the `Serial Terminal app`_) to both UARTs. #. Reset the kit. #. Observe that the device is advertising under the default name ``nRF_BM_NUS``. diff --git a/samples/bluetooth/ble_nus/lpuarte.conf b/samples/bluetooth/ble_nus/lpuarte.conf new file mode 100644 index 0000000000..f0014c0610 --- /dev/null +++ b/samples/bluetooth/ble_nus/lpuarte.conf @@ -0,0 +1,8 @@ +# This file enables Low Power UARTE (LPUARTE) driver for the NUS UART instance. +CONFIG_BM_SW_LPUARTE=y +CONFIG_BM_TIMER=y + +CONFIG_NUS_LPUARTE=y + +# Set number of event handlers used by the LPUART driver +CONFIG_NRFX_GPIOTE_NUM_OF_EVT_HANDLERS=2 diff --git a/samples/bluetooth/ble_nus/prj.conf b/samples/bluetooth/ble_nus/prj.conf index 0c58384791..ad05077cb4 100644 --- a/samples/bluetooth/ble_nus/prj.conf +++ b/samples/bluetooth/ble_nus/prj.conf @@ -19,7 +19,6 @@ CONFIG_BLE_CONN_PARAMS=y # Nordic UART service CONFIG_BLE_NUS=y -CONFIG_BLE_UART_HWFC=y # NUS depends on the Queued Writes module CONFIG_BLE_QWR=y diff --git a/samples/bluetooth/ble_nus/sample.yaml b/samples/bluetooth/ble_nus/sample.yaml index d170315faa..4e388c119a 100644 --- a/samples/bluetooth/ble_nus/sample.yaml +++ b/samples/bluetooth/ble_nus/sample.yaml @@ -4,12 +4,23 @@ tests: sample.ble_nus: build_only: true integration_platforms: + - bm_nrf54l15dk/nrf54l05/cpuapp/s115_softdevice/mcuboot + - bm_nrf54l15dk/nrf54l10/cpuapp/s115_softdevice + - bm_nrf54l15dk/nrf54l15/cpuapp/s115_softdevice + platform_allow: - bm_nrf54l15dk/nrf54l05/cpuapp/s115_softdevice - bm_nrf54l15dk/nrf54l10/cpuapp/s115_softdevice - bm_nrf54l15dk/nrf54l15/cpuapp/s115_softdevice - bm_nrf54l15dk/nrf54l05/cpuapp/s115_softdevice/mcuboot - bm_nrf54l15dk/nrf54l10/cpuapp/s115_softdevice/mcuboot - bm_nrf54l15dk/nrf54l15/cpuapp/s115_softdevice/mcuboot + tags: ci_build + sample.ble_nus.lpuarte: + build_only: true + integration_platforms: + - bm_nrf54l15dk/nrf54l05/cpuapp/s115_softdevice/mcuboot + - bm_nrf54l15dk/nrf54l10/cpuapp/s115_softdevice + - bm_nrf54l15dk/nrf54l15/cpuapp/s115_softdevice platform_allow: - bm_nrf54l15dk/nrf54l05/cpuapp/s115_softdevice - bm_nrf54l15dk/nrf54l10/cpuapp/s115_softdevice @@ -17,4 +28,5 @@ tests: - bm_nrf54l15dk/nrf54l05/cpuapp/s115_softdevice/mcuboot - bm_nrf54l15dk/nrf54l10/cpuapp/s115_softdevice/mcuboot - bm_nrf54l15dk/nrf54l15/cpuapp/s115_softdevice/mcuboot + extra_args: EXTRA_CONF_FILE="lpuarte.conf" tags: ci_build diff --git a/samples/bluetooth/ble_nus/src/main.c b/samples/bluetooth/ble_nus/src/main.c index f38ed31c43..70aa00f9c0 100644 --- a/samples/bluetooth/ble_nus/src/main.c +++ b/samples/bluetooth/ble_nus/src/main.c @@ -18,6 +18,9 @@ #include #include #include +#if defined(CONFIG_NUS_LPUARTE) +#include +#endif #include @@ -30,16 +33,32 @@ BLE_QWR_DEF(ble_qwr); /* BLE QWR instance */ /** Handle of the current connection. */ static uint16_t conn_handle = BLE_CONN_HANDLE_INVALID; -/** NUS UARTE instance */ -static const nrfx_uarte_t uarte_inst = NRF_UARTE_INST_GET(BOARD_APP_UARTE_INST); +/** NUS UARTE instance and board config */ +#if defined(CONFIG_NUS_LPUARTE) +#define NUS_UARTE_INST BOARD_APP_LPUARTE_INST +#define NUS_UARTE_PIN_TX BOARD_APP_LPUARTE_PIN_TX +#define NUS_UARTE_PIN_RX BOARD_APP_LPUARTE_PIN_RX +#define NUS_UARTE_PIN_RDY BOARD_APP_LPUARTE_PIN_RDY +#define NUS_UARTE_PIN_REQ BOARD_APP_LPUARTE_PIN_REQ + +struct bm_lpuarte lpu; +#else +#define NUS_UARTE_INST BOARD_APP_UARTE_INST +#define NUS_UARTE_PIN_TX BOARD_APP_UARTE_PIN_TX +#define NUS_UARTE_PIN_RX BOARD_APP_UARTE_PIN_RX +#define NUS_UARTE_PIN_CTS BOARD_APP_UARTE_PIN_CTS +#define NUS_UARTE_PIN_RTS BOARD_APP_UARTE_PIN_RTS + +static const nrfx_uarte_t nus_uarte_inst = NRFX_UARTE_INSTANCE(NUS_UARTE_INST); +#endif /* CONFIG_NUS_LPUARTE */ /* Maximum length of data (in bytes) that can be transmitted to the peer by the * Nordic UART service module. */ static volatile uint16_t ble_nus_max_data_len = BLE_NUS_MAX_DATA_LEN_CALC(BLE_GATT_ATT_MTU_DEFAULT); -/* Receive buffer used in UART ISR callback */ -static uint8_t uarte_rx_buf[4]; +/* Receive buffers used in UART ISR callback. */ +static uint8_t uarte_rx_buf[CONFIG_NUS_UART_RX_BUF_SIZE][2]; static int buf_idx; /** @@ -48,6 +67,26 @@ static int buf_idx; * @param[in] data Data received. * @param[in] data_len Size of data. */ +#if defined(CONFIG_NUS_LPUARTE) +static void lpuarte_rx_handler(char *data, size_t data_len) +{ + int err; + uint16_t len = data_len; + + LOG_INF("Sending data over BLE NUS, len %d", len); + + do { + err = ble_nus_data_send(&ble_nus, data, &len, conn_handle); + if ((err != 0) && + (err != -EPIPE) && + (err != -EAGAIN) && + (err != -EBADF)) { + LOG_ERR("Failed to send NUS data, err %d", err); + return; + } + } while (err == -EAGAIN); +} +#else static void uarte_rx_handler(char *data, size_t data_len) { int err; @@ -96,6 +135,7 @@ static void uarte_rx_handler(char *data, size_t data_len) } } } +#endif /** * @brief UARTE event handler @@ -107,18 +147,29 @@ static void uarte_evt_handler(nrfx_uarte_event_t const *event, void *ctx) { switch (event->type) { case NRFX_UARTE_EVT_RX_DONE: - LOG_DBG("Received data from UART: %c", event->data.rx.p_buffer[0]); + LOG_DBG("Received data from UART: %.*s (%d)", + event->data.rx.length, event->data.rx.p_buffer, event->data.rx.length); if (event->data.rx.length > 0) { +#if defined(CONFIG_NUS_LPUARTE) + lpuarte_rx_handler(event->data.rx.p_buffer, event->data.rx.length); +#else uarte_rx_handler(event->data.rx.p_buffer, event->data.rx.length); +#endif } - nrfx_uarte_rx_enable(&uarte_inst, 0); +#if !defined(CONFIG_NUS_LPUARTE) + nrfx_uarte_rx_enable(&nus_uarte_inst, 0); +#endif break; case NRFX_UARTE_EVT_RX_BUF_REQUEST: - nrfx_uarte_rx_buffer_set(&uarte_inst, &uarte_rx_buf[buf_idx], 1); +#if defined(CONFIG_NUS_LPUARTE) + bm_lpuarte_rx_buffer_set(&lpu, uarte_rx_buf[buf_idx], CONFIG_NUS_UART_RX_BUF_SIZE); +#else + nrfx_uarte_rx_buffer_set(&nus_uarte_inst, uarte_rx_buf[buf_idx], + CONFIG_NUS_UART_RX_BUF_SIZE); +#endif - buf_idx++; - buf_idx = (buf_idx < sizeof(uarte_rx_buf)) ? buf_idx : 0; + buf_idx = buf_idx ? 0 : 1; break; case NRFX_UARTE_EVT_ERROR: LOG_ERR("uarte error %#x", event->data.error.error_mask); @@ -264,20 +315,36 @@ uint16_t ble_qwr_evt_handler(struct ble_qwr *qwr, const struct ble_qwr_evt *qwr_ static void ble_nus_evt_handler(const struct ble_nus_evt *evt) { const char newline = '\n'; + uint32_t err; if (evt->type != BLE_NUS_EVT_RX_DATA) { return; } /* Handle incoming data */ - LOG_DBG("Received data from BLE NUS: %s", evt->params.rx_data.data); + LOG_DBG("Received data from BLE NUS: %.*s (%d)", + evt->params.rx_data.length, evt->params.rx_data.data, evt->params.rx_data.length); - for (uint32_t i = 0; i < evt->params.rx_data.length; i++) { - nrfx_uarte_tx(&uarte_inst, &evt->params.rx_data.data[i], 1, NRFX_UARTE_TX_BLOCKING); +#if defined(CONFIG_NUS_LPUARTE) + err = bm_lpuarte_tx(&lpu, evt->params.rx_data.data, evt->params.rx_data.length, 3000); + if (err != NRFX_SUCCESS) { + LOG_ERR("bm_lpuarte_tx failed, nrfx_err %#x", err); } +#else + err = nrfx_uarte_tx(&nus_uarte_inst, evt->params.rx_data.data, + evt->params.rx_data.length, NRFX_UARTE_TX_BLOCKING); + if (err != NRFX_SUCCESS) { + LOG_ERR("nrfx_uarte_tx failed, nrfx_err %#x", err); + } +#endif + if (evt->params.rx_data.data[evt->params.rx_data.length - 1] == '\r') { - nrfx_uarte_tx(&uarte_inst, &newline, 1, NRFX_UARTE_TX_BLOCKING); +#if defined(CONFIG_NUS_LPUARTE) + bm_lpuarte_tx(&lpu, &newline, 1, 3000); +#else + nrfx_uarte_tx(&nus_uarte_inst, &newline, 1, NRFX_UARTE_TX_BLOCKING); +#endif } } @@ -287,33 +354,62 @@ static void ble_nus_evt_handler(const struct ble_nus_evt *evt) static int uarte_init(void) { int err; + nrfx_uarte_config_t *uarte_cfg; +#if defined(CONFIG_NUS_LPUARTE) + struct bm_lpuarte_config lpu_cfg = { + .uarte_inst = NRFX_UARTE_INSTANCE(NUS_UARTE_INST), + .uarte_cfg = NRFX_UARTE_DEFAULT_CONFIG(NUS_UARTE_PIN_TX, + NUS_UARTE_PIN_RX), + .req_pin = BOARD_APP_LPUARTE_PIN_REQ, + .rdy_pin = BOARD_APP_LPUARTE_PIN_RDY, + }; - nrfx_uarte_config_t uarte_config = NRFX_UARTE_DEFAULT_CONFIG(BOARD_APP_UARTE_PIN_TX, - BOARD_APP_UARTE_PIN_RX); + uarte_cfg = &lpu_cfg.uarte_cfg; +#else + nrfx_uarte_config_t uarte_config = NRFX_UARTE_DEFAULT_CONFIG(NUS_UARTE_PIN_TX, + NUS_UARTE_PIN_RX); -#if defined(CONFIG_BLE_UART_HWFC) - uarte_config.config.hwfc = NRF_UARTE_HWFC_ENABLED; - uarte_config.cts_pin = BOARD_APP_UARTE_PIN_CTS; - uarte_config.rts_pin = BOARD_APP_UARTE_PIN_RTS; -#endif + uarte_cfg = &uarte_config; + +#if defined(CONFIG_NUS_UART_HWFC) + uarte_cfg->config.hwfc = NRF_UARTE_HWFC_ENABLED; + uarte_cfg->cts_pin = NUS_UARTE_PIN_CTS; + uarte_cfg->rts_pin = NUS_UARTE_PIN_RTS; +#endif /* CONFIG_NUS_UART_HWFC */ +#endif /* CONFIG_NUS_LPUARTE */ -#if defined(CONFIG_BLE_UART_PARITY) - uarte_config.parity = NRF_UARTE_PARITY_INCLUDED; +#if defined(CONFIG_NUS_UART_PARITY) + uarte_cfg->parity = NRF_UARTE_PARITY_INCLUDED; #endif - uarte_config.interrupt_priority = CONFIG_BLE_UART_IRQ_PRIO; + uarte_cfg->interrupt_priority = CONFIG_NUS_UART_IRQ_PRIO; /** We need to connect the IRQ ourselves. */ - IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_UARTE_INST_GET(BOARD_APP_UARTE_INST)), CONFIG_BLE_UART_IRQ_PRIO, - NRFX_UARTE_INST_HANDLER_GET(BOARD_APP_UARTE_INST), 0, 0); - irq_enable(NRFX_IRQ_NUMBER_GET(NRF_UARTE_INST_GET(BOARD_APP_UARTE_INST))); + IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_UARTE_INST_GET(NUS_UARTE_INST)), + CONFIG_NUS_UART_IRQ_PRIO, NRFX_UARTE_INST_HANDLER_GET(NUS_UARTE_INST), 0, 0); - err = nrfx_uarte_init(&uarte_inst, &uarte_config, uarte_evt_handler); + irq_enable(NRFX_IRQ_NUMBER_GET(NRF_UARTE_INST_GET(NUS_UARTE_INST))); + +#if defined(CONFIG_NUS_LPUARTE) + IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_GPIOTE_INST_GET(20)) + NRF_GPIOTE_IRQ_GROUP, + CONFIG_GPIOTE_IRQ_PRIO, NRFX_GPIOTE_INST_HANDLER_GET(20), 0, 0); + + IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_GPIOTE_INST_GET(30)) + NRF_GPIOTE_IRQ_GROUP, + CONFIG_GPIOTE_IRQ_PRIO, NRFX_GPIOTE_INST_HANDLER_GET(30), 0, 0); + + err = bm_lpuarte_init(&lpu, &lpu_cfg, uarte_evt_handler); + if (err != NRFX_SUCCESS) { + LOG_ERR("Failed to initialize UART, nrfx err %d", err); + return err; + } +#else + err = nrfx_uarte_init(&nus_uarte_inst, &uarte_config, uarte_evt_handler); if (err != NRFX_SUCCESS) { LOG_ERR("Failed to initialize UART, nrfx err %d", err); return err; } +#endif /* CONFIG_NUS_LPUARTE */ return 0; } @@ -395,18 +491,25 @@ int main(void) goto idle; } +#if defined(CONFIG_NUS_LPUARTE) + err = bm_lpuarte_rx_enable(&lpu); + if (err != NRFX_SUCCESS) { + LOG_ERR("UART RX failed, nrfx err %d", err); + } +#else const uint8_t out[] = "UART started.\r\n"; - err = nrfx_uarte_tx(&uarte_inst, out, sizeof(out), NRFX_UARTE_TX_BLOCKING); + err = nrfx_uarte_tx(&nus_uarte_inst, out, sizeof(out), NRFX_UARTE_TX_BLOCKING); if (err != NRFX_SUCCESS) { LOG_ERR("UARTE TX failed, nrfx err %d", err); goto idle; } - err = nrfx_uarte_rx_enable(&uarte_inst, 0); + err = nrfx_uarte_rx_enable(&nus_uarte_inst, 0); if (err != NRFX_SUCCESS) { LOG_ERR("UART RX failed, nrfx err %d", err); } +#endif err = ble_adv_start(&ble_adv, BLE_ADV_MODE_FAST); if (err) { @@ -415,9 +518,6 @@ int main(void) } LOG_INF("Advertising as %s", CONFIG_BLE_ADV_NAME); -#if defined(CONFIG_SOC_SERIES_NRF54LX) - LOG_INF("The NUS service is handled at a separate uart instance"); -#endif idle: while (true) {