STM32F Series Overview

The STM32F series is a versatile family of microcontrollers based on ARM Cortex-M cores, designed and manufactured by STMicroelectronics. These microcontrollers are engineered for high performance, low power consumption, and a rich set of peripherals, making them suitable for a wide range of applications from consumer electronics to industrial automation.

The series includes devices built around different Cortex-M cores such as M0, M3, M4, and M7, offering scalable processing power and memory options. STM32F MCUs provide an extensive selection of features including multiple timers, PWM channels, analog-to-digital converters (ADC), digital-to-analog converters (DAC), communication interfaces such as UART, SPI, I2C, CAN, USB, and flexible power modes for energy-efficient designs.

Introduced in the early 2000s, the STM32F series has evolved rapidly to meet modern embedded system demands. It has gained a strong reputation for reliability, extensive software support, and a robust development ecosystem that includes ST’s CubeMX configuration tools, HAL libraries, RTOS support, and a large community of developers. This combination of hardware capability and ecosystem makes STM32F MCUs a popular choice for applications ranging from IoT devices, motor control, and medical instruments to robotics, industrial automation, and advanced consumer electronics.

Beyond processing and connectivity, the STM32F series emphasizes safety and security with features such as hardware cryptography, flash memory protection, and watchdog timers. STMicroelectronics continues to expand the series with higher-speed cores, more memory, and enhanced peripherals, ensuring that STM32F remains relevant for both legacy and cutting-edge applications.

STM32 Bluepill Peripherals

The STM32 Bluepill provides a wide range of peripherals, enabling a variety of applications. Major peripherals include Digital I/O, ADC, PWM, UART, SPI, I2C, Timers, USB OTG, CAN, External Interrupts, SWD, and RTC.

List of Alternative STM32 Board Types
Board CPU Speed RAM Flash Memory Peripherals Operating Voltage Other Specifications
STM 32F103C datasheet
STM32F103C8 (Blue Pill) 72 MHz (ARM Cortex-M3) 20 KB 64 KB 37 GPIO, UART, SPI, I2C, ADC, PWM 3.3V Low-cost, commonly used in hobbyist projects
STM32F103 Series Feature Matrix
Feature 36-pin (LQFP/QFN) 48-pin (LQFP/QFN) 64-pin (LQFP) 100-pin (LQFP) 144-pin (LQFP)
Flash Memory (KB) 16-64 16-128 16-128 64-512 64-512
SRAM (KB) 6-20 6-20 6-20 20-64 20-64
GPIO Pins 26 37 51 80 112
ADC Channels (12-bit) 10 12 16 16 16
Timers (General Purpose) 3 4 4 5 5
SPI Interfaces 1 2 2 3 3
I2C Interfaces 1 2 2 2 2
USART Interfaces 2 3 3 4 5
USB (FS) Yes Yes Yes Yes Yes
CAN Interface No Yes Yes Yes Yes
DMA Channels 7 7 7 12 12
Ethernet MAC No No No No Yes
STM32F401 Series Feature Matrix
Feature 48-pin (LQFP/QFN) 64-pin (LQFP) 100-pin (LQFP/BGA)
Flash Memory (KB) 128-256 128-512 256-512
SRAM (KB) 64 64 96
GPIO Pins 37 51 81
ADC Channels (12-bit) 10 16 16
Timers (General Purpose) 4 4 5
SPI Interfaces 2 3 3
I2C Interfaces 2 2 3
USART Interfaces 3 3 4
USB OTG (FS) Yes Yes Yes
CAN Interface No No No
DMA Channels 7 7 7
Ethernet MAC No No No
STM32F405 Series Feature Matrix
Feature 64-pin (LQFP) 100-pin (LQFP/BGA) 144-pin (LQFP/BGA)
Flash Memory (KB) 256-1024 256-1024 512-1024
SRAM (KB) 128 192 192
GPIO Pins 51 81 112
ADC Channels (12-bit) 16 16 24
Timers (General Purpose) 8 10 12
SPI Interfaces 3 4 5
I2C Interfaces 2 3 4
USART Interfaces 3 4 6
USB OTG (FS/HS) Yes Yes Yes
CAN Interface 1 2 2
DMA Channels 12 16 16
Ethernet MAC No Yes Yes
STM32F407 Series Feature Matrix
Feature 64-pin (LQFP) 100-pin (LQFP/BGA) 144-pin (LQFP/BGA) 176-pin (LQFP/BGA)
Flash Memory (KB) 512-1024 512-1024 512-1024 512-1024
SRAM (KB) 128 192 192 192
GPIO Pins 51 81 112 140
ADC Channels (12-bit) 16 16 24 24
Timers (General Purpose) 8 8 10 12
SPI Interfaces 3 3 4 5
I2C Interfaces 2 3 3 4
USART Interfaces 3 4 6 6
USB OTG (HS/FS) Yes Yes Yes Yes
CAN Interface 1 2 2 2
DMA Channels 12 16 16 16
Ethernet MAC Yes Yes Yes Yes
STM32F Series with External Memory and Parallel Bus Support
Device Series External Memory Support (SRAM, PSRAM, NOR, NAND) Parallel Bus Peripheral Support (FSMC/FMC) Package Size (Pin Count) Comments
STM32F103 (High-Density) FSMC for SRAM, NOR, NAND Yes 100-pin, 144-pin FSMC available on high-density devices only
STM32F207 FSMC for SRAM, NOR, NAND Yes 100-pin, 144-pin, 176-pin Supports external parallel SRAM, NOR, NAND Flash
STM32F217 FSMC for SRAM, NOR, NAND Yes 100-pin, 144-pin, 176-pin Similar to F207 with additional crypto support
STM32F407 FSMC for SRAM, NOR, NAND Yes 100-pin, 144-pin, 176-pin, 208-pin Used in graphic interfaces and memory expansions
STM32F417 FSMC for SRAM, NOR, NAND Yes 100-pin, 144-pin, 176-pin, 208-pin Enhanced version of F407 with additional security features
STM32F427 FMC for SRAM, SDRAM, NOR, NAND Yes 100-pin, 144-pin, 176-pin, 208-pin Supports SDRAM in addition to SRAM, NOR, NAND
STM32F437 FMC for SRAM, SDRAM, NOR, NAND Yes 100-pin, 144-pin, 176-pin, 208-pin Enhanced version of F427 with security features
STM32F469 FMC for SRAM, SDRAM, NOR, NAND Yes 144-pin, 176-pin, 208-pin, BGA Optimized for graphical displays (LCD-TFT controllers)
STM32F479 FMC for SRAM, SDRAM, NOR, NAND Yes 144-pin, 176-pin, 208-pin, BGA Security-enhanced version of F469
STM32F746 FMC for SRAM, SDRAM, NOR, NAND Yes 100-pin, 144-pin, 176-pin, 208-pin, BGA High-performance with FMC, typically used for external memory
STM32F767 FMC for SRAM, SDRAM, NOR, NAND Yes 100-pin, 144-pin, 176-pin, 208-pin, BGA Optimized for memory interfaces with high-speed support
STM32F103 Blue Pill ADC Overview

