Servo PWM Signal auslesen

Hallo Leute,

ich habe eine Funksteuerung mit Empfänger, der Servo-PWM Signale ausgibt.
Ich würde diese Signale jetzt gerne mittels Arduino auslesen und mit den Werten dann weiter Arbeiten.

Mein erster Versuch war folgendes:

int pin = 2;
unsigned long value;

void setup()
{
  pinMode(pin, INPUT);
  Serial.begin(115200);
}

void loop()
{
  value = pulseIn(pin, HIGH);
  
  Serial.println(value );
  
}

Das funktioniert auch, bloß ist es leider sehr langsam.
Ich denke, pulseIn wird da irgendwie verzögern um die Pulse sauber einzulesen.

Ich muss 8 Servo-PWM Signale auslesen und diese dann weiterverarbeiten.
Wenn ich jetzt 8 mal pulsIn nutze, dann dauert das geschätzte 5000ms...

Habt ihr irgendwelche Ideen, wie das schneller geht?
Spontan würden mir da nur irgendwelche Interrupts einfallen, aber das verstehe ich meist gerade nur so, wenn ich was fertiges lese... selbst programmieren ist da schon ne andere Hausnummer.

Was ich vielleicht nicht ergänzen sollte:
Es ist nur einer der 8 Kanäle angeschlossen (zum testen).
Ich weiß, dass pulseIn hier eine gewisse Zeit sperrt und weil an 8 Kanälen kein PWM kommt, ist es hier auch langsamer, als wenn 8 Kanäle angeschlossen wären.
Ich könnte auch ein Timeout machen, aber das wäre auch nicht so das Wahre.
Daher hier in die Runde für die Profis :smiley:

Nein, pulsein wird nicht verzögert. Das ist ein extrem einfache Funktion: Warten auf Zustandsänderung. Zeit merken. Werten aus Zustandsänderung. Schauen wie lange es gedauert hat und zurückgeben. Das Sollte nur ein paar clock-Zyklen länger dauern als der Pulse, also nicht merkbar.
Allerdings blockiert es, so lange es auf die erste und zweite Änderung wartet (oder bis zum angegebenen Timeout, default Sekunde?). Kommt Dein Servosignal wirklich kontinuierlich? Sonst ist das Warten Dein Problem. In dem Fall würdest Du einen kurzen (zwischen ein und zwei Servo-Signalperioden) Timeout und loopst über die 8 Servos. wer zuerst kommt, wird dann halt zuerst ausgelesen.

Reicht Dein Englisch dafür?:
http://www.benripley.com/diy/arduino/three-ways-to-read-a-pwm-signal-with-arduino/
2. fällt weg, weil Du nciht genug Interrupts hast, aber 3. könnte was für Dich sein. Ist aber nicht ganz unkompliziert. Wenn Du damit leben kannst, dass die Messung mindestens 8*Periodenlänge dauert und währenddessen nix anderes passiert, mach mit pulseIn weiter.

Hallo,

naja, ob man nun verzögert oder blockieren dazu sagt ist Jacke wie Hose. Wenn es den restlichen Programmablauf nicht beeinflussen soll, sollte man es nicht verwenden. Ist ähnlich delay. Kann Sinn machen oder auch nicht. In 99% der Fälle soll ein Programm so schnell wie möglich seinen Ablauf durchrasseln und nur auf Ereignisse reagieren wenn sich wirklich etwas getan hat.

Für den Fall hier sehe ich nur den Einsatz und Verwendung von Interrupts als die Lösung.
a) wenn dir +/- 4µs egal sind, dann reicht es mit microseconds die Differenz zu bilden
b) ansonsten muss ein Timer Zählerstand herhalten

Edit:
irgendwie kam mir das Thema bekannt vor :slight_smile:
http://forum.arduino.cc/index.php?topic=433494.0
PWM Frequenz verdoppelt ausgeben - Deutsch - Arduino Forum Themen verwandt

ob man nun verzögert oder blockieren dazu sagt ist Jacke wie Hose

Nö, es ging ja darum, dass pulseIn das Ergebnis später rauswirft, als es könnte, um irgendeine ominöse Wertverbesserung zu machen. Das tut es nicht. Es gibt zurück, sobald es kann. Ja, es blockiert dabei, aber keine 5 Sekunden.
Wenn das PWM-Signal kontinuierlich kommt, dann blockiert es selbst bei 50Hz höchstens 8*20ms.

Hallo,

okay, dann hatte ich das falsch interpretiert. Ich vermute demzufolge aktuell, dass seine 8 Impulse über einen Zeitraum von 5s eintreffen und demzufolge alles 5s dauert. Kann das sein? Oder muss man die Phase drehen? Pulsein startet doch immer erst mit der Messung von Low>High. ? pulseIn() - Arduino Reference Wie sieht sein Signal aus? Irgendwie sowas vermute ich. Der obige Code blockiert jedenfalls so nichts.

