Kampis Elektroecke

XADC und GPIO

Xilinx stellt mit PetaLinux ein komplettes, auf Yocto basierendes, Embedded Linux Entwicklungspaket zur Verfügung, welches es dem Nutzer erlaubt, mit vergleichsweise wenig Aufwand ein eigenes Linux-Betriebssystem mit entsprechenden Hardware-Treibern für das ZYNQ SoC zu erstellen. In diesem Artikel möchte ich zeigen, wie ein minimales Linux-Betriebssystem mit XADC- und GPIO-Support für das ZYBO gebaut werden kann.

Die Erstellung des Linux-Systems:

Für dieses Tutorial wird ein minimales Hardwaredesign, bestehend aus zwei AXI-GPIO IP-Blöcken, dem XADC und dem Processing System, genutzt.

Aus diesem Design wird ein Bitstream erzeugt und eine XSA-Datei erzeugt. Als nächstes wird ein neues PetaLinux-Projekt angelegt und die verwendete Hardware eingelesen:

$ source <PetaLinux-Pfad>/settings.sh
$ petalinux-create --type project --template zynq --name MinimalLinux
$ petalinux-config --get-hw-description ../hardware/System_wrapper.xsa

Über die sich öffnende Systemkonfiguration können bei Bedarf verschiedene Einstellungen an dem Projekt vorgenommen werden (wie z. B. der Hostname oder das root-Passwort).

Für dieses Tutorial werden die Standardeinstellungen verwendet. Damit Linux den Zugriff über die XADC-Kanäle 6, 7, 14 und 15 erlaubt muss der Device-Tree des Systems erweitert werden. Dazu findet sich in dem Verzeichnis project-spec/meta-user/recipes-bsp/device-tree/files eine fertige Vorlage namens system-user.dtsi, die für benutzerspezifische Anpassungen an dem Device-Tree genutzt werden kann. Diese Datei wird folgendermaßen erweitert:

/include/ "system-conf.dtsi"
/ {
};

&XADC{
    xlnx,channels {
        #address-cells = <0x1>;
        #size-cells = <0x0>;
        channelJA1@7 {
           reg = <7>;
           xlnx,bipolar;
        };
        channelJA2@8 {
           reg = <8>;
           xlnx,bipolar;
        };
        channelJA3@15 {
           reg = <15>;
           xlnx,bipolar;
        };
        channelJA4@16 {
           reg = <16>;
           xlnx,bipolar;
        };
    };
};

Als nächstes wird der Build-Prozess gestartet:

$ petalinux-build
INFO: Sourcing build tools
[INFO] Building project
[INFO] Sourcing build environment
[INFO] Generating workspace directory
INFO: bitbake petalinux-image-minimal
Loading cache: 100% |#########################################################################################################################################################################################################| Time: 0:00:01
Loaded 4264 entries from dependency cache.

Dieser Vorgang dauert i. d. R. ein paar Minuten und sobald er abgeschlossen ist, werden die benötigten Dateien zum Booten des Linux-Systems erzeugt:

$ petalinux-package --force --boot --fsbl images/linux/zynq_fsbl.elf --fpga images/linux/system.bit --u-boot
INFO: Sourcing build tools
INFO: File in BOOT BIN: "/var/development/Git/ZYBO/linux/MinimalLinux/petalinux/images/linux/zynq_fsbl.elf"
INFO: File in BOOT BIN: "/var/development/Git/ZYBO/linux/MinimalLinux/petalinux/images/linux/system.bit"
INFO: File in BOOT BIN: "/var/development/Git/ZYBO/linux/MinimalLinux/petalinux/images/linux/u-boot.elf"
INFO: File in BOOT BIN: "/var/development/Git/ZYBO/linux/MinimalLinux/petalinux/images/linux/system.dtb"
INFO: Generating Zynq binary package BOOT.BIN...


