Ich bau mir momentan ein kleines Boot und möchte das mit einer 2.4GHz Fernsteuerung ansteuern (6 Kanal). Ich müsste jetzt mindestens 3 der 6 Kanäle (PWM) mit dem Arduino wieder einlesen. Kann mir vielleicht jemand helfen wie ich das machen muss? Alternativ könnte ich auch versuchen, das PPM Signal zu finden (so müsste nur ein PIN fürs auslesen genutzt werden) falls dies einfacher wäre (glaube ich aber nicht, da dort die info für 6 Kanäle versteckt ist).
Falls das weiter hilft:
Fernsteuerung ist eine relativ alte Titan T-6X
Empfänger ist ein ebenfalls relativ alter Titan R-6X
Als Arduino werde ich entweder den Mega oder UNO verwenden
mit dem uno gehen max. 2 pwm einlesen, besser du suchst das ppm Summensignal. Kann aber durchaus sein, dass die Funke gar nicht mit ppm arbeitet. Ich kenn das Teil nicht
ein PWM Signal einlesen unterscheidet sich eigentlich nicht vom PPM Signal einlesen. Meine aktuelle Meinung. Es müssen die Zeiten der Flanken erfasst und sortiert werden. Was ich anbieten könnte ist, meinen Code zum Takte einlesen. War mal als Datalogger gedacht um Impulse zu erfassen. Soweit getestet, nur mit der seriellen Ausgabe bin ich selbst noch nicht ganz zu 100% zufrieden. Brauchst du aber sicherlich in der Form nicht.
Ich kann mich aber nicht darum kümmern das Ding auf dein Problem vielleicht extra anzupassen. Keine Zeit z.Z.. Das mußtest du schon selbst machen. Das PPM Eingangssignal würde auf 2 Interrupt Eingänge gelegt. Eins detektiert die fallende Flanke und der andere die steigende. Die Zeitpunkte der Flanken werden getrennt in 2 Arrays erfasst und wenn die voll sind, wird alles ausgewertet. Die Arraygröße kennste, kannste festlegen. Hängt ja vom PPM Signal ab. Ich meine die Anzahl der Flanken ist bekannt. Ob das für dich paßt, kann ich nicht sagen. Wäre vielleicht ein Anfang.
nein, bei ppm ist die Anzahl nicht bekannt. hat ne Pause dazwischen, Zeit zwischen den Paketen 20ms standard, gibt aber inzwischen auch andere Zeiten, um mehr Kanäle unterzubringen
doch doch, beim PPM Signal ist die Anzahl der Flanken bekannt. Nur die Zeiten dazwischen nicht.
Selbst wenn ich das nicht genau weiß, weiß ich dennoch das maximal 10 "Pegelwechsel" reinkommen.
Laut Wikipedia dauert so eine PPM Übertragung 20ms. Eine "Servo-Signalperiode" max. 2ms.
Und ich weiß das eine längere Pause ab >2ms die Trennung ist, zwischen den "PPM Paketen".
Meinetwegen will er Signale von 6 Servos einlesen die in einem kompletten PPM Signal stecken.
Dann weiß ich vorher das 6 steigende und 6 fallende Flanken reinkommen.
Die Zeitpunkte der Flanken speichere ich ab und wenn alles vorbei ist, werte ich die Zeiten in Ruhe aus.
Die Arrays mache ich dabei natürlich fast doppelt so groß, weil ich nicht weiß ob die Pause am Anfang, mittendrin oder am Ende liegt. Die Logik steckt dann in der Auswertung der erfassten Daten.
Man kann das wie gesagt als "Stream" an 2 Interrupteingängen komplett einlesen.
Oder alle einzeln. Vergessen darf man nicht das fast alle Eingänge Interruptfähig sind. Egal ob Uno oder Mega.
Nur bei den "einfacheren" INTs muß man sich um die Pegel/Flankenerkennung selbst kümmern. Dafür gibts kein Konfigurationsregister für die Flankenerkennung.
Siki:
Kann mir vielleicht jemand helfen wie ich das machen muss?
Probier mal diesen Code zum Testen aus:
#define TIMEOUTSLICE 21000 // 21 Millisekunden als Zeitscheiben-Timeout
#define TIMEOUTCHANNEL 5000 // 5 Millisekunden als Timeout zwischen den Kanälen
#define TIMEOUTIMPULSE 3000 // 3 Millisekunden Impuls-Timeout
#define CHANNELS 4 // 4 Kanäle
int channelPins[CHANNELS]={2,3,4,5}; // define pin numbers for rc receiver channels
int impulseDuration[CHANNELS];
int readRcTiming(int* impulseTime)
/* Rückgabewert
0 ==> Impulslängen wurden erfolgreich gemessen und stehen im übergebenen int-Array
Negative Rückgabewerte stehen für Timeout
-1 ==> Kein Impuls auf erstem Kanal festgestellt
-2 ==> Keine Folgeimpulse auf weiteren Kanälen festgestellt
-3 ==> Impuls dauert zu lange
*/
{
unsigned long duration, timeout;
unsigned long now=micros();
while (digitalRead(channelPins[0])==HIGH && micros()-now<TIMEOUTIMPULSE) ; // nur warten
if (micros()-now>=TIMEOUTIMPULSE) return -3; // exit mit Timeout-Rückgabewert
now=micros();
for (int i=0;i<CHANNELS;i++)
{
if (i==0) timeout=TIMEOUTSLICE; else timeout=TIMEOUTCHANNEL;
while (digitalRead(channelPins[i])==LOW && micros()-now<timeout) ; // nur warten
if (micros()-now>=timeout)
{
if (timeout==TIMEOUTSLICE) return -1; // exit mit Timeout-Rückgabewert
else return -2; // exit mit Timeout-Rückgabewert
}
now=micros(); // Kanal geht HIGH, Beginn des Impulses
while (digitalRead(channelPins[i])==HIGH && micros()-now<TIMEOUTIMPULSE) ; // nur warten
duration=micros()-now;
if (duration>=TIMEOUTIMPULSE) return -3; // exit mit Timeout-Rückgabewert
impulseTime[i]=duration;
}
return 0; // ende ohne Timeout
}
void setup() {
Serial.begin(9600);
}
void loop() {
char charbuf[10];
int result=readRcTiming(impulseDuration);
if (result<0) Serial.println(result);
else
{
for(int i=0;i<CHANNELS;i++)
{
snprintf(charbuf,sizeof(charbuf),"%6d ",impulseDuration[i]);
Serial.print(charbuf);
}
Serial.println();
}
}
Die Ausgabe von zwei Kanälen auf Serial sollte bei 9600 Baud noch funktionieren.
Wenn Du mehr als 2 Kanäle auslesen und auf Serial ausgeben möchtest, würde ich die Baudrate höher setzen (im Sketch und auch im seriellen Monitor), also bei 4 Kanälen beispielsweise 19200 Baud.
Teste mal und schreib was dazu, ob es so funktioniert oder ob es Probleme gibt!
Hinweis: Die Reihenfolge der Impulse an einem RC-Empfänger ist nicht unbedingt mit der Numerierung der Kanalnummern identisch: Also der Impuls an RC-Kanal-1 muss innerhalb derselben 20ms Zeitscheibe nicht unbedingt vor dem Impuls an RC-Kanal-2 eintreffen, sondern die Impulse önnen auch in der entgegengesetzten Reihenfolge der Numerierung eintreffen oder noch anders.
Kann es sein, dass der Code ziemlich blockiert und sehr zeitkritisch ist, was andere Aufgaben abgeht?
Was die Anzahl der PPM Impulse angeht, das kann je nach Sender unterschiedlich sein, also wenn man etwas universell haben will, ist die Anzahl der Kanäle/Impulse nicht bekannt.
Danke für deine Bemühung @jurs.
Leider kommt bei mir nichts verwertbares Raus. Habe ich das Richtig verstanden, dass ich einfach das Kanal Signal jeweils mit einem der Pin's 2,3,4 oder 5 verbinde? Baud Rate habe ich erhöht, da ich zum testen mal alle 4 Pin's mit jeweils einem Kanal verbunden habe. Sobald ich den ersten Pin belegt habe bekomme ich nur noch "-2" zurück, ob ich jetzt die Restlichen 3 auch einstecke ändert nichts an der Sache. Kommt es auf die Reihenfolge der Kanäle an?
weißt du wie dein PPM Signal in echt aussieht?
Hast du irgendeine Möglichkeit zur Aufzeichnung? Oszi, Logikanalyzer, what ever.
Und was willste dann mit den eingelesenen Daten machen?
@ Spanier.
Also ist das mit den 20ms bei Wikipedia nicht in Stein gemeißelt? Gut, wußte ich nicht.
Wenn man die Randbedingungen in etwa kennt, sollte das laut meiner Auffassung weiterhin kein Problem sein.
Ja, es kommt bei meinem Beispiel-Sketch absolut auf die Reihenfolge der Kanäle an, genau in der Abfolge wie sie im Quelltext deklariert werden müssen die Impulse auch an den Pins ankommen. Aber da die Pins ja immer umlaufend einen Impuls erhalten, ist es egal, mit welchem Pin die Deklaration anfängt:
int channelPins[CHANNELS]={2,3,4,5};
entspricht funktionell
int channelPins[CHANNELS]={3,4,5,2};
entspricht funktionell
int channelPins[CHANNELS]={4,5,2,3};
entspricht funktionell
int channelPins[CHANNELS]={5,2,3,4};
Diese vier Beispiele gelten aber nur, wenn der Impuls an Pin3 beginnt wenn der Impuls an Pin2 beendet ist und
wenn der Impuls an Pin4 beginnt nachdem der Impuls an Pin3 beendet ist
und der Impuls an Pin5 beginnt nachdem der Impuls an Pin4 beendet ist
Ich hoffe, das Prinzip ist klar.
Für eine Permutation aus 4 Pins gibt es übrigens 24 verschiedene Möglichkeiten, die Reihenfolge anzugeben.
Mit welchem Pin das Auslesen anfängt, ist egal.
Aber die Reihenfolge der nachfolgenden Impulse muss für meinen Sketch korrekt sein, sonst funktioniert er nicht.
Die Reihenfolge ermittelt mein Sketch NICHT.
Und wenn die Reihenfolge falsch angegeben ist, siehst Du im seriellen Monitor negative Zahlen -1 oder -2 oder -3 , die für aufgetretene Timeout-Fehler stehen.
@doc: 20ms sind so ein quasi Standard für 8 Kanal PPM, bzw. ist die max. Framelänge. wenn alle auf Vollausschlag sind, wird es eng. Deswegen gibt es bei FrSky z. B. eine 27ms Firmware. Vermutlich war aber früher 20ms fest, weil daraus resultieren die 50Hz PWM für die analogen Servos
Ein 2 Kanal PPM hab ich heute getestet, war bei 12ms. Testweise ins 2,4 Modul rein, am Empfänger Servos an Kanal 1 und 2, ging wie erwartet perfekt.
Jetzt hat es geklappt und ich kann meine 3 Gewünschten PWM Signale einlesen. Danke dafür! Kannst du mir vieleicht noch zeigen wo ich die 3 Werte der PWM Signale abfangen kann? Ich möchte damit 2 Motoren Ansteuern, 1x mit H-Brücke (Richtung und Geschwindigkeit) 1x mit einem einfachen Mosfet. Wie ich die Motoren angesteuert bekomme weiss ich und kann das auch programmieren, insofern ich die Variable kenne mit dem Wert von dem jeweiligen Kanal, oder sind alle Kanalwerte in einer Variable? Liese sich das einfach änder, damit ich 3 Variabeln (oder einfach so viele wie Kanäle/Pin's definiert werden) habe?
#define TIMEOUTSLICE 21000 // 21 Millisekunden als Zeitscheiben-Timeout
#define TIMEOUTCHANNEL 5000 // 5 Millisekunden als Timeout zwischen den Kanälen
#define TIMEOUTIMPULSE 3000 // 3 Millisekunden Impuls-Timeout
#define CHANNELS 6 // 4 Kanäle
int channelPins[CHANNELS] = {2, 3, 4, 5, 6, 7}; // define pin numbers for rc receiver channels, CH1 --> 2, Ch2 --> 4, Ch3 --> 3, CH4 -->5, Ch5 --> 6, Ch6 --> 7
int impulseDuration[CHANNELS]; //[0] --> Ch1, [1] --> Ch3, [2] --> Ch2, [3] --> Ch4, [4] --> Ch5, [5] --> Ch6
int Speed;
int Speedrange;
int pumpe;
int pumpeRange;
int readRcTiming(int* impulseTime)
/* Rückgabewert
0 ==> Impulslängen wurden erfolgreich gemessen und stehen im übergebenen int-Array
Negative Rückgabewerte stehen für Timeout
-1 ==> Kein Impuls auf erstem Kanal festgestellt
-2 ==> Keine Folgeimpulse auf weiteren Kanälen festgestellt
-3 ==> Impuls dauert zu lange
*/
{
unsigned long duration, timeout;
unsigned long now = micros();
while (digitalRead(channelPins[0]) == HIGH && micros() - now < TIMEOUTIMPULSE) ; // nur warten
if (micros() - now >= TIMEOUTIMPULSE) return -3; // exit mit Timeout-Rückgabewert
now = micros();
for (int i = 0; i < CHANNELS; i++)
{
if (i == 0) timeout = TIMEOUTSLICE; else timeout = TIMEOUTCHANNEL;
while (digitalRead(channelPins[i]) == LOW && micros() - now < timeout) ; // nur warten
if (micros() - now >= timeout)
{
if (timeout == TIMEOUTSLICE) return -1; // exit mit Timeout-Rückgabewert
else return -2; // exit mit Timeout-Rückgabewert
}
now = micros(); // Kanal geht HIGH, Beginn des Impulses
while (digitalRead(channelPins[i]) == HIGH && micros() - now < TIMEOUTIMPULSE) ; // nur warten
duration = micros() - now;
if (duration >= TIMEOUTIMPULSE) return -3; // exit mit Timeout-Rückgabewert
impulseTime[i] = duration;
}
return 0; // ende ohne Timeout
}
void setup() {
Serial.begin(19200);
}
void loop() {
char charbuf[10];
int result = readRcTiming(impulseDuration);
if (result < 0) Serial.println(result);
else
{
/*for(int i=0;i<CHANNELS;i++)
{
snprintf(charbuf,sizeof(charbuf),"%6d ",impulseDuration[i]);
Serial.print(charbuf);
}
*/
antrieb();
Pumpe();
}
}
void Pumpe()
{
Serial.print("Pumpe:");
pumpeRange = (impulseDuration[5] - 984)/4 - 1;
if (pumpeRange <= 0)
{
pumpe = 0;
Serial.println(pumpe);
}
else
{
pumpe = pumpeRange;
Serial.println(pumpe);
}
}
void antrieb()
{
Speedrange = (impulseDuration[2] - 1100) / 3.2;
if (impulseDuration[4] > 1600)
{
Serial.print("rueckwaerts:");
if (Speedrange <= 0)
{
Speed = 0;
}
else
{
Speed = Speedrange;
}
Serial.println(Speed);
}
else
{
Serial.print("vorwaerts:");
if (Speedrange <= 0)
{
Speed = 0;
}
else
{
Speed = Speedrange;
}
Serial.println(Speed);
}
}
Natürlich werden später die Variabeln Speed und pumpe nicht mehr via Serial.println an mich geschickt sondern werden mit analogWrite[, Variable] genuzt