Die zweite Funktion zur Interrupt-Steuerung heißt interruptPositionRotate() (Listing 2). Die Software ruft sie bei jedem Signal des Pulsgebers auf. Die Funktion übernimmt zwei Aufgaben: Zum einen zählt sie die Variable position abhängig von der Richtung des Motors herauf oder herunter; zum anderen stoppt sie den Motor, sobald dieser die Zielposition erreicht.
Listing 2
void interruptPositionRotate(void){
if (DEBUG) {printf("Interrupt Rotate Pos\n");}
if (directionRotate == 0) {
positionRotate --;
} else {
positionRotate ++;
}
if (positionRotate==targetRotate) {
motorRotateStop();
}
}
Um ein rundes Bild zu erhalten, werfen Sie am besten noch einen Blick in die Funktion firopiSetup() (Listing 3). Deren erster Teil definiert die Modi für die GPIO-Pins, der zweite installiert die Interrupt-Handler. Als Beispiel dient folgende Zeile:
wiringPiISR(ZeroPositionRotate, INT_EDGE_RISING, &interruptRotate);
Wenn am GPIO-Eingang ZeroPositionRotate eine steigende Flanke auftritt (Wechsel des Signals von 0 nach 1), dann ruft die Software die Funktion interruptRotate() auf. Die wiringPiISR() kennt drei Arten, einen Interrupt auszulösen: INT_EDGE_FALLING (von 1 nach 0), INT_EDGE_RISING (von 0 nach 1) und INT_EDGE_BOTH (beide Übergänge gleichzeitig).
Listing 3
void firopiSetup(void){
pinMode(MotorRotate1, OUTPUT) ;
pinMode(MotorRotate2, OUTPUT) ;
pinMode(MotorLift1, OUTPUT) ;
pinMode(MotorLift2, OUTPUT) ;
pinMode(MotorGoOut1, OUTPUT) ;
pinMode(MotorGoOut2, OUTPUT) ;
pinMode(MotorGrab1, OUTPUT) ;
pinMode(MotorGrab2, OUTPUT) ;
pinMode(ZeroPositionRotate, INPUT);
pinMode(ZeroPositionLift, INPUT);
pinMode(ZeroPositionGoOut, INPUT);
pinMode(ZeroPositionGrab, INPUT);
pinMode(EncoderRotate, INPUT);
pinMode(EncoderLift, INPUT);
pinMode(EncoderGoOut, INPUT);
pinMode(EncoderGrab, INPUT);
// install interrupt handler
wiringPiISR(ZeroPositionRotate, INT_EDGE_RISING, &interruptRotate);
wiringPiISR(ZeroPositionLift, INT_EDGE_RISING, &interruptLift);
wiringPiISR(ZeroPositionGoOut, INT_EDGE_RISING, &interruptGoOut);
wiringPiISR(ZeroPositionGrab, INT_EDGE_RISING, &interruptGrab);
wiringPiISR(EncoderRotate, INT_EDGE_RISING, &interruptPositionRotate);
wiringPiISR(EncoderLift, INT_EDGE_RISING, &interruptPositionLift);
wiringPiISR(EncoderGoOut, INT_EDGE_RISING, &interruptPositionGoOut);
wiringPiISR(EncoderGrab, INT_EDGE_RISING, &interruptPositionGrab);
}
Die Hardware testen
Nachdem Sie sich bisher mit der Bibliothek zur Ansteuerung des Roboters auseinandergesetzt haben, geht es nun darum, die Hardware in Betrieb zu nehmen. Um zu testen, ob Sie alle Komponenten richtig verdrahtet haben, geben Sie in einem Terminal das Watch-Kommando aus der ersten Zeile von Listing 4 ein. Es liest alle 100 Millisekunden den Status der GPIO-Pins aus. Die Ausgabe sollte so aussehen wie in den folgenden Zeilen des Listings. Nun betätigen Sie der Reihe nach alle Taster des Roboters, um zu sehen, ob sich der Status der entsprechenden GPIO-Pins ändert. Die Tabelle “Motoren, Schalter und Pulsgeber” vereinfacht diesen ersten Test der Hardware.
Listing 4
$ watch -n 0.1 gpio readall Every 0.1s: gpio readall raspberrypi: Thu Jun 7 14:49:14 2018 +-----+-----+---------+------+---+---Pi 3---+---+------+---------+-----+-----+ | BCM | wPi | Name | Mode | V | Physical | V | Mode | Name | wPi | BCM | +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+ | | | 3.3v | | | 1 || 2 | | | 5v | | | | 2 | 8 | SDA.1 | IN | 1 | 3 || 4 | | | 5v | | | | 3 | 9 | SCL.1 | IN | 1 | 5 || 6 | | | 0v | | | | 4 | 7 | GPIO. 7 | IN | 0 | 7 || 8 | 0 | IN | TxD | 15 | 14 | | | | 0v | | | 9 || 10 | 1 | IN | RxD | 16 | 15 | | 17 | 0 | GPIO. 0 | IN | 0 | 11 || 12 | 0 | IN | GPIO. 1 | 1 | 18 | | 27 | 2 | GPIO. 2 | IN | 1 | 13 || 14 | | | 0v | | | | 22 | 3 | GPIO. 3 | IN | 1 | 15 || 16 | 1 | IN | GPIO. 4 | 4 | 23 | | | | 3.3v | | | 17 || 18 | 0 | IN | GPIO. 5 | 5 | 24 | | 10 | 12 | MOSI | IN | 1 | 19 || 20 | | | 0v | | | | 9 | 13 | MISO | IN | 1 | 21 || 22 | 0 | IN | GPIO. 6 | 6 | 25 | | 11 | 14 | SCLK | IN | 0 | 23 || 24 | 1 | IN | CE0 | 10 | 8 | | | | 0v | | | 25 || 26 | 1 | IN | CE1 | 11 | 7 | | 0 | 30 | SDA.0 | IN | 1 | 27 || 28 | 1 | IN | SCL.0 | 31 | 1 | | 5 | 21 | GPIO.21 | IN | 1 | 29 || 30 | | | 0v | | | | 6 | 22 | GPIO.22 | IN | 1 | 31 || 32 | 0 | IN | GPIO.26 | 26 | 12 | | 13 | 23 | GPIO.23 | IN | 0 | 33 || 34 | | | 0v | | | | 19 | 24 | GPIO.24 | IN | 0 | 35 || 36 | 0 | IN | GPIO.27 | 27 | 16 | | 26 | 25 | GPIO.25 | IN | 0 | 37 || 38 | 0 | IN | GPIO.28 | 28 | 20 | | | | 0v | | | 39 || 40 | 0 | IN | GPIO.29 | 29 | 21 | +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+ | BCM | wPi | Name | Mode | V | Physical | V | Mode | Name | wPi | BCM | +-----+-----+---------+------+---+---Pi 3---+---+------+---------+-----+-----+
Mit dem kleinen Testprogramm irqtest.c aus Listing 5 überprüfen Sie, ob die Interrupt-Routinen alle richtig ausgelöst werden – also nur ein Mal, wenn Sie den Taster drücken.
Falls ein Taster die Interrupt-Routinen mehrfach auslöst, kann es helfen, einen Kondensator mit einer höheren Kapazität am entsprechenden Eingang zu verbauen. Alternativ bringen Sie einen 1-kOhm-Widerstand in Reihe zum Schalter an. Möglicherweise verursacht aber, wie oben schon beschrieben, auch ein defekter GPIO-Eingang das Problem: Dann versuchen Sie es mit einem anderen. Zu guter Letzt: Selbst ein Profi vergisst gelegentlich, eine Lötstelle zu setzen. In jedem Fall sollten Sie bei sonderbaren Hardware-Fehlern alle Lötstellen überprüfen, die in Betracht kommen.
Die Funktionsweise des Programms ist schnell erklärt. Es initialisiert schlicht alles, sprich: aktiviert die Interrupt-Handler, und wartet dann mithilfe der delay() Funktion einfach nur. Wenn Sie nun einen Taster drücken, kommt der entsprechende Interrupt-Handler zur Ausführung, und Sie sehen die Debug-Ausgaben auf dem Bildschirm.
Listing 5
#include <stdio.h>
#include <stdlib.h>
#include <wiringPi.h>
#include "libfiropi.h"
int main(void) {
wiringPiSetup();
firopiSetup();
delay(20000);
}
Mit dem ersten Kommando aus Listing 6 übersetzen Sie das Programm und starten es dann mit dem Aufruf ./irqtest.
Listing 6
$ cc irqtest.c -lwiringPi libfiropi.o -oirqtest $ cc motortest.c -lwiringPi libfiropi.o -omotortest
Um die Motoren zu prüfen, verwenden Sie das Programm motortest.c aus Listing 7. Es sorgt dafür, dass alle Motoren nacheinander eine Sekunde nach rechts fahren, also in Richtung des Tasters für die Nullposition. Dreht sich der Motor in die andere Richtung, tauschen Sie die Polarität am Motor. Achten Sie darauf, dass die Motoren sich bei Programmstart in Mittelposition befinden, damit sie in beide Richtungen genug Fahrweg haben. Das Motortest-Programm kompilieren Sie analog zum Irqtest-Programm mit der zweiten Zeile aus Listing 6.
Listing 7
#include <stdio.h>
#include <stdlib.h>
#include <wiringPi.h>
#include "libfiropi.h"
int main(void) {
wiringPiSetup();
firopiSetup();
motorRotateTurnRight();
delay(1000);
motorRotateStop();
motorLiftTurnRight();
delay(1000);
motorLiftStop();
motorGoOutTurnRight();
delay(1000);
motorGoOutStop();
motorGrabTurnRight();
delay(1000);
motorGrabStop();
}
Förderbänder beladen
Der Roboter hat die Aufgabe, über zwei Taster (S1 und S2) gesteuert einen Gegenstand von einem Förderband zu heben und ihn auf eines der beiden anderen Bänder abzulegen. In der Rubrik “Hacks” in dieser Ausgabe finden Sie ein weiteres Projekt, in dem ein Controllino-Modul das Ansteuern des Roboters übernimmt. Die Taster dienen hier nur zu Testzwecken.





