Using STM32 HAL with mbedOS

20 Feb 2019

The STM32CubeMX uses the STM32 HAL and it is not mutually exclusive with the mbedOS, though there are a few important points you must pay attention to when using them together. The most straightforward way to set up a dual project is to create two projects: one in mbed and one in CubeMX. Use the mbed project for your code and the CubeMX as a reference.

Setup peripherals:

Setup your CubeMX project with all the system resources you’ll need (peripherals, clock sources, etc) and generate the code. Take a look at the files generated and the functions contained within them. What you’ll want to look for is HAL_PPP_MspInit() and HAL_PPP_MspDeInit() (where PPP is the peripheral, in the actual code this would be replaced by the specific peripheral - ie: HAL_TIM_MspInit) and any Init() functions generated. You’ll want to add these to your mbed project. Take a look at the HAL documentation for the peripheral you’re using to figure out how to use its functionality (the configuration of the peripheral can all be handled within CubeMX) – the HAL is very well documented. Do this for all the peripherals you want to use.

Setup clocks:

The CubeMX generated code also creates a function called SystemClock_Config(). This function is already called by mbed, but mbed’s function won’t contain what you’ll need from the CubeMX. Take a look at the CubeMX code’s SystemClock_Config() and mbed’s system_clock.c:SystemClock_Config() to see what you’ll need to add (this all depends on which resources you’re using, but it will most likely just be the RCC_PeriphCLKInitTypeDef object). mbed’s SystemClock_Config() will be setup for your specific target, so you don’t really want to modify much else outside the peripheral clocks. It’s a good idea to duplicate the contents of system_clock.c and remove it from your mbed project, then modify your local copy.

Using IRQHandlers:

If you want to use IRQHandlers or callbacks in the HAL, enable that option in your CubeMX project. The generated code will create the IRQHandler and the HAL_PPP_MspInit() function will contain calls to HAL_NVIC_SetPriority() and HAL_NVIC_EnableIRQ() for that particular peripheral. WHAT WILL BE MISSING is the call to NVIC_SetVector( PPP_IRQn, (uint32_t)&PPP_IRQHandler). You must add this before enabling the IRQ. The CMSIS documentation states that the vector table must be relocated to RAM in order to call NVIC_SetVector(), but you don’t have to do this. This is already handled by mbed. What you will have to do is override the weak callback functions defined within the peripheral's source file. You can place these in your program code - just look at the weak functions in the source file to see the required format.

This should be enough to get anyone started if they try to use mbed with the STM32 HAL. Good luck!

07 Nov 2018

Hi Stephen,

Thank you for posting this very detailed post about STM32CubeMX and STM32 HAL

Naveen,

Team mbed.

27 Nov 2018

Hello,

I would be glad if you could post an example? I do not quite understand how I can merge Mbed and CubeMX.

22 Jan 2019

Robert, Which step specifically are you having issues with? Stephen

20 Feb 2019

I would be glad if you could post an example? I couldn't find many files when I tried to port.

20 Feb 2019

Carmelo, can you be more specific with the issues you are having?

27 Nov 2019

How do we look at mbed’s system_clock.c:SystemClock_Config() in the online compiler?

09 Dec 2019

@Stephen Davis,

Thanks for taking the time to describe how to do this. It would be awesome if you could post an example! This MBED framework seems really great, but as I am trying to transition from the STM32 HAL framework there are quite a few things missing. In my own case, I need to configure a timer in input capture mode, and trigger a few other timer related callbacks as well. I am just not experienced enough to understand what is / is not needed from a CubeMX generated project.

10 Dec 2019

in many cases, the Mbed classes Timer or Ticker can be used. These work fine in ms range, are available on all targets and do not need additional hardware resources. For better performance in the µs range the hardware timer maybe a neccessary choice. I have written a HWTimer class for my needs, a Ticker for short cycles and a one pulse mode (for driving a stepper with several 10 kHz. This can be extended / modified also for the capture mode: https://github.com/JojoS62/Test-lvgl/blob/master/libs/util/HWTimer.h https://github.com/JojoS62/Test-lvgl/blob/master/libs/util/TARGET_STM32F4/HWTimer.cpp This class uses a CThunk, which is necessary to call non-static member functions of a class.

This is a sample for using the timer, generating stepper pulses with ramp function:

class myStepper {
public:
    DigitalOut motorStep(PE_5);      // stepping pulse
    DigitalOut motorDirection(PE_2); // stepping direction

    float periodStart;
    float periodSet;
    float tm;
    float deltaV;
    float t;
    HWTimer hwTimer;

    myStepper() :
         hwTimer(6-1, 2000, callback(this, &myStepper::fnLedToggle), true)
    {
        periodStart = 800.0f;
        periodSet = 300.0f;
        tm = 1000000;
        deltaV = -0.1;
        t = 0;
    }

    void start() 
    {
        hwTimer.start();
    }

    void fnLedToggle()
    {
        motorStep = 1;
        timeNow = us_ticker_read();     // read system timestamp
        interruptCounter++;             // count any TIM interrupts

        float deltaPeriod = (periodSet - periodStart);
        float deltaT = (t / tm);
        float period = periodStart + deltaPeriod * deltaT;

        if (deltaT < 1.0f) {
            t += period;
        } else {
            t = 0;
            float temp = periodStart;
            periodStart = periodSet;
            periodSet = temp;
            if(periodStart > 300.0f)
                motorDirection = !motorDirection;
        }

        hwTimer.start(period);

        for (int i=0; i<30; i++) {
            interruptCounter++;             // count any TIM interrupts
        }
        motorStep = 0;
    }
};