****** Xilinx Bootgen v2020.2
  **** Build date : Nov 15 2020-06:11:24
    ** Copyright 1986-2020 Xilinx, Inc. All Rights Reserved.


[INFO]   : Bootimage generated successfully

INFO: Binary is ready.

Aus dem Verzeichnis images/linux werden nun die folgenden Dateien auf eine bootfähige FAT32 formatierte Partition einer SD-Karte kopiert:

  • BOOT.bin
  • boot.scr
  • image.ub

Die SD-Karte wird dann in das ZYBO eingelegt und das Board eingeschaltet, worauf das Linux bootet.


Hinweis:

In der Standardkonfiguration lautet das Passwort root.


Auslesen des XADC:

Über das IIO-Subssystem des Linux-Kernels kann auf die einzelnen Kanäle des XADC zugegriffen werden. Die entsprechenden Einträge sind unter /sys/bus/iio/devices/iio:device0 (Standardkanäle), bzw. /sys/bus/iio/devices/iio:device1 (zusätzlichen Kanäle) zu finden. Diese können z. B. mit einem Shell-Skript ausgelesen werden:

#!/bin/bash

while true
do
    Raw=$(cat /sys/bus/iio/devices/iio:device1/in_voltage10_vaux14_raw)

    echo "Raw: ${Raw}"

    sleep 1
done
$ ./ReadADC.sh
Raw: 323
Raw: 514
Raw: 899
Raw: 1194
Raw: 1424

Verwendung der GPIO:

Die GPIO (sowohl die AXI-GPIO, als auch die EMIO oder MIO) sind unter /sys/class/gpio zu finden. Dieses Verzeichnis beinhaltet bei diesem System drei verschiedene Verzeichnisse, wobei jeder Eintrag für einen GPIO-Block steht:

  • gpiochip1012
  • gpiochip1016
  • gpiochip1020

Jedes Verzeichnis beinhaltet eine Datei namens label, welche den Namen des Knotens im Device-Tree enthält und über die Datei base lässt sich der Basispin des GPIO-Blocks, in diesem Beispiel für den LED-Block, ermitteln:

$ cd /sys/class/gpio
$ ls
export  gpiochip1012  gpiochip1016  gpiochip1020  gpiochip894   unexport
$ cd gpiochip1012/
$ cat label
/amba_pl/gpio@41210000
$ cat base
1012

Der Basispin wird als Offset für die Berechnung des zu schaltenden Pins benötigt. Soll z. B. der GPIO 0 des ausgewählten GPIO-Blocks geschaltet, so lautet die I/O-Nummer 1012. Die einzelnen I/O-Nummern müssen in die Datei export geschrieben werden um den I/O anzulegen und bedienen zu können. Anschließend können Die I/O über die Dateien direction und value gesteuert werden.


Achtung:

Auch wenn die GPIO-Blöcke im Vivado als Output only konfiguriert wurden müssen die I/O im Linux als Ausgang konfiguriert werden!


$ echo 1012 > /sys/class/gpio/export
$ echo out > /sys/class/gpio/gpio1012/direction
$ echo 1 > /sys/class/gpio/gpio1012/value

Über das nachfolgende Skript werden die I/O für die LEDs konfiguriert und in einer Endlosschleife nacheinander ein- und wieder ausgeschaltet:

#!/bin/bash

for i in 0 1 2 3
do
    Base=$((1012 + i))
    echo ${Base} > /sys/class/gpio/export
    echo out > /sys/class/gpio/gpio${Base}/direction
done

while true
do
    for i in 0 1 2 3
    do
        Base=$((1012 + i))
        echo 1 > /sys/class/gpio/gpio${Base}/value
        sleep 1
    done

    for i in 0 1 2 3
    do
        Base=$((1012 + i))
        echo 0 > /sys/class/gpio/gpio${Base}/value
        sleep 1
    done

    sleep 10
done

Die Taster und Schalter werden auf ähnliche Weise ausgelesen, wobei bei diesen GPIO-Blöcken direction auf in gesetzt werden muss.

