// lesson: sio-gpio
GPIO Through the SIO
The SIO block at 0xd0000000 is how RP2040 code drives pins. Its GPIO
registers sit at these offsets (datasheet ยง2.3.1.7):
| offset | register | what it does |
|---|---|---|
0x010 |
GPIO_OUT |
output level of each pin |
0x014 |
GPIO_OUT_SET |
write mask: set those OUT bits |
0x018 |
GPIO_OUT_CLR |
write mask: clear those OUT bits |
0x01c |
GPIO_OUT_XOR |
write mask: flip those OUT bits |
0x020 |
GPIO_OE |
output enable for each pin |
0x024 |
GPIO_OE_SET |
write mask: set those OE bits |
0x028 |
GPIO_OE_CLR |
write mask: clear those OE bits |
0x02c |
GPIO_OE_XOR |
write mask: flip those OE bits |
Why the SET/CLR/XOR aliases when last lesson's read-modify-write already
works? Because RMW is three operations, and between your read and your
write an interrupt handler โ or the RP2040's second core; it's a dual-core
chip, and both cores can drive GPIO through the same SIO block โ may also
touch GPIO_OUT. Do a plain RMW and that other write can land in the gap
between your read and your write, and gets silently overwritten. Writing a
mask to GPIO_OUT_SET is one store; the hardware does the modify
atomically. This pattern is all over the RP2040, so drivers barely ever RMW
the SIO.
Instead of casting raw offsets one at a time, real drivers describe a whole register block as a struct whose field layout mirrors the datasheet, then point it at the base address:
struct pico_sio {
volatile uint32_t cpuid; /* 0x000 */
volatile uint32_t gpio_in; /* 0x004 */
volatile uint32_t gpio_hi_in; /* 0x008 */
volatile uint32_t _pad; /* 0x00c (reserved) */
volatile uint32_t gpio_out; /* 0x010 */
volatile uint32_t gpio_out_set; /* 0x014 */
volatile uint32_t gpio_out_clr; /* 0x018 */
volatile uint32_t gpio_out_xor; /* 0x01c */
volatile uint32_t gpio_oe; /* 0x020 */
volatile uint32_t gpio_oe_set; /* 0x024 */
volatile uint32_t gpio_oe_clr; /* 0x028 */
volatile uint32_t gpio_oe_xor; /* 0x02c */
};
#define SIO ((volatile struct pico_sio *)0xd0000000u)
The padding field is load-bearing: every field must land on its datasheet offset, so reserved holes get explicit placeholders. (In a real project the struct lives in a header; here each file carries its own copy because challenge files are self-contained.)
The tests play the part of the hardware: they hand you a struct pico_sio
in ordinary memory and then inspect which alias register your code wrote
to, and with what mask.
โบ Drive a Pin Atomically
15 ptsImplement four pin helpers using only the SET/CLR/XOR alias registers โ
the tests verify that gpio_out and gpio_oe themselves are never written
directly.
Log in to submit a solution and earn points.