Using UART1 in Non-Secure Mode on nRF9151 DK with TF-M Enabled

Hi everyone,

We’re trying to use UART1 on the nRF9151 DK in non-secure mode, mapped to the Arduino R1 connector. However, it seems to be reserved by TF-M, and we cannot disable TF-M because we also need LTE functionality.

We even tried disabling VCOM1 using the Board Configurator, but it didn’t help.

According to the documentation, it should be possible to free UART1 by disabling a configuration, but in our setup it doesn’t work.

We’ve attached our main.c, prj.conf, and .overlay files for reference.

Has anyone successfully used UART1 in non-secure mode while keeping TF-M enabled? Any guidance or examples would be greatly appreciated.

Thanks in advance!

main.cpp:

/*
 * Minimal UART echo (polling) for Zephyr
 */

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/uart.h>

#define UART_NODE DT_ALIAS(rs485_uart)

static const struct device *uart = DEVICE_DT_GET(UART_NODE);

int main(void)
{
	if (!device_is_ready(uart))
	{
		return 0;
	}

	const char *banner = "UART polling echo\r\n";
	for (const char *p = banner; *p; p++)
	{
		uart_poll_out(uart, *p);
	}

	while (true)
	{
		unsigned char c;

		if (uart_poll_in(uart, &c) == 0)
		{
			uart_poll_out(uart, c);
		}
		else
		{
			k_msleep(1);
		}
	}
}

prj.conf:

#
# Copyright (C) 2026 - OWL Services LLC
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License (version 2) as published by the
# FSF - Free Software Foundation
#

# Compiler
CONFIG_DEBUG_OPTIMIZATIONS=y

# C Standard Library implementation
CONFIG_NEWLIB_LIBC=y
CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y

# C++ support
CONFIG_CPP=y
CONFIG_REQUIRES_FULL_LIBCPP=y

# Zephyr Kernel
CONFIG_HEAP_MEM_POOL_SIZE=2048
CONFIG_IDLE_STACK_SIZE=320
CONFIG_ISR_STACK_SIZE=2048
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1152
CONFIG_MAIN_STACK_SIZE=4096

# Logging
CONFIG_LOG=y
CONFIG_LOG_MODE_IMMEDIATE=n
CONFIG_APP_LOG_LEVEL_DBG=n
CONFIG_CBPRINTF_FP_SUPPORT=y

# ZBUS
CONFIG_ZBUS=y

# UART
CONFIG_SERIAL=y

# Zephyr Networking Layer
CONFIG_NETWORKING=y
CONFIG_NET_LOG=n
CONFIG_NET_SOCKETS=y
CONFIG_NET_SOCKETS_OFFLOAD=y
CONFIG_NET_SOCKETS_SERVICE_STACK_SIZE=1200
CONFIG_NET_DEFAULT_IF_FIRST=y
CONFIG_NET_CONFIG_SETTINGS=y
CONFIG_NET_IPV4=y
CONFIG_NET_IPV6=y
CONFIG_NET_TCP=y
CONFIG_NET_UDP=y
CONFIG_NET_CONNECTION_MANAGER=y
CONFIG_NET_MGMT=y
CONFIG_NET_RX_STACK_SIZE=1792
CONFIG_NET_TX_STACK_SIZE=1200
CONFIG_DNS_RESOLVER=y
CONFIG_POSIX_API=y

# MQTT
CONFIG_MQTT_LIB=y
CONFIG_MQTT_LOG_LEVEL_DBG=n

CONFIG_TFM_LOG_LEVEL_SILENCE=y

boards/nrf9151dk_nrf9151_ns.conf:

#
# Copyright (C) 2026 - OWL Services LLC
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License (version 2) as published by the
# FSF - Free Software Foundation
#

# UART
CONFIG_UART_LINE_CTRL=y
CONFIG_UART_INTERRUPT_DRIVEN=y

# Modem
CONFIG_NRF_MODEM_LIB=y

# LTE
CONFIG_LTE_LINK_CONTROL=y
CONFIG_LTE_LINK_CONTROL_LOG_LEVEL_DBG=y
CONFIG_LTE_LC_CONN_EVAL_MODULE=y
CONFIG_LTE_LC_WORKQUEUE_STACK_SIZE=1024

boards/nrf9151dk_nrf9151_ns.overlay:

/*
 * Copyright (C) 2026 - OWL Services LLC
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License (version 2) as published by the
 * FSF - Free Software Foundation
 *
 */

/ {
    aliases {
        rs485-uart = &uart1;
        rs485-de = &rs485de;
    };

    rs485de: rs485-de {
        gpios = <&gpio0 15 GPIO_ACTIVE_HIGH>;
    };
};

/* Pins are defined based on the carrier board usage and the specifications provided in the manual */
&pinctrl {
    uart1_default: uart1_default {
        group1 {
            psels = <NRF_PSEL(UART_RX, 0, 28)>, 
                    <NRF_PSEL(UART_TX, 0, 29)>;
        };
    };

    uart1_sleep: uart1_sleep {
        group1 {
            psels = <NRF_PSEL(UART_RX, 0, 28)>,
                    <NRF_PSEL(UART_TX, 0, 29)>;
            low-power-enable;
        };
    };
};

&uart1 {
    status = "okay";
    current-speed = <115200>;
    pinctrl-0 = <&uart1_default>;
    pinctrl-1 = <&uart1_sleep>;
    pinctrl-names = "default", "sleep";
};

  • Hello,

    UART1 should be available to the application as long as CONFIG_TFM_LOG_LEVEL_SILENCE is selected which I see you have done already in your project configuration file. 

    VCOM1 must be disabled in the board configurator app to change the routing of the UART pins from the Jlink MCU to the pin headers. Had you tested the same without TF-M and verified that it worked?

    Best regards,

    Vidar

  • Hi Vidar,

    Thanks for the clarification.

    We tested again with TF-M enabled and CONFIG_TFM_LOG_LEVEL_SILENCE=y, and UART1 is indeed available to the non-secure application as expected.

    The issue turned out not to be related to TF-M or UART reservation, but rather to a host-side serial terminal problem: on macOS, minicom was not showing the UART output correctly. When testing on Ubuntu, UART1 worked fine.

    So the configuration was correct.

    Thanks again for your help!

    Best regards,

    Federica from OWL Services.

Related