nRF54LM20A DK – Sleep current rises to 270 µA after USB VBUS removal when CONFIG_BT=y is set (NCS v3.2.1)

Board: nRF54LM20A DK NCS version: v3.2.1 Sample base:  nrf/samples/zephyr/subsys/usb/cdc_acm


Background

I am evaluating power consumption on the nRF54LM20A DK before integrating both USB (CDC ACM) and Bluetooth LE into a production product. I noticed an unexpected increase in sleep current when CONFIG_BT=y is added, even though the Bluetooth stack is never initialized or used in the application code.


Test Setup

I started from the standard cdc_acm sample, modified main() as shown below, and added the following to prj.conf:

CONFIG_PM_DEVICE=y 
CONFIG_PM_DEVICE_RUNTIME=y

main.c (simplified):

int main(void) { 
    int ret; 
    if (!device_is_ready(uart_dev)) { 
        LOG_ERR("CDC ACM device not ready"); 
        return 0; 
    } 
    ret = enable_usb_device_next(); 
    if (ret != 0) { 
        LOG_ERR("Failed to enable USB device support"); 
        return 0; 
    } return 0; 
}

All other code and configuration are identical to the stock sample.


Measurement Procedure

  1. Flash firmware, then measure current before inserting USB — result: ~4 µA White check mark
  2. Insert USB VBUS, then remove it — result: current rises to ~270 µA X
  3. Only a full reboot restores the current to ~4 µA

Reproducing the Problem

When I add only the following line to prj.conf (keeping everything else the same), the issue appears:

CONFIG_BT=y

Without CONFIG_BT=y, the current correctly returns to ~4 µA after VBUS is removed.

The Bluetooth stack is never initialized (no bt_enable() call) and no BLE functionality is used in the test code.


Questions

  1. Is this a known interaction between the USB device stack and the Bluetooth subsystem on the nRF54LM20A when CONFIG_BT=y is set, even without the stack being actively used?
  2. Does enabling CONFIG_BT=y implicitly activate peripherals or clocks that are not released properly after USB VBUS removal?
  3. Is there a recommended way to cleanly handle VBUS removal and ensure all relevant peripherals (including those pulled in by CONFIG_BT=y) return to their low-power state without a full reboot?

Any guidance or pointers to relevant documentation or samples would be greatly appreciated. Thank you.

