Homepage Wiki Forum Buy

GPIO

Aus GNUBLIN

Schwierigkeitsgrad Voraussetzung Gnublin Familie
Gnublin logo easy.png Kommandozeile oder Zugriff per Datei lesend oder schreibend Alle

Inhaltsverzeichnis

Digitale Ein- und Ausgänge

Digitale Ein- und Ausgänge (GPIO = general-purpose input/output) kann man für die verschiedensten Einsatzzwecke verwenden. Soll nur eine LED als Status angesteuert werden, oder ein Taster abgefragt werden, ist dies ebenfalls möglich wie die Auswertung eines Impulsgebers oder die Ansteuerung von seriellen Bausteinen etc. Sollte mal ein UART, I2C oder SPI Kanal mehr benötigt werden, hat man hier auch die Möglichkeit dies per Digitalen Ein- und Ausgang zu simulieren.


Die Schnittstellen der einzelnen GNUBLIN Bauformen und deren Position können hier angesehen werden.

Hinweis: GPIO4 kann nur als Eingang verwendet werden! Alle anderen können Ein- und Ausgang sein!

Für Anfänger

Um die Funktion der GPIO Pins kurz zu testen kann man folgendes Tool aufrufen:

gnublin-gpio

GPIO Pin als Ausgang

Den Wert eines GPIO Pin lässt sich folgendermaßen setzten:

gnublin-gpio -o 1 -p 3

Dieser Befehl setzt die LED auf dem Board auf HIGH. Wenn nun die rote LED zu leuchten beginnt, dann funktioniert das Skript und der GPIO Pin einwandfrei.

Mit

gnublin-gpio -o 0 -p 3

kann man die LED wieder ausschalten.

GPIO Pin als Eingang

Denn Wert des aktuellen Pins kann man wie folgt auslesen:

gnublin-gpio -i -p <pin>

Der Wert <pin> steht für den zu verwendeten GPIO Pin.

Für Fortgeschrittene

GPIO per Konsole

Unter Linux kann die Peripherie des Prozessors ganz einfach über das Dateisystem angesteuert werden. Im folgenden wird der Zugriff auf die rote LED auf dem Board beschrieben.

Belegung der GPIOs:

GPIO3...........LED1
GPIO11..........X4-2  J5-12
GPIO13..........
GPIO14..........X4-3  J5-11
GPIO15..........X4-4


GPIO3 ist die rote LED. Über sysfs kann man nun die LED ansteuern. Wechseln in das Verzeichnis

cd /sys/class/gpio

GPIO an dem die rote LED auf dem Board hängt aktivieren

root@gnublin:/sys/class/gpio# echo 3 > export


Wechseln in das neue Verzeichnis für die gpio3

root@gnublin:/sys/class/gpio# cd gpio3/

Anzeigen der aktuellen Einstellung des IO-Pins der Datenrichtung

root@gnublin:/sys/devices/virtual/gpio/gpio3# cat direction

Pin als Ausgang nutzen

root@gnublin:/sys/devices/virtual/gpio/gpio3# echo out > direction

LED einschalten

root@gnublin:/sys/devices/virtual/gpio/gpio3# echo 1 > value

LED ausschalten

root@gnublin:/sys/devices/virtual/gpio/gpio3# echo 0 > value

Trauen Sie sich schon ein kleines Blinkprogramm zu schreiben? Hier gibts Beispiele zu diesem Thema Beispielprogram-GPIO.


Möchte man einen GPIO als Eingang abfragen geht das wie folgt (GPIO 11 an Anschlussklemme):

Am einfachsten klemmt man einen externen Schalter wie folgt an:

Taster-gpio11.jpg

Die Schaltung sieht dabei so aus:

Taster an GPIO11

      VCC 3.3V
         |
        10K
 __T__   |
 o   o---+------> GPIO11 (weiss/blau)
 |
===
GND (blau)
 


root@gnublin:/sys/class/gpio# echo 11 > export

Um den Pin als Eingang nutzen zu können muss man entsprechend die Richtung setzen:

root@gnublin:/sys/class/gpio# cd gpio11/
root@gnublin:/sys/class/gpio/gpio11# echo in > direction

Ausgabe des Wertes (1 = ca. 3.3V, 0 = ca. 0 V bzw. GND) geht einfach in dem man den Inhalt der Datei value mit dem Befehl cat ausgibt.

cat /sys/class/gpio/gpio11/value

So einfach geht das unter Linux.


Aufgabe: Die rote LED1 auf dem Board blinken lassen.

