Raspberry Pi Pico - SDK Abstraction Layers

Electronics
Embedded
Published

December 24, 2022

Modified

December 24, 2022

Developers implement higher-level library functions to interact with an embedded microcontroller such as the RP2040. These functions are defined by a Software Development Kit (SDK) for example the Raspberry Pi Pico C/C++ SDK [01], which provides headers, libraries and the build system necessary to write programs for RP2040-based devices.

Abstraction Layers

The SDK architecture is build with multiple layers stacked on top of each other. From top to bottom…

  • Higher-level libraries…
    • …provide higher-level APIs, concepts and abstractions (e.g. sleep)
    • …may have cross-cutting concerns between multiple pieces of hardware
    • …build upon one or more underlying hardware_ libraries
  • Hardware support libraries
    • …libraries hardware_ providing actual APIs…
    • …for interacting with each piece of physical hardware/peripheral
    • …these libraries do not accessing registers directly
  • Hardware abstraction library …C structures hardware_structs
    • …represent the memory mapped layout of RP2040 registers
    • …contains the layout of the hardware registers in a block
    • …hides the complexity of dealing with individual memory locations
  • Hardware register libraries…
    • hardware_regs library is a complete set of include files…
    • …for all RP2040 registers …auto-generated from the hardware itself

Example

Based on a very basic example form pico-examples/blink/blink.c it easy to follow the call-sequence down the abstraction layers.

#include "pico/stdlib.h"

int main() {
#ifndef PICO_DEFAULT_LED_PIN
#warning blink example requires a board with a regular LED
#else
    const uint LED_PIN = PICO_DEFAULT_LED_PIN;
    gpio_init(LED_PIN);
    gpio_set_dir(LED_PIN, GPIO_OUT);
    while (true) {
        gpio_put(LED_PIN, 1);
        sleep_ms(250);
        gpio_put(LED_PIN, 0);
        sleep_ms(250);
    }
#endif
}

The gpio_put(pinNumber, value) function is used to set a binary value on an IO pin, in this example to flash an onboard LED. The pin for each supported board is different and defined in a board configuration file by preprocessor variables PICO_DEFAULT_LED_PIN. The normal Raspberry Pi Pico use GP25 aka pin 25 for the onboard LED. The exact details on the pinout, registers an memory addresses a described in the RP2040 Datasheet [02].

# ...show the default LED pin on all supported boards...
grep -r PICO_DEFAULT_LED_PIN $PICO_SDK_PATH/src/boards/include/boards

To understand the inner workings of a hardware support API function like gpio_put() it is required to read into the SDK library code defined in $PICO_SDK_PATH/src/rp2_common/hardware_gpio/include/hardware/gpio.h

/*! \brief Drive a single GPIO high/low
 *  \ingroup hardware_gpio
 *
 * \param gpio GPIO number
 * \param value If false clear the GPIO, otherwise set it.
 */
static inline void gpio_put(uint gpio, bool value) {
    uint32_t mask = 1ul << gpio;
    if (value)
        gpio_set_mask(mask);
    else
        gpio_clr_mask(mask);
}

Internally gpio_put() calls another API function gpio_set_mask() for a value of 1. A bit-mask is propagated down-ward which represents the pin (for example 25) to manipulate.

/*! \brief Drive high every GPIO appearing in mask
 *  \ingroup hardware_gpio
 *
 * \param mask Bitmask of GPIO values to set, as bits 0-29
 */
static inline void gpio_set_mask(uint32_t mask) {
    sio_hw->gpio_set = mask;
}

gpio_set_mask() reaches down into the hardware abstraction layer where hardware registers are organized into C struct definitions $PICO_SDK_PATH/src/rp2040/hardware_structs/include/hardware/structs/sio.h. SIO (Single-cycle IO block) provides the interface for the processors to access the shared GPIO registers in a deterministic and concurrency safe way.

typedef struct {
//...
   _REG_(SIO_GPIO_OUT_SET_OFFSET) // SIO_GPIO_OUT_SET
   // GPIO output value set
   // 0x3fffffff [29:0]  : GPIO_OUT_SET (0): Perform an atomic bit-set on GPIO_OUT, i
   io_wo_32 gpio_set;
//...
} sio_hw_t;

#define sio_hw ((sio_hw_t *)SIO_BASE)

The C structure interfaces with the hardware registers layer, which associates readable names to memory addresses defined in $PICO_SDK_PATH/src/rp2040/hardware_regs/include/hardware/regs/sio.h. The details of this mechanism are explained in section 2.3.1.2 GPIO Control in the RP2040 Datasheet [02].

// =============================================================================
// Register    : SIO_GPIO_OUT_SET
// Description : GPIO output value set
//               Perform an atomic bit-set on GPIO_OUT, i.e. `GPIO_OUT |= wdata`
#define SIO_GPIO_OUT_SET_OFFSET _u(0x00000014)
#define SIO_GPIO_OUT_SET_BITS   _u(0x3fffffff)
#define SIO_GPIO_OUT_SET_RESET  _u(0x00000000)
#define SIO_GPIO_OUT_SET_MSB    _u(29)
#define SIO_GPIO_OUT_SET_LSB    _u(0)
#define SIO_GPIO_OUT_SET_ACCESS "WO"

References

[01] Raspberry pi Pico C/C++ SDK
https://datasheets.raspberrypi.com/pico/raspberry-pi-pico-c-sdk.pdf

[02] RP2040 Datasheet
https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf