Making STM32WL55 work with Rust

I recently got my hands on a STM32WL55 development kit (NUCLEO-WL55JC2 to be more precise) and wanted to program it in Rust. Since things did not work out of the box and I had to spend many hours figuring out how to make it work, I thought I document the steps I took to make it work for the next person who bumps into this:

Pre-requisites

Note: The target-gen docs instruct how to run it from the repository but it's not necessary and you can install with cargo install target-gen.

Getting Started

Powering up the board is super easy. Just connect the USB cable to the board and your computer. Now if you're as eager as I was, you'll want to already want to try out the lora-rs examples but if you do that already, you'll get an error:

❯ cargo r --bin lora_p2p_receive
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.07s
     Running `probe-rs run --chip STM32WL55JC target/thumbv7em-none-eabi/debug/lora_p2p_receive`
 WARN probe_rs::probe::stlink: send_jtag_command 242 failed: JtagGetIdcodeError
Error: Connecting to the chip was unsuccessful.

The first thing you'll want to do is to disable security (yeah, I know!). To do that, you'll need to run this script:

write(){
  str=""
  for arg do
    str+=" ${arg}"
  done
  /home/user/STMicroelectronics/STM32Cube/STM32CubeProgrammer/bin/STM32_Programmer_CLI -c port=SWD mode=UR -q -ob "${str}"
}

echo RDP: Read Out protection Level 1
write RDP=0xBB

echo RDP+ESE: Read Out protection Level 0 + Security disabled
write RDP=0xAA ESE=0x0

echo WRP: Write Protection disabled
write WRP1A_STRT=0x7F WRP1A_END=0x0 WRP1B_STRT=0x7F WRP1B_END=0x0

echo ------ User Configuration ------
echo nRST: No reset generated when entering the Stop/Standby/Shutdown modes
write nRST_STOP=0x1 nRST_STDBY=0x1 nRST_SHDW=0x1

echo WDG_SW: Software window/independent watchdogs
write WWDG_SW=0x1 IWDG_SW=0x1

echo IWDG: Independent watchdog counter frozen in Stop/Standby modes
write IWGD_STDBY=0x0 IWDG_STOP=0x0

echo BOOT: CPU1+CPU2 CM0+ Boot lock disabled
write BOOT_LOCK=0x0 C2BOOT_LOCK=0x0

echo ------ Security Configuration ------
echo HDPAD: User Flash hide protection area access disabled
write HDPAD=0x1

echo SPISD: SPI3 security disabled
write SUBGHSPISD=0x1

echo SBRSA: Reset default value of SRAM Start address secure
write SNBRSA=0x1F SBRSA=0x1F

echo SBRV: Reset default value of CPU2 Boot start address
write SBRV=0x8000

Making it all work

Now if you run the example again, you'll get a different error:

❯ cargo r --bin lora_p2p_receive
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.07s
     Running `probe-rs run --chip STM32WL55JC target/thumbv7em-none-eabi/debug/lora_p2p_receive`
Error: The flashing procedure failed for 'target/thumbv7em-none-eabi/debug/lora_p2p_receive'.

Caused by:
    Trying to write flash, but found more than one suitable flash loader algorithim marked as default for NvmRegion { name: Some("BANK_1"), range: 134217728..134479872, cores: ["cm4", "cm0p"], is_alias: false, access: Some(MemoryAccess { read: true, write: false, execute: true, boot: true }) }.

That means you're almost there. You just need to tell probe-rs that all but one flash algorithm are the default. I wish this was as easy as setting a CLI arg but unfortunately you need to a tiny bit more:

