Zephyr Shell on NUCLEO-G0B1RE

Updated: 7 minute read

tl;dr

Zephyr [1] includes a shell [2] subsystem, which is a pretty cool feature. Unfortunately, the default device tree for the NUCLEO-G0B1RE board configures the shell to use usart2 on pins PA2 and PA3 of the STM32G0B1 microcontroller. The issue with this setup is that these pins are also connected to a USART interface of the ST-LINK controller on the board. As a result, this configuration effectively makes it impossible to use Zephyr’s shell.

There are (at least) two solutions to solve the problem:

  1. You can adjust the device tree (s. the Use an Overlay File section).
  2. You can deactivate the ST-LINK controller (s the Deactivate the ST-LINK After Flashing).

Background/Problem

I wanted to try something out using Zephyr’s shell, and since I had a NUCLEO-G0B1RE lying around, I decided to use that board for my tests. What was supposed to be a quick task turned into a bit of a lengthy debugging session.

Here’s what happened:

I have a Zephyr workspace directory on my computer that I use for quick tests like this. It looks like this:

zephyr_workspace/
├── bootloader
├── build
├── .cache
├── modules
├── tools
├── .venv
├── .west
├── zephyr        # Zephyr source code (v4.1.0 commit 21b20de1e)
├── .env
└── README.md

Within that workspace directory, I built Zephyr’s shell_module example:

cd zephyr_workspace
source .venv/bin/activate
west build -p always -b nucleo_g0b1re/stm32g0b1xx zephyr/samples/subsys/shell/shell_module/

A quick look into the file build/zephyr/zephyr.dts revealed, that the default device tree for the STM32G0B1 controller on the NUCLEO-G0B1RE board uses usart2 for the shell, and that pin PA2 is used for TX and pin PA3 is used for RX.

# zephyr.dts
(...)
	chosen {
		zephyr,flash-controller = &flash;
		zephyr,console = &usart2;
		zephyr,shell-uart = &usart2;
		zephyr,uart-mcumgr = &usart2;
		zephyr,sram = &sram0;
		zephyr,flash = &flash0;
		zephyr,code-partition = &slot0_partition;
		zephyr,canbus = &fdcan1;
	};
(...)
		usart2: serial@40004400 {
			compatible = "st,stm32-usart", "st,stm32-uart";
			reg = < 0x40004400 0x400 >;
			clocks = < &rcc 0x3c 0x20000 >;
			resets = < &rctl 0x591 >;
			interrupts = < 0x1c 0x0 >;
			status = "okay";
			pinctrl-0 = < &usart2_tx_pa2 &usart2_rx_pa3 >;
			pinctrl-names = "default";
			current-speed = < 0x1c200 >;
		};
(...)

A quick look at the NUCLEO-G0B1RE schematics showed that pin PA2 of the microcontroller is connected to pin 6 of connector CN10, and pin PA3 to pin 34 of the same connector. So, I hooked up a small UART-to-USB transceiver board to these pins (plus GND), connected that board to my computer, and then connected the NUCLEO-G0B1RE via its onboard USB port.

Once everything was wired up, I launched minicom (a serial communication program on Linux) and configured it to use the USB interface of the UART-to-USB board. Then I flashed the project onto the NUCLEO-G0B1RE using west:

west flash --runner openocd

At first the output of the board in minicom looked promissing:

uart:~$

I did get the shell prompt. But when I tried to interact with it, absolutely nothing happened. I couldn’t type any commands. And that’s where the debugging began:

  • I double-checked all my connections on the board.
  • I deleted the build directory, rebuilt the project, and flashed it again. Just to rule out any build issues.
  • Since I had a second NUCLEO-G0B1RE on hand, I repeated the same steps on that board. Still no success.
  • I looked through the source code of the example project and inspected the device tree of the built project, but nothing obvious stood out.

The first time I tried this, I wasn’t at home and didn’t have access to an oscilloscope to probe the communication lines, so I decided to put the testing on hold. A few days later, when I finally had the chance to examine the signals, I noticed something odd: when sending commands to the NUCLEO-G0B1RE, the voltage at pin PA3 didn’t toggle between 3.3V and 0V as expected during UART communication. It only dropped slightly, just a few millivolts below 3.3V.

And that’s when it hit me: the NUCLEO-G0B1RE has an onboard ST-LINK module used to flash the STM32G0B1 microcontroller. Maybe the to ST-LINK controller and the STM32G0B1 are not only connected via JTAG, but also via UART. A closer look at the schematics confirmed my suspicion. The ST-LINK can communicate with the STM32G0B1 via the usart2 interface. As long as the ST-LINK is powered and active, its TX line holds the RX pin of usart2 on the STM32G0B1 high. That’s why the commands sent from my computer didn’t get to the microcontroller.

Solution

