Hi,
SoC: nRF54L15, SDK 3.1, battery powered application.
Our product uses UART. When power management is used (pm_device_action_run(uart_dev, PM_DEVICE_ACTION_SUSPEND)), the stack crashes. We used Matter door lock samples to try this. Please see the configuration and code used.
Thanks.
Subu
CONFIG_USE_SEGGER_RTT=n
CONFIG_SHELL=n
CONFIG_OPENTHREAD_SHELL=n
CONFIG_CONSOLE=n
CONFIG_UART_CONSOLE=n
#********** Enable UART ********************#
CONFIG_SERIAL=y
CONFIG_UART_ASYNC_API=y
CONFIG_UART_20_ASYNC=n
CONFIG_UART_20_INTERRUPT_DRIVEN=n
CONFIG_UART_20_NRF_ASYNC_LOW_POWER=n
CONFIG_NRFX_UARTE30=y
CONFIG_UART_30_ASYNC=y
CONFIG_UART_30_INTERRUPT_DRIVEN=n
CONFIG_UART_30_NRF_ASYNC_LOW_POWER=y
CONFIG_LOG=n
CONFIG_LOG_MODE_MINIMAL=n
CONFIG_ASSERT_VERBOSE=n
CONFIG_ASSERT_NO_FILE_INFO=n
CONFIG_PRINTK=n
CONFIG_PRINTK_SYNC=n
CONFIG_THREAD_NAME=n
CONFIG_BOOT_BANNER=n
#include "user_console.h"
UserConsole::UserConsole(const struct device *uart_dev)
: uart_dev(uart_dev)
{
atomic_set(&suspended, 0);
}
UserConsole::~UserConsole() {
suspend();
}
bool UserConsole::init() {
if (!device_is_ready(uart_dev)) {
printk("UART not ready\n");
return false;
}
uart_callback_set(uart_dev, uart_event_cb, this);
// Request HFCLK before enabling RX
request_hfclk();
int ret = uart_rx_enable(uart_dev, rx_dma_buf, sizeof(rx_dma_buf), SYS_FOREVER_US);
if (ret) {
printk("uart_rx_enable failed: %d\n", ret);
release_hfclk();
return false;
}
printk("UART Initialized -> Suspending URAT to sleep \n");
// UserConsole::suspend();
return true;
}
void UserConsole::sendChar(uint8_t c) {
resume();
uart_tx(uart_dev, &c, 1, SYS_FOREVER_US);
}
void UserConsole::sendBuffer(const uint8_t *buf, size_t len) {
resume();
uart_tx(uart_dev, buf, len, SYS_FOREVER_US);
}
bool UserConsole::available() {
return rx_head != rx_tail;
}
uint8_t UserConsole::readChar() {
if (rx_head == rx_tail) {
return 0;
}
uint8_t c = rx_ring[rx_tail];
rx_tail = (rx_tail + 1) % RX_BUF_SIZE;
return c;
}
void UserConsole::suspend() {
int err;
if (atomic_cas(&suspended, 0, 1)) {
err = uart_rx_disable(uart_dev);
if(err){
printk("UART RX is not disabled: error=%d \n", err);
return;
}
err = uart_tx_abort(uart_dev);
if(err){
printk("UART TX is not aborted: error=%d \n", err);
return;
}
k_msleep(100);
printk("Call UART Suspend \n");
pm_device_action_run(uart_dev, PM_DEVICE_ACTION_SUSPEND);
if (err) {
printk("Failed to suspend uart (err: %d)", err);
return;
}
printk("Release clock");
release_hfclk();
printk("UART suspended..\n");
}
}
void UserConsole::resume() {
if (atomic_cas(&suspended, 1, 0)) {
pm_device_action_run(uart_dev, PM_DEVICE_ACTION_RESUME);
request_hfclk();
}
}
void UserConsole::uart_event_cb(const struct device *dev,
struct uart_event *evt,
void *user_data) {
auto *self = static_cast<UserConsole *>(user_data);
switch (evt->type) {
case UART_RX_RDY:
for (size_t i = 0; i < evt->data.rx.len; i++) {
uint8_t c = evt->data.rx.buf[evt->data.rx.offset + i];
size_t next = (self->rx_head + 1) % RX_BUF_SIZE;
if (next != self->rx_tail) {
self->rx_ring[self->rx_head] = c;
self->rx_head = next;
}
}
break;
case UART_RX_DISABLED:
// Re-enable RX when disabled
uart_rx_enable(dev, self->rx_dma_buf, sizeof(self->rx_dma_buf), SYS_FOREVER_US);
break;
case UART_TX_DONE:
self->suspend();
break;
default:
break;
}
}
/* ===== HFCLK control ===== */
void UserConsole::rx_hfclk_callback() {
// Nothing needed here; just ensures HFCLK is running before UART RX
}
void UserConsole::request_hfclk() {
struct onoff_manager *mgr =
z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_HF);
sys_notify_init_callback(&clk_client.notify, rx_hfclk_callback);
int err = onoff_request(mgr, &clk_client);
__ASSERT_NO_MSG(err >= 0);
}
void UserConsole::release_hfclk() {
struct onoff_manager *mgr =
z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_HF);
(void)onoff_cancel_or_release(mgr, &clk_client);
}
&uart30 {
status = "okay";
current-speed = <115200>;
pinctrl-0 = <&uart30_default>;
pinctrl-1 = <&uart30_sleep>;
pinctrl-names = "default", "sleep";
// interrupts = <10>, <NRF_DEFAULT_IRQ_PRIORITY>;
};
&uart30_sleep {
group1 {
psels = <NRF_PSEL(UART_TX, 0, 0)>, /* Port P0.0 */
<NRF_PSEL_DISCONNECTED(UART_RTS)>;
low-power-enable;
};
group2 {
psels = <NRF_PSEL(UART_RX, 0, 1)>, /* Port P0.1 */
<NRF_PSEL_DISCONNECTED(UART_CTS)>;
low-power-enable;
bias-pull-up;
};
};
&uart30_default {
group1 {
psels = <NRF_PSEL(UART_TX, 0, 0)>,
<NRF_PSEL_DISCONNECTED(UART_RTS)>;
};
group2 {
psels = <NRF_PSEL(UART_RX, 0, 1)>,
<NRF_PSEL_DISCONNECTED(UART_CTS)>;
bias-pull-up;
};
};
&button0 {
gpios = <&gpio2 10 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>;
label = "Push button 0";
zephyr,code = <INPUT_KEY_0>;
};
&button1 {
status = "disabled";
};
&button2 {
status = "disabled";
};