Raspberry Pi Pico - SDK Abstraction Layers
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
- …libraries
- 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;
(LED_PIN);
gpio_init(LED_PIN, GPIO_OUT);
gpio_set_dirwhile (true) {
(LED_PIN, 1);
gpio_put(250);
sleep_ms(LED_PIN, 0);
gpio_put(250);
sleep_ms}
#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)
(mask);
gpio_set_maskelse
(mask);
gpio_clr_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) {
->gpio_set = mask;
sio_hw}
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 {
//...
(SIO_GPIO_OUT_SET_OFFSET) // SIO_GPIO_OUT_SET
_REG_// 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