Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 11

Demystifying Microcontroller Datasheets

Recommended resources

Hello! Welcome to your third lesson.

In our previous session, we established how to access hardware registers in C by casting memory addresses to volatile pointers. This is the crucial mechanism for ensuring our software's intentions are correctly executed by the hardware without being distorted by compiler optimizations.

Today, we address the next logical question: how do we find the correct addresses and understand what values to write? This lesson is dedicated to today's learning outcome: Interpret a microcontroller datasheet to find peripheral register addresses, bit fields, and functionalities. This skill is the bridge between a hardware specification and a working software driver. It is the single most fundamental competency for a low-level embedded engineer.

1. The "Datasheet" is Often a Documentation Stack

When developers talk about "the datasheet," they're often referring to a collection of documents. A single PDF rarely contains everything. Understanding this hierarchy is the first step.

Microcontroller Firmware from Scratch

To start, let's watch a segment from a talk at FOSDEM titled 'Microcontroller Firmware from Scratch'. The speaker does an excellent job of outlining the typical set of documents you'll need to navigate.

Watch from 01:59 to 03:38. Pay attention to the different types of documents he mentions and their primary purpose.

As the video highlighted, you'll typically work with:

  • Reference Manual (RM): This is the programmer's bible. It contains the detailed memory map, descriptions of every peripheral, their register maps, and functional details. We will spend most of our time here.
  • Datasheet (DS): This document contains device-specific information: high-level features, electrical characteristics (voltage, current, timing), pinouts, and package details. It's crucial for hardware engineers and for quickly confirming a chip's capabilities.
  • Programming Manual (PM): This manual is specific to the processor core (e.g., ARM Cortex-M4). It details the core's architecture, instruction set, and its own internal peripherals like the Nested Vectored Interrupt Controller (NVIC), which we'll cover later.
  • Board Schematics / User Manual: If you're using a development kit, the schematics show how the microcontroller's pins are connected to external components like LEDs, buttons, and communication headers.

For the purpose of writing drivers, the Reference Manual is your primary resource.


2. A Systematic Approach to Reading the Reference Manual

A modern reference manual can be over 1500 pages. You don't read it like a novel; you navigate it with a clear purpose. Here is the systematic process you'll use to configure any peripheral.

Step 1: Find the Memory Map

First, you need a high-level view of the address space. The reference manual will always have a "Memory Map" chapter. This shows you the address ranges for Flash, SRAM, and the different peripheral buses.

STM32 System Architecture: Memory and Bus Interconnections
This system architecture diagram from an STM32 reference manual shows how peripherals are grouped onto different buses (APB1, APB2, AHB). This structure directly relates to the memory map, where all peripherals on a single bus will have base addresses within a specific range.

Step 2: Find the Peripheral's Base Address

Once you know the general region for peripherals, you'll find a table, often called the "Peripheral Register Boundary" or "Peripheral Address Map," that lists the base address for every single peripheral instance (e.g., GPIOA, SPI1, USART2). This address is the starting point for that peripheral's block of registers.

Data Sheet Module Structure and Naming Conventions

The application note 'Getting Started with Writing C-Code for AVR® MCUs' from Microchip provides an excellent, generic overview of how datasheets are structured. Let's read the section that describes this.

Read the introductory section 'Data Sheet Module Structure and Naming Conventions' and the section 'Modules Description'. Focus on how each module has a fixed base address and how the manual provides a table listing these addresses.

Step 3: Find the Register's Offset

With the base address found, navigate to the chapter for that specific peripheral (e.g., "General-purpose I/Os (GPIO)"). At the beginning or end of that chapter, you will always find a "Register Map" or "Register Summary" table.

This table lists every register within that peripheral, its offset from the base address, its name, and its access properties (Read/Write, Read-Only, etc.). The final address of any register is simply Base Address + Offset.

Microcontroller Register Description Table
A typical register map table from a datasheet. It clearly lists the address (often as an offset), the register name (e.g., 'SOFTWARE_RESET'), reset value, and a functional description. This is the lookup table you will constantly reference.

Step 4: Interpret the Register's Bit Fields

This is where you translate functionality into bit values. After the register map, the manual provides a detailed breakdown of each register. For every register, you'll find:

  1. A diagram showing the bit positions (e.g., 31 down to 0).
  2. The names of individual bits or multi-bit groups called bit fields.
  3. A detailed description of what each bit or bit field controls.

Data Sheet Module Structure and Naming Conventions

Let's return to the Microchip document to see how it formally describes register and bit field layouts.

Read the sections 'Modules Description' (focusing on the part about the register description subsection), 'Register Naming Conventions', and 'Bit and Bit Field Naming Conventions'. These sections show exactly how a register is broken down into its functional bit fields, with clear diagrams.

Let's see this in a real-world scenario. Configuring a complex peripheral like SPI requires carefully setting multiple bit fields across several registers.

How To Write An STM32 SPI Driver (Full-Duplex Master Mode)

The video 'How To Write An STM32 SPI Driver' provides a perfect demonstration of this process. The developer needs to configure an SPI peripheral and systematically goes through the control registers, explaining each bit field's purpose by referencing the datasheet.

Watch the segment from 05:30 to 13:12. You don't need to memorize every SPI detail. Instead, focus on the process: how he takes a register (SPI Control Register 1), looks at a bit field (like BR[2:0] for baud rate or CPOL/CPHA for clock mode), explains its function, and decides on the correct value to write.


3. Practical Walkthrough: Configuring a GPIO Pin

Let's consolidate this with a hypothetical example: "Configure pin PC13 as a general-purpose push-pull output."

Your mental workflow should be:

  1. Goal: Control pin PC13. This pin is part of the GPIOC peripheral.
  2. Crucial Prerequisite: Enable the Clock. Before a peripheral can be used, its clock must be enabled. This is a very common mistake for beginners. Clock control is usually managed by the Reset and Clock Control (RCC) peripheral.
    • Action: Go to the Reference Manual's memory map, find the base address for RCC. Then, go to the RCC chapter, find the appropriate clock enable register for GPIOC (e.g., on STM32, this is the AHB1ENR register for GPIOs on the AHB1 bus). Find the GPIOCEN bit within that register and set it to 1.
  3. Find the Peripheral Base Address.
    • Action: In the memory map chapter, find the table of peripheral addresses and look up GPIOC. Let's say it's 0x40020800.
  4. Configure the Pin Mode. We need to set the pin to "output" mode.
    • Action: Go to the GPIO chapter. Find the register map. You'll see a register named GPIOx_MODER (Mode Register). Let's say its offset is 0x00. Its full address is 0x40020800 + 0x00.
  5. Interpret the Bit Fields.
    • Action: Look at the detailed description for GPIOx_MODER. It will show that 2 bits are used to configure each pin. For pin 13, this will be bits MODER13[1:0], which are bits 27 and 26 of the register. The description table will show:
      • 00: Input mode
      • 01: General purpose output mode
      • 10: Alternate function mode
      • 11: Analog mode
    • To set pin 13 to output mode, we need to write 01 to these bits. This means writing 0 to bit 27 and 1 to bit 26.
  6. Configure the Output Type. We want push-pull.
    • Action: Back in the GPIO register map, you'll find a GPIOx_OTYPER (Output Type Register). Let's say its offset is 0x04. Its description will show it uses 1 bit per pin. For pin 13, this is bit 13. The table will show:
      • 0: Output push-pull (reset state)
      • 1: Output open-drain
    • We need to ensure bit 13 is 0.

You have now successfully translated the requirement "configure pin PC13 as push-pull output" into a concrete set of register addresses and bit values. The next step, which we'll cover in the next lesson, is to write the C code to perform these modifications safely.

Test your understanding!

You are tasked with writing a UART driver. You look at the UART chapter in the reference manual and find the Status Register (UART_SR). It has a bit named RXNE (Read data register Not Empty) at bit position 5. The description says: "This bit is set by hardware when received data is ready to be read. It is cleared by a read to the Data Register (UART_DR)."

How would you use this information to write a function that safely waits for and reads a single byte?

Show answer

You would implement a polling mechanism.

  1. Create a while loop that continuously reads the UART_SR register.
  2. Inside the loop, you check if bit 5 (RXNE) is set to 1.
  3. The loop continues as long as the bit is 0 (while ((UART_SR & (1 << 5)) == 0)).
  4. Once the loop exits, it means the RXNE bit is 1, so data is available.
  5. You then perform a read from the UART_DR (Data Register) to get the received byte. This action also automatically clears the RXNE bit, making the hardware ready for the next reception.

This demonstrates a direct application of interpreting a status bit's functionality from the datasheet to control program flow.


Interview Preparation

  • Question: "You've been handed a new microcontroller you've never seen before and told to write a basic I2C driver. What is your process, and what documents do you start with?"

    • Your thought process: Start with the documentation stack. First, get the Reference Manual. I'd navigate to the Memory Map to find the base address for the I2C peripheral I need (e.g., I2C1). Then, I'd jump to the I2C chapter. I would immediately look for the Register Map to get a list of all I2C registers and their offsets (CR1, CR2, DR, SR1, SR2, etc.). Before touching any of them, I'd find the RCC (Reset and Clock Control) chapter to figure out which register and bit enables the clock for I2C1. After enabling the clock, I'd go back to the I2C chapter and read the detailed descriptions of the control registers (CR1, CR2) to configure the I2C mode (Master), speed, and addressing. Finally, I'd study the status registers (SR1, SR2) and the data register (DR) to understand the event sequence for a start condition, sending an address, transmitting/receiving data, and generating a stop condition.
  • Question: "What's the first thing you should do before trying to configure a peripheral's registers?"

    • Answer: Enable its clock in the RCC (or equivalent clock management unit). A peripheral without a clock is just a block of inert silicon and its registers will not respond.

Conclusion

You now understand the "art" of navigating hardware documentation. This isn't about memorization; it's about process. By systematically working from the high-level memory map down to individual bit fields, you can confidently interface with any peripheral on any microcontroller.

Key Takeaways:

  • Embedded development relies on a documentation stack, with the Reference Manual being the programmer's most critical resource.
  • The process for finding register information is: Memory Map -> Peripheral Base Address -> Register Map -> Register Offset -> Bit Field Description.
  • Always remember to enable the peripheral's clock before attempting to configure or use it. This is a classic "gotcha".
  • Interpreting a datasheet is the skill of translating functional requirements into specific register addresses and the bit values needed to achieve that function.

Next Lesson Preview:
Now that we know which bits to change, the next lesson will focus on the C programming techniques to change them safely. We will master read-modify-write operations using bit masking and bitwise operators to ensure we only affect the bits we intend to, leaving all others in the register untouched.