The STM32F103 Blue Pill features an ADC (Analog-to-Digital Converter) that converts analog signals to digital values. This is essential for reading analog sensors and other input devices.

More information on Successive Approximation Conversion

Key Features of STM32 ADC
  • Resolution: 12-bit, providing values from 0 to 4095.
  • Speed: Conversion rates up to several million samples per second.
  • Channels: Multiple channels connected to different analog pins.
  • Modes: Single, continuous, and discontinuous conversion modes.
  • DMA Support: Automatic data transfer to memory without CPU involvement.
ADC Example Code (Arduino C with Maple Library)

This example demonstrates how to configure and use the ADC on the STM32F103 Blue Pill to read analog values.


// Example code to read ADC value on STM32F103 Blue Pill using Arduino C and Maple Library

#include <Wire.h>       // For I2C communication (if needed)
#include <MapleFree>    // Maple library for STM32F103

void setup() {
    Serial.begin(115200);    // Start serial communication at 115200 baud
    analogReadResolution(12); // Set ADC resolution to 12 bits (0-4095)
    analogReadA0();          // Initialize ADC on A0 pin
}

void loop() {
    int adcValue = analogRead(A0); // Read ADC value from pin A0
    Serial.print("ADC Value: ");
    Serial.println(adcValue);      // Print the ADC value to the Serial Monitor
    delay(1000);                   // Wait for 1 second before reading again
}
      
ADC with DMA Example Code (Arduino C with Maple Library)

This example shows how to use DMA with the ADC to continuously sample data and store it in a buffer.


// Example code to use ADC with DMA on STM32F103 Blue Pill using Arduino C and Maple Library

#include <Wire.h>       // For I2C communication (if needed)
#include <libmaple/dma.h>
#include <libmaple/adc.h>

#define BUFFER_SIZE 100
volatile uint16_t adcBuffer[BUFFER_SIZE]; // Buffer to store ADC values

void setup() {
    Serial.begin(115200);      // Start serial communication at 115200 baud
    analogReadResolution(12);  // Set ADC resolution to 12 bits (0-4095)
    analogRead(A0);            // Initialize ADC on A0 pin

    DMA1_Channel1->CCR |= DMA_CCR_EN;           // Enable DMA channel 1
    DMA1_Channel1->CNDTR = BUFFER_SIZE;         // Set number of data items to transfer
    DMA1_Channel1->CPAR = (uint32_t)&ADC1->DR;  // Peripheral address (ADC data register)
    DMA1_Channel1->CMAR = (uint32_t)adcBuffer;  // Memory address (ADC buffer)
}

void loop() {
    // Print ADC buffer contents
    for (int i = 0; i < BUFFER_SIZE; i++) {
        Serial.print("Buffer[");
        Serial.print(i);
        Serial.print("]: ");
        Serial.println(adcBuffer[i]);
    }
    delay(1000); // Wait for 1 second before printing again
}
      
STM32 I2C Communication

I2C (Inter-Integrated Circuit) is a communication protocol that allows multiple devices to communicate over a two-wire bus. One device acts as the master, while others act as slaves. This guide demonstrates STM32 I2C setup using the STM32duino library, with master and slave examples.

Key Concepts
  • I2C Bus: Two wires, SDA (data) and SCL (clock). Master controls clock and initiates communication; slaves respond.
  • Addressing: Each device has a unique address; the master uses it to communicate with specific slaves.
  • Data Transfer: Data is sent in bytes, each acknowledged by the receiver.
Master Example Code

Configure an STM32 microcontroller as an I2C master, send data to a slave, and read responses.


#include <Wire.h>

#define SLAVE_ADDRESS 0x3C  // I2C slave address

void setup() {
    Wire.begin();            // Initialize I2C as master
    Serial.begin(115200);    // Initialize serial communication
}

void loop() {
    Wire.beginTransmission(SLAVE_ADDRESS); // Begin communication with the slave
    Wire.write("Hello Slave");             // Send data to the slave
    Wire.endTransmission();                // End transmission

    delay(500); // Wait for 500 ms

    Wire.requestFrom(SLAVE_ADDRESS, 1);    // Request 1 byte from the slave
    if (Wire.available()) {
        char c = Wire.read();              // Read the received byte
        Serial.print("Received from slave: ");
        Serial.println(c);
    }

    delay(1000); // Wait for 1 second
}
      
Slave Example Code

Configure an STM32 microcontroller as an I2C slave, respond to master requests, and send data.


// STM32 I2C Slave Example

#include <Wire.h>

#define SLAVE_ADDRESS 0x3C  // I2C slave address

void setup() {
    Wire.begin(SLAVE_ADDRESS);  // Initialize I2C as slave
    Wire.onReceive(receiveData); // Register receive event handler
    Wire.onRequest(sendData);   // Register request event handler
    Serial.begin(115200);       // Initialize serial communication
}

void loop() {
    // Main loop does nothing, all actions are handled by the event handlers
}

void receiveData(int numBytes) {
    while (Wire.available()) {
        char c = Wire.read();     // Read received data
        Serial.print("Received: ");
        Serial.println(c);
    }
}