Die Programme können per Copy-Paste nach Gnublin direkt in eine geöffnete Datei eingefügt werden. Auf GNUBLIN ist der Editor vi installiert. Wie er verwendet werden kann ist hier beschrieben.


LED Blink mit Shell Skript

Datei blink.sh anlegen:

# Blink the onboard LED
# http://blog.makezine.com/archive/2009/02/blinking-leds-with-the-beagle-board.html
 
GPIO=3
 
cleanup() { # Release the GPIO port
  echo $GPIO > /sys/class/gpio/unexport
  exit
}
 
# Open the GPIO port ('high' direction is output)
#
echo $GPIO > /sys/class/gpio/export
echo "high" > /sys/class/gpio/gpio$GPIO/direction
 
trap cleanup SIGINT # call cleanup on Ctrl-C
 
# Blink forever
while [ "1" = "1" ]; do
  echo 1 > /sys/class/gpio/gpio$GPIO/value
  sleep 1
  echo 0 > /sys/class/gpio/gpio$GPIO/value
  sleep 1
done
 
cleanup # call the cleanup routine

Um das Skript zu starten gibt man ein:

sh blink.sh

Mit STRG-c bricht man das Programm ab.

Taster abfragen mit Shell Skript

Ein Taster kann ganz einfach mit einem kleine Bash-Skript ausgewertet werden.

An GPIO 11:

Taster-gpio11.jpg

Die Schaltung sieht dabei so aus:

Taster an GPIO11

      VCC 3.3V
         |
        10K
 __T__   |
 o   o---+------> GPIO11 (weiss/blau)
 |
===
GND (blau)

 

Mit Shell den Taster abfragen:

#!/bin/sh
 
GPIO=11
 
cleanup() { # Release the GPIO port
  echo $GPIO > /sys/class/gpio/unexport
  exit
}
 
echo $GPIO > /sys/class/gpio/export
echo "in" > /sys/class/gpio/gpio$GPIO/direction
 
trap cleanup SIGINT # call cleanup on Ctrl-C
 
while [ "1" = "1" ]; do
   cat /sys/class/gpio/gpio$GPIO/value
   sleep 1
done
 
cleanup # call the cleanup routine

GPIO mit Lua Script

-- blink.lua
 
GPIO = 3
 
function wait(n)
  os.execute("sleep " .. tonumber(n))
end
 
function cmd(c)
  print(c)
  os.execute(c)
end
 
cmd("echo " .. GPIO .. " > /sys/class/gpio/export")
cmd("echo out > /sys/class/gpio/gpio" .. GPIO .. "/direction")
 
n = 0
while n < 2 do
   cmd("echo 1 > /sys/class/gpio/gpio"..GPIO.."/value")
   wait(1)
   cmd("echo 0 > /sys/class/gpio/gpio" .. GPIO .. "/value")
   wait(1)
   n = n + 1
end
 
cmd("echo "..GPIO.." > /sys/class/gpio/unexport")

Aufruf:

lua blink.lua

Mit os.path_exists() kann man prüfen, ob Dateien und Verzeichnisse existieren:

-- blink2.lua
-- H. Hoegl, 21. Dec 2011
 
if os.path_exists("/sys/class/gpio/gpio11") == 1 then
   print("remove gpio pin")
   f = io.open("/sys/class/gpio/unexport", "w")
   f:write("11")
   f:close()
else
   print("create gpio pin")
   f = io.open("/sys/class/gpio/export", "w")
   f:write("11")
   f:close()
end
-- und so weiter...


GPIO in C direkt ueber Adresse im Speicher

Folgender Beispielcode kann verwendet werden, um einen direkten Zugriff auf die GPIO-Register zu bekommen:

/*Gnublin User Space GPIO demo using mmap() H. Hoegl, 2012-06-22 Literature:

[1] The Kernel Hackers Guide, chapter "user space device drivers"
    http://tldp.org/LDP/khg/HyperNews/get/devices/fake.html 
[2] man mem
[3] man mmap

Create /dev/mem device (see [2])

   mknod -m 660 /dev/mem c 1 1
   chown root:kmem /dev/mem

Bit numbers in MODE0 and MODE1 registers - Warning: No GPIO4 available for Output!

  0 -> GPIO1  !!!
  1 -> GPIO0
  2 -> GPIO2
  3 -> GPIO3  (red LED)
  5 -> GPIO11 
  6 -> GPIO12 
  7 -> GPIO13 
  8 -> GPIO14
  9 -> GPIO15
  10 -> GPIO16
  11 -> GPIO17 
  12 -> GPIO18
  13 -> GPIO19
  14 -> GPIO20

M1/M0 combinations

  M1   M0    function
  0    0     input/output driver disabled
  0    1     output is controlled by the device 
  1    0     output is driven low 
  1    1     output is driven high*/


#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
 
/* LPC3131 UM chapter 15 */
#define IOCONFIG    0x13003000
#define FBLO_GPIO   0x1C0  /* GPIO functional block */
 
/* offset in GPIO functional block  */
#define PINS        0x00
#define MODE0       0x10  
#define MODE0_SET   0x14
#define MODE0_RESET 0x18
#define MODE1       0x20  
#define MODE1_SET   0x24
#define MODE1_RESET 0x28
 
/* !! Warning !!
 * GPIO4 (1 << 4) is only as INPUT available.
 * Setting the MODE0-Register for Pin 4 to 1 will cause a crash of the system!
 */
#define GPIO3   (1 << 3)
 
#define LED        GPIO3
 
void byebye(void);
 
char *mymem;
 
int main()
{
    int mem_fd;
    atexit(byebye);
    if ((mem_fd = open("/dev/mem", O_RDWR)) < 0) {
        printf("can't open /dev/mem \n");
        exit(-1);
    }
    printf("mem_fd = %d\n", mem_fd);
    printf("getpagesize() -> %d\n", getpagesize());
    mymem = mmap(0, 
                 getpagesize(), 
                 PROT_READ|PROT_WRITE, 
                 MAP_SHARED, 
                 mem_fd, 
                 IOCONFIG);
    if (mymem == MAP_FAILED) {
        printf("mmap error %d\n", errno);
        exit(-1);
    }
    else {
        printf("mymem = 0x%x\n", (unsigned int)mymem);
    }
    *(unsigned int *)(mymem + FBLO_GPIO + MODE1_SET) = LED;
    while (1) {
        printf("LED on\n");
        *(unsigned int *)(mymem + FBLO_GPIO + MODE0_SET) = LED;
        sleep(1);
        printf("LED off\n");
        *(unsigned int *)(mymem + FBLO_GPIO + MODE0_RESET) = LED;
        sleep(1);
    }
    return 0;
}
 
void byebye()
{
    printf("cleaning up\n");
    /* when not explicitly given, munmap() will automatically
       be called on exit (see 'man munmap') */
    munmap(mymem, getpagesize()); 
}


Interrupt eines GPIO Pins aktivieren

Um asynchrone Interrupts der Hardware an den Linux Userspace weiterreichen zu können, bietet Linux den Systemcall poll an. Im Grunde kann ein Prozess der poll aufruft feststellen, ob der spätere Zugriff auf ein oder mehrere Dateien blockieren würde oder nicht.


Bevor man einen Interrupt für einen GPIO Pin aktivieren kann, muss dies über das sysfs dem spezifischen GPIO Pin mitgeteilt werden.

Aktivieren des Interrupts:

echo "TRIGGER_TYPE" >/sys/class/gpio/gpio<x>/edge

Mithilfe des TRIGGER_TYPES kann man dem GPIO Interrupt angeben, wie dieser getriggert werden soll.

TRIGGER_TYPE Bedeutung
none Trigger aus
falling Trigger fallende Flanke
rising Trigger steigende Flanke
both Trigger steigende und fallende Flanke

Will man nun einen Interrupt bei fallender Flanke triggern, gibt man folgenden Befehl ein:

echo "falling" > /sys/class/gpio/gpio<x>/edge

Greift man auf die selbe Datei mit cat zu, so kann man noch einmal nachsehen, welcher TRIGGER_TYPE gerade gesetzt ist:

cat /sys/class/gpio/gpio<x>/edge


Programm Beispiel in C

Im folgendem Beispiel löst eine fallender Flanke am GPIO14 Pin einen Interrupt aus. Bevor man das Programm startet, muss der Interrupt-Trigger auf falling gesetzt worden sein!

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
 
 
const char *filename="/sys/class/gpio/gpio14/value";
 
 
void main(int argc, char **argv)
{
	struct pollfd *poll_fd;
	int fd;
	int pv;
	int bytes_read;
	char buf[2];
 
	fd = open(filename, O_RDWR);
	if(fd < 0) {
		puts("Error open file");
		exit(0);
	}
 
	/* initialize the pollfd structure */
	poll_fd = malloc(sizeof(*poll_fd));
	poll_fd->fd = fd;
	poll_fd->events = POLLPRI;
	poll_fd->revents = 0;
 
 
	/* call read systemcall on file 
         * this has to be called before poll
         */
	bytes_read = read(fd, buf, 1);
 
 
	/* start poll method with 60 seconds timeout 
         * use -1 for endless timeout
         */	
	pv = poll(poll_fd, 1, 60000);
	if(!pv) {
		puts("Poll timeout!\n");
		exit(0);
 
	} else if(pv == -1) {
		puts("Error while polling!\n");
		exit(0);
	} /* poll */
 
 
	printf("IRQ occured!\n");
	close(fd);
}


