
Download the stm32 cube ide, a free IDE for Windows, Linux, and macOS from ST Microelectronics; accept the license and log in or register to start the 729 MB download.
Install Cube IDE from a downloaded package by extracting, running the installer, selecting an installation location, including ST-link and SEGGER J-link drivers, then set a workspace and handle firewall prompts.
Download the STM32 reference manual, data sheet, and Nucleo user guide, and set up a dedicated bare-metal project workspace with a proper project tree to access registers and peripherals.
Learn how to set up a bare-metal STM32f4 project in cube ide, download and curate cmsis header files, and use memory-mapped addresses to access peripherals without hal libraries.
Link chip_headers into the project, set up stm32f4 header include, enable gpioa clock, configure PA5 as output, and blink the LED with a simple loop, validating via IDE and hardware.
Discover how to identify and fix errors using instrumentation like print statements, timers, logic analyzers, and JTAG. Balance intrusiveness with non intrusiveness and explore performance debugging in embedded systems.
Learn the STM32 debug view fundamentals, including SWD setup and core clock configuration. Use memory view, memory browser, live expressions, breakpoints, and step-by-step execution to verify global and local variables.
Explore working with the instrumentation trace macrocell (ITM) to print through SWO, configure the SWV data console, and retarget printf for debugging in embedded systems.
Retarget printf by implementing __io_putchar via ITM_sendChar and stdio.h, then build simple log APIs (log_error, log_info, log_debug) with SWO via SWV ITM console for readable debugging.
Retarget printf to the UART by building a from-scratch UART driver, configuring USART2 on PA2/PA3 with AF7, enabling APB1 clock, and setting baud rate for debugging.
Enable GPIOA clock and configure PA2 as UART2 TX with MODER and AFRL AF7. Enable UART clock on APB1 and set 115200 baud with 16 mhz, TE and UE.
Develop and test a uart_write function that waits for TXE in the USART status register, then writes to the transmit data register, and retargets printf to UART.
Learn to debug GPIO pins with the SWV trace graph in the Cube IDE debugger, configuring PC13 as input and observing the button state via SWV data trace.
Explore how the direct memory access (DMA) transfers data in the background, freeing the cortex-m processor. Learn DMA parameters, streams, channels, transfer modes, sizes, increments, data widths, FIFO, and priority.
Learn to implement a memory-to-memory data transfer using the stm32f4 dma module two, enable its clock via the ahb1 enable bit, and configure parameters, fifo, and nvic interrupt.
Configure dma2 for memory-to-memory transfer by enabling the dma clock on the ahb1 bus and setting half-word data sizes. Enable memory and peripheral increment and select mem-to-mem direction, with interrupts.
Implement the dma2_transfer_start function to perform memory-to-memory transfers. Configure peripheral and memory addresses, NDTR length, and enable the DMA stream; note register details and data width.
Expose and test a dma driver by configuring source and destination buffers, initiating transfers, and using a transfer complete interrupt and a semaphore flag to verify data moves.
Explore the ADC independent modes on STM32F4, including single-channel single conversion, multichannel single conversion, single-channel continuous, multi-channel continuous, and injected continuous conversion. Understand sampling times and use cases.
Develop a bare-metal adc-dma driver to sample multiple channels using dma on stm32f4. Configure pa0 and pa1 as analog inputs and enable gpio, adc, and dma clocks.
Enable gpio port a and set pa0 and pa1 to analog mode, connect adc1 to dma2 stream0, configure a two-channel conversion sequence with sq1 and sq2, and enable dma operation.
Enable the dma clock and configure dma2 stream0 for adc data transfer, set circular mode and memory size, start adc, and test with two channels using potentiometers.
Develop a timer-driven adc-dma workflow, using timer 2 to trigger adc conversions at 100 Hz, with multi-sample dma and PA0 channel.
Enable dma transfer complete interrupts for timer-triggered adc conversions, using a semaphore flag to process adc samples from a memory-to-memory dma transfer and print with printf.
Explore the UART protocol overview: distinguish serial versus parallel communication, synchronous and asynchronous transmission, baud rate, and framing with start and stop bits, parity, word length, and duplex modes.
Create uart_dma.c and uart_dma.h to implement uart2_dma_rx_tx_init, configure pa2/pa3, enable clocks and baudrate, use dma and interrupts, and outline dma1_init with rx and tx streams.
Enable gpio a clock and configure pa2 and pa3 for alternate function. Set the baudrate, enable dma for tx and rx, and finalize with te, re, and tcie.
Configure DMA1 stream5 UART RX and stream6 UART TX by enabling DMA1 clock on the AHB1 bus, selecting channel four, enabling memory increment, and setting transfer complete interrupts with NVIC.
Configure dma1 stream5 for rx by clearing flags, setting peripheral address to the uart data register, enabling memory increment, selecting channel4, and enabling the stream and nvic interrupt.
Fixes DMA stream six setup by disabling the stream, clearing flags, and configuring memory-to-peripheral transfer (address, length, increment, channel) for subsequent interrupt handlers.
Implement interrupt handlers for the uart dma driver, clear transfer flags, and test the firmware using a tx/rx loop with dma stream six and usart interrupts.
Explore the SPI protocol, a synchronous full-duplex master-slave interface with MOSI, MISO, SCK, and SS lines, supporting single or multiple slaves and clock polarity/phasing configurations that govern data timing.
Develop an spi1 dma driver by configuring gpio pins pa4–pa7 for spi1, enabling clocks, setting master mode, clock phase/polarity, and enabling dma for spi communication, with separate rx/tx streams.
Enable gpio a clock and configure pa5, pa6, pa7 to alternate function af5 for spi1, set master mode with ssm and ssi, baud, enable dma in cr2, and enable spi.
Implement dma2 tx stream configuration for spi 1 tx, selecting channel 3 stream 3, enable clocks, disable and wait for the stream, set memory-to-peripheral direction, and enable interrupts and fifo.
Implement dma2 stream2 spi rx init function by configuring dma parameters, setting memory increment and transfer direction to peripheral to memory, enabling interrupts, and enabling the nvic stream interrupt.
Implement the dma transfer and receive functions for spi, configuring addresses and length, and enabling the dma streams while clearing interrupt flags with the low interrupt flag clear register.
Test the bare-metal spi dma driver with the MPU9250 mems sensor, define register addresses such as accelconfig 0x1c and user_ctrl 0x6a, and prepare range switching logic.
Implement the mpu9250 accelerometer config over spi dma, setting accel_range for 2g, 4g, 8g, or 16g. Prepare a 2-byte data buffer and expose the spi_dma transfer function for dma-based writes.
Continue implementing the MPU9250 SPI DMA driver by wiring stream two, handling RX complete, and configuring accelerometer range in the accel config register, awaiting transfer completion.
Implement a dma-driven spi read of the mpu9250 accelerometer starting at register 0x3b, using dummy and accel buffers to fetch x, y, and z values.
Test the MPU9250 mems driver by wiring vcc, gnd, ncs, scl, sda, and sdo, configure pa pins, enable fpu and dma-spi streams, and validate x, y, z accelerometer data.
Explore the I2C protocol, a two-wire interface with SDA and SCL, masters and slaves, start and stop conditions, address and data frames, pull-up resistors, and clock speed options.
Develop an I2C DMA bare-metal driver for I2C1, configure PB8/PB9 as AF4, enable clocks and GPIOB, and assign DMA streams 5 and 6 for RX and TX.
Enable the GPIO port B clock on AHB1, configure PB8 (I2C1 SCL) and PB9 (SDA) as I2C1 alternate function AF4, and set open-drain output to prepare for I2C DMA initialization.
Enable the I2C1 clock on apb1, perform a software reset, disable clock stretching and general call, enable dma, set periph clock, CCR, and T rise, then enable the I2C peripheral.
Initialize the i2c1 tx dma stream six by enabling the DMA clock, selecting channel one, enabling memory increment and transfer complete interrupt, and setting memory-to-peripheral direction with the NVIC interrupt.
Implement I2C DMA RX stream initialization, align function name with the TX version, configure DMA module 1 stream five for I2C1, periph-to-mem, memory increment, transfer-complete interrupt, and enable NVIC.
Implement the I2C DMA stream transfer function by configuring DMA module 1, stream 6, with the I2C1 data register as the peripheral and a uint8_t pointer.
Implement the I2C dma stream receive function for I2C1 (dma stream 5), including clearing the interrupt flag, setting peripheral and memory addresses, transfer length, and enabling the stream.
Implement the i2c read function using the dma receive flow, taking slave address, reg_addr, p_read_buff, and number_of_bytes, and orchestrating bus busy, start, repeated start, and data transfer.
Implement a dma i2c write function that accepts slave address, data buffer, and byte count, waits on busy and sb flags, issues the address with write, and starts dma transfer.
Implement i2c dma stream irq handlers for i2c module zero using streams five and six, handling transfer complete interrupts, clearing flags, and generating a stop condition via cr1 stop bit.
Port the mpu-9250 to the i2c dma driver, define the 0x68 slave address, and test by updating mpu.c to use i2c_dma_write and i2c_dma_read.
Explore STM32 timers to generate time bases, measure external frequencies via input capture, and control outputs with output compare and one-pulse modes, using CNT, ARR, and PSC.
Develop a basic bare-metal pwm driver by configuring two gpio pins pa0 and pa1 for timer 2 pwm channels, with gpio setup, timer configuration, pwm mode, and future dma integration.
Enable GPIO-A clocks and configure PA0 and PA1 for timer 2 alternate function mode, then set up timer 2 pwm with prescaler and auto-reload using CCMR1, CCER, and CR1.
Develop the set_dutycycle() function using a switch state machine to set channel 1 and 2 PWM duty via TIM2 CCR1 and CCR2, then verify with PA0/PA1 LEDs.
Configure a timer-driven pwm with dma to generate a 10 kHz pwm signal on pa0/pa1 using timer 2 and set the duty cycle via dma.
Configure dma 1 streams 5 and 6 to feed timer 2 channels 1 and 2 from 200-element duty cycle lookup tables, enabling memory-to-peripheral transfers with circular mode for pwm.
Test the pwm dma driver with a logic analyzer on pa0/pa1, verify 10 khz output with varying duty cycles, and adapt the driver to accept two duty-cycle arrays for channels.
Catch anomalies in embedded firmware by creating conditions that automatically trigger breakpoints with inline assembly BKPT, and apply it to an ADC project.
Trigger a divide-by-zero to provoke a hard fault, inspect the fault status register for usage faults, then implement the hard fault handler and report via printf over UART.
Debug timers with SWV graph and a logic analyzer, using a PWM timer on PA0/PA1 and SysTick on a Cortex-M4 to verify 10 Hz timing and 30% duty cycle.
Measure execution time of an algorithm using SysTick and a logic analyzer. Demonstrate performance debugging and time a code block with a brute force learning example.
Dump register information into an array to monitor runtime changes of the GPIO A input data register, using a port A data buffer and a dump_port_A_data function.
Learn to use profilers to determine the time and place of execution by recording the times and locations of up to 50 test points with a debug_profile function.
Are you tired of Copying and Pasting code you don't understand?
Here’s an overview of what you’re getting in this advanced level course...
Developing bare-metal DMA Drivers:
This course completely demystifies the Direct Memory Access (DMA) peripheral. Over 50% of the course is spent on this topic, so that by the end of this course you would have mastered the DMA peripheral.
We shall thoroughly look at developing the following bare-metal DMA drivers.
- Bare-Metal DMA Memory-to-Memory Transfer Driver
- Bare-Metal ADC DMA Regular Conversion Driver
- Bare-Metal ADC DMA Timer Triggered Conversion Driver
- Bare-Metal UART RX/TX DMA Driver
- Bare-Metal SPI RX/TX DMA Driver
- Bare-Metal I2C RX/TX DMA Driver
- Bare-Metal PWM DMA Driver
Developing bare-metal RTC Drivers:
The Realtime Clock (RTC) peripheral is another advanced peripheral demystified in this course. We shall study the theory of realtime clocks, take a look at the capabilies of the realtime clock on our microcontroller and then develop the following drivers for the realtime clock.
- Bare-Metal RTC Calendar Driver
- Bare-Metal RTC Alarm Driver
- Bare-Metal RTC Timestamp Driver
- Bare-Metal RTC Tamper Detection Driver
Developing bare-metal Watchdog Timer Drivers:
Watchdog Timers are an essential component of any robust embedded device. In my opinion, no embedded device should be released onto the market without the implementation of an Independent Watchdog Timer (IWDG). You will understand why I hold this opinion in the course.
In this course we shall develop the following Watchdog Timer drivers:
- Bare-Metal Independent Watchdog (IWDG) Timer Driver
- Bare-Metal Windowed Watchdog (WWDG) Timer Driver
Working with Standby Mode and Wakeup:
Knowing how to put your embedded devics into Low-power mode and coming out of it will greatly improve your ability to developer power efficient embedded solutions. This course will teach you how to put your device into Standby mode and come out of it using different methods.
Specifically, we shall develop the following drivers:
- Bare-Metal Standby Mode and Wakeup Pin Driver
- Bare-Metal Standby Mode and RTC Wakeup Timer Driver
The Art of Debugging :
Knowing how to properly debug your firmware will save you lots of time and money, and may even prolong your life because of the level of frustration and stress you will avoid.
In this course you shall grasp the theoretical aspects of debugging, understand the various types of debugging such functional debugging and performance debugging, and learn the various tools and methods used in different scenarios.
Some of the techniques we you will master include:
- Methods of identifying the cause of HardFault
- Catching anomalous results
- Measuring execution time of an algorithm using a Timer
- Measuring execution time of an algorithm using a Logic Analyzer
- Working with arrays dumps
- Debugging techniques for Timers
With a programming based approach, this course is designed to give you a solid foundation in bare-metal firmware development for ARM-based microcontrollers . The goal of this course is to teach you how to navigate the microcontroller reference manual and datasheet to extract the right information to professionally build peripheral drivers and firmware. To achieve this goal, no libraries are used in this course, purely bare-metal embedded-c and register manipulations.
Still keeping it simple, this course comes in different ARM Cortex-M development boards so that students can put the techniques to practice using an ARM Cortex-M development board of their choice. This version of the course uses the STMicroelectronics STM32F4-NUCLEO which has an ARM Cortex-M4 microcontoller.
This is the Advanced level course for the world famous Embedded Systems Bare-Metal Programming Ground Up™ (STM32). You have to take the Embedded Systems Bare-Metal Programming Ground Up™ (STM32) before taking this course
So with that understood, let me tell you…
Exactly What You’re Getting
This is dramatically different from any course you have ever taken because it’s more of a professional hands-on “field guide” to stm32 bare metal firmware development.
The reason why is because there’s no fluff or filler. It immediately gets down to the actual subject, showing you exactly what to do, how to do it, and why.
Plus, it’s easy.
And you’ll immediately “get” the entire mythology I personally use to build firmware for consumer devices in my professional life.
It's About MORE Than Just Getting the Code to Work
See, this course will change your professional life forever. Here is what one student had to say about the Embedded Systems Bare-Metal Programming Ground Up™ (STM32) course :
"I would suggest this course for all the beginners. The concepts have been covered in the right sequence.And also the best part of this lecture series is getting to know how to explore the reference manual and datasheets."
Here is what another student had to say :
"Extremly helpful to get to understand the uC programming deeper! For me it is much easier from now to develop code because I undertstand the base behind, so I'm more confident and more experienced to develop and debug the code. Really, this course is very useful to link the hardware knowledge with the coding skills. This fills the gap between them. Thanks for it! :)"
A third student :
"I am a professional semiconductor chipset application engineer with 30 years in global embedded product design in system applications. I can say this teacher is very straight forward by sharing his many years knowledge to the students with his true heart. Yes. I love his teaching pace and style!"
Taken by 8000+ Students with 1000+ Reviews
If at least one of the following applies to you then keep reading if not then simply skip this course:
" Escape From "
Copying/Pasting code you don’t understand
Using third party libraries and header files like HAL, LL and StdPeriph
Experiencing bugs you don’t understand
Being afraid of technical documentations like the reference manual and datasheet of the chip
Imposter syndrome
" Arrive At "
Building every single line of code from scratch by writing to the microcontroller’s memory space directly.
Using No third party libraries or header files
Understanding and writing every single line of code yourself- no Copy/Paste
Using the debugger effectively to analyze and resolve any bugs
Developing proficiency in your embedded development skills and confidently take the next steps
So like I said, there’s more than just getting each piece of code to work.
Here’s an overview of what you’re getting in this advanced level course...
Developing bare-metal DMA Drivers:
This course completely demystifies the Direct Memory Access (DMA) peripheral. Over 50% of the course is spent on this topic, so that by the end of this course you would have mastered the DMA peripheral.
We shall thoroughly look at developing the following bare-metal DMA drivers.
- Bare-Metal DMA Memory-to-Memory Transfer Driver
- Bare-Metal ADC DMA Regular Conversion Driver
- Bare-Metal ADC DMA Timer Triggered Conversion Driver
- Bare-Metal UART RX/TX DMA Driver
- Bare-Metal SPI RX/TX DMA Driver
- Bare-Metal I2C RX/TX DMA Driver
- Bare-Metal PWM DMA Driver
Developing bare-metal RTC Drivers:
The Realtime Clock (RTC) peripheral is another advanced peripheral demystified in this course. We shall study the theory of realtime clocks, take a look at the capabilies of the realtime clock on our microcontroller and then develop the following drivers for the realtime clock.
- Bare-Metal RTC Calendar Driver
- Bare-Metal RTC Alarm Driver
- Bare-Metal RTC Timestamp Driver
- Bare-Metal RTC Tamper Detection Driver
Developing bare-metal Watchdog Timer Drivers:
Watchdog Timers are an essential component of any robust embedded device. In my opinion, no embedded device should be released onto the market without the implementation of an Independent Watchdog Timer (IWDG). You will understand why I hold this opinion in the course.
In this course we shall develop the following Watchdog Timer drivers:
- Bare-Metal Independent Watchdog (IWDG) Timer Driver
- Bare-Metal Windowed Watchdog (WWDG) Timer Driver
Working with Standby Mode and Wakeup:
Knowing how to put your embedded devices into Low-power mode and coming out of it will greatly improve your ability to developer power efficient embedded solutions. This course will teach you how to put your device into Standby mode and come out of it using different methods.
Specifically, we shall develop the following drivers:
- Bare-Metal Standby Mode and Wakeup Pin Driver
- Bare-Metal Standby Mode and RTC Wakeup Timer Driver
But it gets better because you will also master....
The Art of Debugging :
Knowing how to properly debug your firmware will save you lots of time and money, and may even prolong your life because of the level of frustration and stress you will avoid.
In this course you shall grasp the theoretical aspects of debugging, understand the various types of debugging such functional debugging and performance debugging, and learn the various tools and methods used in different scenarios.
Some of the techniques we you will master include:
- Methods of identifying the cause of HardFault
- Catching anomalous results
- Measuring execution time of an algorithm using a Timer
- Measuring execution time of an algorithm using a Logic Analyzer
- Working with arrays dumps
- Debugging techniques for Timers
Specially Designed For People Who Hate Copy/Paste
Listen. If you don’t like “Copy/Paste” you’re not alone. I can’t stand it either. I’d literally rather have a piece of code that I wrote from scratch that doesn’t work than someone else’s working code I copied and pasted.
And that’s why I’ve spent months designing and recording this course in which I show you how to locate every single register used and the meaning of every hexadecimal value written into the register.
Also it comes with a money back guarantee so you have nothing to lose.