void sendData() {
    Wire.write('A');            // Send data to the master
}
      
Steps to Test I2C Communication
  1. Connect Devices: Connect master and slave STM32 boards using SDA and SCL lines with pull-up resistors (4.7kΩ–10kΩ).
  2. Select Board: In Arduino IDE, go to Tools > Board and select the appropriate STM32 board for both master and slave.
  3. Select Port: Choose the COM port for each STM32 board.
  4. Upload Code: Upload master code to one board, slave code to the other.
  5. Monitor Communication: Open Serial Monitor on the master board to view transmitted and received data.
STM32 CAN Bus Example

The Controller Area Network (CAN) bus is a robust vehicle bus standard designed for communication among in-vehicle systems without a host computer. STM32 microcontrollers feature integrated CAN controllers to implement CAN bus communication.

Key Concepts
  • CAN Bus: Multi-master, message-oriented protocol for real-time data exchange.
  • STM32 CAN Controller: Integrated peripheral for handling CAN communication.
  • Configuration: Involves setting up CAN peripheral, filters, and message routines.
Example Code

Setup CAN communication on an STM32 using the STM32duino library. Includes sending and receiving CAN messages.


// CAN Bus example for STM32 using STM32duino library

<STM32CAN>

CAN can1(CAN1);  // Create CAN object for CAN1 peripheral

void setup() {
    Serial.begin(115200);  // Initialize serial communication
    can1.begin(500E3);     // Initialize CAN1 with 500 kbps bitrate

    // Set up CAN filter to receive all messages
    can1.filter(0, 0xFFFFFFFF, CAN_FILTER_32BIT);
}

void loop() {
    // Check if a CAN message has been received
    if (can1.available()) {
        CANMessage msg;
        can1.read(msg);  // Read the received CAN message
        Serial.print("Received CAN Message: ID = ");
        Serial.print(msg.id, HEX);
        Serial.print(", Data = ");
        for (int i = 0; i < msg.len; i++) {
            Serial.print(msg.data[i], HEX);
            Serial.print(" ");
        }
        Serial.println();
    }

    // Create and send a CAN message every second
    CANMessage txMsg;
    txMsg.id = 0x123;     // CAN ID
    txMsg.len = 8;        // Length of data
    for (int i = 0; i < 8; i++) {
        txMsg.data[i] = i; // Fill data with incremental values
    }
    can1.write(txMsg);  // Send CAN message

    delay(1000);        // Wait for 1 second
}
      
Steps to Test CAN Bus Communication
  1. Connect STM32: Connect your STM32 board to your PC via USB.
  2. CAN Transceiver: Connect a CAN transceiver (e.g., MCP2551) to STM32 CAN pins and to the CAN bus network.
  3. Select Board: In Arduino IDE, go to Tools > Board and select your STM32 board (e.g., Blue Pill F103C8).
  4. Select Port: Go to Tools > Port and select the board’s COM port.
  5. Upload Code: Upload the CAN example code to the STM32 board.
  6. Monitor CAN Bus: Use a CAN analyzer or another STM32 configured to receive CAN messages to view communication.
STM32 SPI Communication

The SPI (Serial Peripheral Interface) bus is a synchronous serial communication protocol used for short-distance communication in embedded systems. It uses four lines: MISO (Master In Slave Out), MOSI (Master Out Slave In), SCK (Serial Clock), and SS (Slave Select). This guide provides examples of SPI communication with an STM32 microcontroller acting as both a master and a slave.

Key Concepts
  • Synchronous Communication: SPI is synchronized with a clock signal generated by the master device.
  • Lines: MOSI (master → slave), MISO (slave → master), SCK (clock), SS (chip select).
  • Data Transfer: Full-duplex mode allows simultaneous sending and receiving of data.
SPI Master Example

This example demonstrates how to configure an STM32 microcontroller as an SPI master to communicate with a slave device.


// STM32 SPI Master Example

#include <SPI.h>

void setup() {
    Serial.begin(115200);         // Initialize serial communication
    SPI.begin();                  // Initialize SPI as master
    SPI.setClockDivider(SPI_CLOCK_DIV8); // Set SPI clock speed
    SPI.setDataMode(SPI_MODE0);   // Set SPI mode
    SPI.setBitOrder(MSBFIRST);    // Set bit order
}

void loop() {
    byte dataToSend = 0xA5;      // Example data to send
    byte receivedData;
    
    digitalWrite(SS, LOW);        // Select the slave device
    receivedData = SPI.transfer(dataToSend); // Send and receive data
    digitalWrite(SS, HIGH);       // Deselect the slave device

    Serial.print("Sent: 0x");
    Serial.print(dataToSend, HEX);
    Serial.print(" Received: 0x");
    Serial.println(receivedData, HEX);

    delay(1000);                  // Wait for 1 second
}
      
SPI Slave Example

This example demonstrates configuring an STM32 microcontroller as an SPI slave, receiving data from the master and responding via interrupts.


// STM32 SPI Slave Example

#include <SPI.h>

volatile byte dataToSend = 0x5A; // Data to send to the master

void setup() {
    Serial.begin(115200);         // Initialize serial communication
    SPI.begin();                  // Initialize SPI as slave
    SPI.setClockDivider(SPI_CLOCK_DIV8); // Set SPI clock speed (match master)
    SPI.setDataMode(SPI_MODE0);   // Set SPI mode (match master)
    SPI.setBitOrder(MSBFIRST);    // Set bit order (match master)
    SPI.usingInterrupt();          // Enable SPI interrupts
    attachInterrupt(digitalPinToInterrupt(MISO), onSPIReceive, FALLING); // Attach interrupt
}

void loop() {
    // Main loop does nothing; handled by interrupts
}

void onSPIReceive() {
    if (SPI.available()) {
        byte receivedData = SPI.transfer(0x00); // Read received data
        Serial.print("Received: 0x");
        Serial.println(receivedData, HEX);
        // Respond with data
        SPI.transfer(dataToSend);
    }
}
      
STM32 Timers Using Arduino

