STM32 Bluepill Pin Description and Functions

The STM32 Bluepill is a powerful microcontroller development board that utilizes the STM32F103C8T6 chip. It offers various pins that serve multiple functions, including GPIO, ADC, UART, I2C, and SPI. Below is a comprehensive breakdown of the pin functions and alternate uses:

STM32 Bluepill pinout

STM32 Bluepill Peripherals

The STM32 Bluepill is based on the STM32F103C8T6 microcontroller, part of the STM32 ARM Cortex-M3 family. It offers a wide array of peripherals, making it versatile for various applications. Below is a description of the major peripherals available on the Bluepill:

1. Digital I/O

The STM32 Bluepill has up to 37 GPIO pins, distributed across Port A, Port B, and Port C. These pins can function as general-purpose input/output (GPIO) or be configured for alternate peripheral functions, including timers, PWM, communication protocols, and more. Key features include:

2. Analog Inputs (ADC)

The STM32 Bluepill has a 12-bit ADC (Analog-to-Digital Converter) that supports up to 10 analog input channels (Pins PA0–PA7, PB0, PB1). These channels can read analog signals and convert them to a digital value ranging from 0 to 4095. The ADC can be used for:

3. PWM (Pulse-Width Modulation)

The STM32 Bluepill provides PWM output through its timers, enabling pulse-width modulation on various pins. These are ideal for motor control, LED dimming, and audio signals. Key pins supporting PWM include PA8, PA9, PA10, PB6, PB7, and others depending on the timer configuration.

4. UART (Serial Communication)

The STM32 Bluepill has up to 3 UART interfaces:

5. SPI (Serial Peripheral Interface)

The STM32 Bluepill supports up to 2 SPI interfaces for high-speed synchronous communication with peripherals such as sensors, displays, and SD cards:

6. I2C (Inter-Integrated Circuit)

The STM32 Bluepill provides 2 I2C interfaces for communication with multiple devices on the same bus:

7. Timers

The STM32F103C8T6 microcontroller has several timers that support various functions such as PWM generation, input capture, and timekeeping:

8. USB OTG

The STM32 Bluepill includes a USB interface (PA11 - USB_DM, PA12 - USB_DP) that supports USB On-The-Go (OTG) functionality. This allows the microcontroller to function as either a USB device or USB host, enabling communication with a PC or USB peripherals.

9. CAN (Controller Area Network)

The STM32 Bluepill has a CAN bus interface (PB8 - CAN_RX, PB9 - CAN_TX), which allows for communication with automotive and industrial systems, making it ideal for robotics and vehicle communication systems.

10. External Interrupts

All GPIO pins on the STM32 Bluepill can be configured to trigger external interrupts on either the rising or falling edge of a signal. This allows for responsive input handling such as button presses, sensor readings, or other events.

11. SWD (Serial Wire Debug)

The STM32 Bluepill provides SWD (Serial Wire Debug) functionality via pins PA13 (SWDIO) and PA14 (SWCLK), allowing for in-circuit debugging and programming of the microcontroller using debugging tools such as ST-Link.

12. RTC (Real-Time Clock)

The STM32 Bluepill features a built-in real-time clock (RTC), allowing for timekeeping functions that continue even when the main microcontroller is powered down, using a backup battery connected to the VBAT pin.

Installing STM32 Support in Arduino IDE

To develop projects using STM32 microcontrollers with the Arduino IDE, you need to install the STM32 board support package. Follow these steps to set up STM32 development in Arduino IDE.

Step-by-Step Installation Guide

1. Open Arduino IDE

Launch the Arduino IDE on your computer. If you haven't installed it yet, download and install it from the Arduino website.

2. Open Preferences

Go to File > Preferences (or Arduino > Preferences on macOS) to open the Preferences window.

Preferences Window

3. Add STM32 Boards URL

In the Preferences window, find the Additional Boards Manager URLs field. Add the following URL to this field:

http://dan.drown.org/stm32duino/package_STM32duino_index.json

If you already have other URLs listed, separate them with commas.

4. Open Boards Manager

Go to Tools > Board > Boards Manager to open the Boards Manager window.

Boards Manager

5. Install STM32 Boards

In the Boards Manager window, type STM32 in the search bar. Find the entry for STM32duino and click the Install button.

Boards Manager Search

6. Select STM32 Board

After installation, go to Tools > Board and select the STM32 board you want to use from the list. For example, select Blue Pill F103C8 if you are using the STM32F103 Blue Pill board.

