Firmware loaded successfully, but not working on stm32

Hi, I state that I am a new user of platformIO. I am trying this extension in order to replace IAR embedded workbench which does not exist for Linux, my main operating system. Unfortunately, trying to load a simple program that turns on a LED if a button is pressed, it fails. The microcontroller I am programming is a STM32F303VC. The problem I think is in the flash phase, even if it does not present any errors… This is because the same source used on IAR works.
The problem is precisely that the software claims to have been loaded successfully, but it has not.
If it helps, I’m using platformIO on vscode on both debian stable and testing.

I am attaching the screens where the firmware flashes, which I assure you works. what can I try?

I think rather the opposite, because flashing works as you’ve shown yourself. Otherwise there would be errors there.

IAR and GCC behave differently as compilers on the same code, so with most likelyness it’s in the code or the setup thereof. I’ve seen plenty of cases here where e.g. someone compiles code that worked in Keil with PlatformIO (GCC) and it doesn’t work, because it optimizes something out, or treats variables used in interrupts differently, etc.

A problem could also lie in the clock configuration for the board. You are using a Discovery STM32F303VC board, or something custom?

Can you share the platformio.ini and code?

But the compiler doesn’t even have any errors, that’s why I don’t know…

Anyway yes, my card is a standard STM32F303VC.

This is the platformio.ini:
[env:disco_f303vc]

platform = ststm32

board = disco_f303vc

framework = spl

This is main.c:
#include "MySTM32F303VC.h"

void main(){

RCC->IOPAEN=1;
RCC->IOPEEN=1;

GPIOE->MODER9=Output;
GPIOA->MODER0=Input;

while(1){

while(GPIOA->IDR0==0); 
while(GPIOA->IDR0==1); 
  
if(GPIOE->ODR9==1) 
   GPIOE->ODR9=0; //Accendo il led
   else GPIOE->ODR9=1; //Spengo il led

}//end while

}//end main

And this is my library:
typedef struct{

   unsigned int CR;
   unsigned int CFGR;
   unsigned int CIR;
   unsigned int APB2RSTR;
   unsigned int APB1RSTR;
   
   union{unsigned int AHBENR;
     struct{
       unsigned DMA1EN:1;   
       unsigned DMA2EN:1;  
       unsigned SRAMEN:1;
       unsigned Res:1;
       unsigned FLITFEN:1;   
       unsigned Res1:1;
       unsigned CRCEN:1;
       unsigned Res2:10;
       unsigned IOPAEN:1;
       unsigned IOPBEN:1;
       unsigned IOPCEN:1;
       unsigned IOPDEN:1;
       unsigned IOPEEN:1;
       unsigned IOPFEN:1;
       unsigned Res3:1;
       unsigned TSCEN:1;
       unsigned Res4:3;
       unsigned ADC12:1;
       unsigned ADC34:1;
       unsigned Res5:2;
                     };//END STRUCT ahbenr
            };//end union AHBENR
   
   
   unsigned int APB2ENR;
   unsigned int APB1ENR;
   unsigned int BDCR;
   unsigned int CSR;
   unsigned int AHBRSTR;
   unsigned int CFGR2;
   unsigned int CFGR3;

}RCC_Type;

typedef struct{
  
  union{unsigned int MODER;
        struct{
          unsigned MODER0:2;
          unsigned MODER1:2;
          unsigned MODER2:2;
          unsigned MODER3:2;
          unsigned MODER4:2;
          unsigned MODER5:2;
          unsigned MODER6:2;
          unsigned MODER7:2;
          unsigned MODER8:2;
          unsigned MODER9:2;
          unsigned MODER10:2;
          unsigned MODER11:2;
          unsigned MODER12:2;
          unsigned MODER13:2;
          unsigned MODER14:2;
          unsigned MODER15:2;
                };
        };
  
       
       unsigned int OTYPER;
       unsigned int OSPEEDR;
       unsigned int PUPDR;
       
       
       union{   unsigned int IDR;
                struct{
                  unsigned IDR0:1;
                  unsigned IDR1:1;
                  unsigned IDR2:1;
                  unsigned IDR3:1;
                  unsigned IDR4:1;
                  unsigned IDR5:1;
                  unsigned IDR6:1;
                  unsigned IDR7:1;
                  unsigned IDR8:1;
                  unsigned IDR9:1;
                  unsigned IDR10:1;
                  unsigned IDR11:1;
                  unsigned IDR12:1;
                  unsigned IDR13:1;
                  unsigned IDR14:1;
                  unsigned IDR15:1;
                  };
             };
       
       
       
       union{     unsigned int ODR;
       
                  struct{
                  unsigned OR0:1;
                  unsigned ODR1:1;
                  unsigned ODR2:1;
                  unsigned ODR3:1;
                  unsigned ODR4:1;
                  unsigned ODR5:1;
                  unsigned ODR6:1;
                  unsigned ODR7:1;
                  unsigned ODR8:1;
                  unsigned ODR9:1;
                  unsigned ODR10:1;
                  unsigned ODR11:1;
                  unsigned ODR12:1;
                  unsigned ODR13:1;
                  unsigned ODR14:1;
                  unsigned ODR15:1;
                  };
                  
       };
       /*
       unsigned int BSRR;
       unsigned int LCKR;
       unsigned int AFRL;
       unsigned int AFRH;
       unsigned int BRR;
       */

}GPIO_Type;

