execution of zephyr-rtos: GPIO emulation in qemu_riscv
In this article, we will go through the process of setting up and running a simple Zephyr RTOS application that uses GPIO (General Purpose Input/Output) emulation on the QEMU RISC-V platform. There are many sample projects already shipped within zephyr and we will focus in how to execute the samples/basic/blinky in the case that we do not have hardware available. For this we will be using QEMU.
Getting the sources-preparing environment
#requirements
sudo apt-get install --no-install-recommends git cmake ninja-build gperf ccache dfu-util device-tree-compiler wget python3-pip python3-setuptools python3-wheel xz-utils file make gcc gcc-multilib
#Zephyr source code and install additional dependencies:
git clone https://github.com/zephyrproject-rtos/zephyr
cd zephyr (* zephyr-main in case you download zip from Github)
pip3 install --user -r scripts/requirements.txt (*some left-out)
#zephyr SDK, move the zip to /opt folder
wget https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.16.8/zephyr-sdk-0.16.8_linux-x86_64.tar.xz
tar xvf zephyr-sdk-0.16.8_linux-x86_64.tar.xz
#Set up the environment (do this always for a shell where you will be compiling/running Zephyr):
export ZEPHYR_TOOLCHAIN_VARIANT=zephyr
export ZEPHYR_SDK_INSTALL_DIR="/opt/zephyr-sdk-0.16.8/"
. ./zephyr-env.sh (source this)
In order to compile and run the blinky project now, we need to :
#pwd:zephyr
mkdir build-example
cd build-example
cmake -DBOARD=qemu_riscv32 $ZEPHYR_BASE/samples/basic/blinky
make
As we can observe, we will encounter errors in the compilation, because there is no GPIO and no leds defined for our application. In order to solve this we will need to do slight modifications in the following files:
Modifications in boards/qemu/riscv32/qemu_riscv32.dts
diff --git a/boards/qemu/riscv32/qemu_riscv32.dts b/boards/qemu/riscv32/qemu_riscv32.dts
index 2c38ca1da1d..ee0597f742c 100644
--- a/boards/qemu/riscv32/qemu_riscv32.dts
+++ b/boards/qemu/riscv32/qemu_riscv32.dts
@@ -3,6 +3,7 @@
/dts-v1/;
#include <qemu/virt-riscv32.dtsi>
+#include <zephyr/dt-bindings/gpio/gpio.h> //understand naming bindings like GPIO_ACTIVE_HIGH etc..
/ {
chosen {
@@ -10,8 +11,38 @@
zephyr,shell-uart = &uart0;
zephyr,sram = &ram0;
};
+
+
+ gpio0: gpio_emul_0 {
+ compatible = "zephyr,gpio-emul";
+ gpio-controller;
+ ngpios = <1>; // Number of GPIO pins
+ label = "GPIO_0";
+ #gpio-cells = <2>;
+
+ };
+
+ resources {
+ compatible = "test-gpio-basic-api";
+ out-gpios = <&gpio0 0 0>; /* Pin 0 */
+ //in-gpios = <&gpio0 1 0>; /* Pin 1 */
+ };
+
+ leds {
+ compatible = "gpio-leds";
+ led0: led0 {
+ gpios = <&gpio0 0 GPIO_ACTIVE_LOW>; // Connects LED to Pin 0
+ label = "LED 0";
+ };
+ };
+
+ aliases {
+ led0 = &led0;
+ };
+
};
&uart0 {
status = "okay";
};
+
In the device tree, there are the declarations now for the gpio0 compatible with "zephyr,gpio-emul", the leds of our interest which is compatible with the "gpio-leds", and for the emulation under the resources node the "test-gpio-basic-api" in order to use the emulated GPIO backend API. Also note the included <zephyr/dt-bindings/gpio/gpio.h> for understanding definitions used f.e GPIO_ACTIVE_LOW.
领英推荐
Modifications in drivers/gpio/Kconfig.emul
diff --git a/drivers/gpio/Kconfig.emul b/drivers/gpio/Kconfig.emul
index 4db2429ed16..f55f5fced1c 100644
--- a/drivers/gpio/Kconfig.emul
+++ b/drivers/gpio/Kconfig.emul
@@ -6,7 +6,7 @@
config GPIO_EMUL
bool "Emulated GPIO driver"
default y
- depends on DT_HAS_ZEPHYR_GPIO_EMUL_ENABLED
+# depends on DT_HAS_ZEPHYR_GPIO_EMUL_ENABLED
help
Enable the emulated GPIO driver. Mainly used for testing, this
driver allows for an arbitrary number of emulated GPIO controllers
Here, the dependency is commented, achieving the configuration of GPIO_EMUL to be true when compiling the project and hold true without been depended on any other config.
Modifications in samples/basic/blinky/prj.conf
diff --git a/samples/basic/blinky/prj.conf b/samples/basic/blinky/prj.conf
index 91c3c15b37d..4c180e3a33e 100644
--- a/samples/basic/blinky/prj.conf
+++ b/samples/basic/blinky/prj.conf
@@ -1 +1,4 @@
CONFIG_GPIO=y
+CONFIG_GPIO_EMUL=y
+CONFIG_LOG=y
+CONFIG_LOG_DEFAULT_LEVEL=4
In this file, the GPIO_EMUL is configured, and very handy logging/debugging configurations levels.
Next we will present our main.c source code of our application
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/device.h>
#include <zephyr/sys/printk.h>
#include <zephyr/drivers/gpio/gpio_emul.h> //essential for calling gpio_emul funcs.
#define SLEEP_TIME_MS 276447232 //max since in emulation time runs faaast!)
// The devicetree node identifier for the "led0" alias
#define LED0_NODE DT_ALIAS(led0)
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
int main(void)
{
int ret,my_val;
bool led_state = true;
if (!gpio_is_ready_dt(&led)) {
printk("Failed gpio_is_ready(&led)\n");
return 0;
}
ret = gpio_pin_configure(led.port,0, GPIO_OUTPUT_ACTIVE);
if (ret < 0) {
printk("error on led configure: %d\n",ret);
return 0;
}
//check if the device is ready
if (led.port && !gpio_is_ready_dt(&led)) {
printk("Error %d: LED device %s is not ready\n",ret, led.port->name);
return 0;
}
//get binding on the device. this dev we will use to read via gpio_emul API
const struct device *dev;
dev = device_get_binding(led.port->name);
if (dev == NULL) {
printk("dev == NULL!\n");
return 0;
}
while (1) {
ret = gpio_pin_toggle_dt(&led);
if (ret < 0) {
printk("gpio_pin_toggle_dt() failed.\n");
return 0;
}
led_state = !led_state;
printf("LED state: %s\n", led_state ? "ON" : "OFF");
my_val = gpio_emul_output_get(dev, 0);
printk(" [ %d ] \n",my_val);
k_msleep(SLEEP_TIME_MS);
}
return 0;
}
Comments have been included inside the source.
Now when we run the commands for compiling/running the project we can get the desired functionality as in the next image.
The modifications done in the zephyr's source files are available in this link:https://github.com/NikosMouzakitis/zephyr_qemu/tree/main/blinky_project_gpio