Skip to main content
Associate
July 18, 2026
Question

Need some help on "bare-metal" initialization for STM32 MCU

  • July 18, 2026
  • 42 replies
  • 629 views

Embedded veteran here, been involved in all manner of MCUs and MPUs professionally (especially safety-critical) but for personal use most of my prior experience has been with PIC16F and 18F (to save money obviously), of course those are only 8-bitters. I’m very interested in learning STM32 MCUs (gee they look ALMOST like DSPs except it’s single-cycle multiply not MAC and no simultaneous pointer adjustments etc.). Thing is my PIC experience has given me a pretty decent collection of chip initialization routines that I can adapt as necessary for small differences with peripheral features and such. My “inherent bias” is bare metal, no FreeRTOS, I understand I may be “going against the grain” a little but I want to get my hands on every nanosecond I may have coming. I don’t necessarily need this in C language, but I would like to get my hands on at least a very detailed list of all the HW registers that COULD need to be set, and the EXACT order in which this is “most safely accomplished”. (Now I don’t really feel it’s unreasonable to ask this, since the reference manuals for the PIC parts that it took me months to understand tended to run about 500 pages in length, whereas the STM32 parts are running about TWICE that.) For right now I would be quite happy with an answer that is correct up to about say STM32F03xxxxx (from what I understand I might be interested in using so far). I guess if I weren’t such a newbie I would know where this might reside on GitHub or something, but hopefully there is a file someone can offer to me they have used successfully on a project somewhere. Thank you for your attention!

42 replies

mƎALLEm
ST Technical Moderator
July 18, 2026

Hello ​@jlawton11 and welcome to the ST community,

That depends on which level of “beremetal” you are meaning here:

  • No RTOS but using C APIs
  • Direct access to the registers

For the first case, you can use directly the HAL (the hardware abstraction layer) provided by ST, available here on Github (for STM32F0 devices): https://github.com/STMicroelectronics/STM32CubeF0. Some examples are available under the same package for some specific boards in https://github.com/STMicroelectronics/STM32CubeF0/tree/master/Projects. These examples can be tailored to any other board. You can also do the initialization and code generation using CubeMx tool available here for download: https://www.st.com/en/development-tools/stm32cubemx.html. Then use STM32CubeIDE to compile and debug your application available here for download: https://www.st.com/en/development-tools/stm32cubeide.html

Please refer to this tutorial: STM32CubeIDE 2.0.0 workflow tutorial

For the second case, you need definetly read the reference manual, obviously not all the RM document but the sections related to the peripherals you plan to use, basically RCC, Flash, GPIO to start a very basic project. You need also to read the datasheet for some specific electrical parameters.

To accelerate things, you can inspire from the HAL implementation and how the registers are accessed in their correct order.

PS: don’t start with STM32F7/STM32H7 MCUs. Start with platforms with simple architectures, at least to be familiar with STM32 and the ecosystem. You can start with STM32F0, STM32F1 or STM32F4.

Hope that helps.

To give better visibility on the answered topics, please click "Best answer" on the reply which solved your issue or answered your question.
waclawek.jan
Super User
July 20, 2026

> ALMOST like DSPs

They can’t be further.

JW

unsigned_char_array
Lead III
July 20, 2026

Couldn’t have said it better. I often consider the bigger 32-bit ARMs to be closer to an MPU than an MCU, since you often use them with external memories. But SoC is a better description. The peripherals no longer feel connected. They are practically external, but happen to be on the same die. The processor runs at a high clock rate, but has poor latency.

I often miss simple 8-bit MCUs when working with 32-bit ARMs. A week ago we had an issue where SPI started transferring before the chip select line switched to low. The cause was a race and even though we set the GPIO pin low first before we started the transfer, the SPI peripheral received its command before the pin switched. We had to insert a DSB() to make sure the pin had actually switched.

 

"Kudo posts if you have the same problem and kudo replies if the solution works.Click ""Accept as Solution"" if a reply solved your problem. If no solution was posted please answer with your own."
AScha.3
Super User
July 20, 2026

Hi,

Let me tell about me first: I came from the other side, AVR 8bit, also in assembly programmed.

Naturally, as most effective and not really complex to learn.

Then began with the arm CPU, with the first learning set available, the Primer, with stm32f103 .

.…

Now latest hobby project: audio player with stm32h743zi CPU, plays wav, flac, MP3, web radio, from SD-card or USB stick, to 3 dacs, with active filters for multi way speakers.

Now… The Times They Are A-Changing …

 

I just can tell you my experience and recommendations:

"Datasheet" for stm32h743 is about 4000 pages, to add arm core manual and programming manual. And then the funny arm assembly, with it's delicious optimization possibilities. Forget to learn this in reasonable time and do it at the level of GCC with optimizer level -O2 .

So take to learn in short time a cheap nucleo board, with st-link debug probe on board.

Then use the older IDE version, Cube IDE V 1.19. , learn how to use it, how to setup a pinout in Cube, generate code, compile and start debug.

This for sure is the best and fastest way to get it running.

Then try a small program, optimizer on -O2 or -Ofast, and then comes the time to think about optimizing something by hand, just using the Hal library for the initialization and standard init, like USB host etc, , then using registers for doing something at highest possible speed.

And use our new electric friends, to ask and get optimized C code. They do it in seconds.

Or ms.

A nice older CPU is the f303, fast ADC; so maybe NUCLEO-F303RE is a good starter for 20$.

 

If you feel a post has answered your question, please click on " Best Answer ".
CTapp.1
Senior III
July 21, 2026

That’s the way I go when I want performance - I let the ST tools generate the code to get the system running (clock tree, stack, ...) and initialise the peripherals, and then take over when the while(1) in main is hit.

You still need to look at the documentation for the peripherals you’ll be using.

All posts are made in a personal capacity. MISRA C++ Chair, MISRA C WG Member, Director The MISRA Consortium Limited (TMCL)
David Littell
Senior II
July 20, 2026

“… I want to get my hands on every nanosecond I may have coming.”  Some famous old guy once said, “Premature optimization is the root of much evil.”  He was right, of course.  Most old guys are.  Another old guy said, “First, make it work.  Then make it fast (but only if you really, really have to).”

TDK
July 20, 2026

> I would like to get my hands on at least a very detailed list of all the HW registers that COULD need to be set, and the EXACT order in which this is “most safely accomplished”

The reference manual is the proper resource here. It lists all of the peripherals, their registers, and how they operate. It also lists the steps needed to do common things, like sending something out over SPI.

ST has explicitly decided not to publish register-level examples for peripherals. You can find some examples floating around, but they aren’t from ST and are of varying quality. Their answer for this is the LL library, which I do not recommend.

"If you feel a post has answered your question, please click ""Accept as Solution""."
David Littell
Senior II
July 21, 2026

I’m curious about your objections to LL.  I haven’t used LL but it seemed like a good middle ground between the HAL abstraction model and a ​@waclawek.jan bare-metal ​Jedi Master approach.

TDK
July 21, 2026

LL adds an unnecessary abstraction layer between the hardware registers and the library. It requires you to not only know what the registers do but also the correct LL call that translates to the register level instruction.

HAL is a good abstracted interface. Direct register access is also good.

 

As a random example, consider:

__STATIC_INLINE void LL_TIM_EnableAllOutputs(TIM_TypeDef *TIMx)
{
SET_BIT(TIMx->BDTR, TIM_BDTR_MOE);
}

Is this really helping the user? The functions claims it enables all outputs, but does it really? Only if they’re already configured, and it is only meant for advanced timers.

You have to dig into the details to know what it’s doing at the lower level whereas “SET_BIT(TIMx->BDTR, TIM_BDTR_MOE)” is immediately obvious and requires no knowledge of LL.

 

The entire LL library is mostly a collection of one-line abstractions like this.

"If you feel a post has answered your question, please click ""Accept as Solution""."
Pavel A.
July 20, 2026

Hello jlawton11,

Let me join the welcoming chorus and wish you to have as much fun with STM32 as we do ))

> hopefully there is a file someone can offer to me they have used successfully on a project somewhere.

STM32s are very popular so there are lots of various videos, course materials, books and example projects. The problem is exactly in the amount and variety. It’s overflowing. The genuine ST documentation is the most reliable source, it takes some time to get used to. For every STM32 chip usually there are 4 main documents: the data sheet (DS), Reference manual (RM), Programming manual (PM) - this one describes the common ARM processor of the STM32 (registers, instruction set, interrupt controller... beginners often miss this). And the Errata.

Fortunately the AI assistant can read all those PDFs for you and explain stuff. Please try it.

 

The only thing you have to tell the AI assistant upfront is the STM32 family. If you want DSP-like functionality and high speed, maybe don’t be shy and start with a powerful STM32H7 or F7 rather than F0 ?

Read the entire collection of tips from ​@waclawek.jan - each one is brilliant.

 

LCE
Principal II
July 21, 2026

Also coming from PIC, AVR, and FPGAs…