#!/bin/bash

for i in 0 1 2 3
do
    SwitchBase=$((1016 + i))
    ButtonBase=$((1020 + i))
    echo ${SwitchBase} > /sys/class/gpio/export
    echo in > /sys/class/gpio/gpio${SwitchBase}/direction
    echo ${ButtonBase} > /sys/class/gpio/export
    echo in > /sys/class/gpio/gpio${ButtonBase}/direction
done

while true
do
    for i in 0 1 2 3
    do
        SwitchBase=$((1016 + i))
        ButtonBase=$((1020 + i))

        Value=$(cat /sys/class/gpio/gpio${SwitchBase}/value)
        echo "Switch ${i}: ${Value}"

        Value=$(cat /sys/class/gpio/gpio${ButtonBase}/value)
        echo "Button ${i}: ${Value}"
    done

    echo "-------------"

    sleep 1
done

Aufsetzen der Toolchain:

Als letztes möchte ich noch zeigen, wie die Vitis Entwicklungsumgebung genutzt werden kann um eine eigene Anwendung für das erzeugte Linux-System zu programmieren und zu kompilieren. Dazu wird ein entsprechendes SDK benötigt, welches das rootfs und die benötigten Bibliotheken des Zielsystems beinhaltet. Dieses SDK kann über PetaLinux erstellt werden:

$ petalinux-build --sdk
INFO: Sourcing build tools
[INFO] Building project
[INFO] Sourcing build environment
[INFO] Generating workspace directory
INFO: bitbake petalinux-image-minimal -c do_populate_sdk
Loading cache: 100% |#########################################################################################################################################################################################################| Time: 0:00:01
Loaded 4264 entries from dependency cache.

Am Ende des Vorgangs findet sich im Ordner images/linux ein Installationsskript namens sdk.sh welches für die Installation des SDKs genutzt werden kann:

$ ./script/sdk.sh
PetaLinux SDK installer version 2020.2
======================================
Enter target directory for SDK (default: /opt/petalinux/2020.2): /var/development/Git/ZYBO/linux/MinimalLinux/sdk
You are about to install the SDK to "/var/development/Git/ZYBO/linux/MinimalLinux/sdk". Proceed [Y/n]? y
Extracting SDK..................

Nach der Installation wird in Vitis ein neues Plattformprojekt angelegt und als Linux-Projekt konfiguriert:

In diesem Projekt müssen nun der Pfad für das rootfs des erzeugen Linux-Systems und der Pfad für das sysroot des SDKs eingetragen werden:


Hinweis:

In diesem Beispiel werden keine Bootkomponenten erzeugt. Daher können die entsprechenden Felder ausgelassen werden.


Das fertige Projekt wird dann kompiliert und ein neues Anwendungsprojekt, welches die erstellte Plattform als Basis verwendet, erstellt.

Das Anwendungsprogramm ließt den Kanal 14 des XADC über den IIO-Treiber aus und gibt die gemessene Spannung in der Konsole aus.

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

float Voltage;
float Scale;

FILE* File;
char Buffer[32];

int main(void)
{
    File = fopen("/sys/bus/iio/devices/iio:device1/in_voltage10_vaux14_scale", "r");
    if(File == NULL)
    {
        printf("[ERROR] Can not open file!");

        return -1;
    }
    fgets(Buffer, 12, File);
    fclose(File);

    Scale = atof(Buffer);
    printf("[INFO] Scale: %4.9f mV / Bit\n\r", Scale);

    while(1)
    {
        File = fopen("/sys/bus/iio/devices/iio:device1/in_voltage10_vaux14_raw", "r");
        if(File != NULL)
        {
            while(fgets(Buffer, sizeof(Buffer), File));
            Voltage = (float)atoi(Buffer) * Scale / 1000.0;
            fclose(File);

            printf("[INFO] Raw: %s", Buffer);
            printf("[INFO] Voltage: %4.2f V\n\r", Voltage);
        }
        else
        {
            printf("[ERROR] Can not open file!");

            return -1;
        }

        sleep(1);
    }

    return 0;
}

