How to Read a Datasheet If You Learned Embedded on Arduino

An open MCU datasheet showing register maps and pin diagrams next to an Arduino board

You’ve built projects with Arduino. You’ve wired up sensors, blinked LEDs, maybe even driven motors. Then someone told you to “check the datasheet,” so you downloaded a 1,200-page PDF, opened it, saw page after page of timing diagrams and hex addresses, and quietly closed it.

Here’s the thing: you were never supposed to read all 1,200 pages. A datasheet is a dictionary, not a novel. You look up what you need, get your answer, and close it. The people who seem fluent in datasheets aren’t smarter than you. They just know which 5 pages to look at for a given task.

By the end of this article, you’ll blink an LED on an STM32 Nucleo board using nothing but the datasheet, the reference manual, and a few lines of C. No HAL library. No Arduino core. Just you and the documentation.

Datasheet vs. Reference Manual: Know Your Documents

Most MCU vendors split their docs into (at least) two separate PDFs. This trips people up immediately.

The datasheet is the shorter one (100 to 200 pages). It covers the pinout, electrical characteristics, memory map overview, and a list of peripherals. Think of it as the spec sheet: what the chip has.

The reference manual is the beast (500 to 1,500 pages). It covers how every peripheral works, with full register maps and bit-field descriptions. This tells you how to actually use the chip.

For our walkthrough, we need two documents for the STM32C031 (the MCU on the NUCLEO-C031C6 board):

  • DS14310: the STM32C031x4/x6 datasheet
  • RM0490: the STM32C0x1xx reference manual

Both are free on ST’s website. Search “STM32C031 datasheet” and “RM0490” and you’ll find them.

Quick note: the ATmega328P on your Arduino Uno actually combines both roles into a single document. That’s the exception. Most vendors split them, and knowing which doc to open for which question saves you a lot of scrolling.

Start With the Goal, Not Page 1

Here’s the task: blink the user LED (LD4) on the NUCLEO-C031C6.

Work backward from that goal. You need answers to exactly 2 questions:

  1. What pin is the LED connected to?
  2. What registers do I configure to drive that pin high and low?

Question 1 lives in the board’s user manual (UM2953) or schematic. Search for “LD4” in that document. You’ll find that LD4 is on PA5, which is Port A, Pin 5.

Question 2 lives in the reference manual’s GPIO chapter.

Think of yourself as a detective, not a student. You have a specific question. The datasheet has the answer somewhere. Ctrl+F is your best friend. You’ll use it more than you use the table of contents.

Step 1: Find Your Pin in the Pinout and Alternate Function Table

Open the datasheet (DS14310), not the reference manual.

Flip (or search) to the pinout diagram. You’ll see a drawing of the chip package with every pin labeled. Find PA5. Confirm it exists, note its pin number on the physical package. You don’t strictly need the package pin number for a dev board (the Nucleo routes it for you), but it’s good practice to verify you’re looking at the right chip.

Then find the pin definition table (search “pin definitions” in the PDF). Look up PA5. You’ll see columns showing its default mode, its alternate functions (things like SPI1_SCK or TIM2_CH1), and confirmation that it can be used as a plain GPIO.

This is the equivalent of knowing that Arduino’s “pin 13” maps to PB5 on the ATmega328P, except you’re reading the map yourself instead of relying on the Arduino core to hide it from you.

Arduino World          Datasheet World
─────────────          ───────────────
"Pin 13"         →     PB5 (ATmega328P)
"LED_BUILTIN"    →     PA5 (STM32C031)

You’ve answered question 1. PA5 is a GPIO-capable pin connected to the LED. On to the registers.

Step 2: Decode the GPIO Register Map

Open the reference manual (RM0490) and search for the GPIO chapter.

A peripheral is a hardware block inside the chip. You control it by writing values to specific memory addresses called registers, where individual bits (or groups of bits) control specific behaviors.

The GPIO chapter starts with a register map table that looks something like this:

Register Name    Offset    Description
─────────────    ──────    ───────────────────────
GPIOx_MODER      0x00     Port mode register
GPIOx_OTYPER     0x04     Output type register
GPIOx_OSPEEDR    0x08     Output speed register
GPIOx_PUPDR      0x0C     Pull-up/pull-down register
GPIOx_IDR        0x10     Input data register
GPIOx_ODR        0x14     Output data register
GPIOx_BSRR       0x18     Bit set/reset register

That’s a lot of registers. For a basic blinky, you only need 2 GPIO registers plus 1 register in a different peripheral:

  1. GPIOA_MODER (set PA5 as an output)
  2. GPIOA_ODR (set PA5 high or low)
  3. RCC_IOPENR (turn on the clock for GPIOA so it actually works)

Let’s decode them one at a time.

MODER: setting the pin direction. Scroll to the GPIOA_MODER register description. Each pin gets 2 bits in this register. Pin 0 uses bits [1:0], pin 1 uses bits [3:2], and so on, with each pin’s field shifting up by 2. So pin 5 lands on bits [11:10].