Parents Reply Children
  • Hi strongman, how are you measuring this? using an Amp meter or a power profiler? 
    I do not see the power consumption changes when you enable or disable  CONFIG_BT in prj.conf. I used the cdc_acm sample from NCSv3.2.4

    Can you give me your sample so that we are testing the same? 

    Have you disabled logs? Have you disconnected the debugger usb wire while testing or does the debugger USB wire is still connected?

  • Hi,
    • Measurement Tool: I am using an Amp meter to measure the current.
    • Logs & Debugger: I have not disabled logs, and the debugger USB wire is still connected during the test.
    I have attached my sample code below so we can ensure we are testing the exact same configuration.

  • cdc_acm.7z

    /*
     * Copyright (c) 2019 Intel Corporation
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <sample_usbd.h>
    
    #include <stdio.h>
    #include <string.h>
    #include <zephyr/device.h>
    #include <zephyr/drivers/uart.h>
    #include <zephyr/kernel.h>
    #include <zephyr/sys/ring_buffer.h>
    
    #include <zephyr/usb/usbd.h>
    #include <zephyr/logging/log.h>
    LOG_MODULE_REGISTER(cdc_acm_echo, LOG_LEVEL_INF);
    
    const struct device *const uart_dev = DEVICE_DT_GET_ONE(zephyr_cdc_acm_uart);
    
    #define RING_BUF_SIZE 1024
    uint8_t ring_buffer[RING_BUF_SIZE];
    
    struct ring_buf ringbuf;
    
    static bool rx_throttled;
    
    static inline void print_baudrate(const struct device *dev)
    {
    	uint32_t baudrate;
    	int ret;
    
    	ret = uart_line_ctrl_get(dev, UART_LINE_CTRL_BAUD_RATE, &baudrate);
    	if (ret) {
    		LOG_WRN("Failed to get baudrate, ret code %d", ret);
    	} else {
    		LOG_INF("Baudrate %u", baudrate);
    	}
    }
    
    static struct usbd_context *sample_usbd;
    K_SEM_DEFINE(dtr_sem, 0, 1);
    
    static void sample_msg_cb(struct usbd_context *const ctx, const struct usbd_msg *msg)
    {
    	LOG_INF("USBD message: %s", usbd_msg_type_string(msg->type));
    
    	if (usbd_can_detect_vbus(ctx)) {
    		if (msg->type == USBD_MSG_VBUS_READY) {
    			if (usbd_enable(ctx)) {
    				LOG_ERR("Failed to enable device support");
    			}
    		}
    
    		if (msg->type == USBD_MSG_VBUS_REMOVED) {
    			if (usbd_disable(ctx)) {
    				LOG_ERR("Failed to disable device support");
    			}
    		}
    	}
    
    	if (msg->type == USBD_MSG_CDC_ACM_CONTROL_LINE_STATE) {
    		uint32_t dtr = 0U;
    
    		uart_line_ctrl_get(msg->dev, UART_LINE_CTRL_DTR, &dtr);
    		if (dtr) {
    			k_sem_give(&dtr_sem);
    		}
    	}
    
    	if (msg->type == USBD_MSG_CDC_ACM_LINE_CODING) {
    		print_baudrate(msg->dev);
    	}
    }
    
    static int enable_usb_device_next(void)
    {
    	int err;
    
    	sample_usbd = sample_usbd_init_device(sample_msg_cb);
    	if (sample_usbd == NULL) {
    		LOG_ERR("Failed to initialize USB device");
    		return -ENODEV;
    	}
    
    	if (!usbd_can_detect_vbus(sample_usbd)) {
    		err = usbd_enable(sample_usbd);
    		if (err) {
    			LOG_ERR("Failed to enable device support");
    			return err;
    		}
    	}
    
    	LOG_INF("USB device support enabled");
    
    	return 0;
    }
    
    static void interrupt_handler(const struct device *dev, void *user_data)
    {
    	ARG_UNUSED(user_data);
    
    	while (uart_irq_update(dev) && uart_irq_is_pending(dev)) {
    		if (!rx_throttled && uart_irq_rx_ready(dev)) {
    			int recv_len, rb_len;
    			uint8_t buffer[64];
    			size_t len = MIN(ring_buf_space_get(&ringbuf),
    					 sizeof(buffer));
    
    			if (len == 0) {
    				/* Throttle because ring buffer is full */
    				uart_irq_rx_disable(dev);
    				rx_throttled = true;
    				continue;
    			}
    
    			recv_len = uart_fifo_read(dev, buffer, len);
    			if (recv_len < 0) {
    				LOG_ERR("Failed to read UART FIFO");
    				recv_len = 0;
    			};
    
    			rb_len = ring_buf_put(&ringbuf, buffer, recv_len);
    			if (rb_len < recv_len) {
    				LOG_ERR("Drop %u bytes", recv_len - rb_len);
    			}
    
    			LOG_DBG("tty fifo -> ringbuf %d bytes", rb_len);
    			if (rb_len) {
    				uart_irq_tx_enable(dev);
    			}
    		}
    
    		if (uart_irq_tx_ready(dev)) {
    			uint8_t buffer[64];
    			int rb_len, send_len;
    
    			rb_len = ring_buf_get(&ringbuf, buffer, sizeof(buffer));
    			if (!rb_len) {
    				LOG_DBG("Ring buffer empty, disable TX IRQ");
    				uart_irq_tx_disable(dev);
    				continue;
    			}
    
    			if (rx_throttled) {
    				uart_irq_rx_enable(dev);
    				rx_throttled = false;
    			}
    
    			send_len = uart_fifo_fill(dev, buffer, rb_len);
    			if (send_len < rb_len) {
    				LOG_ERR("Drop %d bytes", rb_len - send_len);
    			}
    
    			LOG_DBG("ringbuf -> tty fifo %d bytes", send_len);
    		}
    	}
    }
    
    int main(void)
    {
    	int ret;
    
    	if (!device_is_ready(uart_dev)) {
    		LOG_ERR("CDC ACM device not ready");
    		return 0;
    	}
    
    	ret = enable_usb_device_next();
    	if (ret != 0) {
    		LOG_ERR("Failed to enable USB device support");
    		return 0;
    	}
    
    	return 0;
    }
    

  • Hi,
    A key question worth asking: Did you plug in the USB first, then unplug it, and then measure the current?

Related