These developer notes assume almost no knowledge. The goal is to learn everything required to write a bare metal operating system for the STM32 by playing and experimentation, diving deeper each iteration and trying to accomplish progressively more difficult tasks.
Ripping, restoring, and decompiling the factory operating system.
In this tutorial we’re going to review the memory layout of the STM32F103.
We’ll use this information and our tools to copy both the factory bootloader
and factory operating system to our development computer. We’ll finish
by opening both the bootloader and operating system in Ghidra, and
writing some early observations about how they work.
Development techniques.
We will cover three techniques to reverse engineer a running chip using GDB.
First, we will learn how to examine the state of all the incoming and outgoing wires in the chip (GPIO).
Then, we will practice selectively turning off device peripherals to see what happens.
Finally, we will introduce function prototyping. Using GDB, we can reset the device and establish the minimal routine that is necessary
to accomplish some objective, for example, make a pad change a color, or write a character to the LCD screen.
As the STM32F103 has a memory mapped peripheral, most operating system stuff boils down to reading from an address, writing to that address,
and looping. We can prototype in GDB first, and then translate the minimal routine almost line for line to C and build on it.
Learn About the Display
The LCD Display on the MPK249 is a Winstar model number
WH2004a.
Write a USB driver the hard way
USB is a really fast protocol. Unlike driving the LCD, USB messages transact too fast for us to be able to send or receive
specific messages in real time using GDB and stepping through the results. In this tutorial, we’re going to
write a USB driver the slow way. We’re going to learn about every single register that makes USB work, as well
as deep-dive into the protocol itself. We’ll master one transaction at a time, from device enumeration to sending midi back and forth
to the computer.
A note: this technique is excellent for learning, but creates barely-legible spaghetti code if you take it too seriously.
A USB driver is one of the areas where abstraction is really important. Once you learn the basics, you should do
your best to abstract away from them in your code so a reader can understand what is going on.
Sending notes to the computer
In this Tutorial, we’re going to understand how to send key press events to the connected computer.
Detect Pad, Fader and Knob Movements via the Analog to Digital Converter (ADC) and DMA
We’re going to dive in to both the DMA and the ADC in this tutorial.
Colors!
In this tutorial we’re going to figure out how to light up the pads and buttons, as well as read their current values, via the SPI.
Our research into the pad colors yields a meaningful improvement over the factory operating system: we can use timing
and animation to cycle the red, green, and blue LED cycles and achieve 1000s of distinct colors, where the factory default
only permits 17.
Compile-time testing in C-ish
Open Klave is written in C, but its target architecture is a piano, not the development computer. It can be difficult
creating data structures and abstractions for a different chip. You don’t know stuff works until you try to run it.
In this tutorial we review C-ish, which is my term for writing in C, except we also permit the constexpr keyword from C++17 on,
and we use a C++ compiler. By writing in C-ish, we can dramatically reduce the complexity of our operating system
by mostly sticking to C conventions. But the constexpr keyword will permit some compiler testing and sanity-checking
before anything is transmitted to the device.
We will finish by examining Open Klave’s “Global Event Stack,” and write some compile time tests. And we’ll
consider some caveat and limitations to this approach.