Hallo zusammen
ich hab 6 Jahren nicht mehr mit Programmieren gemacht und nun hat es mich wieder gepackt :) und ich bin grad am Überlegen, wie ich mein Programm eigentlich aufsetze. Bin natürlich so gar nicht mehr drin im Programmieren und so wirklich aus dem Anfängerstadium raus war ich noch nie.
Wohl mit Interrupts, ich hab mir gestern auch endlich mal das Arduino Due gegönnt für genug Interrupteingänge ;):). :D
Und zwar hab ich zwei digitale Sensoren, die von tief auf hoch gehen und wieder zurück und einen digitalen Encoder mit A, B und Z-Spur und ich möchte nun wissen, wie für wie viele Encoderpulse das Signal nun hoch war. Die beiden Sensoren sind versetzt auf hoch, Sensor 2 ist immwe apäter.
Der Zeitraum, in dem die beiden Signale für 1-3 ms auf hoch beträgt im schnellsten Fall 20 ms (es kann aber auch alles sehr viel langsamer gemacht werden). In der Zeit werden 214 Encoderencremente durchfahren empfangen (wenn ich alle steigenden und sinkenden Flanken der A & B Spur zähle vermehrt sich das auf 4*214 = 856, oder?)
Aber nun die Frage wie man sowas gescheid und nicht zu rechenintensiv aufbaut.
Ich hab bisher erst sequentiell progrmmiert und muss mich zuerst noch in das Arbeiten mit Interrupts eindenken.
Meine Anfänger-Überlegungen zum Aufsetzen des Programms sind folgende:
- Interrupt 1: A-Spur des Encoders, zählt eine Variable "Encoderpulse" hoch, wenn eine sinkende oder fallende Flanke detektiert wird
- Interrupt 2: B-Spur des Encoders: zählt die gleiche Variable "Encoderpulse" hoch, wenn eine sinkende oder fallende Flanke detektiert wird
(Drehrichtung muss ich nicht detektieren, die ist immer gleich)
- Interrupt 3: Sensor 1 steigende Flanke: Nullen der Variable Encoderpulse
- Interrupt 4: Sensor 1 sinkende Flanke: Schreiben der Anzahl Encoderpulse in eine Variable "Anzahl_Pulse_1"
- Interrupt 5: Sensor 2 steigende Flanke: Nullen der Variable Encoderpulse
- Interrupt 6: Sensor 3 sinkende Flanke: Schreiben der Anzahl Encoderpulse in eine Variable "Anzahl_Pulse_2"
- (Interrupt 7: Z-Spur des Encoders: Nullen einer Mitlaufenden Variabeln?)
Würdet ihr das auch so aufsetzen oder ganz anders?
Kurz nach Interrupt 6 (oder seinem Fehlen) möchte ich die Anzahl_Pulse 1 und 2 Auswerten.
Wie mache ich es am besten, dass ich sicherstellen kann, dass ich eine Auswertung zum richtigen Zeitpunkt stattfindet (also zeitlich nach Interrupt 6 und vor Interrupt 3 des nächsten Zyklus? - auch dann wenn Interrupt 6 mal fehlen sollte, da es nichts zu messen gab).
Die für mich naheliegendste Lösung mit dem Zeitzähler fällt weg, da das ganze mit ganz unterschiedlichen Geschwindigkeiten läuft und ich nicht weiss, zu welchem Zeitpunkt die Messungen soweit sind.
Ein weiterer Interrupt auf der Z-Spur des Encoders hätte sich angeboten, nur kommt die nur alle 7 Messzyklen vorbei (gut rein theoretisch kann ich eine Übersetzung zum Encoder bauen, dass das Z-Signal einmal pro Messzyklus kommt, aber wenns auch ohne geht...)
Würdest ihr eine eine weitere Interruptzählvarianble im ISR 1 definieren, die nur einmal pro Messzyklus (oder sogar einmal pro 7 Messzyklen) genullt wird?
Und sobald die z.B. bei 857 ankommt, die Auswertung starten? Und wenn ja wie? Frage ich da z.B. im ISR 1 jedes mal ab, ob die Variable nun diesen Wert hat und rufe die Auswertefunktion aus, oder wie?
Und wie stelle ich sicher, dass ich jeweils die Anzahl_Pulse_1, die ich auswerte aus dem jeweils aktuellen Messzyklus kommen?
Fehlende Messungen würde ich wohl abfangen, in dem ich Anfangswerte von 0 setze nach dem Auswerten.
Und was passiert eigentlich, wenn ich z.B. in dieser Auswertefunktion bin und Interrupts vorkommen?
(Encoderinterrupts werden ja sicher kommen). Das wechselt einfach kurz in den entsprechenden Interrupt und wieder zurück oder grosse Nebenwirkungen? Zeitverluste?
Ich weiss, dass ich mit fast 20 ms bis der nächste Zyklus bereit ist ausgewertet zu werden, genug Zeit habe um die Anzahl Pulse mit dem zulässigen Bereich abzugleichen und davon abhängig Ausgänge zu setzen. Aber es fällt mir einfach noch schwer zu glauben, dass 20 ms viiiiel Zeit sind.
Meine bisherigen Roboter waren seeeeehr viel gemächlicher. Da lief manchmal für viele Minuten nichts.
Danke schonmal fürs Mitdenken, Überlegungsfehler finden, Verbesserungsvorschläge und Tipps oder auch nur schon für eine Bestätigen, dass es so klappen könnte.
Fühl mich grad echt unsicher nach so vielen Jahren ohne Programmieren und Interrupts hab ich eben sogar noch gar nie versucht.
Liebe Grüsse :D
Getorix
Rabenauge
10.12.2018, 00:38
Sei vorsichtig mit Interrupts.
Die heissen nich umsonst so: sie unterbrechen den normalen Programmablauf.
Was genau passiert, wenn ein Interrupt das tut, und während nun seine ISR abgearbeitet wird, löst ein weiterer Interrupt aus?
Je mehr davon man benutzt, umso höher die Wahrscheinlichkeit, dass dieser Fall mal eintritt.
Interrupts sind eigentlich eine tolle Sache-haben aber ihre Tücken, eben, weil sie den normalen Programmablauf stören.
Beispielsweise kann man eine SD-Karte recht leicht zerstören, indem man, während nem Schreibzugriff darauf, einen Interrupt auslöst.
Das muss man bedenken, und beispielsweise so umschiffen, dass man für die Dauer des Schreibzugriffes die Interrupts sperrt, oder ähnliches.
Also: Vorsicht, bitte....
Ob dein konkretes Beispiel funktionieren wird, kann ich dir leider nicht sagen-ich halt nicht viel vom Due (heisst aber nicht, dass der schlecht wäre).
Was genau passiert, wenn ein Interrupt ausgelöst wird, während noch einer im Gange ist?
Mir fallen dazu 2 grundlegende Strategien ein:
1. Wenn der Code in eine Interrupt-Service-Routine eintritt, werden alle Interrupts gesperrt und vor beenden der ISR wieder freigegeben.
2. Es wird für alle Interrupt-Service-Routinen eine globale Variable definiert, die anzeigt, dass ein Interrupt bearbeitet wird (evtl. auch welcher). Stellt eine ISR fest, dass noch eine andre ISR arbeitet, beendet sie sich wieder, als wäre nichts gewesen.
Wichtig ist in allen Interrupt-Service-Routinen grundsätzlich, dass man veränderte CPU-Register sichert und beim Beenden der ISR wieder herstellt.
MfG
oberallgeier
10.12.2018, 09:47
..
- Interrupt 1: A-Spur des Encoders, zählt eine Variable "Encoderpulse" hoch, wenn eine sinkende oder fallende Flanke detektiert wird
- Interrupt 2: B-Spur des Encoders: zählt die gleiche Variable "Encoderpulse" hoch, wenn eine sinkende oder fallende Flanke detektiert wird
(Drehrichtung muss ich nicht detektieren, die ist immer gleich)
- Interrupt 3: Sensor 1 steigende Flanke: Nullen der Variable Encoderpulse
- Interrupt 4: Sensor 1 sinkende Flanke: Schreiben der Anzahl Encoderpulse in eine Variable "Anzahl_Pulse_1"
- Interrupt 5: Sensor 2 steigende Flanke: Nullen der Variable Encoderpulse
- Interrupt 6: Sensor 3 sinkende Flanke: Schreiben der Anzahl Encoderpulse in eine Variable "Anzahl_Pulse_2"
- (Interrupt 7: Z-Spur des Encoders: Nullen einer Mitlaufenden Variabeln?)
Würdet ihr das auch so aufsetzen oder ganz anders? ..A. ich kenn mich mit arduino nicht aus.
B. Mit ARMs kenne ich mich nicht aus.
C. Mit Interrupts habe ich ein bisschen Erfahrung.
Wenn Du nen Atmel vor Dir hast dann hat der eine bestimmte Reihenfolge in der Interrupts priorisiert werden, siehe Datenblatt unter Interrupts. Sprich: wenn Interrupts zufällig "gleichzeitig" eintreffen, dann gehts der Priorität nach. Ansonsten geht es je nach Reihenfolge des Eintreffens.
Wie schon vorher geschrieben wurde: wenn eine Interrupt-Service-Routine (ISR) läuft sind Interrupts verboten ES SEI DENN dass der Programmierer explizit Interrupts erlaubt. Letzteres gibt dann die Möglichkeit zu "nested" Interrupts = ein Interrupt kann eine andere ISR unterbrechen . . . mit allerlei möglichen Problemchen.
Du schreibst von zwei Encodern und drei Sensoren; das stellt sich für mich die Frage ob die Sensoren 1 bis 3 mit den Motoren/Encodern verbunden sind? Hier wäre eine ausführliche Beschreibung von Aufbau und geplanter Aufgabe von Nutzen.
Vordergründig noch ne Anmerkung um Interrupts einzusparen. Interrupt 4 ist unnötig wenn Du für Sensor 1 jede Flanke nimmst und in der ISR eine Fallunterscheidung machst: Signal hoch <=> letzte Flanke war steigend, Signal low <=> letzte Flanke war fallend.
RP6conrad
10.12.2018, 10:24
AB encoder auswerten geht problemlos mit interrupts.
Interrupt 1 für Kanal A, wird eingestellt auf Low und High Flanke,
Interrupt 2 für Kanal B, wird eingestellt auf Low und High Flanke,
In diese Interrupt fragen sie der Zustand von beide Kanalen ab, und passen dan die Zaehlerstand an :
volatile int Zaehler;
Interrupt 1{
If ((A==high)&(B==high)) Zaehler ++;
If ((A==low)&(B==high)) Zaehler --;
If ((A==high)&(B==low)) Zaehler --;
If ((A==low)&(B==low)) Zaehler ++;
}
Auf diese Weise wird Richtung auch beobachtet. Auch den Fall das ein Spur "ruckelt" wird damit sicher gestellt das nicht gezaehlt wird !
ISR Prioritäten soll man sich nicht zu fiel sorgen machen, bei Zwei Interrupts gleichzeitig, wird erst die eine, und dan die andere abgearbeitet. Bei eine 8-bitter (Arduino UNO), ist es wichtig um zu wissen das Interrupts zu jeden Zeipunkt auftreten konnen, auch wen da mit eine 16bit Zahl gerechnet werd ! Unter umstanden wird dan diese Berechnung beinflusst, wen gerade diese Variable in Interrupt geandert wird !
Variable die in ISR genutzt werden, wussen sie als "Volatile" declarieren, so das den Compiler die nicht "Weg optimiert". Z-spur Kanal ist nicht unbedingt notwendig, konnen sie verwenden für eine "Kalibrierung".
man braucht beim Due keine pinchange-Interrupts, es reicht, die Encoderpins alle 100-1000µs über DueTimer auszulesen (je nach max. Drehgeschwindigkeit und Motorenzahl mehr oder weniger) und daraus die neue Encoderstellung zu berechnen.
Auch beim AVR reichen ähnlich dimensionierte timer Interrupts.
Der Vorteil der timer Intr zeigt sich gegenüber pinchange-Intr, wenn man mehrere Motoren gleichzeitig überwachen will.
für Arduino Due: 6 Motoren mit Rotationsencodern
samt pwm für L293-Typ H-Brücken
/************************************************** **********
* Programm zur Auswertung eines manuell betriebenen
* Drehencoders (Quadraturencoder) mit dem Arduino Due
* per Due-Timer mit einer Abfragefrequenz von rd. 4-10kHz
* Entlehnt an http ://www.meinDUINO.de
************************************************** **********/
#include <DueTimer.h>
char sbuf[100];
#define MAXMOTORS 6
// max number of encoder motors at Arduino Uno=2 // Due=6 // Mega=8
// motor 0
#define pinenc0A 22 // enc0A yellow
#define pinenc0B 23 // enc0B blue
#define pinmot0d1 24 // dir0-1 <<
#define pinmot0d2 25 // dir0-2
#define pinmot0pwm 10 // pwm enable0
// motor 1
#define pinenc1A 26 // enc1A yellow
#define pinenc1B 27 // enc1B blue
#define pinmot1d1 28 // dir1-1 <<
#define pinmot1d2 29 // dir1-2
#define pinmot1pwm 9 // pwm enable1
// motor 2
#define pinenc2A 30 // enc2A yellow
#define pinenc2B 31 // enc2B blue
#define pinmot2d1 32 // dir2-1 <<
#define pinmot2d2 33 // dir2-2
#define pinmot2pwm 8 // pwm enable2
// motor 3
#define pinenc3A 34 // enc3A yellow
#define pinenc3B 35 // enc3B blue
#define pinmot3d1 36 // dir3-1 <<
#define pinmot3d2 37 // dir3-2
#define pinmot3pwm 7 // pwm enable3
// motor 4
#define pinenc4A 38 // enc4A yellow
#define pinenc4B 39 // enc4B blue
#define pinmot4d1 40 // dir4-1 <<
#define pinmot4d2 41 // dir4-2
#define pinmot4pwm 6 // pwm enable4
// motor 5
#define pinenc5A 42 // enc5A yellow
#define pinenc5B 43 // enc5B blue
#define pinmot5d1 47 // dir5-1 <<
#define pinmot5d2 48 // dir5-2
#define pinmot5pwm 5 // pwm enable5
volatile long motenc[MAXMOTORS] = {0, 0, 0, 0, 0, 0},
oldenc[MAXMOTORS] = {0, 0, 0, 0, 0, 0};
byte pinmotdir[MAXMOTORS][ 2] = { // motor direction pin array
{pinmot0d1, pinmot0d2},
{pinmot1d1, pinmot1d2},
{pinmot2d1, pinmot2d2},
{pinmot3d1, pinmot3d2},
{pinmot4d1, pinmot4d2},
{pinmot5d1, pinmot5d2},
};
int pinmotpwm[MAXMOTORS] = { // motor pwm pin array
pinmot0pwm, pinmot1pwm, pinmot2pwm,
pinmot3pwm, pinmot4pwm, pinmot5pwm
};
volatile int8_t ISRab[MAXMOTORS] = {0, 0, 0, 0, 0, 0};
// 1/1 Auflösung
//int8_t schrittTab[16] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
// 1/2 Auflösung ergibt bei Lego-Motoren 1 tick pro Grad (standard wie bei Lego)
int8_t schrittTab[16] = {0, 0,0,0,1,0,0,-1, 0,0,0,1,0,0,-1,0};
// 1/4 Auflösung
//int8_t schrittTab[16] = {0,0,0,0,0,0,0,-1,0,0,0,0,0,1,0,0};
/************************************************** ***********
* Interrupt Handler Routine
************************************************** ***********/
void encHandler() {
ISRab [ 0] <<= 2;
ISRab [ 0] &= B00001100;
ISRab [ 0] |= (digitalRead(pinenc0A) << 1) | digitalRead(pinenc0B);
motenc[ 0] += schrittTab[ISRab[0]]; //
ISRab [ 1] <<= 2;
ISRab [ 1] &= B00001100;
ISRab [ 1] |= (digitalRead(pinenc1A) << 1) | digitalRead(pinenc1B);
motenc[ 1] += schrittTab[ISRab[1]]; //
ISRab [ 2] <<= 2;
ISRab [ 2] &= B00001100;
ISRab [ 2] |= (digitalRead(pinenc2A) << 1) | digitalRead(pinenc2B);
motenc[ 2] += schrittTab[ISRab[2]]; //
ISRab [ 3] <<= 2;
ISRab [ 3] &= B00001100;
ISRab [ 3] |= (digitalRead(pinenc3A) << 1) | digitalRead(pinenc3B);
motenc[ 3] += schrittTab[ISRab[3]]; //
ISRab [ 4] <<= 2;
ISRab [ 4] &= B00001100;
ISRab [ 4] |= (digitalRead(pinenc4A) << 1) | digitalRead(pinenc4B);
motenc[ 4] += schrittTab[ISRab[4]]; //
ISRab [ 5] <<= 2;
ISRab [ 5] &= B00001100;
ISRab [ 5] |= (digitalRead(pinenc5A) << 1) | digitalRead(pinenc5B);
motenc[ 5] += schrittTab[ISRab[5]]; //
}
void setup() {
// motor pin settings
// setup for L293D motor driver
// motor 0
pinMode(pinenc0A, INPUT_PULLUP); // enc0A yellow
pinMode(pinenc0B, INPUT_PULLUP); // enc0B blue
pinMode(pinmot0d1, OUTPUT); // dir0-1
pinMode(pinmot0d2, OUTPUT); // dir0-2
pinMode(pinmot0pwm ,OUTPUT); // enable0
// motor 1
pinMode(pinenc1A, INPUT_PULLUP); // enc1A yellow
pinMode(pinenc1B, INPUT_PULLUP); // enc1B blue
pinMode(pinmot1d1, OUTPUT); // dir1-1
pinMode(pinmot1d2, OUTPUT); // dir1-2
pinMode(pinmot1pwm, OUTPUT); // enable1
// motor 2
pinMode(pinenc2A, INPUT_PULLUP); // enc2A yellow
pinMode(pinenc2B, INPUT_PULLUP); // enc2B blue
pinMode(pinmot2d1, OUTPUT); // dir2-1
pinMode(pinmot2d2, OUTPUT); // dir2-2
pinMode(pinmot2pwm, OUTPUT); // enable2
// motor 3
pinMode(pinenc3A, INPUT_PULLUP); // enc3A yellow
pinMode(pinenc3B, INPUT_PULLUP); // enc3B blue
pinMode(pinmot3d1, OUTPUT); // dir3-1
pinMode(pinmot3d2, OUTPUT); // dir3-2
pinMode(pinmot3pwm, OUTPUT); // enable3
// motor 4
pinMode(pinenc4A, INPUT_PULLUP); // enc4A yellow
pinMode(pinenc4B, INPUT_PULLUP); // enc4B blue
pinMode(pinmot4d1, OUTPUT); // dir4-1
pinMode(pinmot4d2, OUTPUT); // dir4-2
pinMode(pinmot4pwm, OUTPUT); // enable4
// motor 5
pinMode(pinenc5A, INPUT_PULLUP); // encA5 yellow
pinMode(pinenc5B, INPUT_PULLUP); // encB5 blue
pinMode(pinmot5d1, OUTPUT); // dir5-1
pinMode(pinmot5d2, OUTPUT); // dir5-2
pinMode(pinmot5pwm, OUTPUT); // enable5
Timer1.attachInterrupt(encHandler);
Timer1.start(100); // Calls every ...µs
Serial.begin(115200);
Serial.println( "safety delay before start");
delay(1000); // safety delay before start
Serial.println();
}
void loop() {
while(true) {
sprintf(sbuf, " 0=%6d, 1=%6d, 2=%6d, 3=%6d, 4=%6d, 5=%6d",
motenc[ 0], motenc[ 1], motenc[ 2], motenc[ 3], motenc[ 4], motenc[ 5]);
Serial.println(sbuf);
delay(100);
}
}
Auslesen der Encoder von 1 Motor mit Arduino Uno / Mega :
/************************************************** **********
*
* Demo-Programm zur Auswertung eines manuell betriebenen
* Drehencoders (Quadraturencoder) mit dem Arduino im
* Timer-Interrupt mit einer Abfragefrequenz von rd. 1kHz
*
* Kann von jederman frei verwendet werden, aber bitte den
* Hinweis: "Entnommen aus http://www.meinDUINO.de" einfügen
*
************************************************** **********/
// An die Pins 2 und 3 ist der Encoder angeschlossen
#define encoderA 2
#define encoderB 3
// Globale Variablen zur Auswertung in der
// Interrupt-Service-Routine (ISR)
volatile int8_t altAB = 0;
volatile int encoderWert = 0;
// Die beiden Schritt-Tabellen für volle oder 1/4-Auflösung
// 1/1 Auflösung
int8_t schrittTab[16] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
// alternativ:
// 1/2 Auflösung ergibt bei Lego-Motoren 1 tick pro Grad (wie bei Lego)
// int8_t schrittTab[16] = {0, 0,0,0,1,0,0,-1, 0,0,0,1,0,0,-1,0};
// 1/4 Auflösung
//int8_t schrittTab[16] = {0,0,0,0,0,0,0,-1,0,0,0,0,0,1,0,0};
/************************************************** ***********
*
* Interrupt Service Routine
*
* Wird aufgerufen, wenn der entsprechende Interrupt
* ausgelöst wird
*
************************************************** ***********/
ISR(TIMER1_COMPA_vect) {
altAB <<= 2;
altAB &= B00001100;
altAB |= (digitalRead(encoderA) << 1) | digitalRead(encoderB);
encoderWert += schrittTab[altAB];
}
/************************************************** ***********
*
* void setup()
*
* Wird einmal beim Programmstart ausgeführt
*
************************************************** ***********/
void setup() {
pinMode(encoderA, INPUT);
pinMode(encoderB, INPUT);
noInterrupts(); // Jetzt keine Interrupts
TIMSK1 |= (1<<OCIE1A); // Timer 1 Output Compare A Match Interrupt Enable
TCCR1A = 0; // "Normaler" Modus
// WGM12: CTC-Modus einschalten (Clear Timer on Compare match)
// Stimmen OCR1A und Timer überein, wird der Interrupt
// ausgelöst
// Bit CS12 und CS10 setzen = Vorteiler: 1024
TCCR1B = (1<<WGM12) | (1<<CS12) | (1<<CS10);
// Frequenz = 16000000 / 1024 / 15 = rd. 1kHz Abtastfrequenz;
// Überlauf bei 14, weil die Zählung bei 0 beginnt
OCR1A = 14; // OCR1A = 7 für 2kHz = alle 500µs
interrupts(); // Interrupts wieder erlauben
Serial.begin(115200);
}
/************************************************** ***********
*
* void loop()
*
* Wird immer wieder durchlaufen
*
************************************************** ***********/
void loop() {
while(true) {
Serial.println(encoderWert);
delay(100);
}
}
Danke euch für die Antworten :)
Ich entnehm dem, das ich es durchaus mal so probierne kann, wie angedacht :)
Du schreibst von zwei Encodern und drei Sensoren; das stellt sich für mich die Frage ob die Sensoren 1 bis 3 mit den Motoren/Encodern verbunden sind? Hier wäre eine ausführliche Beschreibung von Aufbau und geplanter Aufgabe von Nutzen.
Es sind nur 1 Encoder und zwei Sensoren (das 3 ist ein Tippfehler). Der Encoder gehört zum Antrieb und dieser Antrieb ist ein Förderantrieb und transportiert viele kleine Objekte. Ich möchte mit zwei Lichtschranken feststellen, wie breit die Teilchen an zwei Stellen sind, damit ich weiss, was grad unter den Sensoren liegt und damit entscheiden kann, wohin das Objekt soll.
Wichtig ist in allen Interrupt-Service-Routinen grundsätzlich, dass man veränderte CPU-Register sichert und beim Beenden der ISR wieder herstellt.
Passiert das "von alleine". Ich brauche da manuell nichts tun, ausser die ISR Variablen als volatile zu deklarieren, oder?
Auf diese Weise wird Richtung auch beobachtet. Auch den Fall das ein Spur "ruckelt" wird damit sicher gestellt das nicht gezaehlt wird !
macht Sinn
man braucht beim Due keine pinchange-Interrupts, es reicht, die Encoderpins alle 100-1000µs über DueTimer auszulesen (je nach max. Drehgeschwindigkeit und Motorenzahl mehr oder weniger) und daraus die neue Encoderstellung zu berechnen.
Macht irgendwo auch Sinn. Danke fürs Beispiel.
Was würdest du bei einem Motor auch über die Zeit lösen? Oder doch über Pin-Change?
Mein "Problem" ist ja, dass die Geschwindigkeit sehr verschieden sein kann und ich daher wohl die Zeitabstände so setzen müsste, dass es für "Motor auf Vollgas" reichen muss. Das macht dann bei viel langsameren Geschwindigkeiten viel mehr Interrupts als nötig, was aber egal sein dürfte.
Liebe Grüsse
Getorix
Powered by vBulletin® Version 4.2.5 Copyright ©2024 Adduco Digital e.K. und vBulletin Solutions, Inc. Alle Rechte vorbehalten.