Timers are versatile peripherals in STM32 microcontrollers, used for generating precise delays, handling real-time events, creating PWM signals, and triggering interrupts. This guide explains timer types, registers, and provides practical Arduino examples.

Timer Concepts
  • Basic Timers: Simple counters for time delays and basic events.
  • General-Purpose Timers: Multi-channel timers capable of PWM, input capture, and output compare.
  • Advanced-Control Timers: Timers for motor control and other advanced applications.
Timer Registers
  • CR1: Control register for counter direction, auto-reload, and update behavior.
  • CNT: Current count value of the timer.
  • PSC: Prescaler for controlling counting speed.
  • ARR: Auto-reload register; triggers events when reached.
  • CCR1-CCR4: Capture/Compare registers for PWM or input capture.
  • EGR: Event generation register to manually trigger events.
Basic Timer Example

This example uses a basic timer to create a periodic time event.


// STM32 Basic Timer Example

<Arduino>

void setup() {
    Serial.begin(115200);        // Initialize serial communication
    Timer6.init();               // Initialize Timer6
    Timer6.setPeriod(1000);      // Set period to 1000 ms
    Timer6.attachInterrupt(timerCallback); // Attach interrupt
}

void loop() {
    // Main loop does nothing
}

// Timer interrupt callback
void timerCallback() {
    Serial.println("Timer event occurred");
}
      
PWM Signal Generation

This example shows how to use a general-purpose timer to generate a PWM signal on a pin.


// STM32 PWM Example

<Arduino>

void setup() {
    Serial.begin(115200);          // Initialize serial communication
    Timer2.setMode(PWM, D9);       // Configure Timer2 for PWM on pin D9
    Timer2.setPWM(D9, 1000, 512);  // 1 kHz frequency, 50% duty cycle
}

void loop() {
    // Main loop does nothing
}
      
Timer with Input Capture

This example demonstrates capturing an input signal using a timer.


// STM32 Timer Input Capture Example

<Arduino>

volatile uint32_t captureValue = 0;

void setup() {
    Serial.begin(115200);           // Initialize serial communication
    Timer2.setMode(INPUT_CAPTURE, D3); // Capture input on D3
    Timer2.attachInterrupt(inputCaptureCallback);
}

void loop() {
    Serial.println(captureValue);   // Print captured value
    delay(1000);
}

// Input capture callback
void inputCaptureCallback() {
    captureValue = Timer2.getCapture();
}
      
Timers as DMA and Peripheral Triggers

STM32 timers can trigger DMA channels or peripherals based on timer events. The table below summarizes common connections:

Timer Channel Peripherals Triggered
Timer 1Channel 1DMA4, ADC
Timer 1Channel 2DMA6
Timer 1Channel 3DMA7
Timer 1Channel 4DMA5
Timer 2Channel 1DMA1, DMA5
Timer 2Channel 2DMA7
Timer 2Channel 4DMA2
Timer 3Channel 1DMA4
Timer 3Channel 2DMA6
Timer 3Channel 3DMA1
Timer 3Channel 4DMA3
STM32 Blue Pill - Serial Ports Example

The STM32 Blue Pill has three hardware serial ports: Serial, Serial1, and Serial2. This example demonstrates how to configure and use all three serial ports to communicate simultaneously.

In this code, we configure Serial (USB serial interface), Serial1 (PA9 - TX, PA10 - RX), and Serial2 (PB10 - TX, PB11 - RX) to transmit and receive data.

Code Example


/*
 * STM32 Blue Pill - Using All Three Serial Ports
 * Serial: USB Serial (Communication with PC)
 * Serial1: PA9 (TX) and PA10 (RX)
 * Serial2: PB10 (TX) and PB11 (RX)
 */

void setup() {
    // Initialize all three serial ports
    Serial.begin(115200);   // USB Serial communication (e.g., to Serial Monitor)
    Serial1.begin(115200);  // Hardware serial 1 (PA9 - TX, PA10 - RX)
    Serial2.begin(115200);  // Hardware serial 2 (PB10 - TX, PB11 - RX)

    // Print messages to show initialization
    Serial.println("USB Serial initialized");
    Serial1.println("Serial1 initialized");
    Serial2.println("Serial2 initialized");
}

void loop() {
    // Check if data is available from USB Serial
    if (Serial.available()) {
        char inByte = Serial.read();
        // Echo received data to Serial1 and Serial2
        Serial1.print("Received from USB Serial: ");
        Serial1.println(inByte);
        Serial2.print("Received from USB Serial: ");
        Serial2.println(inByte);
    }

    // Check if data is available from Serial1
    if (Serial1.available()) {
        char inByte = Serial1.read();
        // Echo received data to USB Serial and Serial2
        Serial.print("Received from Serial1: ");
        Serial.println(inByte);
        Serial2.print("Received from Serial1: ");
        Serial2.println(inByte);
    }

    // Check if data is available from Serial2
    if (Serial2.available()) {
        char inByte = Serial2.read();
        // Echo received data to USB Serial and Serial1
        Serial.print("Received from Serial2: ");
        Serial.println(inByte);
        Serial1.print("Received from Serial2: ");
        Serial1.println(inByte);
    }

    // Add a small delay to avoid flooding the serial ports
    delay(100);
}
        

Explanation

  • Serial is used to communicate via the USB interface with the computer, such as the Serial Monitor in the Arduino IDE.
  • Serial1 uses the STM32 hardware UART (USART1), which is connected to pins PA9 (TX) and PA10 (RX).
  • Serial2 uses the STM32 hardware UART (USART2), connected to pins PB10 (TX) and PB11 (RX).

The code reads any available data from one serial port and echoes it to the others, allowing communication between devices connected to the different serial ports. This is useful in applications where you need to communicate with multiple devices simultaneously.

Note: When uploading this code, make sure to configure the STM32 board settings in the Arduino IDE, including selecting the correct port and upload method (usually "STM32duino bootloader" or "Serial").

STM32 Interrupt Examples

This guide demonstrates how to configure interrupts for different peripherals on STM32 microcontrollers. Interrupts are useful for handling asynchronous events, and they improve the efficiency of the system by responding only when required. Below are examples for external interrupts, timers, I2C, SPI, serial, CAN, RTC, and USB.

External Interrupt Example

This example shows how to configure an external interrupt using a push button on a specific pin.


//STM32 External Interrupt Example

void setup() {
    pinMode(PA0, INPUT); // Set pin PA0 as input (Button)
    attachInterrupt(digitalPinToInterrupt(PA0), handleButtonPress, FALLING); // Attach interrupt to PA0
}

void loop() {
    // Main code can run here, interrupt will trigger on button press
}

void handleButtonPress() {
    // Code to run when button is pressed
    Serial.println("Button Pressed!");
}    
                

Serial UART Interrupt Example


// STM32 Serial UART Interrupt Example

void setup() {
    Serial.begin(115200);      // Initialize serial communication
    attachInterrupt(digitalPinToInterrupt(PA9), onSerialReceive, RISING); // Attach interrupt to PA9 (TX pin)
}

void loop() {
    // Main code here
}

void onSerialReceive() {
    if (Serial.available()) {
        char c = Serial.read(); // Read the incoming data
        Serial.print("Received: ");
        Serial.println(c);
    }
}
                

I2C Interrupt Example


// STM32 I2C Interrupt Example

#include 

void setup() {
    Wire.begin();           // Initialize I2C as master
    Wire.onReceive(onI2CReceive);  // Attach interrupt for data reception
}

void loop() {
    // Main code here
}

void onI2CReceive(int numBytes) {
    while (Wire.available()) {
        char c = Wire.read(); // Read received data
        Serial.print("Received: ");
        Serial.println(c);
    }
}
                

SPI Interrupt Example


// STM32 SPI Interrupt Example

#include 

void setup() {
    SPI.begin();            // Initialize SPI as master
    attachInterrupt(digitalPinToInterrupt(MISO), onSPIReceive, FALLING); // Attach interrupt to MISO pin
}

void loop() {
    // Main code here
}

void onSPIReceive() {
    // Handle SPI data reception
    byte receivedData = SPI.transfer(0x00); // Read received data
    Serial.print("Received: ");
    Serial.println(receivedData, HEX);
}
                

CAN Interrupt Example


// TM32 CAN Interrupt Example



CAN_message_t message;

void setup() {
    CAN.begin();            // Initialize CAN bus
    attachInterrupt(digitalPinToInterrupt(PB8), onCANReceive, FALLING); // Attach interrupt to CAN RX pin
}

void loop() {
    // Main code here
}

void onCANReceive() {
    if (CAN.available()) {
        CAN.read(message); // Read incoming CAN message
        Serial.print("CAN Message: ");
        Serial.println(message.id, HEX);
    }
}
                

ADC Interrupt Example


// STM32 ADC Interrupt Example



volatile uint16_t adcValue = 0;

void setup() {
    Serial.begin(115200);  // Initialize serial communication
    ADC_Init(ADC1);        // Initialize ADC1
    ADC_AttachInterrupt(ADC1, onADCComplete); // Attach ADC interrupt
    ADC_Start(ADC1);       // Start ADC conversion
}

void loop() {
    // Main code can run here, interrupt will handle ADC conversions
}

void onADCComplete() {
    adcValue = ADC_Read(ADC1);  // Read ADC value
    Serial.print("ADC Value: ");
    Serial.println(adcValue);
}
                

Timer Interrupt Example


// STM32 Timer Interrupt Example

HardwareTimer timer(TIM1);

void setup() {
    Serial.begin(115200);     // Initialize serial communication
    timer.pause();            // Pause the timer before setting it up
    timer.setPeriod(1000000); // Set timer period to 1 second (1,000,000 µs)
    timer.attachInterrupt(onTimerInterrupt); // Attach interrupt handler
    timer.resume();           // Start the timer
}

void loop() {
    // Main code runs here
}

void onTimerInterrupt() {
    Serial.println("Timer Interrupt Triggered!");
}
                
STM32 RTC with Alarm Example

The Real-Time Clock (RTC) on STM32 microcontrollers is a peripheral that provides calendar and clock functionality. It keeps track of the time and date, even when the microcontroller is powered off (if a backup battery is connected). In this example, we will configure the RTC and set up an alarm that will trigger an event when a specific time is reached.

Features

  • Set up the RTC to keep track of time (hours, minutes, and seconds).
  • Configure an alarm to trigger at a specific time.
  • Use an interrupt to handle the alarm event.

RTC with Alarm Example Code


    
// STM32 RTC with Alarm Example
#include <Wire.h>   // Optional if using external RTC modules
#include <RTClock>

// RTC object using LSI (Low Speed Internal) clock
RTClock rtc(RTCSEL_LSI);

// Callback function for alarm interrupt
void alarmCallback() {
    Serial.println("Alarm Triggered!");
}

void setup() {
    Serial.begin(115200);  // Initialize serial communication

    // Initialize RTC
    rtc.begin();  // Start RTC with internal LSI oscillator

    // Set time: hours, minutes, seconds
    rtc.setTime(12, 30, 0);  // 12:30:00 PM

    // Set date: day, month, year
    rtc.setDate(1, 1, 2024);  // January 1st, 2024

    // Display the current time and date
    printTimeAndDate();

    // Set alarm: Set to trigger at 12:31:00
    rtc.setAlarmTime(12, 31, 0);  // 12:31:00 PM
    rtc.attachInterrupt(alarmCallback);  // Attach alarm interrupt

    // Enable alarm
    rtc.enableAlarm();
}

void loop() {
    // Continuously print the time every second
    printTimeAndDate();
    delay(1000);  // 1 second delay
}

// Function to print current time and date
void printTimeAndDate() {
    // Get current time
    int hour = rtc.getHours();
    int minute = rtc.getMinutes();
    int second = rtc.getSeconds();

    // Get current date
    int day = rtc.getDay();
    int month = rtc.getMonth();
    int year = rtc.getYear();

    // Print time and date to Serial Monitor
    Serial.print("Current Time: ");
    Serial.print(hour); Serial.print(":");
    Serial.print(minute); Serial.print(":");
    Serial.println(second);

    Serial.print("Current Date: ");
    Serial.print(day); Serial.print("/");
    Serial.print(month); Serial.print("/");
    Serial.println(year);
}
                