Hallo ihr beiden,

also erstmal: ich meinte natürlich blockieren und nicht "verzögern".
Mein Programm hatte einfach 8 pulseIn funktionen, die auf unterschiedliche Eingänge gelauscht haben.

Da in der Beschreibung zu pulseIn steht, dass es per default eine Sekunde als timeout hat.
Wenn an einem Eingang was anliegt und an den anderen 7 nicht, dann kommt das ja so einigermaßen mit der langsamen Ausgabe hin.

Zu meinem Vorhaben:
WENN an einem Eingang ein PWM Signal angkommt, dann kontinuierlich, also nicht mit 5 Sekunden verzögerung oder so.
Ich habe momentan beim Testen noch nicht alle 8 Eingänge in Benutzung sondern nur einen.

Was ich letztendlich versuche ist, dass ich meine 8 Kanal Fernbedienung, dessen Empfänger 8 PWM Signale generiert, dazu bringe, mehrere Kanäle auf einen Kanal zu modulieren.
Es werden aktuell also 5 einfache On/Off switches auf einem Kanal moduliert.
Das ganze könnte man mit Multiplexing sogar noch bis ins unendliche erweitern, aber das wird jetzt erstmal zu viel.

Ich nutze also einen Kanal um 5 "Schalter-Kanäle" darauf zu modulieren.
Somit habe ich 7 echte und 5 Schalter Kanäle = 12 Kanäle.
Das reicht mir.

Problem ist nur, dass meine Flugsteuerung (Pixhawk) keine PWM Signale als Eingang akzeptiert.
Bisher habe ich daher PPM genutzt über einen Sketch, den ich kaum verstehe (umfanreich und vor allem mit Interrupts).

Das Problem an PPM ist, dass es ein 22ms gepulstes Signal ist und daher nur Platz für bis zu 8 Kanäle hat.
Daher steige ich nun auf SBUS von Futaba um.
Ich habe es hin bekommen ein SBUS-Signal zu generieren, allerdings habe ich noch 2 Probleme:

  1. Das einlesen der PWM Signale ist zu langsam.

  2. Ich hab es noch nicht hinbekommen das Arduino mit dessem SBUS Signal mit dem Pixhawk zu koppeln.

Danke für den Link ElCaron.
Ich werde mich mal an den "Pin Change Interrupts" mit dessen library versuchen.
Ohne library wäre mir zwar lieber, aber "External Interrupts" scheinen ja nur auf 2 Pins zu funktionieren.
Und selbst schreiben schaff ich nicht :smiley:

EDIT:
Vielen Dank!
Die library funktioniert wunderbar!

was genau macht denn "latest_interrupted_pin"? ich habe den jetzt überall drinne:

[...]
void PWM_1_rising()
{
  latest_interrupted_pin=PCintPort::arduinoPin;
  PCintPort::attachInterrupt(latest_interrupted_pin, &PWM_1_falling, FALLING);
  PWM_Channel_1_prev_time = micros();
}

void PWM_1_falling() {
  latest_interrupted_pin=PCintPort::arduinoPin;
  PCintPort::attachInterrupt(latest_interrupted_pin, &PWM_1_rising, RISING);
  PWM_Channel_1_Value = micros()-PWM_Channel_1_prev_time;
}

void PWM_2_rising()
{
  latest_interrupted_pin=PCintPort::arduinoPin;
  PCintPort::attachInterrupt(latest_interrupted_pin, &PWM_2_falling, FALLING);
  PWM_Channel_2_prev_time = micros();
}

void PWM_2_falling() {
  latest_interrupted_pin=PCintPort::arduinoPin;
  PCintPort::attachInterrupt(latest_interrupted_pin, &PWM_2_rising, RISING);
  PWM_Channel_2_Value = micros()-PWM_Channel_2_prev_time;
}
[...]

Ich muss mich hier doch nochmal melden.

Diese Methode ist leider zu ungenau.

Ich hab mit pulseIn() beispielsweise diese Werte hier für ein "statisches" Signal empfangen:
min 989
max 996
Differenz: 7

Diese Methode hier, mit Interrupts, funktioniert zwar, aber ich bekomme für das selbe Signal diese Werte:
min 964
max 1016
Differenz: 52

Das ist leider zu ungenau.
Die Differenz darf auf gar keinen Fall größer als 30 sein - besser im bereich von maximal 20, damit es sicherer ist - noch kleiner ist noch besser.

Der Code:

//----- Config -----//
// "PWM" input pins 
uint8_t PWM_Channel_1_Pin = 2;
uint8_t PWM_Channel_2_Pin = 3;
uint8_t PWM_Channel_3_Pin = 4;
uint8_t PWM_Channel_4_Pin = 5;
uint8_t PWM_Channel_5_Pin = 6;
uint8_t PWM_Channel_6_Pin = 7;
uint8_t PWM_Channel_7_Pin = 8;
uint8_t PWM_Channel_8_Pin = 9;
//----- Config end-----//


// time counter for sending the SBUS packet not too often
unsigned long previousMillis = 0;   


// ----- Interrupts for PWM captureing -----//
#include <PinChangeInt.h>

// storage for PWM values and time
volatile uint16_t PWM_Channel_1_Value = 0;
volatile uint16_t PWM_Channel_1_prev_time = 0;

volatile uint16_t PWM_Channel_2_Value = 0;
volatile uint16_t PWM_Channel_2_prev_time = 0;

volatile uint16_t PWM_Channel_3_Value = 0;
volatile uint16_t PWM_Channel_3_prev_time = 0;

volatile uint16_t PWM_Channel_4_Value = 0;
volatile uint16_t PWM_Channel_4_prev_time = 0;

volatile uint16_t PWM_Channel_5_Value = 0;
volatile uint16_t PWM_Channel_5_prev_time = 0;

volatile uint16_t PWM_Channel_6_Value = 0;
volatile uint16_t PWM_Channel_6_prev_time = 0;

volatile uint16_t PWM_Channel_7_Value = 0;
volatile uint16_t PWM_Channel_7_prev_time = 0;

volatile uint16_t PWM_Channel_8_Value = 0;
volatile uint16_t PWM_Channel_8_prev_time = 0;

uint8_t latest_interrupted_pin;
//Interrupt functions
void PWM_1_rising()
{
  latest_interrupted_pin=PCintPort::arduinoPin;
  PCintPort::attachInterrupt(latest_interrupted_pin, &PWM_1_falling, FALLING);
  PWM_Channel_1_prev_time = micros();
}

void PWM_1_falling() {
  latest_interrupted_pin=PCintPort::arduinoPin;
  PCintPort::attachInterrupt(latest_interrupted_pin, &PWM_1_rising, RISING);
  PWM_Channel_1_Value = micros()-PWM_Channel_1_prev_time;
}

void PWM_2_rising()
{
  latest_interrupted_pin=PCintPort::arduinoPin;
  PCintPort::attachInterrupt(latest_interrupted_pin, &PWM_2_falling, FALLING);
  PWM_Channel_2_prev_time = micros();
}

void PWM_2_falling() {
  latest_interrupted_pin=PCintPort::arduinoPin;
  PCintPort::attachInterrupt(latest_interrupted_pin, &PWM_2_rising, RISING);
  PWM_Channel_2_Value = micros()-PWM_Channel_2_prev_time;
}

void PWM_3_rising()
{
  latest_interrupted_pin=PCintPort::arduinoPin;
  PCintPort::attachInterrupt(latest_interrupted_pin, &PWM_3_falling, FALLING);
  PWM_Channel_3_prev_time = micros();
}

void PWM_3_falling() {
  latest_interrupted_pin=PCintPort::arduinoPin;
  PCintPort::attachInterrupt(latest_interrupted_pin, &PWM_3_rising, RISING);
  PWM_Channel_3_Value = micros()-PWM_Channel_3_prev_time;
}

void PWM_4_rising()
{
  latest_interrupted_pin=PCintPort::arduinoPin;
  PCintPort::attachInterrupt(latest_interrupted_pin, &PWM_4_falling, FALLING);
  PWM_Channel_4_prev_time = micros();
}


void PWM_4_falling() {
  latest_interrupted_pin=PCintPort::arduinoPin;
  PCintPort::attachInterrupt(latest_interrupted_pin, &PWM_4_rising, RISING);
  PWM_Channel_4_Value = micros()-PWM_Channel_4_prev_time;
}


void PWM_5_rising()
{
  latest_interrupted_pin=PCintPort::arduinoPin;
  PCintPort::attachInterrupt(latest_interrupted_pin, &PWM_5_falling, FALLING);
  PWM_Channel_5_prev_time = micros();
}

void PWM_5_falling() {
  latest_interrupted_pin=PCintPort::arduinoPin;
  PCintPort::attachInterrupt(latest_interrupted_pin, &PWM_5_rising, RISING);
  PWM_Channel_5_Value = micros()-PWM_Channel_5_prev_time;
}

void PWM_6_rising()
{
  latest_interrupted_pin=PCintPort::arduinoPin;
  PCintPort::attachInterrupt(latest_interrupted_pin, &PWM_6_falling, FALLING);
  PWM_Channel_6_prev_time = micros();
}