#define Input           0
#define Output          1
#define Alternate       2
#define Analog          3

#define RCC ((RCC_Type*) 0x40021000)
#define GPIOA ((GPIO_Type*) 0x48000000)
#define GPIOE ((GPIO_Type*) 0x48001000)

However, I repeat that this test program works regularly.

Just FYI, by choosing to include the SPL framework you automatically pull in code from the SPL’s startup assembly code startup_stm32f30x.S and the Reset_Handler() (first functon that is executed)) from system_stm32f30x.c that will initialize the processor and memory, and the tries to initialize the clocks.

The flow here is

Reset_Handler() 
  -> Copy the data segment initializers from flash to SRAM
  -> Zero fill the bss segment
  -> call SystemInit()
    -> init HSI
    -> SetSysClock() [attempts to turn on HSE oscillator]
  -> call main() 

But that should be okay, it shouldn’t get stuck there.

However, I’m 99% sure that the situation that I hinted at before is happening: Compiler optimizatons.

I recommend that in your header file all register fields

Are marked as volatile, just like the STM’s SPL code does with __IO. Otherwise the compiler might try to optimize it and not execute your code correctly.

You can see that this is happening because if I take your header file and compile the firmware I get as a compilatin result

RAM:   [          ]   2.3% (used 1132 bytes from 49152 bytes)
Flash: [          ]   0.7% (used 1792 bytes from 262144 bytes)
Building .pio\build\disco_f303vc\firmware.bin

however, when I replace your header file with this


#define     __IO    volatile             /*!< Defines 'read / write' permissions */

 typedef struct{

   __IO unsigned int CR;
   __IO unsigned int CFGR;
   __IO unsigned int CIR;
   __IO unsigned int APB2RSTR;
   __IO unsigned int APB1RSTR;
   
   union{__IO unsigned int AHBENR;
     struct{
       __IO unsigned DMA1EN:1;   
       __IO unsigned DMA2EN:1;  
       __IO unsigned SRAMEN:1;
       __IO unsigned Res:1;
       __IO unsigned FLITFEN:1;   
       __IO unsigned Res1:1;
       __IO unsigned CRCEN:1;
       __IO unsigned Res2:10;
       __IO unsigned IOPAEN:1;
       __IO unsigned IOPBEN:1;
       __IO unsigned IOPCEN:1;
       __IO unsigned IOPDEN:1;
       __IO unsigned IOPEEN:1;
       __IO unsigned IOPFEN:1;
       __IO unsigned Res3:1;
       __IO unsigned TSCEN:1;
       __IO unsigned Res4:3;
       __IO unsigned ADC12:1;
       __IO unsigned ADC34:1;
       __IO unsigned Res5:2;
                     };//END STRUCT ahbenr
            };//end union AHBENR
   
   
   __IO unsigned int APB2ENR;
   __IO unsigned int APB1ENR;
   __IO unsigned int BDCR;
   __IO unsigned int CSR;
   __IO unsigned int AHBRSTR;
   __IO unsigned int CFGR2;
   __IO unsigned int CFGR3;

}RCC_Type;

typedef struct{
  
  union{__IO unsigned int MODER;
        struct{
          __IO unsigned MODER0:2;
          __IO unsigned MODER1:2;
          __IO unsigned MODER2:2;
          __IO unsigned MODER3:2;
          __IO unsigned MODER4:2;
          __IO unsigned MODER5:2;
          __IO unsigned MODER6:2;
          __IO unsigned MODER7:2;
          __IO unsigned MODER8:2;
          __IO unsigned MODER9:2;
          __IO unsigned MODER10:2;
          __IO unsigned MODER11:2;
          __IO unsigned MODER12:2;
          __IO unsigned MODER13:2;
          __IO unsigned MODER14:2;
          __IO unsigned MODER15:2;
                };
        };
  
       
       __IO unsigned int OTYPER;
       __IO unsigned int OSPEEDR;
       __IO unsigned int PUPDR;
       
       
       union{   __IO unsigned int IDR;
                struct{
                  __IO unsigned IDR0:1;
                  __IO unsigned IDR1:1;
                  __IO unsigned IDR2:1;
                  __IO unsigned IDR3:1;
                  __IO unsigned IDR4:1;
                  __IO unsigned IDR5:1;
                  __IO unsigned IDR6:1;
                  __IO unsigned IDR7:1;
                  __IO unsigned IDR8:1;
                  __IO unsigned IDR9:1;
                  __IO unsigned IDR10:1;
                  __IO unsigned IDR11:1;
                  __IO unsigned IDR12:1;
                  __IO unsigned IDR13:1;
                  __IO unsigned IDR14:1;
                  __IO unsigned IDR15:1;
                  };
             };
       
       
       
       union{     __IO unsigned int ODR;
       
                  struct{
                  __IO unsigned OR0:1;
                  __IO unsigned ODR1:1;
                  __IO unsigned ODR2:1;
                  __IO unsigned ODR3:1;
                  __IO unsigned ODR4:1;
                  __IO unsigned ODR5:1;
                  __IO unsigned ODR6:1;
                  __IO unsigned ODR7:1;
                  __IO unsigned ODR8:1;
                  __IO unsigned ODR9:1;
                  __IO unsigned ODR10:1;
                  __IO unsigned ODR11:1;
                  __IO unsigned ODR12:1;
                  __IO unsigned ODR13:1;
                  __IO unsigned ODR14:1;
                  __IO unsigned ODR15:1;
                  };
                  
       };
       /*
       __IO unsigned int BSRR;
       __IO unsigned int LCKR;
       __IO unsigned int AFRL;
       __IO unsigned int AFRH;
       __IO unsigned int BRR;
       */

}GPIO_Type;

#define Input           0
#define Output          1
#define Alternate       2
#define Analog          3


#define RCC ((RCC_Type*) 0x40021000)
#define GPIOA ((GPIO_Type*) 0x48000000)
#define GPIOE ((GPIO_Type*) 0x48001000)

so, everything is marked as __IO, and I recompile, I get

RAM:   [          ]   2.3% (used 1132 bytes from 49152 bytes)
Flash: [          ]   0.7% (used 1844 bytes from 262144 bytes)

so, 52 more bytes have been added to the code. Those might the those instructions actually reading the register and not doing optimizations like reading it once and assuming it never changes (which GCC does since there’s no volatile on the variable).

I recommend you try the above header file.

Also, I see that you’ve flashed the firmware via the STLink. PlatformIO has built-in debugging capabilities, you can just set a breakpoint anywhere and “Run & Debug” tab at the left side to start debugging.

However, be careful: When using this, the firmware is also recompiled in debug mode (-Og instead of -Os), and then those exact compiler optimizations destroying your firmware previously may not appear in debug mode anymore.

Ok, replacing your header with the mine, it works fine. Thanks for the help.

@maxgerhardt:
Just FYI, by choosing to include the SPL framework you automatically pull in code from the SPL’s
startup assembly code startup_stm32f30x.S and the Reset_Handler()

I don’t need frameworks, in fact I would like not to download anything.

@maxgerhardt: When using this, the firmware is also recompiled in debug mode (-Og instead of -Os ), and then those exact compiler optimizations destroying your firmware previously may not appear in debug mode anymore.

So the firmware might works also with my header?
Maybe I didn’t understand correctly… in fact I’m trying, but with my header file, it doesn’t work even in debug…

If you remove the framework = .. line you compile the firmware bare-metal. But you may also need to take care of the startup script and linker script then if you want something custom. I have a base bare-metal example e.g. at GitHub - maxgerhardt/pio-stm32-native-blink: An example project for programming an STM32 microcontroller without any framework.

Also not if you use -O0 as optimization flag?

debug_build_flags = -O0 -ggdb3 -g3

per docs.

However, during the initialization phase of the project, can I decide not to download any framework? Because I noticed that they also weigh a lot…

@maxgerhardt:
Also not if you use -O0 as optimization flag?

debug_build_flags = -O0 -ggdb3 -g3

Yes, now also the debug works fine.

An last thing: how do I set the optimizations for the compiler even for uploading without debugging?

As per docs, with build_flags and build_unflags. (First remove the default -Os, then inject another).

However, you really really don’t want to do that for production code. Relying on a compiler optimization level or behavior for working code is bad, the code needs to be correct in the first place. And the code will be correct if registers are marked as volatile, as per above, that’s how all the SDK vendors (STM32 HAL etc.) are doing it. Compiling with -O0 also makes the code extremely slow.

In the GUI it’s not possible to select “baremetal” or “none” as framework. You always have to select one and then remove the framework = .. line from the platformio.ini later for a bare-metal project. A feature request issue was opened per Allow "none" or "baremetal" in framework selection for project creation · Issue #3854 · platformio/platformio-home · GitHub.

Ok, many thanks for all.