GPIOA_MODER Bits [11:10] for Pin 5:
┌─────┬──────────────────────────┐
│ 00  │ Input mode (reset state) │
│ 01  │ General purpose output   │
│ 10  │ Alternate function       │
│ 11  │ Analog mode              │
└─────┴──────────────────────────┘

Write 01 to bits [11:10] and PA5 becomes an output. That’s it.

ODR: driving the pin high or low. The output data register is simpler. Each pin gets exactly 1 bit. Bit 5 corresponds to PA5. Write a 1 to turn the LED on. Write a 0 to turn it off.

RCC_IOPENR: enabling the clock. This one catches everyone the first time. On STM32s, peripherals don’t respond until you enable their clock. GPIOA’s clock is controlled by a bit in the RCC_IOPENR register. Search the reference manual for “RCC_IOPENR” and find the bit for GPIOAEN (GPIO port A enable). It’s bit 0. Set it to 1.

Three registers, a handful of bits. That’s everything digitalWrite() was doing behind the scenes.

Step 3: Write the Code

Here’s a minimal C snippet that blinks PA5. Every line maps directly to a register and bit field from the reference manual.

#include "stm32c031xx.h"  // Register definitions from CMSIS headers

int main(void) {
    // 1. Enable GPIOA clock (RCC_IOPENR, bit 0)
    RCC->IOPENR |= (1 << 0);

    // 2. Set PA5 to general-purpose output (MODER bits [11:10] = 01)
    GPIOA->MODER &= ~(3 << 10);  // Clear both bits first
    GPIOA->MODER |=  (1 << 10);  // Set to 01

    while (1) {
        // 3. Turn LED on (ODR bit 5 = 1)
        GPIOA->ODR |= (1 << 5);
        for (volatile int i = 0; i < 200000; i++);  // Crude delay

        // 4. Turn LED off (ODR bit 5 = 0)
        GPIOA->ODR &= ~(1 << 5);
        for (volatile int i = 0; i < 200000; i++);
    }
}

You can flash this using STM32CubeIDE with an empty project targeting the NUCLEO-C031C6 (strip out any auto-generated HAL code and replace main.c with the above).

Here’s the side-by-side with Arduino, so you can see the 1:1 mapping:

Arduino                            Bare Register
───────                            ──────────────
pinMode(LED_BUILTIN, OUTPUT);    → GPIOA->MODER  = ...;
digitalWrite(LED_BUILTIN, HIGH); → GPIOA->ODR   |= (1 << 5);
digitalWrite(LED_BUILTIN, LOW);  → GPIOA->ODR   &= ~(1 << 5);

pinMode() was writing to MODER. digitalWrite() was writing to ODR. The Arduino core just wrapped it in friendlier names and hid the bit manipulation.

The Sections You Can Safely Skip (For Now)

You don’t need to understand electrical characteristics, AC/DC timing specs, or package thermal data to write firmware. Those sections exist for hardware engineers designing circuit boards and selecting components.

Read Now              Read Later            Read When Needed
──────────            ──────────            ────────────────
Pinout / Pin table    Interrupt vectors     Electrical specs
GPIO registers        Timer peripherals     Package / thermal
RCC (clock enable)    UART/SPI/I2C regs    Absolute max ratings
Memory map overview   DMA                   Ordering info

Skip the “Read When Needed” column until you’re actually designing a PCB or debugging a power issue. It’ll still be there.

Tips for Navigating Any Datasheet Quickly

Use Ctrl+F relentlessly. You’re hunting for keywords like “MODER” or “IOPENR” or “PA5,” not browsing chapter by chapter. This single habit cuts your search time by 80%.

Bookmark the register map pages in your PDF reader. You’ll flip back to them constantly. Most PDF readers let you add bookmarks with a right-click.

Pin assignments live in the datasheet; register details live in the reference manual. Cross-reference between documents. Mixing them up is the most common source of frustration.

Read the functional description paragraphs that come before the register tables. They explain why the registers are structured the way they are, which makes the bit fields much easier to decode.

Don’t overlook block diagrams and figures. The GPIO block diagram, for example, shows the entire signal path from the register to the physical pin, compressing pages of text into one visual.

Try UART Next

You just did the core loop of bare-metal development: found a pin, decoded a register map, enabled a clock, and toggled an output. That loop applies to every peripheral on every MCU.

A good next step: try sending a character over UART. It’s one more peripheral (USART), a few more registers, and the same process. Find the TX pin in the datasheet, open the USART chapter in the reference manual, configure the baud rate registers, and write a byte to the transmit data register.

The datasheet didn’t get shorter. You just stopped being afraid of it.


Hubble Network connects your Bluetooth devices directly to satellite — no gateways, no infrastructure, no datasheet for the sky. See how it works →