Der Linux-Kernel abstrahiert und verwaltet die Zugriffe auf GPIOs, also jene Ein- und Ausgabekanäle, die beispielsweise auf dem Raspberry Pi zu finden sind. Wer sich mit einem eigenen Treiber dort einklinkt, kann mit erheblich schnelleren Peripheriezugriffen rechnen.
Es ist einfacher als gedacht und macht Spaß, den Raspberry Pi zum Ansteuern von Hardware zu verwenden. Die 26-polige Steckerleiste des preiswerten Kleincomputers stellt dazu insgesamt 17 von den 54 Ein- und Ausgabeleitungen des Prozessors zur Verfügung, die so genannten GPIOs (General Purpose Input Output).
Jeder GPIO-Pin ist flexibel konfigurierbar: Er kann als Ausgabeleitung dienen, an der programmgesteuert entweder 3,3 V (logisch 1) oder 0 V (logisch 0) anliegen. Als Eingabe konfiguriert, übernimmt der Prozessor nach Aufforderung eine 1, falls Spannung anliegt, ansonsten eine 0.
Wer eine gelbe, grüne oder rote Leuchtdiode (LED), einen Widerstand um die 300 Ohm und ein paar Kabel zur Hand hat, kann schon mit den ersten Versuchen loslegen.
Bastelzeit
Wie in Abbildung 1 zu sehen ist, sind Widerstand und LED in Serie geschaltet und mit der Steckerleiste auf dem Raspberry Pi verbunden. Die Seite der Reihenschaltung, an der die Kathode der LED (die Seite mit dem kürzeren Beinchen beziehungsweise mit der abgeflachten Unterkante) liegt, wird mit GND (Pin 6) verbunden; die andere Seite mit GPIO 4 (Pin 7). Hilfreich für den Aufbau ist ein so genanntes Breadboard, in das man die Drähte der Bauteile steckt.
Ein einfacher Test der Schaltung ist über das »/sys« -Dateisystem mit einem Shellskript möglich. Bevor dieses den Wert der Ausgabeleitung GPIO 4 auf 0 oder 1 setzen kann, muss es die Leitung reservieren und konfigurieren (Abbildung 2). Zur Reservierung schreibt das Skript die Nummer der GPIO-Leitung (aus Sicht der CPU, also nicht die Nummer des Pins an der Doppelstiftleiste) in die Datei »/sys/class/gpio/export« . Dazu sind allerdings Rootrechte notwendig. Daraufhin legt der Kernel ein neues Verzeichnis an, für GPIO 4 beispielsweise »gpio4« .
In diesem Verzeichnis gibt es eine Datei »direction« , in der man mit den Schlüsselworten »out« und »in« festlegt, ob es sich um eine Ausgabe- oder eine Eingabeleitung handelt. Das Kommando
echo "1" >/sys/class/gpio/gpio4/value
setzt die Spannung auf 3,3 V – die LED leuchtet. Eine 0 in diese Datei geschrieben stellt eine Spannung von 0 V ein und schaltet die LED wieder aus.
Listing 1 zeigt ein Skript, das aus der LED ein Blinklicht macht. Dazu schaltet es die LED ein, schläft eine Sekunde, schaltet sie aus und schläft nochmals eine Sekunde. Der Befehl »trap« dient übrigens dazu, beim Abbrechen des Skripts, beispielsweise per [Strg]+[C], den Pin wieder freizugeben.
Listing 1
Zugriff per Shellskript
01 #!/bin/bash 02 03 trap "echo \"4\" >/sys/class/gpio/unexport" EXIT 04 05 echo "4" >/sys/class/gpio/export 06 echo "out" >/sys/class/gpio/gpio4/direction 07 08 while true 09 do 10 echo "1" >/sys/class/gpio/gpio4/value 11 sleep 1 12 echo "0" >/sys/class/gpio/gpio4/value 13 sleep 1 14 done
250-mal schneller
Eine Messung ergibt, dass sich eine solche Ein-Aus-Sequenz per Sys-Filesystem (ohne die Sleep-Befehle) abhängig vom Kernel und dessen Konfiguration 1000- bis 1500-mal pro Sekunde (mit 1 kHz bis 1,5 kHz) auf dem Raspberry Pi ausführen lässt. Wer die GPIOs jedoch über einen dedizierten Treiber im Kernel anspricht, kann Steigerungen dieser Frequenz um den Faktor 250 erreichen.
Ein normaler, zeichenorientierter Gerätetreiber implementiert typischerweise Schnittstellenfunktionen, die der Kernel beim Laden des Treibers (»mod_init()« ) aufruft, beim Entladen (»mod_exit()« ), beim Öffnen der zugeordneten Gerätedatei (»driver_open()« ), beim Schließen derselben (»driver_close()« ), beim Lesen von der Gerätedatei (»driver_read()« ) und beim Schreiben auf die Gerätedatei (»driver_write()« ).
Ein bisschen abgekupfert
Wer einen Treiber schreiben möchte, der die Ein- und Ausgabeleitungen nutzt, kann die Funktionen »mod_init()« und »mod_exit()« einfach von einem anderen Treiber abkupfern. Im Rahmen dieser Funktionen meldet sich der Treiber beim Kernel mit einem Namen an und übergibt dabei eine Tabelle mit den Funktionen (»driver_open()« , »driver_read()« , »driver_write()« und »driver_close()« ), die bei einem Zugriff durch eine Applikation aufgerufen werden.
Diese Funktionen sind auch relevant, wenn es um den Hardwarezugriff geht. Dabei muss der Treiber logischerweise die gleichen Schritte (Reservieren, Ein- oder Ausgabe festlegen, eigentlicher Zugriff und die Freigabe, Abbildung 2) durchführen wie beim Zugriff über das Sys-Filesystem.
Zum Reservieren (Export) eines GPIO im Kernel dient die Funktion »gpio_request()« (Tabelle 1), für die Freigabe (Unexport) »gpio_free()« . Soll die Leitung als Ausgabe dienen, wird »gpio_direction_output()« , als Eingabe »gpio_direction_input()« aufgerufen. Um die Spannung auf 3,3 V oder 0 V zu setzen, wird »gpio_set_value()« mit den Parametern GPIO-Nummer und auszugebender Wert (1 oder 0) abgesetzt [1].
Tabelle 1
Kernelinterne Funktionen für GPIO (Auswahl)
|
Managementfunktionen |
|
|---|---|
|
int gpio_request(unsigned gpio, const char *label) |
Reserviert GPIO mit der Nummer »gpio« unter dem Namen »label« . |
|
void gpio_free(unsigned gpio) |
Gibt den reservierten GPIO-Pin »gpio« frei. |
|
int gpio_direction_input(unsigned gpio) |
Konfiguriert den Pin »gpio« als Eingabe. |
|
int gpio_direction_output(unsigned gpio, int value) |
Konfiguriert den Pin »gpio« als Ausgabe. |
|
int gpio_set_debounce(unsigned gpio, unsigned debounce) |
Setzt die Entprell-Zeit (in Mikrosekunden), in der der Pin nach einem Zustandswechsel den gleichen Wert haben muss. |
|
int gpio_cansleep(unsigned gpio) |
Die Funktion gibt einen Wert ungleich null zurück, falls Kommits auf einen Bus warten (schlafen) können. In diesem Fall darf kein Zugriff aus einem Interrupt-Kontext heraus erfolgen. |
|
Zugriffsfunktionen |
|
|
inline bool gpio_is_valid(int number) |
Überprüft, ob »number« existiert. |
|
int gpio_get_value(unsigned gpio) |
Liest GPIO-Pin »gpio« ein. |
|
void gpio_set_value(unsigned gpio, int value) |
Falls »value« gleich 0 ist, »gpio« auf 0 V setzen, andernfalls auf 3,3 V. |
|
int gpio_get_value_cansleep(unsigned gpio) |
Liest den GPIO-Pin aus einem Nicht-Interrupt-Kontext heraus ein. |
|
void gpio_set_value_cansleep(unsigned gpio, int value) |
Setzt den GPIO-Pin aus einem Nicht-Interrupt-Kontext heraus auf »value« . |
|
Integrationsfunktionen |
|
|
int gpio_export(unsigned gpio, bool direction_may_change) |
Gibt die Reservierung von »gpio« für den Zugriff über das Sys-Filesystem frei. |
|
void gpio_unexport(unsigned gpio) |
Hebt den Zugriff über das Sys-Filesystem auf. |
|
extern int gpio_export_link(struct device *dev, const char *name, unsigned gpio) |
Die Funktion dient dazu, einen symbolischen Link zwischen Treiber und ins Sys-Filesystem exportierten Links herzustellen. |
|
extern int gpio_sysfs_set_active_low(unsigned gpio, int value) |
Markiert »gpio« als Active-Low-Signal. |
Listing 2 zeigt das Einbinden dieser Funktionen in einen minimalistischen Treiber, sodass dieser GPIO 4 auf 0 V setzt, wenn eine Applikation eine 0 auf die Gerätedatei »/dev/fastgpio4« schreibt, und auf 3,3 V bei jedem anderen Wert. In »driver_open()« findet dazu das Reservieren (Zeile 18) und Festlegen der Ein- und Ausgaberichtung (Zeile 23) statt. Die Freigabe erfolgt in »driver_close()« (Zeile 37), also wenn die Anwendung die Ressource nicht mehr benötigt und den Systemcall »close()« aufruft.
Listing 2
Kernel-Treiber fastgpio.c
001 #include <linux/module.h>
002 #include <linux/fs.h>
003 #include <linux/cdev.h>
004 #include <linux/device.h>
005 #include <linux/gpio.h>
006 #include <asm/uaccess.h>
007
008 static dev_t gpio_dev_nr;
009 static struct cdev *driver_object;
010 static struct class *gpio_class;
011 static struct device *gpio_dev;
012
013 static int driver_open( struct inode
014 *geraete_datei,struct file *instanz)
015 {
016 int err;
017
018 err = gpio_request( 4, "rpi-gpio-4" );
019 if (err) {
020 printk("gpio_request %d\n",err);
021 return -1;
022 }
023 err = gpio_direction_output( 4, 0 );
024 if (err) {
025 printk("gpio_dir_output %d\n", err);
026 gpio_free( 4 );
027 return -1;
028 }
029 printk("gpio 4 configured\n");
030 return 0;
031 }
032
033 static int driver_close( struct inode
034 *geraete_datei, struct file *instanz)
035 {
036 printk( "driver_close called\n" );
037 gpio_free( 4 );
038 return 0;
039 }
040
041 static ssize_t driver_write( struct file
042 *instanz, const char __user *user,
043 size_t count, loff_t *offset )
044 {
045 unsigned long not_copied, to_copy;
046 u32 value=0;
047
048 to_copy = min( count, sizeof(value) );
049 not_copied=copy_from_user(&value,user,
050 to_copy);
051 if (value==1)
052 gpio_set_value(4,1);
053 else
054 gpio_set_value(4,0);
055 //gpio_set_value( 4, value?0:1 );
056 return to_copy-not_copied;
057 }
058
059 static struct file_operations fops = {
060 .owner= THIS_MODULE,
061 .write= driver_write,
062 .open= driver_open,
063 .release= driver_close,
064 };
065
066 static int __init mod_init( void )
067 {
068 if( alloc_chrdev_region(&gpio_dev_nr,
069 0,1,"fastgpio4")<0 )
070 return -EIO;
071 driver_object = cdev_alloc();
072 if( driver_object==NULL )
073 goto free_device_number;
074 driver_object->owner = THIS_MODULE;
075 driver_object->ops = &fops;
076 if( cdev_add(driver_object,
077 gpio_dev_nr,1) )
078 goto free_cdev;
079 gpio_class = class_create( THIS_MODULE,
080 "fastgpio4" );
081 if( IS_ERR( gpio_class ) ) {
082 pr_err("fastgpio4:no udev support\n");
083 goto free_cdev;
084 }
085 gpio_dev=device_create(gpio_class,NULL,
086 gpio_dev_nr,NULL,"%s","fastgpio4");
087 dev_info(gpio_dev, "mod_init");
088 return 0;
089 free_cdev:
090 kobject_put( &driver_object->kobj );
091 free_device_number:
092 unregister_chrdev_region(gpio_dev_nr,1);
093 return -EIO;
094 }
095
096 static void __exit mod_exit( void )
097 {
098 dev_info(gpio_dev, "mod_exit");
099 device_destroy(gpio_class,gpio_dev_nr);
100 class_destroy( gpio_class );
101 cdev_del( driver_object );
102 unregister_chrdev_region(gpio_dev_nr,1);
103 return;
104 }
105
106 module_init( mod_init );
107 module_exit( mod_exit );
108 MODULE_LICENSE("GPL");
Die eigentlichen Hardwarezugriffe sind in »driver_write()« kodiert (Zeile 41). Diese Funktion kommt zum Einsatz, wenn eine Applikation »write()« verwendet. Die Schreibfunktion im Kernel kopiert dank der Minimum-Funktion die richtige Menge Daten zwischen Anwendung und Kernel. So darf der Treiber nicht mehr Bytes kopieren, als die Applikation anfordert, und auch nicht mehr Daten, als im Kernel überhaupt zur Verfügung stehen. Die nachfolgende Zeile kopiert schließlich per »copy_from_user()« die Daten von der Applikation in den Kernel und schreibt sie per »gpio_set_value()« (Zeilen 52 und 54) auf die Hardware.
Leider ist das Übersetzen des Treibers für den Raspberry Pi nicht ganz so einfach wie für einen herkömmliche PC. Hierfür sind nämlich vorkompilierte Kernelquellen notwendig. Die vorige “Kern-Technik” [2] hat die erforderlichen Vorbereitungen beschrieben, sowohl für eine Cross-Entwicklung als auch für die Entwicklung direkt auf dem Mini-Computer selbst. Allerdings ist dort ein Fehler zu berichtigen: Zum Generieren des Kernels ist »make bcmrpi_defconfig« (mit Unterstrich) aufzurufen.
Das Makefile (Listing 3) übersetzt den Treiber beim Aufruf von »make« , das Kommando »insmod fastgpio.ko« lädt ihn auf dem Raspberry Pi in den Kernel. Geht alles gut, existiert danach die Gerätedatei »/dev/fastgpio4« . Für einen effizienten Zugriff bietet sich eine eigene Applikation an, wie in Listing 4 vorgestellt. Zum Übersetzen der im Heimatverzeichnis des Users Pi liegenden Quelltextdatei »appl.c« eignet sich das folgende Kommando:
Listing 4
Effizienter Zugriff mit C-Programm appl.c
01 #include <stdio.h>
02 #include <unistd.h>
03 #include <fcntl.h>
04 #include <time.h>
05
06 int main( int argc, char **argv )
07 {
08 int fd_gpio;
09 int value;
10 struct timespec sleeptime;
11
12 fd_gpio=open("/dev/fastgpio4",O_WRONLY);
13 if (fd_gpio<0) {
14 printf("kann GPIO nicht oeffnen\n");
15 return -1;
16 }
17
18 sleeptime.tv_sec = 0;
19 sleeptime.tv_nsec= 500000000;
20 while (1) {
21 value = 1;
22 write(fd_gpio,&value,sizeof(value));
23 clock_nanosleep( CLOCK_MONOTONIC, 0,
24 &sleeptime, NULL );
25 value = 0;
26 write(fd_gpio,&value,sizeof(value));
27 clock_nanosleep( CLOCK_MONOTONIC, 0,
28 &sleeptime, NULL );
29 }
30 return 0;
31 }
Listing 3
Makefile zum Fastgpio-Treiber
01 ARCH := arm 02 UNAME := $(shell uname -m) 03 ifeq ($(UNAME),armv6l) 04 KDIR := /usr/src/linux/ 05 else 06 CROSS_COMPILE:=arm-linux-gnueabi- 07 KDIR := /usr/src/linux/arm 08 endif 09 10 ifneq ($(KERNELRELEASE),) 11 obj-m := fastgpio.o 12 13 else 14 15 default: 16 $(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KDIR) M=$(PWD) modules 17 endif 18 19 clean: 20 rm -rf *.ko *.o .*.cmd .tmp_versions Module.symvers 21 rm -rf modules.order *.mod.c
$ cc -g -Wall appl.c -lrt -o appl
Da das Programm auf die Funktion »clock_nanosleep()« – die moderne Art des Schlafens [3] – zurückgreift, benötigt sie die Realtime-Bibliothek. Dazu dient der Parameter »-lrt« (für “lib realtime”). Falls der Treiber geladen ist, kann Root die Applikation »appl« starten:
# chrt 50 ./appl
Ist die Schaltung korrekt aufgebaut, blinkt nun die LED. Das Kommando »chrt 50« sorgt übrigens dafür, dass das Programm die Realzeit-Priorität 50 erhält und sich auf diese Weise besser für zeitgesteuerte Aufgaben eignet.
In Kilohertz ausgedrückt
Wer die Funktion »clock_nanosleep()« weglässt, kann grob die mit einem Treiber maximal erreichbare Frequenz messen. Auf einem Raspbian der Version 2013-07-25 [4] waren das bei einem Versuch der Autoren immerhin 280 kHz, auf einem Raspbian 3.6.11 waren per Digital-Oszilloskop sogar knapp 380 kHz zu messen. Anders ausgedrückt: Jede Ein-Aus-Sequenz war nur 2,6 Mikrosekunden lang (Abbildung 3).
Allerdings muss dabei die Treiberfunktion »driver_write()« auf das hier abgedruckte Minimum reduziert bleiben. Bereits eine Debugmeldung per »printk« verschlechtert das Zeitverhalten deutlich. Und noch ein Hinweis: Mit Realzeitpriorität, also mit Hilfe des Kommandos »chrt 50« gestartet, kann das Programm nur abbrechen, wer entweder die Funktion »clock_nanosleep()« eingebaut lässt oder der Shell eine noch höhere Priorität zuweist.
Hinter den Kulissen
Am Beispiel des Zugriffs auf die GPIOs des Raspberry Pi lässt sich erläutern, welche komplexen Aufgaben der Linux-Kernel dem Programmierer abnimmt. Hardware-affine Entwickler wissen bereits, dass sich die Funktionen der Hardware durch Lesen und Schreiben auf spezifische Adressen ansprechen lassen. So adressierte Speicherzellen heißen Register.
Beim Raspberry Pi kann der stolze Besitzer dem Handbuch [5] entnehmen, dass die Register für den GPIO-Zugriff im Adressraum ab 0xF2200000 angesiedelt sind (Tabelle 2). Abbildung 4 zeigt zusätzlich den Aufbau der Konfigurationsregister. Leicht lässt sich da erkennen, dass einzelnen Bits spezifische Funktionen zugeordnet sind. Im Fall des GPIO-Konfigurationsregisters sind für jede GPIO-Leitung drei Bits zusammengefasst. Tabelle 3 zeigt hierfür eine Auswahl relevanter Codes.
Tabelle 2
Die GPIO-Register des Raspberry Pi (Auswahl, [5], Seite 90)
|
Register |
Adresse |
Beschreibung |
|---|---|---|
|
GPFSEL0 |
0xF2200000 |
Konfiguration GPIO 0-9 |
|
GPFSEL1 |
0xF2200004 |
Konfiguration GPIO 10-19 |
|
GPFSEL2 |
0xF2200008 |
Konfiguration GPIO 20-29 |
|
GPFSEL3 |
0xF220000C |
Konfiguration GPIO 30-39 |
|
GPFSEL4 |
0xF2200010 |
Konfiguration GPIO 40-49 |
|
GPFSEL5 |
0xF2200014 |
Konfiguration GPIO 50-53 |
|
GPSET0 |
0xF220001c |
Ausgang 0 bis 31 auf 1 setzen |
|
GPSET1 |
0xF2200020 |
Ausgang 32 bis 53 auf 1 setzen |
|
GPCLR0 |
0xF2200028 |
Ausgang 0 bis 31 auf 0 setzen |
|
GPCLR1 |
0xF220002C |
Ausgang 32 bis 53 auf 0 setzen |
|
GPLEV0 |
0xF2200034 |
Eingang 0 bis 31 einlesen |
|
GPLEV1 |
0xF2200038 |
Eingang 32 bis 53 einlesen |
Tabelle 3
Bitcodierung für die GPIO-Konfiguration
|
Bitcodierung |
Bedeutung |
|---|---|
|
»000« |
GPIO-Pin als Eingabe |
|
»001« |
GPIO-Pin als Ausgabe |
|
»010« bis »111« |
sonstige Funktion |
Der Dokumentation kann der Leser weiterhin entnehmen, dass GPIO 4 als Ausgang fungiert, falls im Register »GPFSEL0« , also an der Adresse 0xF2200000, die Bits 12, 13 und 14 den Bitcode »001« aufweisen. Da das Register aber immer nur als Ganzes geschrieben werden kann, würde das plumpe Schreiben des Hexwerts dazu führen, dass GPIO 4 zwar als Ausgang, die GPIOs 0 bis 3 und 5 bis 9 aber als Eingang konfiguriert sind. Das ist natürlich nicht gewollt.
Um den Zustand der übrigen GPIOs beim Schreiben nicht zu ändern, liest der Bastler daher zunächst das komplette Register aus. Danach setzt er per bitweiser Und-Verknüpfung die relevanten Bits auf Null, anschließend mit der Konfigurations-Bitfolge »001« per bitweiser Oder-Verknüpfung neu. Die in Kasten A in Abbildung 5 dargestellte Befehlsfolge realisiert dieses Vorgehen.
Der Programmierer kann im Fall des Raspberry Pi zwar per Zeiger direkt auf die Hardware-Adressen zugreifen, erhält dann aber keinen portierbaren Code. Die in der Software verwendeten Adressen entsprechen nämlich nicht auf jeder Plattform auch den physischen Adressen. Dazwischen liegt noch die Memory-Management-Unit, die je nach Plattform auch für I/O-Bereiche eine Adressenumsetzung durchführt.
Linus sei Dank
Um den Programmierer davon abzuschirmen und diesen Vorgang transparent zu gestalten, hat Linus Torvalds Zugriffsfunktionen eingeführt: »read[bwl]()« und »write[bwl]()« . »b« steht dabei für Byte, »w« für Word und »l« für long. »readb()« führt einen Byte-Zugriff, »readw()« einen Zwei-Byte-Zugriff und »readl()« einen Vier-Byte-Zugriff durch. Die damit zum obigen Code funktional identische, Linux-konforme Implementierung zeigt der Kasten B in Abbildung 5.
Bei der Kodierung gibt es noch einen weiteren Fallstrick. So sortieren moderne CPUs schon mal gerne aus Optimierungsgründen die Reihenfolge der programmierten Befehle um. Das machen sie, wenn die neue Reihenfolge aus Sicht der CPU funktional identisch zur programmierten Reihenfolge ist. Da der Prozessor aber nicht zwischen normalen Speicherzellen und Registern unterscheidet, kann ein Reordering von Registeradressen zu unvorhersehbaren Ergebnissen (Race Conditions) führen.
Barrieren
Hiergegen helfen so genannte Memory Barriers [6], die vor Lese- beziehungsweise nach Schreibzugriffen einzusetzen sind. Je nach Plattform aktiviert der Entwickler die Memory Barriers unterschiedlich. Linux abstrahiert das mit vier Funktionen: »rmb()« ist für Lesezugriffe, »wmb()« für Schreibzugriffe, »mb()« für Lese- und Schreibzugriffe zuständig. Die Funktion »barrier()« schaltet das Umsortieren durch den Compiler ab. Um das Reordering programmtechnisch zu berücksichtigen, muss man also noch, wie in Abbildung 5 (Kasten C) dargestellt, die Barrier-Funktionen einfügen.
Atomar
Noch ein weiteres Problem erfordert dringend die Hilfestellung durch den Kernel. Angenommen zwei unabhängige Programme konfigurieren jeweils einen anderen GPIO, Programm A etwa GPIO 4 als Input und Programm B GPIO 7 als Output. Dazu lesen beide Tasks den gegenwärtigen Konfigurationswert ein, jede führt die Änderungen aus und speichert die Werte zurück.
Da aber beide Programme ihre Änderungen ausgehend vom gemeinsamen Grundwert unabhängig voneinander ausgeführt haben, überschreibt das Programm, das seinen Wert zuletzt abspeichert, die Änderungen des anderen. Eine klassische Race Condition. Daher muss die Befehlssequenz (Lesen, Löschen, Setzen, Zurückschreiben) atomar, also ohne Unterbrechung beziehungsweise ohne Parallelität, abgearbeitet werden. Ohne Verankerung im Betriebssystemkern ist dieses Problem nicht zu lösen.
Es lohnt sich
Es ist also nicht nur praktisch, sondern auch technisch notwendig, dass der Linux-Kernel ein GPIO-Interface anbietet. Es empfiehlt sich daher, keinen Code zu verwenden, der das GPIO-Interface umgeht und direkt auf die Hardware zugreift, auch wenn derlei Software gelegentlich im Raspberry-Pi-Forum zu finden ist [7].
Das GPIO-Subsystem ist übrigens vergleichweise jung. Daher sollte es niemanden überraschen, wenn es in den kommenden Linux-Versionen noch grundsätzliche Änderungen an dessen Kernel-Interface gibt. Unabhängig davon lohnt es sich, den GPIO-Zugriff über einen Gerätetreiber zu realisieren. Gerade der eher leistungsschwache Raspberry Pi trumpft dann richtig auf. (mhu)
Infos
- Dokumentation zu GPIO im Kernelquellcode: https://www.kernel.org/doc/Documentation/gpio.txt
- Quade, Kunst, “Kern-Technik”: Linux-Magazin 08/13, S. 86
- Quade, Mächtel, “Moderne Realzeitsysteme kompakt”: Dpunkt-Verlag, 2012
- Raspbian: http://www.raspbian.org
- Broadcom Corporation, “BCM2835 ARM Peripherals”: http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf
- Quade, Kunst, “Linux-Treiber entwickeln”, 3. Auflage: Dpunkt-Verlag, 2011, S. 220
- Raspberry-Pi-Forum, “RaspiUtils GPIO using a device driver”: http://www.raspberrypi.org/phpBB3/viewtopic.php?f=33&t=32152
- Listings zum Artikel: https://www.linux-magazin.de/static/listings/magazin/2013/10/kern-technik/