Select Board

7. Install STM32 Core Libraries

For advanced functionality, you may need to install additional core libraries for your specific STM32 board. Check the STM32duino GitHub repository for more details and additional libraries if needed.

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
STM32F411CE (Black Pill) 100 MHz (ARM Cortex-M4) 128 KB 512 KB 37 GPIO, UART, SPI, I2C, ADC, PWM, USB OTG 3.3V Higher performance than Blue Pill, with USB OTG
STM32F401RE (Nucleo F401RE) 84 MHz (ARM Cortex-M4) 96 KB 512 KB 50 GPIO, UART, SPI, I2C, ADC, DAC, PWM 3.3V Compatible with Arduino IDE and libraries, built-in ST-LINK
STM32F407G-DISC1 (Discovery Kit) 168 MHz (ARM Cortex-M4) 192 KB 1 MB 50 GPIO, UART, SPI, I2C, CAN, ADC, DAC, PWM, Ethernet 3.3V Features audio codec, MEMS accelerometer, Ethernet support
STM32H743ZI (Nucleo H743ZI) 480 MHz (ARM Cortex-M7) 1 MB 2 MB 144 GPIO, UART, SPI, I2C, CAN, ADC, DAC, PWM, Ethernet 3.3V High-performance, ideal for advanced applications
STM32L476RG (Nucleo L476RG) 80 MHz (ARM Cortex-M4) 128 KB 1 MB 50 GPIO, UART, SPI, I2C, CAN, ADC, DAC, PWM 3.3V Ultra-low power, energy-efficient, ideal for battery-powered projects
STM32F746G-DISCO (Discovery Kit) 216 MHz (ARM Cortex-M7) 320 KB 1 MB 100 GPIO, UART, SPI, I2C, CAN, ADC, DAC, PWM, Ethernet, LCD support 3.3V Includes touchscreen LCD, multimedia-rich, advanced peripherals
STM32WB55RG (Nucleo WB55RG) 64 MHz (ARM Cortex-M4) + 32 MHz (Cortex-M0+) 256 KB 1 MB 47 GPIO, UART, SPI, I2C, ADC, DAC, Bluetooth 5.0, ZigBee 3.3V Wireless connectivity, dual-core architecture, BLE & ZigBee support
STM32F446RE (Nucleo F446RE) 180 MHz (ARM Cortex-M4) 128 KB 512 KB 50 GPIO, UART, SPI, I2C, CAN, ADC, DAC, PWM 3.3V High-speed peripherals, ideal for applications requiring precise timing
STM32G031K6 (Nucleo G031K8) 64 MHz (ARM Cortex-M0+) 8 KB 32 KB 21 GPIO, UART, SPI, I2C, ADC, PWM 3.3V Entry-level board, designed for cost-sensitive applications
STM32F769I-DISCO (Discovery Kit) 216 MHz (ARM Cortex-M7) 512 KB 2 MB 62 GPIO, UART, SPI, I2C, CAN, ADC, DAC, PWM, Ethernet, LCD, USB OTG 3.3V Advanced multimedia, Ethernet, USB, large TFT touchscreen display

STM32F Series Feature Matrix

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 and Examples

The STM32F103 Blue Pill features an ADC (Analog-to-Digital Converter) that allows conversion of an analog signal to a digital value. This is essential for reading analog sensors and other analog input devices.

More information on Successive Approximation Conversion

Key Features of STM32 ADC

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 popular communication protocol that allows multiple devices to communicate over a two-wire bus. One device acts as the master, while the others are slaves. This guide demonstrates how to set up I2C communication on STM32 microcontrollers using the STM32duino library, with examples for both master and slave configurations.

Key Concepts

Master Example Code

This example demonstrates how to configure an STM32 microcontroller as an I2C master, send data to a slave device, and receive data from it.

Basic I2C Example (Master Mode):


#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 of data 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

This example demonstrates how to configure an STM32 microcontroller as an I2C slave, respond to requests from a master, and send data to it.


// 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 the master and slave STM32 boards together using the SDA and SCL lines. Ensure you have pull-up resistors on both lines (typically 4.7kΩ to 10kΩ).
  2. Select Board: In the Arduino IDE, go to Tools > Board and select the appropriate STM32 board for both master and slave.
  3. Select Port: Choose the COM port assigned to each STM32 board in the Arduino IDE.
  4. Upload Code: Upload the master code to one STM32 board and the slave code to the other.
  5. Monitor Communication: Open the Serial Monitor for 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 to facilitate communication among various in-vehicle systems without a host computer. STM32 microcontrollers feature integrated CAN controllers for implementing CAN bus communication.