Die Berechnung der Spannung erfolgt mit Hilfe des Skalierungsfaktors aus der entsprechenden Datei und dem ausgelesenen Analogwert.


Achtung:

Bei dem ausgelesenen Wert handelt es sich um den Wert in Millivolt!


Für die Ausführung, bzw. zum Debuggen der Anwendung wird das Target (also das ZYBO) im Projekt angelegt, wodurch die Anwendung bequem per Ethernet auf das ZYBO übertragen und ausgeführt, bzw. debuggt werden kann. Dazu wir das Menü Debug (oder die Run) Configurations geöffnet und über die Schaltfläche New im Reiter Main eine neue Verbindung angelegt.

Jetzt kann die Anwendung gestartet werden. Sämtliche Konsolenausgaben werden in Vitis ausgegeben

[INFO] Scale: 0.244140625 mV / Bit
[INFO] Raw: 0
[INFO] Voltage: 0.00 V
[INFO] Raw: 790
[INFO] Voltage: 0.19 V
[INFO] Raw: 1673
[INFO] Voltage: 0.41 V
[INFO] Raw: 1744
[INFO] Voltage: 0.43 V
[INFO] Raw: 2047
[INFO] Voltage: 0.50 V

Integration der App in den PetaLinux Build-Prozess:

Die fertige Anwendung soll abschließend noch in den Build-Prozess von PetaLinux integriert werden, sodass die Anwendung automatisch kompiliert und in das erstellte Dateisystem integriert wird. Dazu wird als erstes ein neues Anwendungsprojekt erstellt:

$ petalinux-create -t apps --name adc-reader-app --template c
INFO: Create apps: ADC-Reader
INFO: New apps successfully created in /var/development/Git/ZYBO/linux/MinimalLinux/petalinux/project-spec/meta-user/recipes-apps/adc-reader-app

Die eigentliche Anwendung ist in dem Unterverzeichnis files zu finden und wird durch das bereits erstellte Programm ersetzt. Anschließend wird die Anwendung in das rootfs integriert, indem die entsprechende Anwendung (app) aktiviert wird:

$ petalinux-config -c rootfs

Die vorgenommenen Einstellungen werden gespeichert und das Image mit der Anwendung gebaut:

$ petalinux-build -c adc-reader-app
INFO: Sourcing build tools
[INFO] Building adc-reader-app
[INFO] Sourcing build environment
[INFO] Generating workspace directory
INFO: bitbake adc-reader-app
...
INFO: Successfully copied built images to tftp dir: /tftpboot
[INFO] Successfully built adc-reader-app
$ petalinux-build
...
[INFO] Successfully built project
$ petalinux-package --force --boot --fsbl images/linux/zynq_fsbl.elf --fpga images/linux/system.bit --u-boot
...
INFO: Binary is ready.

Alle neu erstellten Daten werden anschließend erneut auf die SD-Karte kopiert und das Board gebootet. Die Anwendung ist im Verzeichnis /usr/bin zu finden:

root@MinimalLinux:~# /usr/bin/adc-reader-app
[INFO] Scale: 0.244140625 mV / Bit
[INFO] Raw: 437
[INFO] Voltage: 0.11 V
[INFO] Raw: 436
[INFO] Voltage: 0.11 V
[INFO] Raw: 489
[INFO] Voltage: 0.12 V
[INFO] Raw: 99
[INFO] Voltage: 0.02 V
[INFO] Raw: 284
[INFO] Voltage: 0.07 V
[INFO] Raw: 968
[INFO] Voltage: 0.24 V
[INFO] Raw: 1135
[INFO] Voltage: 0.28 V
[INFO] Raw: 1130
[INFO] Voltage: 0.28 V

Das komplette Projekt steht in meinem GitLab-Repository zum Download bereit.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.