With the world still overcoming chip shortages, firmware portability has not been a more advantageous feature in embedded firmware design! This is where the Zephyr OS fits in nicely.

Using Zephyr, your code is written for a particular hardware target, and if that target needs to change (not uncommon these days), the code can be fairly easily ported over (fingered crossed) to another target.

The magic is in the use of device trees which include board files and device overlays. Board files tell the OS about the hardware configuration of your target while device overlays are used to specify new hardware or re-assign existing hardware e.g. the project specific pin assignments of GPIO or an I2C bus.

In this post, we go through a simple firmware example that uses the Zephyr OS, device trees and bindings to control GPIO.

Our set-up

  • A Lemon IoT – BLE nRF52832 board (target)
  • A nRF9160 DK (as an ARM programmer)
  • A Lemon IoT – Development board (to power the target and provide USB-UART used for debugging)
  • Visual studio code with the nRF connect extension installed
  • nRF Connect 2.2.0 SDK

Step 1 – Create a new application

In Visual Studio Code (VCS), and in the nRF connect extension, click on Create a new application

For our example, we’ll use Zephyr’s “hello_world” example as a starting point, however you can choose whatever best suits your project and we’re naming our application “basic-gpio”.

Step 2 – Create a build configuration

This step tells the compiler what hardware we are going to target.

Under Applications basic_gpio (or whatever you’ve called yours), click on No build configurations Click to create one

Select the board you are targeting and change the default Build directory name, if needed. In this example we are targeting is the Lemon IoT – BLE nRF82532 board. If you have not installed the board file for you board, follow this guide to do so: LINK…

Click on Build Configuration to create and build this new configuration. This might take a couple of minutes…

Step 3 – Create a build configuration

This step helps describe the file structure and the files that we’ll be modifying/adding.

Once you have your application built, a new section should appear bellow Applications. In our example, we now have BASIC_GPIO which are going to expand and explore. You’ll notice:

  • main.c under Sourcefiles/Application/src which as the name suggests, is your main application file.
  • lemon_iot_ble_nrf52832.dts under Inputs files, which is the Lemon IoT – BLE nRF52832 board file. This contains all the default/generic board configurations.
  • No overlay files Click to create one, under Input files, which is what we are going to address in this post. This is where you can added application/project-specific configurations (or overwrite board level ones!).

This is a great time to explore the board file (*.dts) and especially the areas where leds and buttons are defined, along with aliases. These are the things that we’ll be adding to our application-specific overlay.

Step 4 – Creating an overlay

Under BASIC_GPIO/Input files click on No overlay files Click to create one. This will create an overlay and prompt you to perform a Pristine build.

In similar format to the device board, add a section that starts with /{ and ends with }; Between these brackets, we’ll add our output and input definitions along with aliases (sometimes not needed).

Step 5 – Device/GPIO binding

Back in main.c and somewhere outside of the main function, we need to define the GPIO bindings. There are a few ways of performing this task, but we’ll focus on the one that we have found to be more straight forward. We’ll be defining the GPIO bindings using aliases. Notice that bindings for devices/GPIO in the overlay are similar to the one in the board file.

Step 6 – Configure the GPIO

In this step, let’s configure all the inputs and outputs. While we are at it, lets configure an interrupt for an input and set up a callback function.

In the main function, lets configure the inputs and outputs. Here’s a snapshot.

Step 7 – Access the GPIO!

The final step is to access the GPIO via the bindings we set up! Here are an example easy-to-use defines for inputs and outputs. Note whether each pin is ACTIVE_LOW or ACTIVE_HIGH in the overlay or board file!

And we are done – finally! This might’ve not been the easiest way of accessing/controlling GPIO, but wait until you have to change your target hardware because it suddenly went our of stock!