ST HAL Support in Miosix
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 installing the needed programs
- 2. Include "miosix.h" in the main file of the code (remember that the file main file must be named "main.cpp");
- 3. Remove the clock configuration
- Miosix already handles the clock configuration and keeping it in the code would result in a lock of the board.
- 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();
}
}
- 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. Include also the file wrapper.cpp that handles the function HAL_Delay() for Miosix, since Miosix handles the clock
- 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".
Interrupt
Miosix handles interrupts differently than ST and thus the code needs to be adapted.
In Miosix interrupts need to be registered in order to be called. This is done by creating a lock and registering the IRQ like this 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 lock is made using a RAII style, so the lock exist as long the variable exists and the lock, thus encapsulating in brackets will deallocate it automatically when no longer needed.