STM32 Development and Remote Debugging: Difference between revisions

From JoBaPedia
Jump to navigation Jump to search
No edit summary
 
(10 intermediate revisions by the same user not shown)
Line 26: Line 26:


* Pull down Pin 1 (Boot0) to Gnd (10k)
* Pull down Pin 1 (Boot0) to Gnd (10k)
* Connect your STLink (mine is on a Nucleo F401RE, Connectors CN4 and JP1) to your STM32, Ground first:
* Prepare ST-Link (mine is on a Nucleo F401RE
* Remove jumpers from CN2 (1,2) and (3,4)
* Switch 5V from the STM32F401 on the nucleo board to external J5 from U5V to E5V
* Connect your STLink (mine has Connectors CN4 for data and JP1 for power) to your STM32, Ground first:
  * CN4-3 -  9 (VSS aka ground)
  * CN4-3 -  9 (VSS aka ground)
  * CN4-2 - 14 (SWCLK)
  * CN4-2 - 14 (SWCLK)
Line 41: Line 44:
* Give the project a name, rest default, select Finish
* Give the project a name, rest default, select Finish
* CubeMX starts and shows your chip (this helps configuring the clocks and pins)
* CubeMX starts and shows your chip (this helps configuring the clocks and pins)
* On first tab (Pinout and Config) select System Core/SYS and Check Debug Serial Wire
** On first tab (Pinout and Config) select System Core/SYS and Check Debug Serial Wire
* Select pin PA0 and select GPIO_Output as function
** Select pin PA0 and select GPIO_Output as function
* Select Save and allow generationg of code, if asked
** Select Save and allow generationg of code, if asked
* Open file Core/Src/main.c
* Open file Core/Src/main.c
* Add the two HAL lines below, right after the while (1):
* Add the two HAL lines below, right after the while (1):
  /* Infinite loop */
/* Infinite loop */
  /* USER CODE BEGIN WHILE */
/* USER CODE BEGIN WHILE */
  while (1)
while (1)
  {
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0);
  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0);
HAL_Delay(200);
  HAL_Delay(200);
    /* USER CODE END WHILE */
* Click on Run (green circle with white triangle)
* Click on Run (green circle with white triangle)


Code should get compiled, flashed and started now: the LED blinks
Code should get compiled, flashed and started now: the LED blinks
=== Flashing ===
I cannot make flashing clones (e.g. CH32F103C8) work with STM32CubeIDE (1.9.0).
But flashing the elf files with platformio openocd works fine:
elf="/home/joachim/STM32CubeIDE/workspace_1.3.0/ch32f103-blink/Debug/ch32f103-blink.elf"
/home/joachim/.platformio/packages/tool-openocd/bin/openocd -d2 \
  -c "set CONNECT_UNDER_RESET 1; set CPUTAPID 0" \
  -s /home/joachim/.platformio/packages/tool-openocd/scripts -f interface/stlink.cfg \
  -c "transport select hla_swd" \
  -f target/stm32f1x.cfg \
  -c "reset_config none" \
  -c "program {$elf}  verify reset; shutdown;"
* CONNECT_UNDER_RESET 1: press reset, start openocd, then release reset (boot0 not needed)
* CPUTAPID 0: ignore chip type to allow clones
Looks like STM32CubeIDE just got downgraded to a project start code generator (clones actively blocked).
At least for boards that are supported by PlatformIO/VSCode.


=== Debugging ===
=== Debugging ===


To debug your code, select Debug (the small green bug) instead of run.
To debug your code in STM32CubeIDE, select Debug (the small green bug) instead of run.
The code will be flashed, started and then paused before the first instruction in main(): HAL_Init()
The code will be flashed, started and then paused before the first instruction in main(): HAL_Init()
Some options:
Some options:
Line 68: Line 89:
* Suspend -> Pause code wherever it is right now
* Suspend -> Pause code wherever it is right now
While the execution is suspended, you can examine variables, memory and registers - no need for printf :)
While the execution is suspended, you can examine variables, memory and registers - no need for printf :)
This also works with VSCode. No special config in platformio.ini is needed.
For STM32F103C8 I needed to change reset_config to none in ~/.platformio/packages/tool-openocd/scripts/target/stm32f1x.cfg