There are (at least) two solutions to this problem.

Use an Overlay File

To work around the issue, you can use an overlay file to configure a different USART interface on the STM32G0B1. Create a file named nucleo_g0b1re.overlay in the zephyr/samples/subsys/shell/shell_module/boards/ directory with the following content:

/ {
	chosen {
		zephyr,console = &usart1;
		zephyr,shell-uart = &usart1;
		zephyr,uart-mcumgr = &usart1;
	};
};

Then, rebuild the project using the same command as before. If you check the file build/zephyr/zephyr.dts again, you’ll see the following:

(...)
		zephyr,console = &usart1;
		zephyr,shell-uart = &usart1;
		zephyr,uart-mcumgr = &usart1;
(...)
			pinctrl-0 = < &usart1_tx_pc4 &usart1_rx_pc5 >;
(...)

So, you’ll need to change the pins your UART-to-USB board is connected to. Pin PC4 connects to pin 35 of connector CN10, and pin PC5 connects to pin 37 of the same connector.

After changing the connections, flash the project again using the command from above. You’ll see the same output in minicom as before, but this time, you can actually interact with the shell:

uart:~$ help
Please press the <Tab> button to see all available commands.
You can also use the <Tab> button to prompt or auto-complete all commands or its subcommands.
You can try to call commands with <-h> or <--help> parameter for more information.

Shell supports following meta-keys:
  Ctrl + (a key from: abcdefklnpuw)
  Alt  + (a key from: bf)
Please refer to shell documentation for more details.

Available commands:
  bypass              : Bypass shell
  clear               : Clear screen.
  date                : Date commands
  demo                : Demo commands
  device              : Device commands
  devmem              : Read/write physical memory
                        Usage:
                        Read memory at address with optional width:
                        devmem <address> [<width>]
                        Write memory at address with mandatory width and value:
                        devmem <address> <width> <value>
  dynamic             : Demonstrate dynamic command usage.
  help                : Prints the help message.
  history             : Command history.
  kernel              : Kernel commands
  log                 : Commands for controlling logger
  log_test            : Log test
  rem                 : Ignore lines beginning with 'rem '
  resize              : Console gets terminal screen size or assumes default in
                        case the readout fails. It must be executed after each
                        terminal width change to ensure correct text display.
  retval              : Print return value of most recent command
  section_cmd         : Demo command using section for subcommand registration
  shell               : Useful, not Unix-like shell commands.
  shell_uart_release  : Uninitialize shell instance and release uart, start
                        loopback on uart. Shell instance is reinitialized when
                        'x' is pressed
  stats               : Stats commands
  version             : Show kernel version
uart:~$

Alternatively, it should be possible to configure usart2 to use different pins, but I haven’t checked whether the NUCLEO-G0B1RE board has any available pins that won’t interfere with other connections.

There’s another way to work around the problem without changing any software. You can put the ST-LINK controller into reset after flashing the STM32G0B1. This is what the pin header JP1 next to the USB port is for. Simply place a jumper across the two pins to short them.

However, this also disables the power supply to the STM32G0B1, which of corse isn’t ideal. To avoid that, you need to move the jumper on pin header JP2. By default (at least on my boards), the jumper on JP2 shorts the two pins labeled “STLK”. Move this jumper all the way to the other side, onto the pins labeled “CHG”.

It’s probably best to do this while the board is powered off. So the sequence would be:

  1. Remove power by unplugging the USB cable.
  2. Move the jumper on pin header JP2 to the new position.
  3. Reconnect the UART-to-USB board to pins 6 (PA2) and 34 (PA3) of connector CN10.
  4. Power up the board.
  5. Build the project without using any board overlay file.
  6. Flash the STM32G0B1 controller.
  7. Place a jumper on JP1.
  8. Reset the STM32G0B1 by pressing the reset button on the board.

Now you should see the shell prompt in minicom and be able to interact with it.

Conclusion

I’m not entirely sure why the default device tree setup for the NUCLEO-G0B1RE board in Zephyr results in a configuration that makes using the shell impossible. I’ll try to investigate the reason behind this setup and see if it can be changed. But until then, you’ll need to make some adjustments to either the software or hardware to get Zephyr’s shell_module sample running.

Well, I hope this was helpful!
\

Change Log

  • 2025-05-27:

    • Made some corrections about the communication between the ST-LINK controller and the STM32G0B1.



Take care,
Andreas


References

  1. Zephyr Project, “Zephyr.” [Online]. Available at: https://www.zephyrproject.org. [Accessed: 26-May-2025].
  2. Zephyr Project members and individual contributors, “Zephyr Docs: Shell.” [Online]. Available at: https://docs.zephyrproject.org/4.1.0/services/shell/index.html. [Accessed: 26-May-2025].

Updated:

Leave a comment