--- still under construction ---
Ein Incrementalgeber ist ein Drehwinkel Meßinstrument, welches
die relative Winkelposition ausgiebt.
Relativ, da ich keinen Absolutwinkel erhalte, sondern nur deren Änderung.
Das bedeutet, solange ich nicht eine Referenzposition angefahren habe,
kenne ich nicht den Winkel.
Nachdem ich die Referenz N
angefahren habe,
muss ich entsprechend der Phase der Signale A
und B
entweder hoch oder runter zählen um die jetzige Position zu bestimmen.
Das N-Signal habe ich an beliebiger Stelle eingezeichnet
entsprechend der Bedingung: N = 1 → A = 1 & B = 1
Wie man den Raspberry startet, konfiguriert, remote darauf zugreift, etc.
habe ich bereits unter
Raspberry Zero W
beschrieben.
Zunächst habe ich nur einen Incrementalgeber an PGIO14, 15 und 23
angeschlossen und gucke ob der funktioniert.
Entsprechende Testschaltung findet sich unter
Motortest.
Die Pinbelegung habe ich unter
Raspberry Zero W IO beschrieben.
Nun nehme ich an, wir befinden uns im Benutzer-Ordner z.B. /home/pi
Hier schreibe ich mit einen Editor z.B. nano, ein paar Zeilen Python.
nano inctst01.py #!/usr/bin/python import RPi.GPIO as GPIO import time GPIO.setmode(GPIO.BCM) # GPIO.BOARD für Pins GPIO.setwarnings(False) nRES = 12 # reset low active A = 23 # A, B, N from incremental sensor N = 15 B = 14 GPIO.setup(nRES, GPIO.OUT) GPIO.output(nRES, GPIO.LOW) # reset DRV8825 (Motor deaktiviert) GPIO.setup(A, GPIO.IN) GPIO.setup(B, GPIO.IN) GPIO.setup(N, GPIO.IN) inAnew = GPIO.input(A) inBnew = GPIO.input(B) cnt = 0 while abs(cnt) < 10000: # never reached inAold = inAnew inBold = inBnew inAnew = GPIO.input(A) inBnew = GPIO.input(B) if GPIO.input(N): # == True: cnt = 0 print ( 'N=1 A= '+str(inAnew)+' B= '+str(inBnew) ) elif inAnew > inAold and inBnew == 0 and inBold == 0: cnt += 1 print ( 'counter= '+str(cnt) ) elif inAnew > inAold and inBnew == 1 and inBold == 1: cnt -= 1 print ( 'counter= '+str(cnt) ) elif inAnew < inAold and inBnew == 0 and inBold == 0: cnt -= 1 print ( 'counter= '+str(cnt) ) elif inAnew < inAold and inBnew == 1 and inBold == 1: cnt += 1 print ( 'counter= '+str(cnt) ) elif inAnew == 0 and inAold == 0 and inBnew < inBold: cnt += 1 print ( 'counter= '+str(cnt) ) elif inAnew == 0 and inAold == 0 and inBnew > inBold: cnt -= 1 print ( 'counter= '+str(cnt) ) elif inAnew == 1 and inAold == 1 and inBnew < inBold: cnt -= 1 print ( 'counter= '+str(cnt) ) elif inAnew == 1 and inAold == 1 and inBnew > inBold: cnt += 1 print ( 'counter= '+str(cnt) ) elif inAnew != inAold and inBnew != inBold: print ( "---lost step---" ) print ("fertig") # never reached
Danach mache ich den Code noch ausführbar und starte Ihn.
Ausgeführt wird er dann über den python-Interpreter.
chmod u+x inctst01.py ./inctst01.py
Nun sollten beim Drehen des Incrementalgebers unterschiedliche Zahlen auf den Bildschirm erscheinen.
Interessant ist, dass ich bei dieser Art der Auswertung bis ±4096 (-4065 - 4085) komme.
Leicht sind ein paar Veränderungen nicht erfasst, da ich zu schnell drehte.
Das könnte aber auch an den print Befehlen liegen, welche über WLAN übertragen werden.
Zumindest zeigt der erste Versuch, das python auf einem Zero W zu langsam für diese Anwendung, ist.
Mit CTRL-C
kann man den Code unterbrechen.
Von python gibt es verschiedene Derivate (2.x versus 3.x) und Bibliotheken.
z.B. readthedocs.io
gpiozero (verwendet PCB Pins)
abyz.me.uk
pigpio (Steuerung über Netzwerk möglich, verwendet GPIO Bezeichnung)
pypi.org
RPi.GPIO (Klassiker mit GPIO Bezeichnung oder PCB Pins)
Zum Anfang
C wird im Gegensatz zu python schon vor dem Start kompiliert und nicht zur Laufzeit interpretiert.
Somit ist C wesentlich schneller als python.
Vergleichbar mit python, gibt es hier auch verschiedene Bibliotheken oder
sogar die Möglichkeit direkt auf die Register zuzugreifen, was aufgrund der asnchronen Abarbeitung,
nicht zu empfehlen ist.
Asynchron, weil die schnelle CPU in verschiedene FIFOs schreibt, deren Inhalt wird dann langsam
von verschiedenen "IO-state machines" abgearbeitet.
#include <wiringPi.h>
root
funktionieren allerdings nur die Grundfunktionen.
Wenn man die WiringPi Bibliothek verwendet, muss man aufpassen, das die Bezeichnungen anders sind
und nach Pin 28 Schluss ist, vom 40 poligen Stecker.
Also habe ich mich für die schlankeste Bibliothek bcm2835
entschieden.
Diese muss zunächst runtergeladen, entpackt und kompiliert werden.
wget http://www.airspayce.com/mikem/bcm2835/bcm2835-1.73.tar.gz … 2023-08-26 21:29:05 (307 KB/s) - ‘bcm2835-1.73.tar.gz’ saved [281850/281850] tar -xvzf bcm2835-1.73.tar.gz cd bcm2835-1.73/ ./configure checking for a BSD-compatible install... /usr/bin/install -c … make make all-recursive … sudo make install Making install in src …
Nun kann ich die Bibliothek verwenden und erstelle wieder ein Programm.
Zur besseren Wiedererkennung, habe ich es möglichst identisch zum Python Programm aufgebaut.
nano inctst.c #include <stdio.h> #include <bcm2835.h> // #include <wiringPi.h> #include <time.h> #define nRes RPI_V2_GPIO_P1_32 // dieses Mal die Pins und nicht die Ports #define inA RPI_V2_GPIO_P1_16 // #define inA 16 #define inB RPI_V2_GPIO_P1_08 #define inN RPI_V2_GPIO_P1_10 /***************************************************** * DELAY FOR # uS WITHOUT SLEEPING * Using delayMicroseconds lets the linux scheduler decide to jump to another process. * Using this function avoids letting the scheduler know * we are pausing and provides much faster operation if you are needing to use lots of delays. * */ void delayMicrosecondsNoSleep (int delay_us) { long int start_time; long int time_difference; struct timespec gettime_now; clock_gettime(CLOCK_REALTIME, &gettime_now); start_time = gettime_now.tv_nsec; // Get nS value while (1) { clock_gettime(CLOCK_REALTIME, &gettime_now); time_difference = gettime_now.tv_nsec - start_time; if (time_difference < 0) time_difference += 1000000000; // Rolls over every 1 second if (time_difference > (delay_us * 1000)) // Delay for # nS break; } } /********************* * main */ int main( void ) { if( !bcm2835_init() ) { printf("no lib found\n"); return 1; } printf("inctst.c started\n"); // if( wiringPiSetup() == -1 ) {return 1;} uint8_t inanew, inbnew, inaold, inbold; int cnt; bcm2835_gpio_fsel( nRes, BCM2835_GPIO_FSEL_OUTP); // pinMode( nRes, OUTPUT); bcm2835_gpio_write( nRes, LOW ); // digitalWrite(nRes,0); // reset DRV8825 bcm2835_gpio_fsel( inA, BCM2835_GPIO_FSEL_INPT); // pinMode( inA, INPUT); bcm2835_gpio_fsel( inB, BCM2835_GPIO_FSEL_INPT); bcm2835_gpio_fsel( inN, BCM2835_GPIO_FSEL_INPT); inanew = bcm2835_gpio_lev(inA); // bcm2835_gpio_lev returns uint8_t inbnew = bcm2835_gpio_lev(inB); // digitalRead(inB) cnt = 0; while( abs(cnt) < 10000 ) { inaold = inanew; inbold = inbnew; inanew = bcm2835_gpio_lev(inA); inbnew = bcm2835_gpio_lev(inB); if( bcm2835_gpio_lev( inN ) ) { cnt = 0; } else if( inanew > inaold && inbnew == 0 && inbold == 0 ) { cnt++; printf( " %d\n", cnt ); } else if( inanew > inaold && inbnew == 1 && inbold == 1 ) { cnt--; printf( " %d\n", cnt ); } else if( inanew < inaold && inbnew == 0 && inbold == 0 ) { cnt--; printf( " %d\n", cnt ); } else if( inanew < inaold && inbnew == 1 && inbold == 1 ) { cnt++; printf( " %d\n", cnt ); } else if( inanew == 0 && inaold == 0 && inbnew < inbold ) { cnt++; printf( " %d\n", cnt ); } else if( inanew == 0 && inaold == 0 && inbnew > inbold ) { cnt--; printf( " %d\n", cnt ); } else if( inanew == 1 && inaold == 1 && inbnew < inbold ) { cnt--; printf( " %d\n", cnt ); } else if( inanew == 1 && inaold == 1 && inbnew > inbold ) { cnt++; printf( " %d\n", cnt ); } else if( inanew != inaold && inbnew != inbold ) { printf( "lost step\n" ); // return 2; // auskommentiert, da sonst zu schnell beendet } delayMicrosecondsNoSleep( 100 ); // mit 100 µs Pause → 19% CPU } // while( abs(cnt) < 10000; ) return 0; }
Um mir das Leben zu vereinfachen, kompiliere und starte ich in einen 2. Fenster.
Dummerweise sind die Pins 27 und 28 nicht in der Bibliothek enthalten.
Daher muss ich die Platine nocheinmal ändern und den Widerstand für nRes auf Pin 32 legen.
gcc -o tst inctest.c -lbcm2835 ./tst
sudo
war übrigens zum Start des Programms nicht nötig.
Abbruch des Programms erfolgt wieder über CTRL-C.
Beim Kompilieren bekam ich natürlich auch Fehlermeldungen.
gcc mottst.c -o tst -lbcm2835 mottst.c: In function ‘main’: mottst.c:10:17: error: ‘RPI_V2_GPIO_PI_10’ undeclared (first use in this function); did you mean ‘RPI_V2_GPIO_P1_10’? #define inN RPI_V2_GPIO_PI_10 ^~~~~~~~~~~~~~~~~
Nach vielen Versuchen, habe ich exakt den Text aus der Webseite
airspayce.com kopiert
und dann lief es ???
Auch dieses Programm konnte ich leicht beenden, indem ich etwas schneller gedreht habe,
was ich so nicht erwartet hätte.
Die 100% CPU-Last haben offensichtlich zum "throttling" der CPU im Sekunden-Takt geführt,
wo dann Schritte verloren gingen. Daher habe ich eine Pause ergänzt und der CPU einen Kühlkörper spendiert.
Es gibt aber noch ein Problem mit delayMicroseconds( 100 )
was ich auf der Seite raspberry-projects.com fand.
→ raspberry-projects.com clock_gettime() For Acurate Timing
Leider verliere ich immer noch Schritte.
Zum Anfang
Die bisherigen Programme setzten auf sogenanntes Polling.
Viel eleganter ist es allerdings nur bei Änderungen via Interrupts zu reagieren.
Zum Anfang
python.org
python
mint-first.de
python Übungsaufgaben
w3schools.com
Python Tutorial
github.com
Simple Electronics with GPIO Zero.PDF
Zum Anfang