=== Hints ===
=== Hints ===
Line 107: Line 131:
==== UART ====
==== UART ====


...
Uarts are the serial ports. You can send any data over one line and receive over another. Blocking or asynchronous. Here is just the simplest form: blocking send.
* Enable LPUART1 or USART2 or whatever port you want and does not collide with already used pins in CubeMX (Pinout & Configuration, Conectivity)
* As Mode select Half Duplex (can send/receive synchronously)
* In Parameter Settings select communication parameters like (common values in parantheses)
** Baud Rate (115200)
** Word Length (8)
** Parity (None)
** Stop Bits (1)
** Data Direction (For sending only debug prints "Transmit Only" is enough)
* Generate the code adds a in MX_LPUART1_UART_Init() to your main()
* To send over lpuart1, just call
HAL_UART_Transmit(&hlpuart1, buffer, bufferlength, HAL_MAX_DELAY);
 
Again, quite resource hungry. Just the HAL init and one send is near the 8k flash limit.
So let's try LL API:
 
* In CubeMX Project manager switch LPUART driver from HAL to LL
* Regenerate sources and change in HAL_UART_Transmit(&hlpuart1, "Hello, serial world!\n", 21, HAL_MAX_DELAY); to its LL equivalent:
LL_LPUART_Enable(LPUART1);
const char *str = "Hello, serial world!\n";
while( *str ) {
  while( !LL_LPUART_IsActiveFlag_TXE(LPUART1) ) ;
  LL_LPUART_TransmitData8(LPUART1, *str++);
}
while( !LL_LPUART_IsActiveFlag_TC(LPUART1) ) ;
LL_LPUART_Disable(LPUART1);
 
Et voila: longer source code (that you can pack into a function) but only 5k flash.
 
=== CH32F103 Clones ===
 
On job4 there is a ch32f103-blink and a ch32F103-stepper project that runs in STM32CubeIDE
* blink example tested with flash and debugging. Led is inverted at pc13. uses openocd
* stepper example only tested with flash, used MX config target STM32F103C6, SYS Debug -> Serial Wire, uses standard debug config
 
* Both don't need the boot0 jumper changed.
* Both need swclk, swdio, gnd and nrst connected to st-link for flashing (no manual reset needed)
 
Maybe I changed STM32 system files to make debug work...
see file ch32f103 Debug.cfg in bilink project:
 
source [find interface/stlink-dap.cfg]
set WORKAREASIZE 0x5000
transport select "dapdirect_swd"
set CHIPNAME STM32F103C8Tx
set BOARDNAME genericBoard
set ENABLE_LOW_POWER 1
set STOP_WATCHDOG 1
set CLOCK_FREQ 8000
reset_config srst_only srst_nogate connect_assert_srst
set CONNECT_UNDER_RESET 1
set CORE_RESET 0
set AP_NUM 0
set GDB_PORT 3333
source [find target/stm32f1x.cfg]

Latest revision as of 20:12, 16 June 2022

STM32 Development

Prerequisites

  • Get free account at https://my.st.com
  • Get an STLink adapter or a Nucleo Board with embedded STLink adapter for flashing and remote debugging
  • Linux (better with libusbx >= 1.0) or Windows or Mac
  • An STM32 chip, of course

Total cost starts at: adapter ~3€ and Chip ~1€

Installation

unzip en.st-stm32cubeide_1.3.0_5720_20200220_1053_amd64.rpm_bundle.sh.zip
sudo sh st-stm32cubeide_1.3.0_5720_20200220_1053_amd64.rpm_bundle.sh
  • If this fails on linux because libusbx is missing, then repeat, and while you are asked for first license accept, do this:
cd st-stm32cubeide_1.3.0_5720_20200220_1053_amd64.rpm_bundle.sh.root/
rpm -ihv --nodeps st-stlink-server-1.3.0-4-linux-amd64.rpm

