Many have a hate relationship with ST's HAL. I have a like/hate one and was wondering if my approach will bite me later on.

I like the peripheral and clock setup of current CubeMx. The code generated is so much more readable than my own bare metal stuff and it is complete all the time. And I believe it will help me with porting between different models. Example of code I like:

  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLN = 8;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
//    Error_Handler();

and for instance:

static void MX_LPUART1_UART_Init(void)
  hlpuart1.Instance = LPUART1;
  hlpuart1.Init.BaudRate = 209700;
  hlpuart1.Init.WordLength = UART_WORDLENGTH_8B;
  hlpuart1.Init.StopBits = UART_STOPBITS_1;
  hlpuart1.Init.Parity = UART_PARITY_NONE;
  hlpuart1.Init.Mode = UART_MODE_TX_RX;
  hlpuart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  hlpuart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  hlpuart1.Init.ClockPrescaler = UART_PRESCALER_DIV1;
  hlpuart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;

It really works all the time. And it is only executed at startup and even then, the compiler makes this quite efficient (does it?).

But what I really hate is HAL overhead during running an application. E.g. getting a single byte from a UART after its interrupt using HAL. I rather do that directly.

My question: what would be wrong with just using CubeMx/HAL for the configuration part such that that remains so readable as it is in the examples above and not using it any further at all?

I figured that if you delete HAL_Init() and comment out the error handlers for the clock and peripherals setup that are triggered by all kind of checks of HAL's systicks, things work pretty well. But question is: will it byte me at the end?

  • \$\begingroup\$ I don't use either HAL or CubeMX \$\endgroup\$ Commented Aug 11, 2021 at 13:47
  • 1
    \$\begingroup\$ I hate reading HAL because it makes me have to relearn what is already in the reference manual. \$\endgroup\$
    – DKNguyen
    Commented Aug 11, 2021 at 14:00

2 Answers 2


You usually get the bad with the good. In a recent project I had to read pins statuses even though they were set as outputs. This is where the bare metal stuff comes in handy.

Another aspect is speed and handling interrupts. Handling multiple external interrupts that may be spaced very closely apart is best done with bare metal purely because it’s fast.

I believe any function that is not time critical can be implemented using HAL and for everything else there is bare metal.

I don’t know much about this, but I think if you are building software for industries such as medical, aerospace etc HAL would not pass qualification.

  • \$\begingroup\$ Exactly the point: no HAL handling interrupts. But for setup, quite nice \$\endgroup\$ Commented Aug 11, 2021 at 17:03

Yes you can mix HAL code with whatever custom code you like. The initialization of clocks and peripheral by using the mouse is quite nice and easy so in practice you have a prototype working within minutes instead of spending hours of reading the reference manual. And it still provides an example how to use the peripheral if you decide to write a driver yourself (e.g receving UART bytes into a ring buffer or state machine in an interruopt, so you don't need to specify how many bytes you want to receive if it is unknown beforehand).

So HAL is great until you find out it does not initialize everything correctly and then some peripheral does not work because it has a non-existent clock source (which has happened).

And the HAL is nice as long as it provides an useful interface to the peripheral, but when it does not then you have to use the peripheral yourself (which has happened).

A common HAL for a peripheral is nice and portable, until you ran into a MCU that has a different peripheral version, so it also has a different HAL how to use it (which has happened).

HAL is also nice when it simply works and you don't need to care about it, but it can also be buggy and cause problems, like maybe it works perfectly on bare metal, but as soon as it is run under the provided FreeRTOS it may fail to work sometimes because it does not tolerate being interrupted and the more complex program you make the HAL starts to jam the communication more and more. And then you have to read the HAL code only to find out the HAL does things in a way that are not in line how it should be done according to reference manual. Updating the HAL code to newer version may also fix some issues, may not fix some issues that you still need to fix yourself, and it may introduce new issues, or the updated code makes it work differently than before so it still needs code modifications in your code (all these have happened).

So yes, HAL is neat and helps and you can mix your own code in easily. But like I mentioned above, it may not make code portable between devices in all cases.

You can have your own API which then calls HAL code, so then if it needs changes they will be in one place, and it enables you to replace the HAL with the LL functions or simply register accesses to peripherals.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.