Federico Fuga

Engineering, Tech, Informatics & science

Enabling an USB serial (CDC-ACM) on Zephyr (and other stuff)

17 Mar 2023 18:46 CET

Enabling the Serial communication on USB, for example for logging or for the shell subsystem is very easy, you just need to add some line in the device tree and enable the configuration on the config file. But for a bug or whatever, you also need to add a configuration line that is not documented, otherwise autostart will not work.

But let’s start from the beginning.

The USB CDC/ACM support can be enabled on the configuration only if it is enabled in the device tree. You need a node in your kconfig overlay:

...
&zephyr_udc0 {
    cdc_acm_uart0: cdc_acm_uart0 {
        compatible = "zephyr,cdc-acm-uart";
    };
};

without this, the USB_CDC_ACM entry will not be enabled (and indeed, west will complain during the merging of the config files).

Then you need to add a few lines in your config file:

CONFIG_USB_DEVICE_STACK=y
CONFIG_USB_CDC_ACM=y
CONFIG_USB_DEVICE_PID=0x0110
CONFIG_USB_DEVICE_INITIALIZE_AT_BOOT=y
CONFIG_UART_INTERRUPT_DRIVEN=y 

The first line enables the USB device stack, the second the CDC_ACM service, so that when connected the device will emulate a MODEM or serial port (on linux it will appear as /dev/ttyACM* node).

The third line will define the PID of the USB device; also a VID can be specified in the same way, but you need to register the proper VID code. Without it, the compiler will use a code reserved for testing, not suitable for production.

The fourth line (CONFIG_USB_DEVICE_INITIALIZE_AT_BOOT) avoids you the need to initialize and setting up of the USB stack, but Zephyr will initialize it for you. Check the documentation when it is ok and how to configure it eventually.

The last line is needed if you want to use the USB uart with the logger of with the shell. It is not clear why, but indeed without it the logger will not work.

Last thing, if you want to use the Logger or the shell, you must to define a chosen node on the overlay again:

/ {
    chosen {
        zephyr,console = &cdc_acm_uart0;
        zephyr,shell-uart = &cdc_acm_uart0;
    };
    ...
};

With it the Logger source should work without any modification.

Compiling for multiple boards

If you want to compiler for different hardware, the best way is to put configuration files and device tree overlay in a proper file that will be merged at compile time.

This is especially useful because different boards may have different hardware configuration: for example, you may want to support a USB UART on one board and TTL UART on another.

For example, suppose you have, like me, two boards: an adafruit feather nrf52840 and an adafruit kb2040.

Create a boards subdirectory in your main project directory and put there those files:

  • adafruit_kb2040.overlay for the kb2040 overlayfile
  • adafruit_kb2040.conf the kconfig file for the kb2040
  • adafruit_feather_nrf52840.overlay is the dt overlay for the feather
  • adafruit_feather_nrf52840 is the kconfig for the feather

There must be no app.overlay files, because it would be override all the other device tree overlay. On the other hand, the can be a main prj.conf that will be merged with the board specific files.

Adding an ESP32 (Adafruit Huzzah32)

The Adafruit Huzzah32 board is a development board / Module based on the Espressif SoM ESP32.

Porting our experiment to it is simple, but requires some additional comment.

First you need to setup the environment. Zephyr requires some BLOB to be installed. Go in the zephyr folder and install it:

$ cd zephyrproject
$ west update
$ west blobs fetch hal_espressif

Now, back to our project. Let’s add a devicetree overlay in the boards directory, because the board doesn’t require any additional configuration even if we want to use the USB as a serial port. Indeed the board has a small bootloader that sets up the USB as a CDC port and we can use it to program the board and output the logs.

Huzzah provides a red LED connected to the GPIO13.

let’s add our Device tree overlay:

/ {
    aliases {
        led0 = &led_blue;
    };

    leds {
        compatible = "gpio-leds";
        led_blue: led_0 {
            gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
        };
    };

};

This is almost identicaly to the nrf52840 version, except for the GPIO number.

To compile, we use the esp32 board specifications, because there is no specific support for the Huzzah.

$ west build -b esp32 -d build-esp32

To flash the board, simply use west:

$ west flash -d build-esp32

Note that you don’t have to specify the board but just the building directory.