Why?
I've always had an itch for learning electronics and building stuff. That's why my office is currently littered with all kinds of boxes with all kinds of electronic components, Arduinos, Raspberry PI Pico's, ESP32's, books about electronics, books about physics etc. The thing is, I never had the time to dive deeper into this subject more than playing a bit with the Arduino using that IDE that it comes with and spending a few days trying to compile a small firmware for the Pico to make it send some data over Wifi using PlatformIO. Safe to say, the Arduino experience was underwhelming while the PlatformIO one was overwhelming.
Coming from a programming background, I wanted to do more than using the n abstraction layers that the Arduino puts on top of the board to make it accessible at any level of experience (which is an amazing idea and a great way to lower the barrier of entry into the field). At the same time, I had 0 understanding about how microcontrollers worked and not much C experience besides my highschool programming classes, so when I was trying to do more than the Arduino, I was pretty much following tutorials and guides blindly.
Recently, I've quit my job in order to start a company with some friends, and with this came a much healthier work environment, flexibility with my schedule and my passion around building stuff, seems to have been reignited. This has led me to have some nights where, after putting the kids to sleep, I was searching for something to do. Previously, I would have worked on the business idea, but that has become my actual job, so I decided to try to make an LED blink using just C. The fact that I chose to do it using the micro:bit from all of the options available in my electronics boxes was either laziness or the unconscious desire to just focus on the the task at hand without having to deal with wiring LEDs.
.c
Compiling The first thing that I did was to search for a tutorial on bare-metal programming and I landed on Blinky To Bootloader: Bare Metal Programming Series. After watching the first video in full, a few things became obvious to me, in this order:
- they use VSCode so I need to figure out how to set up the C LSP in Neovim (I use vim btw)
- apparently I can use a tool called
bear
to generate the files that makeclangd
work nicely bear
needs to compile the code in order to figure out what those file should contain- even though the tutorial uses a different microcontroller, theirs is also an ARM Cortex-M4
- I can copy their Makefile and modify it to suit my needs
- I don't know how
make
and Makefiles work, so I can't modify what they've done - maybe I can find a Makefile for the micro:bit V2
- I can't find any Makefile for compiling bare-metal C for the micro:bit V2, or I found one but I don't know enough to know if it's good
- before learning about bare-metal programming I need to learn how C compilers work and how make works
So, with my goal (of making an LED blink) in mind, I've set out to gather the missing skills.
NOTE
I can't emphasise enough how useful a tool like Claude and ChatGPT proved while trying to learn. I've used them as personal mentors that are able to answer your every question. My goal was for the AI to facilitate my learning not to provide me with solutions even though I think it's pretty good at that as well.
make
I could have gone and read make
's friendly manual but that's quite long and I wanted something more interactive so I found this Makefile tutor which felt like it can provide enough information that I can build something with. I've also read a bit from this page and asked Claude to clarify some things.
This was enough for me to write a Makefile that I could use bear
on. With my new found knowledge, I took another look at the Makefile from the bare-metal tutorial, the one that I've tried to copy and modify earlier and a few things became apparent:
- it's not using
clang
to compile the code, it's usingarm-none-eabi-gcc
- there is a linker script in the repo, and it is used in compiling the project
Regarding the compiler, I knew from my PlatformIO days that arm-non-eabi-gcc
must be a cross-compiler which made sense since I was trying to compile for a different platform. The problem was that when I switched my project to use it as well, all of a sudden bear
was not generating a compile_commands.json
file anymore and my LSP was acting all kinds of crazy. Multiple attempts trying to make bear
recognise the cross-compiler have ended up in failure. The man
pages were not very helpful either so after battling with it for a while I just gave up and switched to compiledb
which did the same thing and it just worked.
My next problem was the linker script. I knew what it did but I didn't know how. I also found out in the meantime that I might need a startup file. So all my reading and researching led me to this amazing tutorial which is part of a bigger course on Udemy but the playlist on YouTube had everything I needed. The first 5 videos thaught me what a startup script is and how to write my own, what interrupts are, what an interrupt vector table is, what a linker script is and how to write my own, how to look for information in a datasheet and many more useful things.
TIP
Kiran is an amazing teacher btw; I now remember that I've watched his Embedded C Programming course 2 jobs ago where they provided us with Udemy access and that prompted me to buy these two courses for when inevitably I would want to pursue this hobby again.
Armed with a better understanding of what I'm trying to achieve and how to do it, a proper Makefile
, a startup script and a linker script I've managed to blink the LED on the micro:bit without using libopencm3
like the original tutorial was doing! It was the middle of the night and I was so excited that I almost woke up everyone. I can't remember the last time I felt like this but I can remember when the first time was: when I first rolled up my own authentication using PHP and MySQL after reading this book back in 2007-2008 when I was just starting my journey in programming.
Here's the code:
In the meantime, I've managed to make the LED blink using the SysTick
timer, again without using libopencm3
. I've also found out that libopencm3
comes with both a startup script and a linker script (you just need to define the memory part by yourself) but I'm honestly way happier that I didn't know this and ended up learning how to do it all by myself. Even if it took a few nights of learning and tinkering.
The code was cleaned up a bit and uploaded to a GitHub repo in case anyone comes across this article while trying to figure out how to do what I've done and they don't want to go through the entire journey or if they want to skip certain steps. It includes a Makefile that can compile the project with or without libopencm3
in case you want to use it.