Prepare the STM32 chip

In my case an STM32L011D3 (TSSOP14), YMMV

  • Pull down Pin 1 (Boot0) to Gnd (10k)
  • Prepare ST-Link (mine is on a Nucleo F401RE
* Remove jumpers from CN2 (1,2) and (3,4)
* Switch 5V from the STM32F401 on the nucleo board to external J5 from U5V to E5V 
  • Connect your STLink (mine has Connectors CN4 for data and JP1 for power) to your STM32, Ground first:
* CN4-3 -  9 (VSS aka ground)
* CN4-2 - 14 (SWCLK)
* CN4-4 - 13 (SWDIO)
* CN4-5 -  4 (NRST) 
* JP1-1 -  8 (VDD)
  • Connect an LED between PA0 (anode, +) and VSS (Kathode, -)

First Steps (Blink)

  • Start the IDE
  • Create new STM32 project
  • Select your chip from the query form
  • Give the project a name, rest default, select Finish
  • CubeMX starts and shows your chip (this helps configuring the clocks and pins)
    • On first tab (Pinout and Config) select System Core/SYS and Check Debug Serial Wire
    • Select pin PA0 and select GPIO_Output as function
    • Select Save and allow generationg of code, if asked
  • Open file Core/Src/main.c
  • Add the two HAL lines below, right after the while (1):
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0);
  HAL_Delay(200);
  • Click on Run (green circle with white triangle)

Code should get compiled, flashed and started now: the LED blinks

Flashing

I cannot make flashing clones (e.g. CH32F103C8) work with STM32CubeIDE (1.9.0). But flashing the elf files with platformio openocd works fine:

elf="/home/joachim/STM32CubeIDE/workspace_1.3.0/ch32f103-blink/Debug/ch32f103-blink.elf"
/home/joachim/.platformio/packages/tool-openocd/bin/openocd -d2 \
 -c "set CONNECT_UNDER_RESET 1; set CPUTAPID 0" \
 -s /home/joachim/.platformio/packages/tool-openocd/scripts -f interface/stlink.cfg \
 -c "transport select hla_swd" \
 -f target/stm32f1x.cfg \
 -c "reset_config none" \
 -c "program {$elf}  verify reset; shutdown;"
  • CONNECT_UNDER_RESET 1: press reset, start openocd, then release reset (boot0 not needed)
  • CPUTAPID 0: ignore chip type to allow clones

Looks like STM32CubeIDE just got downgraded to a project start code generator (clones actively blocked). At least for boards that are supported by PlatformIO/VSCode.

Debugging

To debug your code in STM32CubeIDE, select Debug (the small green bug) instead of run. The code will be flashed, started and then paused before the first instruction in main(): HAL_Init() Some options:

  • Step over (F6) -> Execute the next statement, but dont descend into functions
  • Step Into (F5) -> Execute the next statement, if it is a function, go to its first instruction
  • Set a breakpoint (Double click before the line number) -> Execution will pause before that line
  • Resume (F8) -> Code runs until it hits a breakpoint
  • Suspend -> Pause code wherever it is right now

While the execution is suspended, you can examine variables, memory and registers - no need for printf :)

This also works with VSCode. No special config in platformio.ini is needed. For STM32F103C8 I needed to change reset_config to none in ~/.platformio/packages/tool-openocd/scripts/target/stm32f1x.cfg

Hints

HAL

HAL is the Hardware Abstraction Layer for ST chips. It is aiming at giving the same high level interface on all STM32 chips. Drawback is resource usage: The blink example alone is already using 5432 bytes. Not good if you only have 8192, like me with the STM32L011D3. You can't even enable all functions like STI, I2C and UARTs before hitting a limit.

LL