Explanation of the Code

In this example, the STM32's built-in RTC is configured to keep track of the current time and date. We also set an alarm to trigger at a specific time (12:31:00 in this case). When the alarm time is reached, an interrupt is triggered, and the callback function alarmCallback() is executed, printing "Alarm Triggered!" to the Serial Monitor.

Key Functions:

  • rtc.begin(): Initializes the RTC using the LSI clock.
  • rtc.setTime(hour, minute, second): Sets the current time on the RTC.
  • rtc.setDate(day, month, year): Sets the current date on the RTC.
  • rtc.setAlarmTime(hour, minute, second): Configures the alarm time.
  • rtc.attachInterrupt(callback): Attaches the callback function that will be called when the alarm is triggered.
USB on STM32 Microcontrollers - Detailed Description

The Universal Serial Bus (USB) is a widely used interface for communication between devices and host systems. STM32 microcontrollers come with built-in USB peripherals that allow them to act as a USB device or even a USB host, enabling flexible applications like USB mass storage, virtual COM ports (CDC), HID devices (mouse/keyboard), and more. This guide provides a detailed description of the USB architecture, features, and typical uses on STM32 microcontrollers.

USB Features on STM32

  • USB Device and Host Support: STM32 microcontrollers can operate as both USB devices and USB hosts, depending on the specific series (e.g., STM32F1, STM32F4, STM32H7).
  • Full-Speed (FS) and High-Speed (HS) Support: STM32 supports USB 2.0 Full-Speed (12 Mbps) and, in certain models, USB 2.0 High-Speed (480 Mbps) communication.
  • Peripheral Modes: STM32 can be configured as various types of USB devices, such as Mass Storage Device (MSC), Communication Device Class (CDC), Human Interface Device (HID), and more.
  • USB OTG (On-The-Go): STM32 devices with OTG support can switch between device and host roles dynamically.
  • USB Power Management: STM32 manages power for USB devices, including handling VBUS detection and control.
  • Support for USB Libraries: STM32 provides support for the USB Device Library, USB Host Library, and USB OTG Middleware, simplifying USB application development.

USB Architecture on STM32

STM32 microcontrollers with USB support have dedicated USB peripherals, which handle the low-level protocol and electrical interface. The USB controller communicates with the microcontroller’s core through a set of registers and interrupts. Depending on the USB mode (device or host), different USB-related endpoints and functions are enabled.

USB Device Mode

In device mode, the STM32 acts as a USB peripheral, communicating with a host system (such as a PC). Common applications of STM32 in USB device mode include:

  • CDC (Communications Device Class): Used to create virtual COM ports for serial communication over USB, replacing traditional RS-232 ports.
  • MSC (Mass Storage Class): Used to emulate a USB flash drive or external storage device, allowing data transfer between the STM32 and a host.
  • HID (Human Interface Device): Used to create devices like USB mice, keyboards, or game controllers.

USB Host Mode

In host mode, the STM32 acts as the master, controlling USB peripherals connected to it. For example, it can read data from a USB flash drive or communicate with a USB keyboard. Some STM32 devices, such as those with OTG (On-The-Go) support, can switch dynamically between host and device roles.

USB OTG (On-The-Go)

STM32 microcontrollers with USB OTG support can operate as either a USB device or a USB host. OTG allows dynamic role switching based on the connected device. For example, an STM32 OTG-enabled microcontroller can connect to a PC as a device or control a USB flash drive as a host.

USB Data Transfer Types

USB communication consists of four main types of data transfer:

  • Control Transfers: Used for sending control information, such as device setup requests. Control transfers are essential for device enumeration and configuration.
  • Bulk Transfers: Used for large amounts of data without strict timing requirements, such as file transfers in USB mass storage devices.
  • Interrupt Transfers: Used for sending small amounts of data with guaranteed latency, often used in HID devices like keyboards or mice.
  • Isochronous Transfers: Used for time-critical data such as audio or video streaming, where data must be delivered at regular intervals.

Common STM32 USB Applications

  • Virtual COM Ports: Using the CDC class, STM32 can replace RS-232 serial ports, providing serial communication over USB.
  • USB Mass Storage Devices: STM32 can act as a USB flash drive or external storage, enabling data transfer between the MCU and a host computer.
  • Human Interface Devices (HID): STM32 can be configured as a USB keyboard, mouse, joystick, or other input devices.
  • USB Host for Flash Drives: STM32 can act as a host to communicate with USB storage devices, such as reading or writing data from a USB flash drive.

Example USB Setup on STM32

The following is an example of setting up USB communication on an STM32 microcontroller using the CDC (Communication Device Class) to emulate a virtual COM port:



// STM32 USB CDC (Virtual COM Port) Example

#include "usbd_core.h"
#include "usbd_cdc.h"
#include "usb_device.h"

// Function to initialize USB
void USB_Init(void) {
    MX_USB_DEVICE_Init(); // Initialize the USB device
}

// Main program loop
int main(void) {
    HAL_Init();            // Initialize the HAL Library
    SystemClock_Config();  // Configure the system clock

    // Initialize the USB device (CDC class for Virtual COM Port)
    USB_Init();

    while (1) {
        // Continuously process USB communication
        MX_USB_DEVICE_Process();
    }
}

// Example function for sending data over USB CDC
void CDC_TransmitData(uint8_t* data, uint16_t length) {
    CDC_Transmit_FS(data, length);  // Send data to the host via USB
}

        

Steps to Implement USB on STM32

  1. Configure the USB Peripheral: In STM32CubeMX, enable the USB peripheral and configure it for the desired USB mode (Device or Host).
  2. Select the USB Class: Choose the appropriate USB class (e.g., CDC for virtual COM port, MSC for mass storage) based on your application.
  3. Generate Code: Generate the initialization code using STM32CubeMX, which will automatically set up the USB peripheral.
  4. Modify the Application Code: Customize the application code to handle specific USB tasks, such as sending or receiving data.
  5. Test the USB Connection: Connect the STM32 to a host system (e.g., a PC) and test the USB communication (e.g., using a terminal program for CDC devices).