Programm Beispiel: Interrupt mehrer GPIOs zählen

Interrupt mehrer GPIOs aktivieren und Anzahl der Flanken ausgeben (Frequenz-Zähler) in C/C++

Im folgenden Beispiel werden GPIO 11, 14 und 15 per Poll Interrupt in einem Thread gezählt, und in einem weiteren Thread jede Sekunde mit Uhrzeit ausgegeben und in eine Datei geschrieben. Dieser Forumgsbeitrag beschreibt die Ausgangssituation. Das exportieren, als Eingang definieren und Unexportieren der GPIOs wird mithilfe der API erledigt, demnach muss beim kompilieren die gnublin.cpp und gnublin.h im selben Verzeichniss liegen.

hamster.cpp:

#include "gnublin.h"
#include <poll.h>
#include <fcntl.h>
#include <signal.h>
#include <stropts.h>
#include <pthread.h> 
#include <ctime>
 
 
using namespace std;
void byebye(int s);
void *interrupt(void*);
void *printer(void*);
 
pthread_mutex_t region_mutex = PTHREAD_MUTEX_INITIALIZER;
 
 
 
int rad[3];
gnublin_gpio gpio;
 
 
int main()  
{
	//run byebye function on "CTRL+c"
	signal (SIGINT,byebye);
	//initialize handler
	pthread_t interrupt_thread, printer_thread; 
	//create threads
	pthread_create(&interrupt_thread,NULL,interrupt,NULL);
	pthread_create(&printer_thread,NULL,printer,NULL);
	//wait until the threads are terminated.... never(while (1))....
	pthread_join(interrupt_thread,NULL);
	pthread_join(printer_thread,NULL);
}
 
 
 
void *interrupt(void*){
	const char *file1="/sys/class/gpio/gpio11/value";
	const char *file2="/sys/class/gpio/gpio14/value";
	const char *file3="/sys/class/gpio/gpio15/value";
	struct pollfd fds[3];
	int bytes_read;
	char buf[2];
	int pin=0, pin_num, ret, i;
 
	//export gpios and switch 'em to input
	gpio.pinMode(11,INPUT);
	gpio.pinMode(14,INPUT);
	gpio.pinMode(15,INPUT);
 
	//set triggertype to rising
	for(pin=0;pin<3;pin++){
		if(pin==0)
			pin_num=11;
		else if(pin==1)
			pin_num=14;
		else if(pin==2)
			pin_num=15;
		string dir = "/sys/class/gpio/gpio"+numberToString(pin_num)+"/edge";
		ofstream file (dir.c_str());
		if (file < 0) {
			exit(0);
			cout << "cannot open file: "<< dir << endl;
		}
		file << "rising";
		file.close();
	}
 
 
	/* Open STREAM devices */
	fds[0].fd = open(file1, O_RDWR);
	fds[1].fd = open(file2, O_RDWR);
	fds[2].fd = open(file3, O_RDWR);
	    fds[0].events = POLLPRI;
	    fds[1].events = POLLPRI;
	    fds[2].events = POLLPRI;
 
 
	while(1){
		// call read systemcall on file. this has to be called before poll
		bytes_read = read(fds[0].fd, buf, 1);
		bytes_read = read(fds[1].fd, buf, 1);
		bytes_read = read(fds[2].fd, buf, 1);
		ret = poll(fds, 3, -1);
		if (ret > 0) {
			// An event on one of the fds has occurred. 
			for (i=0; i<3; i++) {
				if (fds[i].revents & POLLPRI) {
					//lock rad
					pthread_mutex_lock(&region_mutex);
					rad[i]++;
					//unlock rad
					pthread_mutex_unlock(&region_mutex);
				}
			}
		}
	}
	pthread_exit(0);
	return 0;
}
 
