Zephyr Shell on NUCLEO-G0B1RE
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:
- You can adjust the device tree (s. the Use an Overlay File section).
- 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
builddirectory, 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.
Deactivate the ST-LINK After Flashing
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:
- Remove power by unplugging the USB cable.
- Move the jumper on pin header
JP2to the new position. - Reconnect the UART-to-USB board to pins 6 (
PA2) and 34 (PA3) of connectorCN10. - Power up the board.
- Build the project without using any board overlay file.
- Flash the STM32G0B1 controller.
- Place a jumper on
JP1. - 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
- Zephyr Project, “Zephyr.” [Online]. Available at: https://www.zephyrproject.org. [Accessed: 26-May-2025].
- 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].
Leave a comment