Conclusion

USB is a powerful and versatile communication interface, and STM32 microcontrollers offer extensive support for USB peripherals, including device, host, and OTG modes. With STM32’s USB libraries and middleware, developers can easily integrate USB functionality into their projects, enabling applications like virtual COM ports, mass storage devices, HID devices, and more.

Understanding DMA (Direct Memory Access)

Direct Memory Access (DMA) allows peripherals to transfer data directly to/from memory without CPU involvement. This improves system performance in real-time applications by offloading data transfer tasks.

How DMA Works

Without DMA, the CPU handles all peripheral-to-memory or memory-to-peripheral transfers. DMA automates this process, reducing CPU cycles and enabling efficient high-speed data handling.

Basic DMA Operation

DMA operations follow this sequence:

  1. Initialization: CPU configures DMA controller with source, destination, size, and mode.
  2. Trigger: DMA transfer is triggered by a peripheral event or CPU software trigger.
  3. Transfer: DMA moves data between peripheral and memory autonomously.
  4. Completion: DMA can generate an interrupt to notify the CPU after transfer completion.
DMA Transfer Modes
  • Normal Mode: Transfers a fixed number of items, then stops.
  • Circular Mode: Continuously transfers data in a circular buffer for real-time streams.
  • Peripheral-to-Memory: Transfers data from peripherals to memory (e.g., ADC data).
  • Memory-to-Peripheral: Transfers data from memory to peripherals (e.g., UART transmission).
  • Memory-to-Memory: Transfers data between memory regions for fast copying or rearrangement.
DMA Trigger Sources
  • Peripheral Requests: Peripherals trigger DMA when ready to send/receive data (e.g., UART, SPI, ADC).
  • Timer Events: Timers trigger DMA at intervals for time-sensitive transfers.
  • Software Triggers: CPU can initiate DMA directly via software commands.
Benefits of Using DMA
  • Reduced CPU Load: CPU is free for other tasks.
  • Higher Data Throughput: High-speed, uninterrupted transfers for audio, video, or sensor data.
  • Low Latency: Minimal transfer latency improves responsiveness in real-time applications.
DMA Limitations
  • Peripheral Compatibility: Only DMA-capable peripherals can use DMA.
  • Memory Access Conflicts: DMA competes with CPU for memory bus access, which can affect performance.
Conclusion

DMA optimizes data transfer efficiency, reduces CPU load, and enhances real-time performance. Properly configuring DMA allows embedded systems to handle complex, responsive applications with minimal CPU intervention.

STM32 Bluepill Pin DMA Matrix for STM32F103
Peripheral Associated DMA Channels
Timer 1 (TIM1)DMA Channel 1, DMA Channel 5
Timer 2 (TIM2)DMA Channel 2, DMA Channel 3, DMA Channel 7
Timer 3 (TIM3)DMA Channel 4, DMA Channel 6
Timer 4 (TIM4)DMA Channel 1, DMA Channel 7
ADC1DMA Channel 1
SPI1 (TX, RX)DMA Channel 2 (TX), DMA Channel 3 (RX)
SPI2 (TX, RX)DMA Channel 4 (TX), DMA Channel 5 (RX)
I2C1 (TX, RX)DMA Channel 6 (TX), DMA Channel 7 (RX)
I2C2 (TX, RX)DMA Channel 4 (TX), DMA Channel 5 (RX)
USART1 (TX, RX)DMA Channel 4 (TX), DMA Channel 5 (RX)
USART2 (TX, RX)DMA Channel 6 (TX), DMA Channel 7 (RX)
USART3 (TX, RX)DMA Channel 2 (TX), DMA Channel 3 (RX)
DMA ChannelAssociated Peripherals
DMA Channel 1TIM1, TIM4, ADC1
DMA Channel 2TIM2, USART3 (TX), SPI1 (TX)
DMA Channel 3TIM2, USART3 (RX), SPI1 (RX)
DMA Channel 4TIM3, I2C2 (TX), USART1 (TX), SPI2 (TX)
DMA Channel 5TIM1, USART1 (RX), SPI2 (RX), I2C2 (RX)
DMA Channel 6TIM3, I2C1 (TX), USART2 (TX)
DMA Channel 7TIM2, I2C1 (RX), USART2 (RX)
DMA ChannelTimer Channel Trigger
DMA Channel 1Timer 2 Channel 1, Timer 3 Channel 3
DMA Channel 2Timer 2 Channel 4
DMA Channel 3Timer 3 Channel 4
DMA Channel 4Timer 1 Channel 1, Timer 3 Channel 1
DMA Channel 5Timer 1 Channel 4, Timer 2 Channel 1
DMA Channel 6Timer 1 Channel 2, Timer 3 Channel 2
DMA Channel 7Timer 2 Channel 2, Timer 1 Channel 3
Requesting Device Valid DMA Channels DMA Channel Valid Requesting Devices
ADC1DMA1 Channel 1Channel 1ADC1
SPI1_RXDMA1 Channel 2Channel 2SPI1_RX, TIM2_CH3, TIM4_UP
SPI1_TXDMA1 Channel 3Channel 3SPI1_TX, TIM2_CH4, TIM4_CH1
USART1_RXDMA1 Channel 5Channel 5USART1_RX, TIM1_UP, TIM2_CH1
USART1_TXDMA1 Channel 4Channel 4USART1_TX, TIM1_CH1, TIM2_UP
USART2_RXDMA1 Channel 6Channel 6USART2_RX, TIM1_CH2, TIM3_CH3
USART2_TXDMA1 Channel 7Channel 7USART2_TX, TIM2_CH2, TIM3_CH4
USART3_RXDMA1 Channel 3Channel 3USART3_RX, TIM2_CH4, TIM4_CH1
USART3_TXDMA1 Channel 2Channel 2USART3_TX, TIM2_CH3, TIM4_UP
TIM1_UPDMA1 Channel 5Channel 5TIM1_UP, USART1_RX, TIM2_CH1
TIM1_CH1DMA1 Channel 4Channel 4TIM1_CH1, USART1_TX, TIM2_UP
TIM1_CH2DMA1 Channel 6Channel 6TIM1_CH2, USART2_RX, TIM3_CH3
TIM1_CH3DMA1 Channel 6Channel 6TIM1_CH3, TIM3_CH3, TIM4_UP
TIM2_CH1DMA1 Channel 5Channel 5TIM2_CH1, TIM1_UP, USART1_RX
TIM2_CH2DMA1 Channel 7Channel 7TIM2_CH2, USART2_TX, TIM3_CH4
TIM2_CH3DMA1 Channel 2Channel 2TIM2_CH3, USART3_TX, SPI1_RX
TIM2_CH4DMA1 Channel 3Channel 3TIM2_CH4, USART3_RX, SPI1_TX
TIM3_CH3DMA1 Channel 6Channel 6TIM3_CH3, TIM1_CH2, USART2_RX
TIM3_CH4DMA1 Channel 7Channel 7TIM3_CH4, TIM2_CH2, USART2_TX
SPI2_RXDMA1 Channel 4Channel 4SPI2_RX, TIM2_UP, TIM1_CH1
SPI2_TXDMA1 Channel 5Channel 5SPI2_TX, TIM1_UP, TIM2_CH1
Programming The STM32 (Blue Pill) Using Arduino IDE