void *printer(void*){
	int rad_buf[3];
	//open log file
	string dir = "hamster.txt";
	ofstream file (dir.c_str());
	if (file < 0) {
		cout << "cannot open file: " << dir << endl;
		exit(0);
	}
 
	time_t t;
 
	while(1){
		//lock rad
		pthread_mutex_lock(&region_mutex);
		rad_buf[0]=rad[0];
		rad_buf[1]=rad[1];
		rad_buf[2]=rad[2];
		rad[0]=0;
		rad[1]=0;
		rad[2]=0;
		//unlock rad
		pthread_mutex_unlock(&region_mutex);
		time(&t);
		//print time and counted edges on terminal
		cout << ctime(&t) <<"Rad 1: " << rad_buf[0] << " Rad 2: "<< rad_buf[1] << " Rad 3: " << rad_buf[2] << endl;
		//print time and counted edges 
		file << ctime(&t) <<"Rad 1: " << rad_buf[0] << " Rad 2: "<< rad_buf[1] << " Rad 3: " << rad_buf[2] << endl;
		sleep(1);
	}
	file.close();
	pthread_exit(0);	
}
 
void byebye(int s){
	//unexport GPIOs on exit
	gpio.unexport(11);
	gpio.unexport(14);
	gpio.unexport(15);
        exit(0);
}

Das benutzte Makefile zum Crosskompilieren und übertragen sieht so aus:


Makefile:

objects = hamster
CLIENTIP =  10.42.0.75
 
all: gnublin.o $(objects)
 
gnublin.o: gnublin.cpp gnublin.h
	arm-linux-gnueabi-g++ -c gnublin.cpp 
 
$(objects): $(objects).cpp gnublin.o
	arm-linux-gnueabi-g++ -o $(objects) $(objects).cpp gnublin.o -lpthread -Wall
 
clean: 
	rm -Rf *.o $(objects)
 
install: $(objects)
	scp $(objects) root@$(CLIENTIP):

Programm Beispiel in Python

Pythonskript das die GPIO Pins 11 und 14 exportiert und auf Interrupts per epoll reagiert.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
import select, sys
 
#Hier GPIO Ports eintragen
ports = ["11","14"]
 
 
#mögliche Interrupts
flanke = ["raising", "falling", "both"]
#Interrupt per Listenindex wählen
edge = flanke[1]
 
fds = {}
#interruptzähler
n1 = 0
epoll = select.epoll()
 
#Für alle Ports Konfiguration durchführen
for port in ports:
	#testen ob Port bereits exportiert ist und direction setzen	
	try:
		f = open("/sys/class/gpio/gpio" + port +"/direction", "w")
		f.write("in")
	except:
		#wenn nicht, erst port exportieren		
		try:
 
			f1 = open("/sys/class/gpio/export", "w")
			f1.write(port)
			f1.close()
			f = open("/sys/class/gpio/gpio" + port +"/direction", "w")
			f.write("in")
		except:
			print "Port " + port + "konnte nicht exportiert werden"
			sys.exit(1)
		else:
			print "GPIO" + port + " exportiert"
	else:
		print "GPIO" + port + " als input festgelegt"
	finally:
		f.close()
 
	#Interrupt auf wert in edge setzten
	try:
		f2 = open("/sys/class/gpio/gpio" + port +"/edge", "w")
		f2.write(edge)
	except:
		print "konnte interrupt nicht setzen"
		sys.exit(1)
	else:
		print "Interrupt auf '" + edge + "' gesetzt"
	finally:
		f2.close()
	#fd für port öffnen	
	temp = open ("/sys/class/gpio/gpio" + port +"/value")
	#fd in epoll registrieren	
	epoll.register(temp , select.EPOLLIN | select.EPOLLET)
	#fd in dic eintragen	
	fds.update( {port : temp})
 
print "Start epoll\n-----------"
 
#eventschleife
while True:
	#epoll events holen	
	events = epoll.poll()
 
	#liste mit epoll evends entpacken	
	for fileno, event in events:
		for port in ports:		
			if fileno == fds[port].fileno():
				#interruptzähler inkrementrieren
				n1 += 1
				#interruptnachicht ausgeben
				print str(n1) + " Interrupt GPIO" + port

Weiterführende Links

Der folgende Link führt zu einem Beitrag von Schwabix im Gnublin Forum, in welchem eine gpio-lib mit verschiedensten Funktionen zu GPIO enthalten sind:

http://forum.gnublin.org/viewtopic.php?f=3&t=227&hilit=schwabix&start=10#p1552


Der nachfolgende Link führt zu einem Beitrag von Kubuntufan im Gnublin Forum, in welchem ein Framework zum Download angeboten wird.

http://forum.gnublin.org/viewtopic.php?f=10&t=43&p=125#p125

In anderen Sprachen