ST HAL Support in Miosix

From Miosix Wiki
Jump to navigation Jump to search

If you are developing or have existing code that uses the HAL from ST Microelectronics, you can port it fairly easily into Miosix by following this guide:

Note: The content of this page was tested on Miosix v3.

Note: Miosix uses C++ instead of C

1. Follow the Quick start guide to configure the kernel and install all the required tools
2. Include "miosix.h" in your program's main file (remember that the file main file must be named "main.cpp") and wherever else you need to use Miosix's APIs;
3. Remove the clock configuration
Miosix already handles the clock configuration and keeping it in the code would result in a lockup of the microcontroller.
Usually the clock configuration is generated by STM32Cube and it looks like this:
static void SystemClock_Config(void)
{
 RCC_ClkInitTypeDef RCC_ClkInitStruct;
 RCC_OscInitTypeDef RCC_OscInitStruct;
 
 /* Enable Power Control clock */
 __HAL_RCC_PWR_CLK_ENABLE();
 
 /* The voltage scaling allows optimizing the power consumption when the device is 
    clocked below the maximum system frequency, to update the voltage scaling value 
    regarding system frequency refer to product datasheet.  */
 __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE3);
 
 /* Enable MSI Oscillator */
 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;
 RCC_OscInitStruct.MSIState = RCC_MSI_ON;
 RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_5;
 RCC_OscInitStruct.MSICalibrationValue=0x00;
 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
 if(HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
 {
   /* Initialization Error */
   Error_Handler();
 }
}

For some peripherals you might need to adjust peripheral-level clock configuration registers.

If your code contains interrupts refer to the section below.
Here an example code of the main.cpp for a blinking led in a Nucleo-L053R8 board:
#include "miosix.h"
#include "stm32l053xx.h"
#include "stm32l0xx_hal.h"
#include <interfaces/bsp.h>
using namespace std;
using namespace miosix;

static void MX_GPIO_Init(void);

int main() {
  iprintf("Hello world, write your application here\n");
  HAL_Init();

  MX_GPIO_Init();
  for (;;) {
    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
    HAL_Delay(500);
  }
}
static void MX_GPIO_Init(void) {
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

  GPIO_InitTypeDef GPIO_InitStruct = {0};

  // Configure Led pin
  GPIO_InitStruct.Pin = GPIO_PIN_5;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;

  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
4. Include in the Makefile all the libraries and all the file .c needed to compile the program

Remember to include the HAL libraries and .c files used

5. add a CFLAGS and CXXFLAGS that defines the type of board that you are using in the Makefile
(ex: CFLAGS += -DSTM32L053xx and CXXFLAGS += -DSTM32L053xx for a stm32l053-R8).
If you want, you can instead uncomment the type of the board in the Miosix folder (ex: for an stm32l053-R8, you need to uncomment "#define STM32L053xx" in "miosix/arch/common/CMSIS/Device/ST/STM32L0xx/Include/stm32l0xx.h")
5b. You need to do that also for any Shield that you are using.
6. Create an implementation of the function HAL_Delay() by using the Miosix primitive Thread
:Sleep(). You can put the implementation in a separate file (called for example wrapper.cpp) or wherever else you like.
Here the part that you need to modify in the example of the Makefile for the previous example:
#Prevoius code
SRC :=main.cpp \
    STM32CubeL0/Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal.c \
    STM32CubeL0/Drivers/BSP/STM32L0xx_Nucleo/stm32l0xx_nucleo.c \
	STM32CubeL0/Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_gpio.h \
	STM32CubeL0/Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_cortex.c \
	wrapper.cpp

INCLUDE_DIRS := -ISTM32CubeL0/Drivers/STM32L0xx_HAL_Driver/Inc \
	-ISTM32CubeL0/Drivers/BSP/STM32L0xx_Nucleo \
	-Imiosix/arch/common/CMSIS/Device/ST/STM32L0xx/Include/ \
	-ISTM32CubeL0/Projects/NUCLEO-L053R8/Examples/GPIO/GPIO_IOToggle/Inc

#Other code

CFLAGS += -DSTM32L053xx

CXXFLAGS += -DSTM32L053xx

#Other code
7. Compile and flash the program. This is done by using the command "make" and "make program".

Interrupts

ST's HAL expects interrupt handler functions to be defined statically with weak symbols, allowing to define an interrupt handler for a given IRQ number by simply defining a function with the correct name. This does not work in Miosix and thus the code needs to be adapted.

In Miosix interrupt handlers need to be registered at runtime in order to be called. This is done by creating a lock and registering the IRQ like in the following example:

{
  GlobalIrqLock lock; //disable interrupts on the core that acquired the lock (all in single core)
  IRQregisterIrq(lock, SPI2_IRQn, &SPIx_IRQHandler); //setup for SPI2, SPIx_IRQHandler is the handler called when the SPI finished sending a message
}

This needs to be done for each interrupt that you have to register.

Keep in mind that the GlobalIrqLock object is a RAII style acquisition primitive. In other words the lock is held as long the "lock" variable exists, thus encapsulating its declaration in a block (curly brackets) will release the lock automatically when the block ends.