This page explains the different methods of programming the STM32F103 Blue Pill using the Arduino IDE: Serial Bootloader, ST-Link SWD, and USB DFU. Instructions are provided for both Windows and Linux hosts.

Required software:
Arduino IDE: https://www.arduino.cc/en/software
STM32 Arduino cores: https://github.com/stm32duino/Arduino_Core_STM32
DFU-UTIL: http://dfu-util.sourceforge.net/
OpenOCD: http://openocd.org/
ST-Link Utilities: https://www.st.com/en/development-tools/stsw-link004.html

Linux users can install most tools via apt:

sudo apt update && sudo apt install arduino dfu-util openocd stlink-tools

Configuring Arduino IDE to Use STM32

Before uploading code to your STM32, you need to install the STM32 cores in Arduino IDE.

Step 1: Add STM32 Board Manager URL

  • Open Arduino IDE → File → Preferences
  • In “Additional Board Manager URLs” paste:
    https://github.com/stm32duino/BoardManagerFiles/raw/main/package_stmicroelectronics_index.json
  • Click OK to save

Step 2: Install STM32 Core

  • Tools → Board → Boards Manager
  • Search for “STM32”
  • Select STM32 MCU based boards by STMicroelectronics and click Install

Step 3: Select Board and Upload Method

  • Tools → Board → STM32 Boards → Generic STM32F1 series
  • Tools → Board Part Number → BluePill F103C8
  • Tools → Upload Method → choose Serial, STLink, or DFU

Step 4: Linux Command Line Verification


# Check if Arduino IDE is installed
arduino --version

# Verify board upload method packages
ls ~/.arduino15/packages/stmicroelectronics/hardware/stm32/
    

Step 5: Windows Verification


# Open Arduino IDE
# File → Preferences → check "Additional Board Manager URLs"
# Tools → Board → Boards Manager → STM32 installed
# Ensure USB drivers or ST-Link drivers are installed
    

After completing these steps, your Arduino IDE is ready to upload sketches to STM32 Blue Pill boards.

Programming via Serial (Built-in ROM Bootloader)

The STM32F103 includes a factory ROM bootloader that supports programming over UART1 (pins A9 = TX, A10 = RX).

Hardware Setup

  • USB-to-Serial adapter (3.3V output required!)
  • Connect A9 → RX, A10 → TX
  • BOOT0 pin → High, BOOT1 → Low
  • Power the board normally via USB or 5V pin

Upload in Arduino IDE

  • Tools → Upload Method → Serial
  • Select correct COM/TTY port
  • Click Upload

Linux example (check serial port)


ls /dev/ttyUSB*
dmesg | grep ttyUSB
sudo apt install arduino
      

Windows example (check COM port)


mode
wmic path Win32_SerialPort get DeviceID,Name
Download Arduino IDE: https://www.arduino.cc/en/software
      
Programming via ST-Link (SWD Debug Port)

ST-Link is the most reliable method for programming and debugging the STM32F103. It uses the SWD interface: SWDIO, SWCLK, 3.3V, GND.

Wiring

  • SWCLK → SWCLK
  • SWDIO → SWDIO
  • 3.3V → 3.3V
  • GND → GND

Arduino IDE Settings

  • Tools → Upload Method → STLink

Linux: verify ST-Link


sudo apt install openocd stlink-tools
lsusb | grep ST-LINK
stm32flash -l
openocd -f interface/stlink.cfg -f target/stm32f1x.cfg
      

Windows: verify ST-Link


Download ST-Link Utilities: https://www.st.com/en/development-tools/stsw-link004.html
st-info --probe
st-flash read firmware.bin 0x08000000 0x10000
      
Programming via USB DFU Bootloader

The Blue Pill does NOT come with a USB bootloader by default. You must flash one first using Serial or ST-Link. After that, the board can be programmed directly over USB using DFU mode.

Entering DFU Mode

  • BOOT0 → High, BOOT1 → Low
  • Press reset
  • Board enumerates as DFU device

Linux: list DFU devices


sudo apt install dfu-util
dfu-util -l
dfu-util -a 0 -s 0x08000000:leave -D firmware.bin
      

Windows: using dfu-util


Download dfu-util.exe: http://dfu-util.sourceforge.net/
dfu-util.exe -l
dfu-util.exe -a 0 -D firmware.bin -s 0x08000000:leave
      
STM32® – Legal & Trademark Notice

STM32® is a registered trademark of STMicroelectronics. Visit STMicroelectronics website. For technical reference, see the STM32 family microcontrollers overview page.

© 2025 STMicroelectronics. All rights reserved.

STM32 Boards & Development Kits

Includes Nucleo, Discovery, and bare MCU boards — suitable for hobbyist to professional development.