2024-11-08


Messing with Microcontrollers (STM32) — Making an LED Blink the Hard Way

Motivation

My adventures in embedded systems started about a year ago, with Embedded Systems Bare Metal Programming, a paid course taught by Israel Gbati. This course is highly-regarded, comprehensive, and teaches the student to write bare metal firmware without the use of libraries and header files. Throughout the course, the instructor stresses the importance of referencing and understanding the chip manual and board documentation, along with other professional best practices. The course is taught using C, but I had worked through the infamous K&R book a couple of years ago, so I was comfortable with it. So I worked through the first projects in the course, consulted textbooks, videos, articles, and LLMs (ChatGPT) until I had constructed a satisfactory mental model of embedded systems and how they work.

Wanting to learn and challenge myself further, I decided my goal was going be the implementation of drivers for the various peripherals of an STM32-based development board. I would be using the Rust programming language. What better way to begin than by blinking an LED?

Goal: Blinking an LED on the STM32F411 "BlackPill" Development Board using embedded Rust

Embedded Concepts

Bare Metal

Bare Metal (MCU) → PAC → HAL → BSP

  • Bare Metal represents the lowest level of abstraction in embedded programming. In a bare metal environment, no code has been loaded before your program, including the standard library.
  • Peripheral access crate (PAC) is a Rust abstraction layer that provides access to peripherals. It is generated from System View Description (SVD) files provided by the chip vendor.
  • The Hardware abstraction layer (HAL) is implemented on top of of a PAC.
  • A Board-Support Crate (BSP) provides a high-level interface for a specific board.

Memory-Mapped I/O

  • Memory-mapped I/O is a construct where the the memory space of the microcontroller is mapped to peripheral registers. Memory addresses pertaining to peripheral registers are referenced (cast to pointer type) and manipulated directly. The Memory Map for a microcontroller can be found in the chip's reference manual.
  • Accessing peripheral registers must be done in a "volatile" way. The state of memory can change due to HW or other events outside of the CPU's control, thus we restrict the compiler from implementing some optimizations:
    • Caching is disabled
    • Omitting multiple reads or writes to the same memory address is disabled

Tool Chain

  • Host: Lenovo T480 running Arch Linux
  • HW debugger: ST-Link V2
  • IDE: Neovim with LSP configured for Rust
  • SW Debugger: arm-none-eabi-gdb

Development Board (Target)

black-pill
  • Development board: WeAct Studio BlackPill v3.1 MCU is STM32F411CEU6
    • 512 KB of flash
    • 128 KB of SRAM
    • Runs at 100 Mhz USB-C connector with 3.3V 100mA LDO regulator 25mhz and 32.768 KHz crystals on board Peripherals
    • UART, I2C, SPI, I2S
    • ADC multiplexed to 10 inputs
    • User controlled LED Pin-Out Diagram
    • See Pin-Out Diagram

Tools and Consumables

soldering-materials
  • Soldering iron: PINECEL Smart Mini Portable Soldering Iron
  • Solder: Rosin core, .031(8mm), Sn63/Pb37
    • Rosin core negates the need for flux
  • Desoldering Wick: Soder-Wick Rosin Size 2, 80-2-5
  • Tip Cleaner: Brass Sponge
  • Helping Hands (magnifying glass and clamps with stand)
  • Breadboard

Soldering the Header Pins

Setup

helping-hands
  • Mount the headers onto a breadboard, then mount the development board onto the headers
  • Use a Helping Hands tool with a magnifier and alligator clips

Technique

  • Pre-heat the soldering iron to between 350° and 400°C (375° C)
  • Tin the tip for better heat conductivity and to extend the life of the iron
  • Avoid excessive heat warpage.
  • Avoid crossing the dev board with the iron, i.e. the iron should come in from the periphery and not from across the dev board
  • Use desoldering wick as needed

Soldering Results

soldered-board

Cleaning and Equipment Care

  • To cleaning the tip, use a brass coil sponge. It is also possible to use a wet sponge, but the rapid cooling of the wet sponse may decrease the life of the iron.
  • Tin the iron before turning it off

Connecting the HW Debugger

hw-debugger-setup
  • The HW debugger is a bridge between the host and the target
    • connected to the host via USB
    • connects to the target via the Serial Wire Debug (SWD) interface

Writing the LED driver

Flashing the Firmware

  • openocd -d -f interface/stlink.cfg -f target/stm32f4x.cfg -c "program <BINARY> verify reset exit"

Success!

blink-led-gif

References

  1. Embedded Rust Book
    The Embedded Rust Book. Retrieved from https://docs.rust-embedded.org/book/.

  2. Embedded Systems Course by Israel Gbati
    Gbati, I. Embedded Systems Programming on ARM Cortex-M. Udemy Course. Retrieved from https://www.udemy.com.

  3. STM32F411CEU6 Reference Manual
    STMicroelectronics. (n.d.). RM0383: STM32F411xC/E Advanced ARM®-based 32-bit MCUs Reference Manual. Retrieved from https://www.st.com.

  4. Computer Organization and Design ARM Edition
    Patterson, D. A., & Hennessy, J. L. (2016). Computer Organization and Design ARM Edition: The Hardware Software Interface (1st ed.). Morgan Kaufmann.

  5. WeAct. STM32F411CE WeAct Black Pill Schematic
    Retrieved from https://github.com/WeActTC/MiniSTM32F4x1

  6. STMicroelectronics. (n.d.). STM32F411CE MCU Data Sheet.
    Retrieved from https://www.st.com.

  7. Proietti, M. (2017). Mastering STM32
    Leanpub. Retrieved from https://leanpub.com/mastering-stm32.