❯ target-gen  arm -f "STM32WLxx_DFP"
2025-03-16T12:17:56.163918Z  WARN target_gen::generate: Device STM32WL54CCUx, memory region SRAM1 has no processor name, but this is required for a multicore device. Assigning memory to all cores!
2025-03-16T12:17:56.163936Z  WARN target_gen::generate: Device STM32WL54CCUx, memory region SRAM2 has no processor name, but this is required for a multicore device. Assigning memory to all cores!
2025-03-16T12:17:56.163938Z  WARN target_gen::generate: Device STM32WL54CCUx, memory region FLASH has no processor name, but this is required for a multicore device. Assigning memory to all cores!
2025-03-16T12:17:56.164440Z  WARN target_gen::generate: Device STM32WL54JCIx, memory region SRAM1 has no processor name, but this is required for a multicore device. Assigning memory to all cores!
2025-03-16T12:17:56.164443Z  WARN target_gen::generate: Device STM32WL54JCIx, memory region SRAM2 has no processor name, but this is required for a multicore device. Assigning memory to all cores!
2025-03-16T12:17:56.164445Z  WARN target_gen::generate: Device STM32WL54JCIx, memory region FLASH has no processor name, but this is required for a multicore device. Assigning memory to all cores!
2025-03-16T12:17:56.164948Z  WARN target_gen::generate: Device STM32WL55CCUx, memory region SRAM1 has no processor name, but this is required for a multicore device. Assigning memory to all cores!
2025-03-16T12:17:56.164954Z  WARN target_gen::generate: Device STM32WL55CCUx, memory region SRAM2 has no processor name, but this is required for a multicore device. Assigning memory to all cores!
2025-03-16T12:17:56.164956Z  WARN target_gen::generate: Device STM32WL55CCUx, memory region FLASH has no processor name, but this is required for a multicore device. Assigning memory to all cores!
2025-03-16T12:17:56.165458Z  WARN target_gen::generate: Device STM32WL55JCIx, memory region SRAM1 has no processor name, but this is required for a multicore device. Assigning memory to all cores!
2025-03-16T12:17:56.165463Z  WARN target_gen::generate: Device STM32WL55JCIx, memory region SRAM2 has no processor name, but this is required for a multicore device. Assigning memory to all cores!
2025-03-16T12:17:56.165465Z  WARN target_gen::generate: Device STM32WL55JCIx, memory region FLASH has no processor name, but this is required for a multicore device. Assigning memory to all cores!
2025-03-16T12:17:56.166001Z  WARN target_gen::generate: Device STM32WL5MOCHx, memory region SRAM1 has no processor name, but this is required for a multicore device. Assigning memory to all cores!
2025-03-16T12:17:56.166005Z  WARN target_gen::generate: Device STM32WL5MOCHx, memory region SRAM2 has no processor name, but this is required for a multicore device. Assigning memory to all cores!
2025-03-16T12:17:56.166007Z  WARN target_gen::generate: Device STM32WL5MOCHx, memory region FLASH has no processor name, but this is required for a multicore device. Assigning memory to all cores!
Generated 1 target definition(s):
    /home/user/lora-rs/STM32WL_Series.yaml
Finished in 3.191890047s

Now edit this file and change all default: true lines under flash_algorithms to default: false, except for the one under stm32wlxx_cm4 (the core we want to use). Then edit the .cargo/config.toml file as well and change the probe-rs commandline in it, to make use of this chip description file by adding --chip-description-path STM32WL_Series.yaml to it.

At this point everything should work and you should be able to flash and run the lora-rs examples:

❯ cargo r --bin lora_p2p_receive
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.04s
     Running `probe-rs run --chip STM32WLE5JCIx --chip-description-path STM32WL_Series.yaml target/thumbv7em-none-eabi/debug/lora_p2p_receive`
      Erasing ✔ 100% [####################] 140.00 KiB @  61.45 KiB/s (took 2s)
  Programming ✔ 100% [####################] 139.00 KiB @  41.50 KiB/s (took 3s)                                                                                                                                                                                                                                                                                                                                                                  Finished in 5.63s
0.000000 TRACE BDCR ok: 00008200
└─ embassy_stm32::rcc::bd::{impl#3}::init @ /home/user/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/embassy-stm32-0.2.0/src/rcc/bd.rs:216
0.000000 DEBUG rcc: Clocks { hclk1: MaybeHertz(48000000), hclk3: MaybeHertz(48000000), hsi: MaybeHertz(0), lse: MaybeHertz(0), lsi: MaybeHertz(32000), msi: MaybeHertz(4000000), pclk1: MaybeHertz(48000000), pclk1_tim: MaybeHertz(48000000), pclk2: MaybeHertz(48000000), pclk2_tim: MaybeHertz(48000000), pclk3: MaybeHertz(48000000), pll1_p: MaybeHertz(0), pll1_q: MaybeHertz(48000000), rtc: MaybeHertz(32000), sys: MaybeHertz(48000000) }
...

Comments

Popular posts from this blog

Welcome to the virtual world!

clutter-gst

Shame Ubuntu shame