LL is the low level programming API for ST chips. It is much closer to programming the registers, but much more conservative with resources. To use it, open the projects ioc file (where the pin config is stored). Select tab Project Manager, subsection Advanced Settings and change HAL to LL for each component in the Driver Selector. Now replace the HAL functions with LL functions. E.g.

  • HAL_Delay(100) -> LL_mDelay(100)
  • HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0) -> LL_GPIO_TogglePin(GPIOA, LL_GPIO_PIN_0)

Now the blink code is less than half the size (2648) for the same function!

Clocks

So far the chip ran with its default speed of only 2MHz. For more (up to 32MHz for my L0), configure it in CubeMX: open the iot file and select Clock Configuration. What I know so far is these options:

  • MSIRC: internal medium speed (default)
  • HSI: internal high speed (16MHz RC that can be multiplied by PLL to up to 32MHz)
  • HSE: external quarz for more precise speed than internal RC
  • LSE: low power speed for RTC

To use 32MHz HSI, select PLL Source Mux HSI16, *PLLMul x4 and /PLLDiv /2, Then System Clock Mux PLLClk. This is then used as basis for CPU clock and peripheral clocks. Just consider that faster also means more power usage, so keep it as low as possible for your application for all components.

SPI

The TSSOP14 of the STM32L011D3 has not enough pins for SPI and online debugging (Debug Serial Wire). They share the same pins and you can only have one at runtime :(

UART

Uarts are the serial ports. You can send any data over one line and receive over another. Blocking or asynchronous. Here is just the simplest form: blocking send.

  • Enable LPUART1 or USART2 or whatever port you want and does not collide with already used pins in CubeMX (Pinout & Configuration, Conectivity)
  • As Mode select Half Duplex (can send/receive synchronously)
  • In Parameter Settings select communication parameters like (common values in parantheses)
    • Baud Rate (115200)
    • Word Length (8)
    • Parity (None)
    • Stop Bits (1)
    • Data Direction (For sending only debug prints "Transmit Only" is enough)
  • Generate the code adds a in MX_LPUART1_UART_Init() to your main()
  • To send over lpuart1, just call
HAL_UART_Transmit(&hlpuart1, buffer, bufferlength, HAL_MAX_DELAY);

Again, quite resource hungry. Just the HAL init and one send is near the 8k flash limit. So let's try LL API:

  • In CubeMX Project manager switch LPUART driver from HAL to LL
  • Regenerate sources and change in HAL_UART_Transmit(&hlpuart1, "Hello, serial world!\n", 21, HAL_MAX_DELAY); to its LL equivalent:
LL_LPUART_Enable(LPUART1);
const char *str = "Hello, serial world!\n";
while( *str ) {
  while( !LL_LPUART_IsActiveFlag_TXE(LPUART1) ) ;
  LL_LPUART_TransmitData8(LPUART1, *str++);
}
while( !LL_LPUART_IsActiveFlag_TC(LPUART1) ) ;
LL_LPUART_Disable(LPUART1);

Et voila: longer source code (that you can pack into a function) but only 5k flash.

CH32F103 Clones

On job4 there is a ch32f103-blink and a ch32F103-stepper project that runs in STM32CubeIDE

  • blink example tested with flash and debugging. Led is inverted at pc13. uses openocd
  • stepper example only tested with flash, used MX config target STM32F103C6, SYS Debug -> Serial Wire, uses standard debug config
  • Both don't need the boot0 jumper changed.
  • Both need swclk, swdio, gnd and nrst connected to st-link for flashing (no manual reset needed)

Maybe I changed STM32 system files to make debug work... see file ch32f103 Debug.cfg in bilink project:

source [find interface/stlink-dap.cfg]
set WORKAREASIZE 0x5000
transport select "dapdirect_swd"
set CHIPNAME STM32F103C8Tx
set BOARDNAME genericBoard
set ENABLE_LOW_POWER 1
set STOP_WATCHDOG 1
set CLOCK_FREQ 8000
reset_config srst_only srst_nogate connect_assert_srst
set CONNECT_UNDER_RESET 1
set CORE_RESET 0
set AP_NUM 0
set GDB_PORT 3333
source [find target/stm32f1x.cfg]