/pi-pico/rp2040.md
<< Back# Raspberry Pi Pico SDK (RP2040) – Module Guide
This guide summarizes the Raspberry Pi Pico SDK modules relevant to RP2040 (Cortex‑M0+), with a focus on:
- What each module does
- What it exports (CMake target name, primary headers, and notable APIs)
Notes
- CMake targets generally come in pairs: `<module>` and `<module>_headers`. Link against the `<module>` target; the `_headers` target carries include paths and compile defs.
- Higher‑level umbrella targets (e.g., `pico_stdlib`, `pico_runtime`) pull in many dependencies for you.
- Some modules exist in the tree but are not applicable to RP2040 (e.g., RP2350‑only features). Those are omitted here.
## Core/Base
### `pico_base_headers`
**Purpose:** Provides the core types, macros, and error-handling definitions used across the entire SDK. It forms the foundational layer upon which other modules are built.
**CMake Target:** `pico_base_headers`
**Source Files:**
- `src/common/pico_base_headers/CMakeLists.txt`
- `src/common/pico_base_headers/include/pico.h`
- `src/common/pico_base_headers/include/pico/types.h`
- `src/common/pico_base_headers/include/pico/assert.h`
- `src/common/pico_base_headers/include/pico/error.h`
---
#### **Key APIs and Definitions**
#### `pico/types.h`
This header defines fundamental data types for time, dates, and hardware access.
**Types:**
- `absolute_time_t`: An opaque 64-bit timestamp representing microseconds since boot. Using a dedicated type helps prevent accidental mixing of relative and absolute times.
- `datetime_t`: A structure for holding calendar date and time information, primarily for use with the Real-Time Clock (RTC).
- `year` (int16_t): 0-4095
- `month` (int8_t): 1-12 (1 = January)
- `day` (int8_t): 1-31
- `dotw` (int8_t): 0-6 (Day of week, 0 = Sunday)
- `hour` (int8_t): 0-23
- `min` (int8_t): 0-59
- `sec` (int8_t): 0-59
**Functions (Time Conversion):**
- `uint64_t to_us_since_boot(absolute_time_t t)`
- **Description:** Converts an `absolute_time_t` timestamp into a standard `uint64_t` value representing the number of microseconds since the device booted.
- `void update_us_since_boot(absolute_time_t *t, uint64_t us_since_boot)`
- **Description:** Updates an existing `absolute_time_t` variable to a new time based on the provided number of microseconds since boot.
- `absolute_time_t from_us_since_boot(uint64_t us_since_boot)`
- **Description:** Creates an `absolute_time_t` from a `uint64_t` value of microseconds since boot.
---
#### `pico/assert.h`
Provides macros for performing runtime checks and assertions, which are critical for debugging and ensuring program correctness. Assertions can be globally enabled or disabled.
**Macros:**
- `valid_params_if(group, test)`
- **Description:** Asserts that a given `test` evaluates to `true`. This is typically used to validate function parameters and other preconditions. The assertion is only active if assertions for the specified `group` are enabled.
- `invalid_params_if(group, test)`
- **Description:** Asserts that a given `test` evaluates to `false`. This is the inverse of `valid_params_if`.
- `hard_assert(condition)`
- **Description:** A non-overridable assertion that calls `hard_assertion_failure()` if the condition is false. This is used for critical checks that should never fail in a correctly functioning program.
---
#### `pico/error.h`
Defines a standard set of negative error codes returned by SDK functions.
**Enum:** `pico_error_codes`
- `PICO_OK` (0): The operation completed successfully.
- `PICO_ERROR_GENERIC` (-1): An unspecified error occurred.
- `PICO_ERROR_TIMEOUT` (-2): The operation did not complete in the required time.
- `PICO_ERROR_NO_DATA` (-3): An attempt was made to read from an empty buffer or FIFO.
- `PICO_ERROR_NOT_PERMITTED` (-4): The operation is not allowed (e.g., writing to a read-only memory region).
- `PICO_ERROR_INVALID_ARG` (-5): An argument provided to a function was outside its valid range.
- `PICO_ERROR_IO` (-6): A generic I/O error occurred.
- `PICO_ERROR_BADAUTH` (-7): Authentication failed.
- `PICO_ERROR_CONNECT_FAILED` (-8): A network connection attempt failed.
- `PICO_ERROR_INSUFFICIENT_RESOURCES` (-9): Failed to allocate a required resource (e.g., memory).
- `PICO_ERROR_INVALID_ADDRESS` (-10): An address was out-of-bounds or inaccessible.
- `PICO_ERROR_BAD_ALIGNMENT` (-11): An address was misaligned.
- `PICO_ERROR_INVALID_STATE` (-12): The system is in a state that prevents servicing the request.
- `PICO_ERROR_BUFFER_TOO_SMALL` (-13): A user-provided buffer was not large enough.
- `PICO_ERROR_PRECONDITION_NOT_MET` (-14): A required prerequisite function was not called first.
- `PICO_ERROR_MODIFIED_DATA` (-15): Cached data was found to be inconsistent with its source.
- `PICO_ERROR_INVALID_DATA` (-16): A data structure failed validation.
- `PICO_ERROR_NOT_FOUND` (-17): A search failed or the requested item does not exist.
- `PICO_ERROR_UNSUPPORTED_MODIFICATION` (-18): An attempted write is not possible (e.g., clearing an OTP bit).
- `PICO_ERROR_LOCK_REQUIRED` (-19): A required lock was not acquired.
- `PICO_ERROR_VERSION_MISMATCH` (-20): A version mismatch was detected.
- `PICO_ERROR_RESOURCE_IN_USE` (-21): A required resource was unavailable because it is already in use.
### `pico_stdlib_headers`
**Purpose:** A convenience module that aggregates a core subset of the most commonly used SDK libraries. It is intended to be the standard starting point for most applications, providing a foundation for basic MCU tasks.
**CMake Target:** `pico_stdlib_headers`
**Source Files:**
- `src/common/pico_stdlib_headers/CMakeLists.txt`
- `src/common/pico_stdlib_headers/include/pico/stdlib.h`
---
#### **Overview**
The `pico_stdlib.h` header is an aggregate header; it does not define many of its own APIs. Instead, it includes a collection of other essential headers to provide a baseline of functionality. By including `pico/stdlib.h`, you get everything you need for basic tasks like printing to a serial console, blinking an LED, and using timers, without needing to include each underlying header manually.
---
#### **Aggregated Modules**
Including `pico/stdlib.h` provides the public APIs from the following SDK modules. These will be detailed in their own sections.
- **`pico_base`**: Core types and macros.
- **`pico_platform`**: Platform-specific configuration and lifecycle.
- **`pico_runtime`**: Core runtime support.
- **`pico_stdio`**: Unified Standard I/O library.
- **`pico_time`**: Timer and sleep functions.
- **`pico_util`**: Miscellaneous utility functions and data structures.
- **`hardware_gpio`**: Hardware GPIO control.
- **`hardware_uart`**: Hardware UART control.
- **`hardware_divider`**: Optimized hardware integer division.
---
#### **Utility Functions**
- `void setup_default_uart(void)`
- **Description:** Initializes the default UART peripheral and configures it for stdio communication. By default, this uses UART0 with a baud rate of 115200, routing TX to GPIO 0 and RX to GPIO 1. These defaults can be overridden by build configuration. This function is automatically called as part of `stdio_init_all()`.
- **Configurable:** The UART instance, baud rate, and pins can be changed via build-time definitions (e.g., `PICO_DEFAULT_UART_BAUD_RATE`).
---
#### **Configuration**
The behavior of the standard library can be customized with the following build-time definitions, which are typically set in a board-specific configuration header.
- `PICO_DEFAULT_LED_PIN`: Defines the GPIO pin connected to a general-purpose on-board LED.
- `PICO_DEFAULT_LED_PIN_INVERTED`: Set to `1` if the on-board LED is active-low (i.e., setting the pin to `0` turns it on).
- `PICO_DEFAULT_WS2812_PIN`: Defines the GPIO pin used for the data line of a WS2812 "NeoPixel" addressable LED.
- `PICO_DEFAULT_WS2812_POWER_PIN`: Defines the GPIO pin used to control power to the WS2812 LED, if applicable.
## Platform/Runtime
### `hardware_regs`
**Purpose:** Provides the lowest-level definitions for RP2040 hardware interaction, including memory-mapped addresses, register offsets, and bitfield masks for every peripheral.
**CMake Target:** `hardware_regs`
**Source Files:**
- `src/rp2040/hardware_regs/CMakeLists.txt`
- `src/rp2040/hardware_regs/include/hardware/regs/` (contains one header per peripheral)
---
#### **Overview**
The `hardware_regs` module is the foundation of all hardware interaction in the SDK. It consists of a collection of header files, each corresponding to a specific hardware peripheral on the RP2040 (e.g., `dma.h`, `uart.h`, `spi.h`). These files are automatically generated and contain preprocessor macros that define the absolute memory address for each register and the bit masks for each field within those registers.
This module is not typically used directly in application code. Instead, it serves as a dependency for the `hardware_structs` and high-level `hardware_*` driver modules, which provide a safer and more convenient C-level interface (structs and functions) for accessing the hardware.
---
#### **Included Peripherals**
This module provides register definitions for all RP2040 peripherals, including:
- `adc`
- `busctrl`
- `clocks`
- `dma`
- `i2c`
- `io_bank0`
- `pio`
- `pwm`
- `rtc`
- `spi`
- `timer`
- `uart`
- `usb`
- `watchdog`
- ...and many more.
---
#### **Example: DMA Register Definitions (`dma.h`)**
To illustrate the content of these headers, here are a few definitions from `hardware/regs/dma.h`:
- `DMA_CH0_READ_ADDR_OFFSET`: Defines the memory offset for the Channel 0 Read Address Pointer.
- `DMA_CH0_CTRL_TRIG_BITS`: Defines the bitmask for all valid bits in the Channel 0 Control and Status register.
- `DMA_CH0_CTRL_TRIG_BUSY_LSB`, `DMA_CH0_CTRL_TRIG_BUSY_MSB`: Define the bit position of the `BUSY` flag within the control register.
- `DMA_CH0_CTRL_TRIG_TREQ_SEL_VALUE_TIMER0`: Defines the specific value to select Timer 0 as the DMA transfer request (TREQ) signal.
These macros allow other code to access hardware registers in a way that is independent of the absolute memory map, making the code more portable and readable. For example, `*(volatile uint32_t *)(DMA_BASE + DMA_CH0_READ_ADDR_OFFSET)`.
### `hardware_structs`
**Purpose:** Provides C `struct` overlays for RP2040 hardware peripherals. This offers a more convenient, readable, and type-safe way to access hardware registers compared to the raw address and bitmask definitions found in the `hardware_regs` module.
**CMake Target:** `hardware_structs`
**Source Files:**
- `src/rp2040/hardware_structs/CMakeLists.txt`
- `src/rp2040/hardware_structs/include/hardware/structs/` (contains one header per peripheral)
---
#### **Overview**
The `hardware_structs` module is the next layer of abstraction above `hardware_regs`. For each hardware peripheral (like DMA, UART, I2C), it defines a C `struct` where each member corresponds to a memory-mapped register in that peripheral. The layout of the struct precisely matches the memory layout of the hardware registers.
This is the most common and recommended method for direct hardware manipulation in the SDK, especially when a higher-level driver function is not available for a specific task. The high-level `hardware_*` driver libraries are themselves built using these structs.
---
#### **Usage**
For each peripheral, the corresponding header file defines a global pointer variable with an `_hw` suffix (e.g., `dma_hw`, `uart0_hw`, `spi1_hw`). This pointer is already cast to the correct struct type and points to the base memory address of that peripheral. To access registers, you simply dereference this pointer and use the standard C struct member access operators (`->` and `[]`).
---
#### **Example: DMA Structs (`dma.h`)**
The `hardware/structs/dma.h` header provides a clear example of this pattern.
**Key Structs:**
- `dma_channel_hw_t`: A struct that maps the registers for a *single* DMA channel.
- `read_addr` (io_rw_32)
- `write_addr` (io_rw_32)
- `transfer_count` (io_rw_32)
- `ctrl_trig` (io_rw_32)
- ...and several alias members for different trigger combinations.
- `dma_hw_t`: A larger struct that represents the *entire* DMA peripheral block.
- `ch[12]` (dma_channel_hw_t): An array of structs, one for each of the 12 DMA channels.
- `intr` (io_rw_32): Global interrupt status register.
- `inte0`, `intf0`, `ints0`: Interrupt control registers for IRQ 0.
- `timer[4]` (io_rw_32): Pacing timers.
- `sniff_ctrl`, `sniff_data`: DMA sniffer control.
- `abort` (io_wo_32): A write-only register to abort transfers on one or more channels.
**Accessing Registers:**
To use these structs, you include the relevant header and then use the predefined global pointer.
```c
#include "hardware/structs/dma.h"
// Set the read address for DMA channel 0
dma_hw->ch[0].read_addr = 0x10002000;
// Read the current transfer count from DMA channel 3
uint32_t count = dma_hw->ch[3].transfer_count;
// Abort any in-progress transfer on channel 7
dma_hw->abort = (1u << 7);
```
This approach is much cleaner, safer, and less error-prone than manually calculating pointer addresses from the offsets defined in `hardware_regs`.
### `pico_platform`
**Purpose:** Provides a hardware abstraction layer that handles platform-specific details, compiler differences, and critical runtime features like panic handling and memory section management.
**CMake Target:** `pico_platform`
**Source Files:**
- `src/rp2040/pico_platform/CMakeLists.txt`
- `src/rp2040/pico_platform/include/pico/platform.h`
- `src/rp2_common/pico_platform_compiler/include/pico/platform/compiler.h`
- `src/rp2_common/pico_platform_panic/include/pico/platform/panic.h`
- `src/rp2_common/pico_platform_sections/include/pico/platform/sections.h`
---
#### **Overview**
The `pico_platform` module is an aggregate of several sub-modules that abstract away the specifics of the target hardware and the compiler toolchain. It provides a consistent API for tasks like placing code in performance-critical sections of memory, handling fatal errors, and querying platform-level information. It is a fundamental dependency for most other SDK modules.
---
#### **Platform and Versioning (`platform.h`)**
The main `pico/platform.h` header provides macros to identify the target hardware and the SDK version, as well as the memory-mapped base addresses for all hardware peripherals.
**Platform-defining Macros:**
- `PICO_RP2040`: Defined as `1` when compiling for the RP2040.
- `PICO_ON_DEVICE`: Defined as `1` when compiling for on-device code (as opposed to host-based tools).
- `PICO_SDK_VERSION_STRING`: A string literal representing the current SDK version (e.g., "1.5.1").
- `PICO_SDK_VERSION_MAJOR`, `PICO_SDK_VERSION_MINOR`, `PICO_SDK_VERSION_REVISION`: Integer values for the SDK version components.
- **Peripheral Base Addresses**: Defines pointers to the base addresses of all RP2040 peripherals (e.g., `ADC_BASE`, `DMA_BASE`, `UART0_BASE`).
---
#### **Compiler Abstraction (`compiler.h`)**
This header provides a set of macros to control compiler behavior, such as function inlining and, most importantly, memory placement. This allows code and data to be placed in RAM for performance or to allow for flash operations.
**Function Attributes:**
- `__not_in_flash_func(func)`: Places the specified function in RAM. This is critical for functions that need to run while the flash is being written or erased.
- `__time_critical_func(func)`: An alias for `__not_in_flash_func`, used to explicitly mark functions where execution time is critical and the latency of flash memory access must be avoided.
- `__no_inline_func(func)`: Instructs the compiler not to inline the function.
- `__always_inline_func(func)`: Instructs the compiler to always inline the function if possible.
**Data Attributes:**
- `__in_flash(group)`: Places data in a specific, named section within flash memory.
- `__uninitialized_data(group)`: Places data in a `.bss` section that is not initialized to zero at startup, preserving its state across resets (if not power-cycled).
---
#### **Panic Handler (`panic.h`)**
This provides the mechanism for handling fatal, unrecoverable errors in a controlled way.
**Functions:**
- `void panic(const char *fmt, ...)`
- **Description:** The primary function for handling fatal errors. It halts all execution in a tight loop. By default, it also prints a formatted error message to the configured `stdio` output. This function does not return.
- `void panic_unsupported()`: A shorthand for `panic("NOT SUPPORTED")`, used for unimplemented functionality.
- `panic_handler_t`: A function pointer type for defining a custom panic handler: `typedef void (*panic_handler_t)(const char *fmt, ...);`.
- `void set_panic_handler(panic_handler_t handler)`:
- **Description:** Overrides the default panic behavior with a custom handler function. This allows an application to implement its own panic behavior, such as blinking an LED in a specific pattern or logging to a different interface.
---
#### **Memory Sections (`sections.h`)**
This header provides macros for placing code and data into specific, named memory sections that are defined by the linker script. This is a lower-level mechanism than the attributes provided in `compiler.h`.
**Standard Sections:**
- `.text`: The default section for executable code.
- `.data`: The default section for initialized data.
- `.bss`: The default section for zero-initialized data.
- `.uninitialized_data`: For data that should not be initialized at startup.
- `.ram_vector_table`: For the Cortex-M0+ vector table when it is copied to RAM for faster interrupt handling.
- `.no_flash_binary_header`: For the special binary header used by the bootrom.
- `.boot2`: For the second-stage bootloader code.
### `pico_runtime` & `pico_runtime_init`
**Purpose:** These modules work together to establish the fundamental C runtime environment, manage the hardware initialization sequence, and provide hooks for other libraries and user code to execute automatically during the startup process, before `main()` is called.
**CMake Target:** `pico_runtime` (which includes and depends on `pico_runtime_init`)
**Source Files:**
- `src/rp2_common/pico_runtime/CMakeLists.txt`
- `src/rp2_common/pico_runtime/include/pico/runtime.h`
- `src/rp2_common/pico_runtime_init/CMakeLists.txt`
- `src/rp2_common/pico_runtime_init/include/pico/runtime_init.h`
---
#### **Overview**
The runtime system is logically split into two main parts:
1. **`pico_runtime`**: Provides the high-level framework for initialization. It uses a system of "constructors"—functions that are automatically executed by the C runtime before `main()`—to ensure that all necessary SDK modules are initialized in the correct order. It also provides hooks for applications to run their own early initialization code.
2. **`pico_runtime_init`**: Provides the default, concrete implementation of the low-level hardware initialization sequence. This is called automatically by `pico_runtime` and is responsible for essential tasks like setting up system clocks and the scheduler.
---
#### **Key APIs and Concepts**
#### `pico_runtime`
This module's primary role is to orchestrate the startup process using C/C++ constructors.
**Functions and Hooks:**
- `__attribute__((constructor(100))) void pico_init_main(void)`
- **Description:** This is a special function, marked as a "constructor" with priority 100, which guarantees it runs before other SDK constructors (which have lower priorities like 101, 102, etc.). It is responsible for calling `pico_runtime_init()` to set up the hardware. This is the core mechanism that enables the SDK's automatic initialization.
- `pico_set_binary_type(type)`
- **Description:** A function intended to be used within a `binary_info` tag to declare the type of the binary (e.g., `BINARY_TYPE_EXECUTABLE`, `BINARY_TYPE_BOOTLOADER_FIRST_STAGE`). This metadata can be useful for other tools.
**CMake Functions:**
- `pico_minimize_runtime(target)`
- **Description:** This CMake function significantly reduces binary size by stripping out much of the automatic runtime initialization code and dependencies. This is for advanced users who wish to take full manual control of the hardware initialization. When this is used, the application is responsible for calling `pico_runtime_init()` and performing any other necessary setup itself.
---
#### `pico_runtime_init`
This module performs the actual low-level hardware setup.
**Functions:**
- `void pico_runtime_init(void)`
- **Description:** This function performs the default initialization of the RP2040 hardware. It is called automatically by the runtime unless `pico_minimize_runtime()` is used. Its key responsibilities include:
- Initializing the main system clocks (PLLs).
- Configuring the stack guard to protect against stack overflows.
- Enabling the Cortex-M0+ cycle counter for fine-grained timing.
- Initializing the scheduler and time-slicing functionality used by the SDK's cooperative multitasking features.
- `void pico_runtime_deinit(void)`
- **Description:** Reverses the initialization process, disabling clocks and other hardware to save power. This is rarely used.
### `pico_crt0`
**Purpose:** Provides the C Runtime startup code, commonly known as `crt0`. This is the very first piece of user code to run on the RP2040 after the 2nd-stage bootloader completes its work. Its primary job is to prepare the hardware and memory for the C environment before calling the `main()` function.
**CMake Target:** `pico_crt0`
**Source Files:**
- `src/rp2_common/pico_crt0/CMakeLists.txt`
- `src/rp2_common/pico_crt0/crt0.S`
---
#### **Overview**
`crt0` (short for C runtime) is a small but essential piece of assembly code that acts as the bridge between the raw hardware's reset state and the application's `main()` function. It is automatically linked into every executable and is responsible for fundamental setup tasks that must happen before any C code can properly execute. This module is fundamental to the operation of the SDK and is not intended to be modified or directly interacted with by the user.
---
#### **Initialization Sequence**
The `_reset_handler` within `crt0.S` is the true entry point for all user code. It is executed by the processor immediately after a reset. It performs the following critical sequence of operations:
1. **Core 0 Check:** It first reads the core ID from the SIO hardware block. If the current core is not Core 0, it is sent back to the bootrom to wait in a dormant state. This ensures that the main runtime setup only happens once on the primary core.
2. **Initialize `.data` Section:** It copies the initial values for all global and static variables from their storage location in flash memory to their final location in RAM. This is the mechanism that allows an initialized global variable like `int my_global = 10;` to have its correct value at startup. This step is skipped for RAM-only (`PICO_NO_FLASH`) builds, as the data is already in place.
3. **Initialize `.bss` Section:** It zeroes out the entire `.bss` memory section. This section contains all global and static variables that are not explicitly initialized (e.g., `int my_uninitialized_array[10];`).
4. **Call Runtime Init:** It makes a call to the `runtime_init()` function (provided by the `pico_runtime_init` module). This is where higher-level C-based initialization, such as setting up clocks and running C++ constructors, takes place.
5. **Call `main()`:** After the C runtime environment is fully prepared, it finally calls the application's `main()` function, where user code typically begins.
6. **Call `exit()`:** If `main()` ever returns, `crt0` calls the `exit()` function, which ultimately leads to the core halting in an infinite loop.
---
#### **Vector Table**
`crt0.S` also defines the default interrupt vector table for the application. This table is located at the very beginning of the application binary in flash and contains:
- The initial address for the stack pointer (`__StackTop`).
- The address of the `_reset_handler`.
- Default handlers for all Cortex-M0+ system exceptions (NMI, HardFault, SVCall, etc.).
- Default, weakly-linked handlers for all of the RP2040's peripheral interrupts. These default handlers are simple `bkpt #0` (breakpoint) instructions, which are invaluable for debugging as they cause the processor to halt if an unexpected or unhandled interrupt occurs.
### `pico_standard_link`
**Purpose:** Provides the crucial linker scripts and associated logic that define how an application is laid out in the RP2040's memory. It controls where code (`.text`), initialized data (`.data`), uninitialized data (`.bss`), and the heap/stack are placed in both flash and RAM.
**CMake Target:** `pico_standard_link`
**Source Files:**
- `src/rp2_common/pico_standard_link/CMakeLists.txt`
- `src/rp2_common/pico_crt0/rp2040/memmap_default.ld`
- `src/rp2_common/pico_crt0/rp2040/memmap_copy_to_ram.ld`
- `src/rp2_common/pico_crt0/rp2040/memmap_no_flash.ld`
- `src/rp2_common/pico_crt0/rp2040/memmap_blocked_ram.ld`
---
#### **Overview**
This module is not a typical library with C code, but rather a collection of linker scripts (`.ld` files) and the CMake logic to select and configure them. The linker script is a fundamental part of the build process. It instructs the linker how to arrange the compiled code and data from all the different source files into a final, executable binary with a specific memory map.
The SDK provides several standard memory maps to support different application scenarios. The choice of memory map is usually controlled by setting a CMake variable in the project's `CMakeLists.txt`.
---
#### **Standard Memory Maps**
- **`memmap_default.ld` (Default)**
- **Description:** This is the standard and most common memory map. Executable code (`.text`) and read-only data reside in flash memory, while writable data (`.data`, `.bss`), the heap, and the stack are placed in RAM.
- **Use Case:** The vast majority of applications.
- **`memmap_copy_to_ram.ld`**
- **Description:** The application is stored in flash, but at startup, the `crt0` code copies the entire executable code section into RAM. The application then runs exclusively from RAM. This provides a significant performance boost by eliminating flash latency, at the cost of a slightly longer startup time and reduced available RAM for data.
- **Use Case:** Performance-critical applications where flash latency is a bottleneck.
- **Activation:** Set `PICO_COPY_TO_RAM=1` in the project's CMake file.
- **`memmap_no_flash.ld`**
- **Description:** The application is built to run entirely from RAM. No flash memory is used. The binary is loaded directly into RAM (e.g., via `picotool load` or a debugger) and executed from there.
- **Use Case:** Debugging, rapid prototyping, or applications that are loaded dynamically over USB or UART.
- **Activation:** Set `PICO_NO_FLASH=1` in the project's CMake file.
- **`memmap_blocked_ram.ld`**
- **Description:** A special layout where the application only uses the lower 128KB of RAM. The upper 128KB is reserved and left untouched by the linker, making it available for manual management.
- **Use Case:** Applications that need to reserve a large, contiguous block of RAM for a specific purpose, such as a video framebuffer, a large buffer shared with the second core, or custom data structures.
- **Activation:** Set `PICO_USE_BLOCKED_RAM=1` in the project's CMake file.
---
#### **Linker Features**
This module also configures several important linker features to optimize the final binary:
- **Garbage Collection (`--gc-sections`):** The linker is instructed to identify and remove any functions and data sections that are not referenced anywhere in the code. This significantly reduces the final binary size by discarding unused code from the SDK and other libraries.
- **Flash Size:** A `pico_flash_region.ld` file is automatically generated based on the `PICO_FLASH_SIZE` variable (e.g., `2M` for a standard Pico) to inform the linker of the available flash storage size.
### `pico_standard_binary_info`
**Purpose:** Provides a standardized mechanism for embedding machine-readable metadata into the final application binary. This information can be used to identify the binary, describe its purpose, list its features, and specify hardware configurations like which pins are used for which functions.
**CMake Target:** `pico_standard_binary_info` (which depends on the core `pico_binary_info` module)
**Source Files:**
- `src/common/pico_binary_info/include/pico/binary_info.h`
- `src/common/pico_binary_info/include/pico/binary_info/structure.h`
- `src/common/pico_binary_info/include/pico/binary_info/code.h`
---
#### **Overview**
The binary info system allows developers to attach key-value pairs of data to an executable at compile time. This data is stored in a special, dedicated section of the binary file. It can be read by host-side tools (most notably `picotool`) to inspect the binary's properties without needing access to the original source code.
This is extremely useful for:
- Giving a program a name, version, and description.
- Documenting which GPIO pins are used for specific functions (e.g., "Pin 15: I2C SDA").
- Advertising which optional features are included in the build.
- Defining flash memory regions to be used as block devices for filesystems.
- Providing a URL for program documentation.
---
#### **Usage**
The primary way to add information to the binary is by using the `bi_decl()` macro and its various helpers, which are defined in `pico/binary_info.h`. These macros create a special `binary_info_t` struct instance and instruct the linker to place it in a dedicated `.binary_info` section of the executable.
**Core Macro:**
- `bi_decl(X)`
- **Description:** The fundamental macro for creating a binary info entry. It is almost always used with one of the helper macros below as its argument. You can use this macro at file scope in any C or C++ source file.
**Helper Macros:**
The SDK provides a rich set of helpers for common information types:
- **Program Information:**
- `bi_program_name("My Cool Program")`
- `bi_program_version_string("1.2.3")`
- `bi_program_description("A program that does cool things.")`
- `bi_program_url("http://example.com")`
- **Pin Information:**
- `bi_pin_mask(group, mask)`: Associates a bitmask of pins with a group name (e.g., `bi_pin_mask("I2C0", (1u << 4) | (1u << 5))`).
- `bi_1pin_with_name(pin, name)`: Names a single pin.
- `bi_2pins_with_names(pin1, name1, pin2, name2)`: Names two pins.
- ...and so on for up to 4 pins.
- **Other Information:**
- `bi_feature_disabled(name)`: Declares that a named feature is disabled in this build.
- `bi_block_device(name, start, size, extra, flags)`: Defines a region of flash to be used as a block storage device.
---
#### **Example**
Here is how you would typically add binary info to your application. This code can be placed at the top level of any C/C++ source file.
```c
#include "pico/binary_info.h"
#include "pico/stdlib.h" // Or any other header that defines PICO_DEFAULT_LED_PIN
// Add a name and description for the program
bi_decl(bi_program_name("My LED Blinker"));
bi_decl(bi_program_description("This is an example of blinking an LED."));
// Document the pin used for the on-board LED
bi_decl(bi_1pin_with_name(PICO_DEFAULT_LED_PIN, "On-board LED"));
```
---
#### **Inspecting Binary Info**
Once the program is compiled, you can use the `picotool` utility on a host computer to read the embedded information from the `.elf` or `.uf2` file.
```bash
# From your build directory
picotool info -a my_program.elf
# Expected output:
#
# === Program Information ===
# Name: My LED Blinker
# Description: This is an example of blinking an LED.
#
# === Pin Information ===
# Pin 25: On-board LED
```
### `boot_stage2` (RP2040)
**Purpose:** Provides the essential 2nd-stage bootloader, a tiny piece of code that runs immediately after the on-chip bootrom to initialize the external flash memory, enabling the main application to be executed directly from it.
**CMake Target:** `boot_stage2`
**Source Files:**
- `src/rp2040/boot_stage2/CMakeLists.txt`
- `src/rp2040/boot_stage2/boot_stage2.ld`
- `src/rp2040/boot_stage2/*.S` (multiple assembly files for different flash chips)
---
#### **Overview**
The 2nd-stage bootloader is a critical component for any application that runs from flash memory on the RP2040. Because the RP2040 does not have any internal program flash, it relies on an external QSPI flash chip. The processor's permanent on-chip bootrom (the 1st-stage bootloader) is generic and does not know the specific commands required to initialize the vast array of different QSPI flash chips available. The job of the 2nd-stage bootloader is to bridge this gap.
This module is not a library that you link against in the traditional sense. Instead, it is compiled into a tiny, 256-byte standalone binary which is then prepended to the front of your main application binary by the build system.
---
#### **The Boot Sequence**
The startup process for a flash-based application follows these steps:
1. **Bootrom (1st Stage):** When the RP2040 powers on, the immutable on-chip bootrom runs. This bootrom can load code from various sources, including the QSPI flash interface.
2. **Load `boot_stage2`:** The bootrom reads the first 256 bytes from the external flash chip (where `boot_stage2` has been placed) into a small, dedicated area of RAM at address `0x20040000`.
3. **Execute `boot_stage2`:** The bootrom verifies a checksum and then jumps to the start of this RAM area, executing the 2nd-stage bootloader.
4. **Flash Init:** The `boot_stage2` code, now running from RAM, contains the specific QSPI commands needed to initialize the particular model of external flash chip being used on the board. It communicates with the flash chip to put it into a known, ready state.
5. **XIP Enable:** Once the flash chip is ready, `boot_stage2` configures the RP2040's XIP (eXecute-In-Place) peripheral. The XIP block acts as a hardware cache, making the external flash memory appear to the processor as if it were regular memory mapped at address `0x10000000`.
6. **Jump to Main Application:** With the XIP system running, `boot_stage2` makes a final jump to the start of the main application code (the `_reset_handler` in your `crt0` module), which is now accessible at its flash address starting at `0x10000000`.
From this point on, the processor fetches and executes application code directly from the external flash, and the 2nd-stage bootloader's job is done.
---
#### **Configuration**
Different boards use different flash memory chips, which require different initialization command sequences. The SDK provides several pre-written `boot_stage2` assembly files (e.g., `boot2_w25q080.S` for the Winbond W25Q080 chip used on the Raspberry Pi Pico).
The correct bootloader is typically selected automatically by the board configuration file you are building for (e.g., `pico.board`). You can also specify a custom bootloader for your application using the `pico_set_boot_stage2()` function in your `CMakeLists.txt`. This is an advanced feature, only necessary if you are designing custom hardware with a flash chip not directly supported by the SDK.
## Timing, Sync, Utilities
### `pico_time`
**Purpose:** Provides a comprehensive and user-friendly set of functions for handling time-based operations, including sleeps, busy-waits, alarms, and repeating timers. It abstracts the underlying hardware timers to offer a robust and flexible timing API suitable for most applications.
**CMake Target:** `pico_time`
**Source Files:**
- `src/common/pico_time/CMakeLists.txt`
- `src/common/pico_time/include/pico/time.h`
- `src/common/pico_time/include/pico/timeout_helper.h`
---
#### **Overview**
The `pico_time` library is the standard way to manage time in the Pico SDK. It provides higher-level functionality than the basic `hardware_timer` module, including support for multiple concurrent alarms through a software "alarm pool" that multiplexes a single hardware timer.
All absolute time values are handled using the `absolute_time_t` type to prevent common bugs that arise from mixing up relative delays and absolute timestamps.
---
#### **Getting the Current Time**
- `absolute_time_t get_absolute_time(void)`
- **Description:** Returns the current time as an `absolute_time_t` timestamp. This value represents the number of microseconds since the device booted. This is the fundamental function for all time-based operations.
- `uint64_t to_us_since_boot(absolute_time_t t)`
- **Description:** A utility function to convert an `absolute_time_t` value into a simple `uint64_t` integer representing the number of microseconds since boot.
---
#### **Sleeping and Busy-Waiting**
- `void sleep_us(uint64_t us)` / `void sleep_ms(uint32_t ms)`
- **Description:** Pauses the current core's execution for at least the specified number of microseconds or milliseconds. This is a low-power sleep where the core can be put into a dormant state while waiting for a timer interrupt.
- `void sleep_until(absolute_time_t target)`
- **Description:** Pauses the current core's execution until a specific target time is reached.
- `void busy_wait_us(uint64_t us)` / `void busy_wait_ms(uint32_t ms)`
- **Description:** Performs a "busy-wait" by executing a tight loop for the specified duration. This keeps the processor core fully active and consumes significant power. It should only be used for very short, critical timing delays where the overhead of a true sleep is too high.
- `bool time_reached(absolute_time_t target)`
- **Description:** A non-blocking check to see if a target time has been reached. It returns `true` if the current time is later than or equal to the `target` time. This is useful for polling loops.
---
#### **Alarms and Repeating Timers**
The SDK maintains a software "alarm pool" that uses a single underlying hardware timer to provide multiple, concurrent virtual alarms, managed for you automatically.
**Types and Callbacks:**
- `typedef int64_t (*alarm_callback_t)(alarm_id_t id, void *user_data)`
- **Description:** The required function signature for an alarm callback. The function receives the ID of the alarm that fired and the `user_data` pointer that was passed when the alarm was set.
- **Return Value:** If the callback returns `0` or a negative value, the alarm is cancelled. If it returns a positive value `t`, the alarm will be rescheduled to fire again after `t` microseconds.
- `struct repeating_timer`
- **Description:** A small struct used to hold the state of a repeating timer. You must pass a pointer to an instance of this struct when creating a repeating timer.
**Functions:**
- `alarm_id_t add_alarm_in_us(uint64_t us, alarm_callback_t callback, void *user_data, bool fire_if_past)`
- **Description:** Schedules a callback to fire once after a specified number of microseconds have elapsed. Returns an ID that can be used to cancel the alarm if needed.
- `bool add_repeating_timer_us(int64_t us, alarm_callback_t callback, void *user_data, repeating_timer_t *out)`
- **Description:** Schedules a callback to fire repeatedly at a specified interval in microseconds. The `repeating_timer_t` struct passed via the `out` pointer is used to manage the timer's state.
- `bool cancel_repeating_timer(repeating_timer_t *timer)`
- **Description:** Cancels an active repeating timer.
- `bool cancel_alarm(alarm_id_t id)`
- **Description:** Cancels an active one-shot alarm.