· Antony0101 · Raspberry Pi Pico Task Scheduler  · 4 min read

Experimenting with Raspberry Pi Pico - Blinky without standard libraries (Part 3)

Rewriting the blinky example without standard library.

Rewriting the blinky example without standard library.

Introduction

This is third post in the series of creating simple task scheduler for raspberry pi pico. In this post we rewrite earlier blinky example without using the standard libraries. We will be manipulating the registers directly to control the GPIO pins.

Getting Started

When we need to directly manipulate the registers, we need to know the memory addresses of the registers. The datasheet of the RP2040 chip provides the information about the memory addresses of the registers.

In this post we will be accessing the GPIO registers in SIO (Single-cycle IO block) to control the GPIO pins. There are other ways to control the GPIO pins like PIO (Programmable IO) and PWM, but this is the simplest way to control the GPIO pins.

Single-cycle IO block (SIO)

This is a special block in RP2040 which is dedicated to each cpu core. This block is only accessible from the core it is associated with. It provides registers for identifying the core, controlling the GPIO pins, and other functions. This is the best way to control the GPIO pins directly. You can find the details of the SIO block in the datasheet at page 31.

The registers for the SIO block starts at address 0xd0000000. The register for controlling the GPIO registers relevent to us in the SIO are GPIO_OUT (0xd0000010), GPIO_OUT_SET (0xd0000014), GPIO_OUT_CLR (0xd0000018) and GPIO_OE (0xd0000020).

Setting up the gpio pins

To blink the LED on the board, we need to set the GPIO pin 25 as output. But before that we need to configue gpio multiplexer to use SIO registers for its operation. The gpio multiplexer is place where the gpio pins configured to use for the different peripherals in rp2040. The address of the gpio multiplexer is 0x40014000.

The registers in the gpio multiplexer which is relevant to us is GPIO_CTRL. There is also a GPIO_STATUS register which provides the status of the gpio pin but we are not using it in this example. Each GPIO pin has a corresponding GPIO_STATUS register and GPIO_CTRL register. To get the address of the GPIO_CTRL register for a pin use the forumula (0x40014000 + (pin_number * 8)).

The register contains a lot of configuration bits but we are only interested in the [4:0] bits which are used to select the function of the pin. To set the pin to use the SIO registers, we need to set the register to value 5 (0x5).

Blinky Code

#include <stdint.h>

#define GPIO_BASE 0x40014000
#define GPIO_CTRL_BASE (GPIO_BASE + 0x04)
#define SIO_BASE  0xd0000000

#define GPIO_OUT   (*(volatile uint32_t *)(SIO_BASE + 0x010))
#define GPIO_OUT_SET (*(volatile uint32_t *)(SIO_BASE + 0x014))
#define GPIO_OUT_CLR (*(volatile uint32_t *)(SIO_BASE + 0x018))
#define GPIO_OE    (*(volatile uint32_t *)(SIO_BASE + 0x020))


void set_gpio_pin_to_output(uint8_t pin) {
    volatile uint32_t *gpio_ctrl = (volatile uint32_t *)(GPIO_CTRL_BASE + pin * 8);
    *gpio_ctrl = 5; // 5 means SIO function

    // Set GPIO pin as output
    GPIO_OE |= (1 << pin);
}

void gpio_pin_on(uint8_t pin) {
    // Set GPIO PIN
    GPIO_OUT_SET = (1 << pin);
}

void gpio_pin_off(uint8_t pin) {
    // Clear GPIO PIN
    GPIO_OUT_CLR = (1 << pin);
}

int main() {
    set_gpio_pin_to_output(25);


    while (1) {
        gpio_pin_on(25);
        for (volatile int i = 0; i < 10000000; i++); // delay
        gpio_pin_off(25);
        for (volatile int i = 0; i < 10000000; i++); // delay
    }

    return 0;
}

In above code first we defined all relevant memory addresses of the registers. Next declared the function set_gpio_pin_to_output to set the gpio pin to output. The function takes the pin number as argument and sets the GPIO_CTRL register to 5 which set the pin to use the SIO registers and also set the gpio mode in SIO to output mode. Next we defined the functions gpio_pin_on and gpio_pin_off to set and clear the gpio pin. In the main function we set the pin 25 to output mode and then in the infinite loop we turn on and off the pin with some delay.

Building the code

if you have already setup the build tools then you should refer the previous post to build the code.

Conclusion

In this post we rewrote the blinky example without using the standard libraries. We directly manipulated the registers to control the GPIO pins. In the next post we will be looking into flashing binary date to the raspberry pi pico flash memory at the desired address.

Back to Blog

Related Posts

View All Posts »