So I'm switching from PICs to ARM and I bought an STM32F4 discovery board. So far I understand that to program it you can either access all registers directly in memory (obvious way) and also there are 3 main libraries you can use to make your life easier. Now my question is, which one of those 3 (CMSIS, HAL, Std Peripherals Lib) is the most LOW level one? ie. the one with the less overhead. My goal is to learn the controller's inner workings and not make my life easier (only a little), so I would like to know which of these is closer to the core without resorting to use assembly.
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, that just spending time to learn just a 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.
I have used both, just bare metal register access and the std peripheral library. I find it easier just to deal with the registers. Also if you are using a debugger you can view the registers and confirm they contain what you programmed them to be. I think you do learn more about the operation of the chip in that way also.
Comming from the 8bit world I was always used to programming peripherals via registers. Microcontroller data sheets (i.e STM32 reference manuals) describe the peripherals exclusively in register notation. Since the programmer must read this very same documentation to know about the peripheral funtions and features before any attempt is made to use it then it seems natural to me to start programming the registers. With carful code layout and commenting I find the code can be pleasantly to read and modify even after coming back to it months later.