CMSIS vs HAL vs Standard Peripherals Library

Definitely the CMSIS. It is not exactly a library, it mostly contains definitions for the various registers.

It is exactly what one needs to access the microcontroller's registers easy, so as to implement his/her own HAL. It has no overhead, since you just access the registers.

Keep in mind that CMSIS, unlike the other two, is defined by ARM and not ST. This means that the various CMSIS libraries out there for the various microcontrollers are quite similar, which greatly aids in portability.

Furthermore, CMSIS is the simpler one so it is (IMO) the most versatile, and most reliable, with possibly fewer (or no) bugs. Some hal libraries for the various mcu's that I've used are quite infamous for their bugs.

On the other hand, CMSIS needs quite more work from you. It is however my personal choice, since I prefer to invest my time creating quality libraries, that suit my needs, and understanding how the chip works, than just spending time to learn another, new library.


To learn how it works you want to use none of the above. Get an arm cross compiler and the documentation from st, done. Start coding. these chips are generally really easy to program. the documentation tells you what bits in what registers do what.

Any/all of these libraries are intended to remove that understanding/burden/work from you and make it feel like a just call an api like application programming experience. Which is what a lot of folks want. You can use all of the source for these libraries to help understand, but as you get better at it you find holes and problems in the libraries, sometimes very scary code. code tossed together, written generically and roughly ported from one chip to another, perhaps supporting features your chip doesnt have, etc. And they all have an excessive amount of overhead. 10 to 100 times too much code for the task, sure a lot of it may optimize away but why have it there in the first place?

Whether you go your own or use one of these libraries, you should still look at the source for the libraries you use to see if you are comfortable with what they are doing, if it makes sense, matches the chip documentation, etc. When something goes wrong you are likely having to dig through their stuff as much as yours to find out why.

Note the chip docs are not perfect either, that is part of the fun.

I dont understand why assembly comes up in a discussion about bare metal programming. You can get by with very little assembly. For these cortex-m chips, you technically only need this much asm to get booted:

.globl _start
_start:
.word 0x20001000
.word main

You cant rely on data nor bss and you cant return from main with that minimal of asm. But that is all the asm you NEED for the barest of bare metal. Now if you want to do interrupts you need more entries in the vector table. more .word lines. I recommend more asm, but maybe 10 or 20 lines more.

this is typically all the asm I use.

.cpu cortex-m0
.thumb
.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.thumb_func
reset:
    bl notmain
    b hang
.thumb_func
hang:   b .
.align
.thumb_func
.globl PUT16
PUT16:
    strh r1,[r0]
    bx lr
.thumb_func
.globl PUT32
PUT32:
    str r1,[r0]
    bx lr
.thumb_func
.globl GET32
GET32:
    ldr r0,[r0]
    bx lr
.thumb_func
.globl GET16
GET16:
    ldrh r0,[r0]
    bx lr
.thumb_func
.globl dummy
dummy:
    bx lr
.end

Yeah it says cortex-m0 but this is actual bootstrap for my m4 code. I prefer this to be thumb not thumb2. And I just reuse this code from one cortex-m to another, changing the stack pointer address as needed, so it works for m0, m3 and m4. I dont have an m7 yet nor have I researched it much.

Enabling the fpu might require a few more lines of asm as specific instructions are needed. But the point is dont confuse low level programming and asm. C has what you need to configure the chip, as well as write an application. The libraries you are talking about are written in C not asm, so obviously they dont need to use asm either.

If you want to learn the inner workings, write your own code. Dont use these libraries other than as a reference. Sometimes it is easier to just hack at it than try to read through their code. (not just ST but all of the vendors. One of the vendors had a line of code so alarming I use it as a interview question, nope not going to post it here).

ST definitely but other vendors as well, to save power, have clock enables for sections of the chip, so before you go in and try to blink an led, you need to find the enable bit for that gpio block and see if it comes out of reset enabled, if not then enable it, talking to that gpio logic without a clock enabling it simply hangs the processor as it is waiting for a response from logic that never will respond. They dont always tell you about these enables. Once enabled then they sometimes walk you through the init for some particular peripheral. ST docs are pretty good. Coming from microchip which gets a pretty bad grade for documentation, you shouldnt have a problem.


Until now I have used CMSIS definitions and enjoyed using registers directly. Meanwhile, I used HAL libraries in few projects. It had a considerable influence on code running time so I quit it. Although CMSIS serves my interest, these days I am going to be a fan of libopencm3. It is like LL libraries provided by ST. However, it covers more micro-controllers even in ST families:

The libopencm3 project (previously known as libopenstm32) aims to create a free/libre/open-source firmware library for various ARM Cortex-M3 microcontrollers, including ST STM32, Toshiba TX03, Atmel SAM3U, NXP LPC1000 and others.

please note that:

Despite the name, libopencm3 also supports other ARM Cortex "related" microcontrollers such as Cortex-M0 or Cortex-M4/Cortex-M4F ones, for example.

you can find the list of supported micro-controllers here.