// lesson: funcsel

Routing Pins with FUNCSEL

The SIO can only drive a pin the pin mux actually routes to it. Each of the RP2040's 30 user GPIOs has a control register in the IO_BANK0 block (base 0x40014000): pin N's GPIO_CTRL register sits at offset N*8 + 4. Its low five bits โ€” the FUNCSEL field, bits 4:0 โ€” pick which peripheral owns the pin: UART, SPI, PWMโ€ฆ Function 5 is the SIO, i.e. "software controls this pin". Until you select it, all the GPIO_OUT writing in the world does nothing visible.

FUNCSEL is where last lesson's warning bites: GPIO_CTRL packs other fields into the same word (output overrides, interrupt config). Most RP2040 peripherals โ€” IO_BANK0 included โ€” actually do get atomic bit manipulation for free: every register is given a 4KB address slot, and writing to its address plus 0x1000/0x2000/0x3000 atomically XORs/sets/clears bits with no read-modify-write at all (datasheet ยง2.1.2). The SIO you just used is the exception โ€” it's wired straight to the cores off the normal bus, so it can't do that trick, which is exactly why it needed its own hand-built SET/CLR/XOR registers. That alias mechanism is one more address computation on top of what this lesson is really after, though: the field-update shape below โ€” mask, or, store โ€” which the tests exercise directly on a plain register variable, and which you need to understand regardless of which write mechanism eventually lands the bits. So here you'll do it by hand:

uint32_t v = *ctrl;
v &= ~0x1fu;        /* clear the FUNCSEL field   */
v |= funcsel;       /* install the new function  */
*ctrl = v;

Mask first, then or โ€” the classic field update. Getting this wrong by writing the whole word would silently zero every other field in the register.

โ€บ Select a Pin Function

10 pts

Implement gpio_set_funcsel: update only bits 4:0 of the control register, preserving all 27 other bits.

Log in to submit a solution and earn points.