Key Concepts

Example Code

This example demonstrates how to set up CAN communication on an STM32 microcontroller using the STM32duino library. It includes code for 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 using a USB cable.
  2. CAN Transceiver: Ensure you have a CAN transceiver connected to the STM32 CAN pins (e.g., MCP2551) and connected to a CAN bus network.
  3. Select Board: In the 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 COM port associated with your STM32 board.
  5. Upload Code: Upload the example code to your STM32 board.
  6. Monitor CAN Bus: Use a CAN bus analyzer or another STM32 board configured to receive CAN messages to observe communication on the CAN bus.

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

Master Example Code

This example demonstrates how to configure an STM32 microcontroller as an SPI master. It sends and receives data to/from 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 Code


// 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 (should match master)
    SPI.setDataMode(SPI_MODE0); // Set SPI mode (should match master)
    SPI.setBitOrder(MSBFIRST); // Set bit order (should match master)
    SPI.usingInterrupt(); // Enable interrupts for SPI
    attachInterrupt(digitalPinToInterrupt(MISO), onSPIReceive, FALLING); // Attach SPI receive interrupt
}

void loop() {
    // Main loop does nothing, all actions are 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 - Detailed Description

Understanding Timers in Microcontrollers (MCUs) like STM32

Timers are one of the most versatile peripherals found in MCUs, including the popular STM32 series. They are essential for various time-based functions such as generating precise delays, handling real-time events, and generating pulse-width modulation (PWM) signals. STM32 timers offer advanced features beyond basic timekeeping, making them critical in embedded systems.

Basic Concepts of Timers

A timer is essentially a counter that increments or decrements at a specific clock rate, and when it reaches a defined value, it triggers an event. Timers operate using the system clock or an external clock source, and their main purpose is to measure time or generate periodic interrupts. Timers in STM32 MCUs are usually highly configurable and have multiple modes to suit different use cases.

Types of Timers in STM32

STM32 microcontrollers typically feature several types of timers, categorized as:

Timer Registers

Timers are controlled using several registers, which dictate how they count, compare values, and trigger interrupts or other events. Some of the key registers used in STM32 timers are:

STM32 microcontrollers, when programmed using the Arduino framework, offer powerful and flexible timers that can be used for a variety of applications.

STM32 timers are essential for time-based operations such as generating PWM signals, creating time delays, and counting events. When using STM32 with the Arduino IDE, you can take advantage of the hardware timers provided by the STM32 microcontroller to accomplish these tasks efficiently.

Interrupts: Timers can generate interrupts to execute specific actions at set intervals.

Examples of Using STM32 Timers with Arduino

1. Basic Timer Example

This example demonstrates how to use a basic timer to create a time delay.



// STM32 Basic Timer Example

<Arduino>

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

    // Initialize Timer6
    Timer6.init();
    Timer6.setPeriod(1000); // Set period to 1000 milliseconds
    Timer6.attachInterrupt(timerCallback);
}

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

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

2. PWM Signal Generation

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



// STM32 PWM Example

<Arduino>

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

    // Initialize Timer2 for PWM on pin D9
    Timer2.setMode(PWM, D9);
    Timer2.setPWM(D9, 1000, 512); // 1 kHz frequency, 50% duty cycle
}

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

3. Timer with Input Capture

This example shows how to use a general-purpose timer to capture the input signal on a pin.



// STM32 Timer Input Capture Example

<Arduino>

volatile uint32_t captureValue = 0;

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

    // Initialize Timer2 for input capture
    Timer2.setMode(INPUT_CAPTURE, D3); // Assuming input signal is on pin D3
    Timer2.attachInterrupt(inputCaptureCallback);
}

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

// Timer input capture callback function
void inputCaptureCallback() {
    captureValue = Timer2.getCapture();
}
    

Timers as triggers for DMA and peripherals

Timer Channel Peripherals Triggered
Timer 1 Channel 1 DMA Channel 4, ADC
Timer 1 Channel 2 DMA Channel 6
Timer 1 Channel 3 DMA Channel 7
Timer 1 Channel 4 DMA Channel 5
Timer 2 Channel 1 DMA Channel 1, DMA Channel 5
Timer 2 Channel 2 DMA Channel 7
Timer 2 Channel 4 DMA Channel 2
Timer 3 Channel 1 DMA Channel 4
Timer 3 Channel 2 DMA Channel 6
Timer 3 Channel 3 DMA Channel 1
Timer 3 Channel 4 DMA Channel 3

