|
| 1 | +--- |
| 2 | +title: GPIO & Real Boards |
| 3 | +description: This tutorial explains how to use GPIO in RIOT to control LEDs or read button presses. |
| 4 | +code_folder: examples/guides/gpio/ |
| 5 | +--- |
| 6 | + |
| 7 | +import Contact from '@components/contact.astro'; |
| 8 | +import WorkingVideo from './img/gpio/05_working_video.mp4'; |
| 9 | +import ButtonVideo from './img/gpio/08_buttons.mp4'; |
| 10 | + |
| 11 | +So far we have been running all our code using the `arduino-feather-nrf52840-sense` board. |
| 12 | + |
| 13 | +In this tutorial, we will learn how to use the GPIO pins on a real board to control LEDs or read button presses. |
| 14 | + |
| 15 | +For this tutorial I will be using a the `arduino-feather-nrf52840-sense` board with the [Teamagochi PCB](https://github.com/smartuni/teamagochi), but you can use any board that has GPIO pins. |
| 16 | + |
| 17 | +## Step 1: Configuring our Board |
| 18 | + |
| 19 | +<Contact /> |
| 20 | + |
| 21 | +First, we need to inform RIOT about the board we are using. |
| 22 | + |
| 23 | +To do this we adapt the `BOARD` variable in our `Makefile` to the board we are using. |
| 24 | + |
| 25 | +```make |
| 26 | +BOARD ?= arduino-feather-nrf52840-sense |
| 27 | +``` |
| 28 | + |
| 29 | + |
| 30 | + |
| 31 | +First we want to make sure that we can build the code for the board. |
| 32 | +To do this we can simply run `make all` in the terminal, |
| 33 | +this will build the code for the board and check if everything is set up correctly. |
| 34 | + |
| 35 | +If everything is set up correctly, we should now be able to move on to flashing the board. |
| 36 | + |
| 37 | +:::tip |
| 38 | +It might return something like: |
| 39 | + |
| 40 | +``` |
| 41 | +/bin/sh: line 1: arm-none-eabi-gcc: command not found |
| 42 | +Compiler arm-none-eabi-gcc is required but not found in PATH. Aborting. |
| 43 | +``` |
| 44 | + |
| 45 | +This means that you need to install the ARM toolchain, |
| 46 | +you can do this by running `sudo apt install gcc-arm-none-eabi` on Ubuntu |
| 47 | +or `sudo pacman -S arm-none-eabi-gcc` on Arch Linux. |
| 48 | + |
| 49 | +If it still doesn't work, consider running it via the Docker container using `BUILD_IN_DOCKER=1 make flash`. |
| 50 | + |
| 51 | +Please refer to the [Flashing a RIOT Application](/getting-started/flashing) guide for more information. |
| 52 | +::: |
| 53 | + |
| 54 | +Try flashing the board with `make flash` and see if it works, |
| 55 | +it should still execute the same code as before when running RIOT natively on your |
| 56 | +own hardware but now using the actual board. |
| 57 | + |
| 58 | + |
| 59 | + |
| 60 | +Now if we type `make term` we should see the output of the board in the terminal. |
| 61 | +Make sure that you have the board connected to your computer via USB and |
| 62 | +that your user has the necessary permissions to access the serial port. |
| 63 | + |
| 64 | +:::tip |
| 65 | +On Arch Linux, you might need to add your user to the `uucp` group to access the serial port. |
| 66 | + |
| 67 | +```bash |
| 68 | +sudo usermod -a -G uucp $USER |
| 69 | +``` |
| 70 | +::: |
| 71 | + |
| 72 | +## Step 2: Controlling LEDs |
| 73 | + |
| 74 | +Now that we have the board working, let's try to control the LEDs on the board. |
| 75 | + |
| 76 | +The exact pins that control the LEDs might vary depending on the board you are using, |
| 77 | +but in the case of the `arduino-feather-nrf52840-sense` board, the LED would be connected |
| 78 | +on Port 1 Pin 9. |
| 79 | + |
| 80 | +First we need to include the necessary modules in our projects `Makefile`. |
| 81 | + |
| 82 | +```make |
| 83 | +# Add the gpio module to the build |
| 84 | +USEMODULE += periph_gpio |
| 85 | +USEMODULE += periph_gpio_irq |
| 86 | + |
| 87 | +# Enable the milliseconds timer. |
| 88 | +USEMODULE += ztimer |
| 89 | +USEMODULE += ztimer_msec |
| 90 | +``` |
| 91 | + |
| 92 | + |
| 93 | + |
| 94 | +This allows us to both control the GPIO pins and timers. |
| 95 | + |
| 96 | +Now we need to actually define the pin that we want to control in our code. |
| 97 | +To do this include the following lines **before** the `main` function. |
| 98 | +{/*<!--skip ci-->*/} |
| 99 | +```c |
| 100 | +#include "board.h" |
| 101 | +#include "periph/gpio.h" |
| 102 | +#include "ztimer.h" |
| 103 | + |
| 104 | +/* Define the LED0 pin and mode */ |
| 105 | +gpio_t led0 = GPIO_PIN(1, 9); |
| 106 | +gpio_mode_t led0_mode = GPIO_OUT; |
| 107 | +``` |
| 108 | + |
| 109 | +Now we can control the LED. First we initialize the pin and afterwards we turn the LED off by clearing the pin |
| 110 | + |
| 111 | +{/*<!--skip ci-->*/} |
| 112 | +```c |
| 113 | +int main(void) { |
| 114 | + /* Initialize the LED0 pin */ |
| 115 | + gpio_init(led0, led0_mode); |
| 116 | + /* Turn off the LED0 pin */ |
| 117 | + gpio_clear(led0); |
| 118 | + |
| 119 | + /* Loop forever */ |
| 120 | + while (1) { |
| 121 | + |
| 122 | + } |
| 123 | +} |
| 124 | +``` |
| 125 | +
|
| 126 | +Turning the LED off when the board starts is quite boring, |
| 127 | +so let's make it blink by adding a delay and toggling the LED. |
| 128 | +
|
| 129 | +```c |
| 130 | + /* Loop forever */ |
| 131 | + while (1) { |
| 132 | + /* Toggle the LED0 pin every 500 milliseconds */ |
| 133 | + gpio_toggle(led0); |
| 134 | + ztimer_sleep(ZTIMER_MSEC, 500); |
| 135 | + } |
| 136 | +``` |
| 137 | + |
| 138 | + |
| 139 | + |
| 140 | +If we now `make flash` and then `make term` we should see the LED blinking. |
| 141 | + |
| 142 | +<video controls> |
| 143 | + <source src={WorkingVideo} type="video/mp4" /> |
| 144 | + Your browser does not support the video tag. |
| 145 | +</video> |
| 146 | + |
| 147 | +## Step 3: Reading Button Presses |
| 148 | + |
| 149 | +If you remember what we did in the timers tutorial, |
| 150 | +we can use quite similar code to read button presses. |
| 151 | + |
| 152 | +On a constrained device you usually don't want to poll the button state, |
| 153 | +which is why we will use an interrupt to detect the button press, |
| 154 | +that way we can drastically reduce the power consumption of the device. |
| 155 | + |
| 156 | + |
| 157 | +First we need to define the callback function that will be called when the button is pressed. |
| 158 | + |
| 159 | +```c title="Define the LED1 pin and mode" |
| 160 | +/* Define the LED1 pin and mode */ |
| 161 | +gpio_t led1 = GPIO_PIN(1, 10); |
| 162 | +gpio_mode_t led1_mode = GPIO_OUT; |
| 163 | +``` |
| 164 | + |
| 165 | +```c title="Define the button callback function" |
| 166 | +/* This callback function will be called when the button state changes */ |
| 167 | +void button_callback(void *arg) { |
| 168 | + /* the argument is not used */ |
| 169 | + (void)arg; |
| 170 | + |
| 171 | + /* Toggle the LED1 pin based on the button state */ |
| 172 | + if (gpio_read(button)) { |
| 173 | + gpio_clear(led1); |
| 174 | + } else { |
| 175 | + gpio_set(led1); |
| 176 | + } |
| 177 | +} |
| 178 | +``` |
| 179 | +
|
| 180 | +Now we need to define the button pin and mode and initialize it. |
| 181 | +
|
| 182 | +```c {1-2, 26-27} |
| 183 | +/* Define the button pin */ |
| 184 | +gpio_t button = GPIO_PIN(1, 2); |
| 185 | +
|
| 186 | +/* This callback function will be called when the button state changes */ |
| 187 | +void button_callback(void *arg) { |
| 188 | + /* the argument is not used */ |
| 189 | + (void)arg; |
| 190 | +
|
| 191 | + /* Toggle the LED1 pin based on the button state */ |
| 192 | + if (gpio_read(button)) { |
| 193 | + gpio_clear(led1); |
| 194 | + } else { |
| 195 | + gpio_set(led1); |
| 196 | + } |
| 197 | +} |
| 198 | +
|
| 199 | +int main(void) { |
| 200 | + /* Initialize the LED0 pin */ |
| 201 | + gpio_init(led0, led0_mode); |
| 202 | + /* Turn off the LED0 pin */ |
| 203 | + gpio_clear(led0); |
| 204 | +
|
| 205 | + /* Initialize the LED1 pin */ |
| 206 | + gpio_init(led1, led1_mode); |
| 207 | + /* Turn off the LED1 pin */ |
| 208 | + gpio_clear(led1); |
| 209 | +
|
| 210 | + /* Initialize the button pin */ |
| 211 | + gpio_init_int(button, GPIO_IN_PU, GPIO_BOTH, button_callback, NULL); |
| 212 | +
|
| 213 | + /* Loop forever */ |
| 214 | + while (1) { |
| 215 | + /* Toggle the LED0 pin every 500 milliseconds */ |
| 216 | + gpio_toggle(led0); |
| 217 | + ztimer_sleep(ZTIMER_MSEC, 500); |
| 218 | + } |
| 219 | +} |
| 220 | +``` |
| 221 | + |
| 222 | +This code will initialize the button pin and call the `button_callback` function whenever the button is pressed. |
| 223 | + |
| 224 | + |
| 225 | + |
| 226 | +If we now `make flash` and then `make term` we should see the LED turn on when the button is pressed. |
| 227 | + |
| 228 | +:::tip |
| 229 | +If you are using the `arduino-feather-nrf52840-sense` board, the button is on the back of the board. |
| 230 | + |
| 231 | +Be careful to not press the reset button by mistake 😉 |
| 232 | +::: |
| 233 | + |
| 234 | +<video controls> |
| 235 | + <source src={ButtonVideo} type="video/mp4" /> |
| 236 | + Your browser does not support the video tag. |
| 237 | +</video> |
| 238 | + |
| 239 | +## Conclusion |
| 240 | + |
| 241 | +In this tutorial we learned how to use the GPIO pins on a real board to control LEDs or read button presses. |
| 242 | + |
| 243 | +This is a very basic example, but it should give you a good starting point to build more complex applications. |
| 244 | + |
| 245 | +<Contact /> |
| 246 | + |
| 247 | +:::note |
| 248 | +The source code for this tutorial can be found [HERE](https://github.com/RIOT-OS/RIOT/tree/master/examples/guides/gpio). |
| 249 | + |
| 250 | +If your project is not working as expected, you can compare your code with the code in this repository to see if you missed anything. |
| 251 | +::: |
0 commit comments