Hallo zusammen,
ich möchte Euch zeigen, wie man das Signal eines Sensors in einer Interrupt-Routine auswertet.
Meine Beschreibung bezieht sich jetzt auf die Programmierung in C, ich habe aber mal meine Fühler ausgestreckt und zwei Board-Gurus um eine Erweiterung in Python und in PHP zu bitten.
//EDIT:
Auf Anregung von dbv - Vorab noch eine kurze Erklärung, was mit Interrupt Service Routinen (ISR) gemeint ist:
Manchmal ist es notwendig, einen Signalwechsel von HIGH auf LOW oder umgekehrt an einem der GPIOs abzufragen.
Das ist z.B. bei einem Schalter der Fall, oder bei einem PIR-Sensor, einem Line-Hunter, einem Geräuschsensor, oder, oder, oder ...
Oft wird diese Aufgabe mit einer Schleife gelöst, die den Pin abfragt und dann einen sleep() oder ähnlich ausführt, um das System nicht zu sehr zu belasten.
Die elegantere Methode ist allerdings die Nutzung einer ISR. Sie hat den Vorteil, dass man sich nicht mehr grossartig um den Statuswechsel eines Pins kümmern muss.
Ein Funktionsaufruf der API bewirkt, dass von der entsprechenden Bibliothek der Aufruf einer selbst definierten Funktion durchgeführt wird, sobald der Pin seinen Zustand ändert.
Meist ist es möglich, den Statuswechsel näher zu definieren - also dass der Aufruf nur bei einem Wechsel von LOW auf HIGH erfolgt oder vice versa.
Durch Einsatz der Interrupt-Routinen wird die Systemlast des Programms auf ein Minimum reduziert.
Ich habe drei verschiedene APIs ausgetestet, und lediglich eine ist beim Thema Interrupt-Handling gescheitert.
wiringPi:
wiringPi herunterladen und nach Anleitung installieren falls noch nicht geschehen.
Im Beispiel-Code seht ihr dann, dass nach der Initialisierung ein Pin als INPUT definiert und anschliessend eine ISR definiert wird.
Das Programm läuft dann in einer Endlosschleife und wartet auf das Eintreten eine Interrupts.
Es ist auch möglich, den Timeout zu ändern und einen sleep() in die Schleife einzubauen.
//
// gcc -o interrupt interrupt.c -L/usr/local/lib -lwiringPi
//
#include <stdio.h>
#include <wiringPi.h>
void interrupt_0(void)
{
printf("Interrupt: rising to high!");
}
main()
{
wiringPiSetup();
pinMode(0, INPUT);
wiringPiISR (0, INT_EDGE_RISING, interrupt_0) ;
for ( ; ; )
{
waitForInterrupt (0, -1);
}
}
Alles anzeigen
bcm2835:
ACHTUNG: Diese Bibliothek ist bei meinen Versuchen in Sachen Interrupt leider durchgefallen!
Je nach Kernel-Version kann es sein, dass die Library das gesamte Programm blockiert. Der Autor lässt sich leider nicht darüber aus, ob er das behebt oder welche Kernel-Versionen davon betroffen sind.
Also, nur der Vollständigkeit halber: die BCM2835 Library herunterladen und nach Anleitung installieren.
Im Prinzip funktioniert das Ganze genau so, wie mit wiringPi. Ich habe Euch einen entsprechenden Beispiel-Code mal hier eingefügt.
//
// gcc -o irq-bcm2835 irq-bcm2835.c -l rt -l bcm2835
//
#include <stdio.h>
#include <bcm2835.h>
#define S0_SUCCESS 0
#define S0_FAIL -1
// ########################################################################
//
// bcm2835 - functions
//
// ########################################################################
short dmn_bcm2835_waitevent(uint8_t pin)
{
short exit_code = S0_FAIL;
for( ; ; )
{
if (bcm2835_gpio_eds(pin) == HIGH)
{
// Now clear the eds flag by setting it to 1
bcm2835_gpio_set_eds(pin);
return(S0_SUCCESS);
}
}
return( exit_code );
}
short dmn_bcm2835_init()
{
if (bcm2835_init())
{
return( S0_SUCCESS );
}
return( S0_FAIL );
}
void dmn_bcm2835_setup(uint8_t pin)
{
short exit_code = S0_SUCCESS;
// Set pin to be an input
bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_INPT);
// and rising edge detect enable
bcm2835_gpio_ren(pin);
return;
}
short dmn_bcm2835_close()
{
if( bcm2835_close() )
{
return( S0_SUCCESS );
}
return( S0_FAIL );
}
/*
* *************************************************************
* Detect board revision
* *************************************************************
*/
short get_board_revision (void)
{
return 2;
}
void interrupt_0(void)
{
printf("Interrupt: rising to high!");
}
main()
{
uint8_t pin = RPI_V2_GPIO_P1_12;
dmn_bcm2835_init();
dmn_bcm2835_setup(pin);
for ( ; ; )
{
if( dmn_bcm2835_waitevent(pin) == S0_SUCCESS )
{
}
delay(100);
}
dmn_bcm2835_close();
}
Alles anzeigen
Und schliesslich noch meine Favoriten-Bibliothek: pigpio
pigpio herunterladen und nach anleitung installieren, falls noch nicht geschehen.
Auch hier läuft es nach demselben Schema: Initialisieren, Pin für den Interrupt definieren und Service Routine installieren.
Ich finde es allerdings um einiges eleganter, weil die Service-Routine - wie es sich gehört - im Hintergrund läuft und man sich, im Gegensatz zu wiringPi, nicht mehr drum kümmern muss.
#include <stdio.h>
#include <pigpio.h>
/*
cc -o LDR LDR.c -lpigpio -lrt -lpthread
*/
#define LDR 11 /* GPIO 11 as interrupt input */
/* forward declaration */
void alert(int pin, int level, uint32_t tick);
int main (int argc, char *argv[])
{
if (gpioInitialise()<0) return 1;
gpioSetAlertFunc(LDR, alert); /* call alert when LDR changes state */
gpioSetMode(LDR, PI_INPUT);
while (1)
{
gpioDelay(10000); /* nominal 100 readings per second */
}
gpioTerminate();
}
void alert(int pin, int level, uint32_t tick)
{
fprintf(stderr, "Sensor Alarm!\n");
}
Alles anzeigen
Nun, das war's mal mit der C-Schnittstelle.
Ich hoffe, dass das einigermassen hilfreich für den einen oder anderen ist.
cu,
-ds-