Triggers for timers

Timer Start/Stop Signals Trigger Inputs (External/Timers) Other Controls
Timer 1 - External signals (ETR input pin)
- Timer 2 (Master Mode)
- Timer 3 (Master Mode)
- External interrupt
- External trigger input (ETR)
- Timer 2/3/4 (Internal trigger from slave mode)
- Slave Mode Controller (reset, enable, gate)
- ADC (Trigger for ADC conversions)
Timer 2 - External signals (ETR input pin)
- Timer 1 (Master Mode)
- Timer 3 (Master Mode)
- External interrupt
- External trigger input (ETR)
- Timer 1/3/4 (Internal trigger from slave mode)
- Slave Mode Controller (reset, enable, gate)
- DMA (Used for DMA triggers)
Timer 3 - External signals (ETR input pin)
- Timer 1 (Master Mode)
- Timer 2 (Master Mode)
- External interrupt
- External trigger input (ETR)
- Timer 1/2/4 (Internal trigger from slave mode)
- Slave Mode Controller (reset, enable, gate)
- ADC (Trigger for ADC conversions)
Timer 4 - External signals (ETR input pin)
- Timer 1 (Master Mode)
- Timer 2/3 (Master Mode)
- External interrupt
- External trigger input (ETR)
- Timer 1/2/3 (Internal trigger from slave mode)
- Slave Mode Controller (reset, enable, gate)
- DMA (Used for DMA triggers)
Timer 5 - External signals (ETR input pin)
- Timer 2/3/4 (Master Mode)
- External interrupt
- External trigger input (ETR)
- Timer 2/3/4 (Internal trigger from slave mode)
- Slave Mode Controller (reset, enable, gate)
- DMA (Used for DMA triggers)
Timer 6 - External signals (ETR input pin)
- Timer 2/3/4 (Master Mode)
- External interrupt
- External trigger input (ETR)
- Timer 2/3/4 (Internal trigger from slave mode)
- Slave Mode Controller (reset, enable, gate)
- DAC (Used for DAC trigger)
Timer 7 - External signals (ETR input pin)
- Timer 2/3/4 (Master Mode)
- External interrupt
- External trigger input (ETR)
- Timer 2/3/4 (Internal trigger from slave mode)
- Slave Mode Controller (reset, enable, gate)
- DAC (Used for DAC trigger)
Timer 8 - External signals (ETR input pin)
- Timer 2/3/4 (Master Mode)
- External interrupt
- External trigger input (ETR)
- Timer 2/3/4 (Internal trigger from slave mode)
- Slave Mode Controller (reset, enable, gate)
- ADC (Trigger for ADC conversions)

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

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 <Wire.h>

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 <SPI.h>

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

<STM32CAN>

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

<ADC>

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

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:

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 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:

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:

Common STM32 USB Applications

STM32 microcontrollers are commonly used in a wide range of USB applications, such as:

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) is a feature used in microcontrollers and other computing systems to transfer data directly between peripherals and memory, bypassing the CPU. By offloading these data transfers to the DMA controller, the CPU can perform other tasks, significantly improving system performance, especially in real-time applications.

How DMA Works

In a typical embedded system without DMA, the CPU is responsible for reading data from a peripheral (e.g., an ADC, UART, or SPI) and writing it to memory, or vice versa. This process consumes significant CPU cycles, especially for high-frequency or large-volume data transfers. DMA automates these transfers by allowing peripherals to communicate directly with memory, reducing the CPU's involvement.

Basic DMA Operation

The DMA controller operates as a separate entity within the microcontroller, managing the data transfer between memory and peripherals. The following sequence outlines the basic operation of a DMA transfer:

  1. Initialization: The CPU configures the DMA controller, specifying the source and destination addresses, the size of the data to be transferred, and the transfer mode.
  2. Trigger: The DMA transfer is triggered by an event, such as a peripheral request (e.g., a timer, UART, or ADC) or a software trigger initiated by the CPU.
  3. Transfer: Once triggered, the DMA controller reads the data from the source (peripheral or memory) and writes it to the destination (memory or peripheral), without CPU intervention.
  4. Completion: After the transfer is complete, the DMA controller can generate an interrupt to notify the CPU, allowing the CPU to handle post-transfer processing if necessary.

DMA Transfer Modes

DMA supports several modes of operation, depending on the needs of the application:

DMA Trigger Sources

DMA transfers can be triggered by a variety of sources, including:

Benefits of Using DMA

DMA provides several advantages over CPU-driven data transfers:

DMA Limitations

Despite its benefits, DMA has some limitations:

Conclusion

DMA is an essential feature in embedded systems, offering improved data transfer efficiency, reduced CPU load, and enhanced real-time performance. By offloading data transfer tasks from the CPU to the DMA controller, system resources are optimized, allowing for more complex and responsive applications. Understanding how to configure and utilize DMA effectively is a key skill in embedded systems development.

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
ADC1 DMA 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 Channel Associated Peripherals
DMA Channel 1 TIM1, TIM4, ADC1
DMA Channel 2 TIM2, USART3 (TX), SPI1 (TX)
DMA Channel 3 TIM2, USART3 (RX), SPI1 (RX)
DMA Channel 4 TIM3, I2C2 (TX), USART1 (TX), SPI2 (TX)
DMA Channel 5 TIM1, USART1 (RX), SPI2 (RX), I2C2 (RX)
DMA Channel 6 TIM3, I2C1 (TX), USART2 (TX)
DMA Channel 7 TIM2, I2C1 (RX), USART2 (RX)
DMA Channel Timer Channel Trigger
DMA Channel 1 Timer 2 Channel 1, Timer 3 Channel 3
DMA Channel 2 Timer 2 Channel 4
DMA Channel 3 Timer 3 Channel 4
DMA Channel 4 Timer 1 Channel 1, Timer 3 Channel 1
DMA Channel 5 Timer 1 Channel 4, Timer 2 Channel 1
DMA Channel 6 Timer 1 Channel 2, Timer 3 Channel 2
DMA Channel 7 Timer 2 Channel 2, Timer 1 Channel 3
Requesting Device Valid DMA Channels DMA Channel Valid Requesting Devices
ADC1 DMA1 Channel 1 Channel 1 ADC1
SPI1_RX DMA1 Channel 2 Channel 2 SPI1_RX, TIM2_CH3, TIM4_UP
SPI1_TX DMA1 Channel 3 Channel 3 SPI1_TX, TIM2_CH4, TIM4_CH1
USART1_RX DMA1 Channel 5 Channel 5 USART1_RX, TIM1_UP, TIM2_CH1
USART1_TX DMA1 Channel 4 Channel 4 USART1_TX, TIM1_CH1, TIM2_UP
USART2_RX DMA1 Channel 6 Channel 6 USART2_RX, TIM1_CH2, TIM3_CH3
USART2_TX DMA1 Channel 7 Channel 7 USART2_TX, TIM2_CH2, TIM3_CH4
USART3_RX DMA1 Channel 3 Channel 3 USART3_RX, TIM2_CH4, TIM4_CH1
USART3_TX DMA1 Channel 2 Channel 2 USART3_TX, TIM2_CH3, TIM4_UP
TIM1_UP DMA1 Channel 5 Channel 5 TIM1_UP, USART1_RX, TIM2_CH1
TIM1_CH1 DMA1 Channel 4 Channel 4 TIM1_CH1, USART1_TX, TIM2_UP
TIM1_CH2 DMA1 Channel 6 Channel 6 TIM1_CH2, USART2_RX, TIM3_CH3
TIM1_CH3 DMA1 Channel 6 Channel 6 TIM1_CH3, TIM3_CH3, TIM4_UP
TIM2_CH1 DMA1 Channel 5 Channel 5 TIM2_CH1, TIM1_UP, USART1_RX
TIM2_CH2 DMA1 Channel 7 Channel 7 TIM2_CH2, USART2_TX, TIM3_CH4
TIM2_CH3 DMA1 Channel 2 Channel 2 TIM2_CH3, USART3_TX, SPI1_RX
TIM2_CH4 DMA1 Channel 3 Channel 3 TIM2_CH4, USART3_RX, SPI1_TX
TIM3_CH3 DMA1 Channel 6 Channel 6 TIM3_CH3, TIM1_CH2, USART2_RX
TIM3_CH4 DMA1 Channel 7 Channel 7 TIM3_CH4, TIM2_CH2, USART2_TX
SPI2_RX DMA1 Channel 4 Channel 4 SPI2_RX, TIM2_UP, TIM1_CH1
SPI2_TX DMA1 Channel 5 Channel 5 SPI2_TX, TIM1_UP, TIM2_CH1