void PWM_6_falling() {
  latest_interrupted_pin=PCintPort::arduinoPin;
  PCintPort::attachInterrupt(latest_interrupted_pin, &PWM_6_rising, RISING);
  PWM_Channel_6_Value = micros()-PWM_Channel_6_prev_time;
}

void PWM_7_rising()
{
  latest_interrupted_pin=PCintPort::arduinoPin;
  PCintPort::attachInterrupt(latest_interrupted_pin, &PWM_7_falling, FALLING);
  PWM_Channel_7_prev_time = micros();
}

void PWM_7_falling() {
  latest_interrupted_pin=PCintPort::arduinoPin;
  PCintPort::attachInterrupt(latest_interrupted_pin, &PWM_7_rising, RISING);
  PWM_Channel_7_Value = micros()-PWM_Channel_7_prev_time;
}

void PWM_8_rising()
{
  latest_interrupted_pin=PCintPort::arduinoPin;
  PCintPort::attachInterrupt(latest_interrupted_pin, &PWM_8_falling, FALLING);
  PWM_Channel_8_prev_time = micros();
}

void PWM_8_falling() {
  latest_interrupted_pin=PCintPort::arduinoPin;
  PCintPort::attachInterrupt(latest_interrupted_pin, &PWM_8_rising, RISING);
  PWM_Channel_8_Value = micros()-PWM_Channel_8_prev_time;
}


int minimum = 0;
int maximum = 0;
int differenz = 0;

void print_debug()
{
  if (minimum == 0 || PWM_Channel_6_Value < minimum)
  {
    minimum = PWM_Channel_6_Value;
  }

  if (maximum == 0 || PWM_Channel_6_Value > maximum)
  {
    maximum = PWM_Channel_6_Value;
  }
  
  differenz = maximum-minimum;
  
  Serial.print("Minimum: ");
  Serial.print(minimum);
  Serial.print(" Maximum: ");
  Serial.print(maximum);
  Serial.print(" Differenz: ");
  Serial.print(differenz);
  Serial.print(" Aktuell: ");
  Serial.println(PWM_Channel_6_Value);
}


void setup()
{
  pinMode(PWM_Channel_1_Pin, INPUT); digitalWrite(PWM_Channel_1_Pin, HIGH);
  pinMode(PWM_Channel_2_Pin, INPUT); digitalWrite(PWM_Channel_2_Pin, HIGH);
  pinMode(PWM_Channel_3_Pin, INPUT); digitalWrite(PWM_Channel_3_Pin, HIGH);
  pinMode(PWM_Channel_4_Pin, INPUT); digitalWrite(PWM_Channel_4_Pin, HIGH);
  pinMode(PWM_Channel_5_Pin, INPUT); digitalWrite(PWM_Channel_5_Pin, HIGH);
  pinMode(PWM_Channel_6_Pin, INPUT); digitalWrite(PWM_Channel_6_Pin, HIGH);
  pinMode(PWM_Channel_7_Pin, INPUT); digitalWrite(PWM_Channel_7_Pin, HIGH);
  pinMode(PWM_Channel_8_Pin, INPUT); digitalWrite(PWM_Channel_8_Pin, HIGH);
  
  PCintPort::attachInterrupt(PWM_Channel_1_Pin, &PWM_1_rising, RISING);
  PCintPort::attachInterrupt(PWM_Channel_2_Pin, &PWM_2_rising, RISING);
  PCintPort::attachInterrupt(PWM_Channel_3_Pin, &PWM_3_rising, RISING);
  PCintPort::attachInterrupt(PWM_Channel_4_Pin, &PWM_4_rising, RISING);
  PCintPort::attachInterrupt(PWM_Channel_5_Pin, &PWM_5_rising, RISING);
  PCintPort::attachInterrupt(PWM_Channel_6_Pin, &PWM_6_rising, RISING);
  PCintPort::attachInterrupt(PWM_Channel_7_Pin, &PWM_7_rising, RISING);
  PCintPort::attachInterrupt(PWM_Channel_8_Pin, &PWM_8_rising, RISING);

  Serial.begin(115200);
}


void loop()
{
   print_debug();
}

Verstehe ich das jetzt richtig, du sendest 8 Kanäle ppm zum Empfänger, der gibt 8 mal pwm aus. das liest du mit dem Arduino ein und machst sbus draus?

Ja das ist richtig :smiley:
Ich nehme sozusagen alles mit, was die Funkstandards hergeben :wink:

Falls du einen Weg kennst das Signal im Empfänger (per hack) direkt abzugreifen, dann immer her damit :slight_smile:

Es ist einfacher, einen passenden Empfänger zu kaufen.

Aber mit nem Oszi müsste das Signal zu finden sein