To make the recommendation short for a start with STM32:

  • get a Nucleo board for a relatively “small” STM32
    • Nucleo boards have the flashing and debug adapter on-board
  • get CubeMX as a starting point, otherwise it will take very long to get anywhere
    • CubeMX lets you select and set clocks and peripherals, then generates some basic setup source code
    • using either “LL” (low level) or “HAL” libraries from ST
    • then go through these examples to get an idea how things are set up
    • then throw out the (partly bloated, because it is written for all kinds of uses) ST stuff and do some direct register settings
CTapp.1
Senior III
July 21, 2026

I sometimes use a mix of LL and HAL - mainly when I want to get CAN running (which is HAL only) and I’m not working on critical code (when I take the HAL code and simplify it so it is easier to test).

All posts are made in a personal capacity. MISRA C++ Chair, MISRA C WG Member, Director The MISRA Consortium Limited (TMCL)
LCE
Principal II
July 22, 2026

I’m also using HAL for clock configuration and some minor stuff or for testing.

And LL (clock config) for some L0s with small flash size.

Associate II
July 21, 2026

To ​@jlawton11 

Hi, if you want to learn bare metal programming, start with STM32F103 Bluepill board. This will help you get on board with STM32 family. Moreover there are bare metal tutorials for this specific mcu like gpio toggling, ADC, PWM, timer, IIC, USART, SPI, DMA, multi channel ADC, etc. Start with STM32F103 Bluepill and crawl your way upto your target mcu. Always refer to reference manual (RM0008) during programming.

 

Here is the tutorial link I refer for programming.

Pavel A.
July 21, 2026

STM32F1 isn’t sincerely recommended anymore, even less so Bluepill boards. Except you have specific requirement for that.

Also, your videos use the Keil uVision IDE, which the OP likely does not have.

 

Associate II
July 22, 2026

Keil IDE (community edition) if free to use which I am using as of now. Also, platform.io is available for use for same bare-metal programming. I think platform.io is better then Keil MDK-ARM IDE as it is simple plug and play type. Nucleo F103RB is recommendable which is better than Bluepill. Besides I agree with the fact that STM32F1 Bluepill is not sincere for using but for studying it is fine.

waclawek.jan
Super User
July 24, 2026

> my question to OP was, do these MCUs (F0, F1) fit his goal at all, or he should be looking at F7 or H7?

Let’s split this into 3, based on processor core:

1. Cortex-M0 (‘F0) or M0+ (‘L0, ‘G0, ‘U0)

2. Cortex-M3 (‘F1, ‘L1, ‘F2) or Cortex-M4 (‘F3, ‘F4, ‘L4) (to some extent the newest families with Cortex-M33 belong here, but they bring in additional complexity which unless their specific features desperately needed can be an obstacle)

3. Cortex-M7 (‘F7, ‘H7)

I would strongly recommend not to use the ‘F1, not because of the core, but because of the early GPIO. Workable, but pain in the back. The ‘F2 is an outlier and ‘L1 is hard to justify against ‘L4.

OP may want to start with the ‘F0 as he suggested - its complexity is perhaps the lowest so the manual is the shortest, and he may learn a lot with relative ease. Transitioning into group 2 is not THAT painful either. As Pavel said, transitioning to group 3 is a big leap, but rewarded by brutal number crunching performance.

That performance strongly increases 1->3 (and, mostly for marketing reasons, the raw max clock does too), but so does complexity of core and latency/jitter-introducing features. At the end of the day, the order-of-magnitude increase of raw computing power between 1 and 3 is accompanied by roughly similar order-of-magnitude increase in uncertainty of execution time. And going from 1 to 3 it’s increasingly harder to argue about a particular number for the worst-case latency/jitter, both for complexity shooting through the roof beyond reasonable calculability, and for simple lack of relevant information.

So the real question is, what’s the real hard requirement for latency/jitter. Because “hard real time” can mean many things. I recollect a Microsoft whitepaper article, where they argued about using Windows-NT in real time applications, which indeed brought up a relevant point: if your real-timeness requirements are in say hundreds of milliseconds, a preemptive multitasking environment (which W-NT was) with well-behaving hardware (which the PC of that era was) may be able to deliver that purely through its raw computing power, timer-based interrupts, and a well-designed priority system. And in its basic functionality, W-NT was in no way different from what we call RTOS today.

In the 32-bitters, the basic guideline is to solve all hard real-time requirements in hardware. Even interrupts mean one or two orders of magnitude worse latency/jitter (or three, if you stick to “libraries” like Cube/HAL). One is not supposed to cycle-count and bit-bang. One may attempt to benchmark, but that’s far from being trivial either, and requires deep understanding of what the system consists of.

> Then all old examples and youtubes become distracting noise.

Maybe.

JW

PS. Maybe relevant points also in this recent thread.