Variable PWM messen

Moin Moin,

ich bin gerade dabei mir einen Datenlogger für ein PWM Signal zu bauen.

Kurz ein paar Eckdaten zum Signal:
Es handelt sich hierbei um ein Rechtecksignal mit einer Frequenz von etwa 100 Hz. Das PWM-Signal kann feste Werte annehmen oder pendeln. Das Pendeln geschieht so zwischen 45 - 55 % in einer Zeit von etwa 1s pro Hub. Ich habe ein Oszilloskop Bild mit angehängt. Die Spannungswerte oben rechts bitte ignorieren, diese sind falsch. Die restlichen Daten auf dem Bild sind in Ordnung.

Das Signal habe ich mit einem Transisotor auf die 5V des Arduino angepasst. Feste PWM Werte kann ich sehr gut erfassen, leider funktioniert mein Code noch nicht bei einem pendelnden Signal. Hierbei kommt es häufig zu gleichen PWM-Werten, obwohl das PWM Signal definitiv unterschiedliche Werte annimmt. Zusätzlich tauchen häufig unplausible Werte auf wie 99 % oder ähnliches.

Mein Arduinoprogramm schafft es nicht jede Periode auszuwerten, aber mit einer derzeitigen Messzeit von 100 ms sollte es trotzdem ausreichen um ein pendeln der Werte zu erkennen.
Hier ist der Codeauszug aus meiner Loop Funktion und meinem Inerrupt.
Die Funktion logger schreibt nach jedem Messwert den Wert auf eine SD Karte.
So sieht ein Auszug aus:

Zeit in ms, PWM in %
46732.00, 49.27
46824.00, 49.25
46916.00, 49.27
47008.00, 49.27
47100.00, 49.29
47193.00, 49.26
47285.00, 49.27
47377.00, 49.25
47469.00, 49.31
47561.00, 49.24
47653.00, 89.75
47725.00, 49.27
47817.00, 49.27
47909.00, 49.24
48002.00, 49.26

Erwarten würde ich Werte zwischen 45 - 55% und eine steigende oder fallende Folge.

void loop() {

  // Einlesen der Zeiten und Berechnung des Tastverhältnis
  if (calc == 1) {
    dutyWerte[i] = (highTime / (lowTime + highTime)) * 100;
    measTime = millis();
    // Weiterverarbeiten der Daten
    logger();
    i++;
    calc = 0;
  }
  if (i == zahlAvg) {
    average();
    if (digitalRead(7) == HIGH) {
      disp();
    }
    i = 0;
  }

}
void pin_pwm() {
  if (digitalRead(2) == HIGH) {
    lowTime = micros() - startmicros;
    startmicros = micros();
    calc = 1;
  } else {
    highTime = micros() - startmicros;
    startmicros = micros();
    calc = 0;
  }
}

Habt ihr eine Idee warum ständig ein ähnlicher Wert gemessen wird?

Gruß Nevo

Zeig bitte den ganzen Sketch, so ist das ne Rumraterei.

Hey,

klar kein Problem :slight_smile: Das ist der gesamte Code mit Logger und Display.

#include <Arduino.h>
#include <U8x8lib.h>
#include <SPI.h>
#include <SD.h>

volatile int interrupcion = 0;
float dutyWerte[50]; //max. 255 wegen i
float dutyAvg = 0;
byte dutyMax = 0; //Bestimmung Maximal Wert
byte dutyMin = 0; //Bestimmung Minimal Wert
int zahlAvg = 50; //Anzahl der Werte für die Mittelwertberechnung
int bar = 0; //Variable für BarGraph
float sum = 0;   // Summe für Average Berechnung
float measTime = 0;
byte j = 0;
byte i = 0; //max. 255
byte calc = 0;
byte range = 2; //Eingrenzung von Bereichen
float highTime = 0;
float lowTime = 0;
volatile float startmicros = 0;
float start = 0;  //Laufzeit messen
float loopTime = 0; //Laufzeit messen

U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE);   // OLEDs without Reset of the Display


void setup() {
  Serial.begin(9600);

  //SD Karte einbinden
  Serial.print("Initializing SD card...");
  // see if the card is present and can be initialized:
  if (!SD.begin(10)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    while (1);
  }
  Serial.println("card initialized.");

  // Display vordefinieren
  u8x8.begin();
  u8x8.setPowerSave(0);
  u8x8.setFont(u8x8_font_chroma48medium8_r);
  u8x8.drawString(0, 0, "Actual");
  u8x8.drawString(15, 0, "%");
  u8x8.drawString(0, 1, "Maximum");
  u8x8.drawString(15, 1, "%");
  u8x8.drawString(0, 2, "Average");
  u8x8.drawString(15, 2, "%");
  u8x8.drawString(0, 3, "Minimum");
  u8x8.drawString(15, 3, "%");
  u8x8.drawString(0, 4, "40   50   60");
  u8x8.drawString(0, 5, "|....|....|");

  // Pins definieren
  pinMode(2, INPUT);  // Input PWM
  pinMode(7, INPUT);  // Input ob Display verwendet wird oder nicht
  pinMode(9, OUTPUT); // Output PWM zum Testen
  analogWrite(9, 128); //Festlegen des Tastverhältnis
  attachInterrupt(interrupcion, pin_pwm, CHANGE);
  delay(1000);
}


void loop() {

  // Einlesen der Zeiten und Berechnung des Tastverhältnis
  start = micros(); //Startzeit speichern
  if (calc == 1) {
    dutyWerte[i] = (highTime / (lowTime + highTime)) * 100;
    measTime = millis();   
    
   // Wert auf Bargraph anzeigen
    bar = map(dutyWerte[i], 40, 60, 0, 10);
    if (digitalRead(7) == HIGH) {
      for (int point = 0; point < 15; point++) {
        if (point == bar) {
          u8x8.setCursor(point, 6);
          u8x8.print("|");
          u8x8.setCursor(point, 6);
          u8x8.print(" ");
        }
      }
      u8x8.setCursor(10, 0);
      u8x8.print(dutyWerte[i]);
    }
    
    // Weiterverarbeiten der Daten
    logger();
    i++;
    calc = 0;
    loopTime = micros() - start; //Loop Time berechnen
    Serial.print("Loop Time"); Serial.println(loopTime);
  }
  //Average berechnen und Ausgabe auf dem Display wenn Pin 7 = High
  if (i == zahlAvg) {
    average();
    if (digitalRead(7) == HIGH) {
      disp();
    }
    i = 0;
  }

}

void logger() {

  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  File dataFile = SD.open("datalog.txt", FILE_WRITE);

  //if the file is available, write to it:
  if (dataFile) {
    dataFile.print(measTime); dataFile.print(","); dataFile.print(dutyWerte[i]); dataFile.print(","); dataFile.print(dutyAvg);
    dataFile.println();
    dataFile.close();
    //print to the serial port too:
    //Serial.print(measTime); Serial.print(","); Serial.print(dutyWerte[i]); Serial.print(","); Serial.print(dutyAvg); 
    //Serial.println();
  }
  // if the file isn't open, pop up an error:
  else {
    Serial.println("error opening datalog.txt");
  }
}

void disp() {
  //Min, Max und Average Werte auf dem Display anzeigen
  u8x8.setCursor(10, 1);
  u8x8.print(dutyMax);
  u8x8.setCursor(10, 2);
  u8x8.print(dutyAvg);
  u8x8.setCursor(10, 3);
  u8x8.print(dutyMin);
}

void average() {
  //Average Wert berechnen
  sum = 0;
  j = 0;
  dutyMax = 0;
  dutyMin = 100;
  for (j = 0; j < zahlAvg; j++) {
    sum = sum + dutyWerte[j];
    dutyMax = max(dutyWerte[j], dutyMax);
    dutyMin = min(dutyWerte[j], dutyMin);
  }
  dutyAvg = sum / zahlAvg;
}

void pin_pwm() {
  if (digitalRead(2) == HIGH) {
    lowTime = micros() - startmicros;
    startmicros = micros();
    calc = 1;
  } else {
    highTime = micros() - startmicros;
    startmicros = micros();
    calc = 0;
  }
}

Hi

Ich verstehe Dein Sketch nicht. Wenn Du nicht schnell genug durch die loop() kommst, um auf den Pegelwechsel zu reagieren, dann nimm einen Interrupt und merke Dir dort die aktuelle Zeit. Wobei 100Hz nun Alles andere, als schnell ist. Wenn Du nicht jeden PWM-Impuls auf's Display bringen musst, dann lies beide Anteile nacheinander ein - pulseIn() sollte Dir hier helfen - Das blockiert zwar, aber nach spätestens 10ms (100Hz) wäre irgend was erfasst und Es ginge weiter. Zum Aktualisieren des Display sollte Das dicke schnell genug sein.

MfG

Anmerkung: micros() springt in 4er-Schritten, also nicht wundern, wenn das Ergebnis 'nicht ganz rund' wird.

Mein Sketch arbeitet schon mit einem Interrupt. Im code ist das die unterste Funktion void pin_pwm()

void pin_pwm reagiert auf einen Flankenwechsel und speichert damit die High oder die Low Zeit. Mit der calc-Variable möchte ich, dass eine Berechnung im Hauptprogramm nur dann stattfindet wenn eine neue high und eine neue low Zeit kam.

100 Hz ist nicht wirklich schnell, deswegen verstehe ich auch nicht warum das nicht klappt. Statische Zustände wie zum Beispiel 50% klappen super. Sobald das Signal aber anfängt zu pendeln, dann kommt Mist raus. Die gleiche Problematik hatte ich auch mit der pulseIn() Funktion.

Das Signal am Eingang sieht auch gut aus (siehe Anhang im Anfangspost). Deswegen vermute ich, dass mein Programm nicht die beiden Zeiten von einer Periode erwischt. Aber erklären kann ich mir das nicht.

Was meinst du mit micros() springt in 4-Schritten? Edit: Ich habe es gerade nochmal nachgelesen. Für mich ist diese Ungenauigkeit akzeptabel, weil ich eh im millisekunden Bereich bin.

Schonmal Danke für deine Antwort.

Hallo,

was hast du für einen Arduino? Was ist interrupcion? Ein Wert oder ein Pin?

float highTime = 0;
float lowTime = 0;

Diese Variablen müssen volatile sein und sollten uint32_t sein.

Alle Zugriffe auf diese Variablen müssen atomar ausgeführt werde, also mit abgeschalteten Interrupts.

volatile float startmicros = 0;
volatile int interrupcion = 0;

Beide Variablen sollten nicht volatile sein.

interrupcion solltest du gegen eine Pinnummer und digitalPinToInterrupt() im attachInterrupt tauschen.

Doc_Arduino: Hallo,

was hast du für einen Arduino? Was ist interrupcion? Ein Wert oder ein Pin?

Ich habe einen Arduino Nano. Öhm...interrupcion sollte eigentlich ein Pin sein. Aber warum das 0 ist, weiß ich gerade nicht. Ich habe meine Interrupt Funktion nochmal wie unter Arduino reference beschrieben angepasst.

Ich habe die Variablen und die Interrupt Anweisung wie Whandall schrieb angepasst. Zusätzlich habe ich während der Berechnung die Interrupt Funktion abgeschaltet und danach wieder aktiviert.

Leider funktioniert die Auswertung immernoch nicht wie gewollt.

Beispiel: Wenn ich mit einem digitalen Multimeter das Tastverhältnis messe, dann ist ein pendel zu erkennen. Aufgrund des digitalen Displays und der zu langsamen Zeit nicht ganz sauber, aber in Ordnung. Mit einem analogen Multimeter würde man den Zeiger wunderbar pendeln sehen. Wenn ich mit dem Arduino messe, dann bekomme ich fast nur feste Werte. Selbst wenn ich ohne Display messe und einen Messzeit von 30 ms habe. Ich habe mir nochmal die beiden Zeiten "highTime" und "lowTime" mit plotten lassen. Zusammengezählt passt die Frequenz mit der erwarteten überein. Das Tastverhältnis allerdings nicht, weil auch die Zeiten nahezu konstant sind. Ich vermute, dass entweder das Signal doch nicht richtig am Pin ankommt, was ich mir aber nicht vorstellen kann. Siehe auch das Oszilloskop Bild direkt an dem Pin. Oder der Arduino misst nicht exakt, ich weiß aber nicht warum.

Schwierig, schwierig...

Nevo2: Ich habe die Variablen und die Interrupt Anweisung wie Whandall schrieb angepasst. Zusätzlich habe ich während der Berechnung die Interrupt Funktion abgeschaltet und danach wieder aktiviert.

Ohne den neuen Kode zu posten.

Nevo2: Schwierig, schwierig...

Ohne Kode ist mir das zu schwierig, bin heute nicht in Ratelaune.

Ups, entschuldige bitte. Hast ja recht, ohne ist immer schwierig.
Hier ist der überarbeitete Sketch.

#include <Arduino.h>
#include <U8x8lib.h>
#include <SPI.h>
#include <SD.h>

const byte interruptPin = 2;
float dutyWerte[50]; //max. 255 wegen i
float dutyAvg = 0;
byte dutyMax = 0; //Bestimmung Maximal Wert
byte dutyMin = 0; //Bestimmung Minimal Wert
int zahlAvg = 50; //Anzahl der Werte für die Mittelwertberechnung
int bar = 0; //Variable für BarGraph
float sum = 0;   // Summe für Average Berechnung
float measTime = 0;
byte j = 0;
byte i = 0; //max. 255
byte calc = 0;
byte range = 2; //Eingrenzung von Bereichen
volatile uint32_t highTime = 0;
volatile uint32_t lowTime = 0;
int high = 0;
int low = 0;

uint32_t startmicros = 0;
float start = 0;  //Laufzeit messen
float loopTime = 0; //Laufzeit messen

U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE);   // OLEDs without Reset of the Display


void setup() {
  Serial.begin(9600);

  //SD Karte einbinden
  Serial.print("Initializing SD card...");
  // see if the card is present and can be initialized:
  if (!SD.begin(10)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    while (1);
  }
  Serial.println("card initialized.");

  File dataFile = SD.open("datalog.txt", FILE_WRITE);
  //if the file is available, write to it:
  if (dataFile) {
    dataFile.println("Neue Messreihe");
    dataFile.close();
  }
  
  // Display vordefinieren
  u8x8.begin();
  u8x8.setPowerSave(0);
  u8x8.setFont(u8x8_font_chroma48medium8_r);
  u8x8.drawString(0, 0, "Actual");
  u8x8.drawString(15, 0, "%");
  u8x8.drawString(0, 1, "Maximum");
  u8x8.drawString(15, 1, "%");
  u8x8.drawString(0, 2, "Average");
  u8x8.drawString(15, 2, "%");
  u8x8.drawString(0, 3, "Minimum");
  u8x8.drawString(15, 3, "%");
  u8x8.drawString(0, 4, "40   50   60");
  u8x8.drawString(0, 5, "|....|....|");

  // Pins definieren
  pinMode(2, INPUT);  // Input PWM
  pinMode(7, INPUT);  // Input ob Display verwendet wird oder nicht
  pinMode(9, OUTPUT); // Output PWM zum Testen
  analogWrite(9, 128); //Festlegen des Tastverhältnis
  attachInterrupt(digitalPinToInterrupt(interruptPin), pin_pwm, CHANGE);
}


void loop() {

  // Einlesen der Zeiten und Berechnung des Tastverhältnis
  start = micros(); //Startzeit speichern
  if (calc == 1) {
    noInterrupts();
    dutyWerte[i] = ((float)highTime / (float)(lowTime + highTime)) * 100;
    high = highTime;
    low = lowTime;
    highTime = 1;
    lowTime = 1;
    interrupts();
    measTime = millis();
    Serial.println(highTime); Serial.println(lowTime);

    // Wert auf Bargraph anzeigen
    bar = map(dutyWerte[i], 40, 60, 0, 10);
    if (digitalRead(7) == HIGH) {
      for (int point = 0; point < 15; point++) {
        if (point == bar) {
          u8x8.setCursor(point, 6);
          u8x8.print("|");
          u8x8.setCursor(point, 6);
          u8x8.print(" ");
        }
      }
      u8x8.setCursor(10, 0);
      u8x8.print(dutyWerte[i]);
    }

    // Weiterverarbeiten der Daten
    logger();
    i++;
    calc = 0;
    loopTime = micros() - start; //Loop Time berechnen
    Serial.print("Loop Time"); Serial.println(loopTime);
  }
  //Average berechnen und Ausgabe auf dem Display wenn Pin 7 = High
  if (i == zahlAvg) {
    average();
    if (digitalRead(7) == HIGH) {
      disp();
    }
    i = 0;
  }

}

void logger() {
  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  File dataFile = SD.open("datalog.txt", FILE_WRITE);

  //if the file is available, write to it:
  if (dataFile) {
    dataFile.print(measTime); dataFile.print(","); dataFile.print(dutyWerte[i]); dataFile.print(","); dataFile.print(dutyAvg); 
    dataFile.print(","); dataFile.print(high); dataFile.print(","); dataFile.print(low);
    dataFile.println();
    dataFile.close();
    //print to the serial port too:
    //Serial.print(measTime); Serial.print(","); Serial.print(dutyWerte[i]); Serial.print(","); Serial.print(dutyAvg);
    //Serial.println();
  }
  // if the file isn't open, pop up an error:
  else {
    Serial.println("error opening datalog.txt");
  }
}

void disp() {
  //Min, Max und Average Werte auf dem Display anzeigen
  u8x8.setCursor(10, 1);
  u8x8.print(dutyMax);
  u8x8.setCursor(10, 2);
  u8x8.print(dutyAvg);
  u8x8.setCursor(10, 3);
  u8x8.print(dutyMin);
}

void average() {
  //Average Wert berechnen
  sum = 0;
  j = 0;
  dutyMax = 0;
  dutyMin = 100;
  for (j = 0; j < zahlAvg; j++) {
    sum = sum + dutyWerte[j];
    dutyMax = max(dutyWerte[j], dutyMax);
    dutyMin = min(dutyWerte[j], dutyMin);
  }
  dutyAvg = sum / zahlAvg;
}

void pin_pwm() {
  if (digitalRead(2) == HIGH) {
    lowTime = micros() - startmicros;
    startmicros = micros();
    calc = 1;
  } else {
    highTime = micros() - startmicros;
    startmicros = micros();
    calc = 0;
  }
}

Hallo,

das muss man erstmal auf was testbares runterbrechen und paar Korrekturen vornehmen. In loop übergibts du unsigned Werte signed Variablen. startmicros ist nicht volatile. Das average muss auch erstmal raus um den Blick frei zubekommen.

float dutyWerte[50]; //max. 255 wegen i

Warum float, was soll der Kommentar bedeuten?

int zahlAvg = 50; //Anzahl der Werte für die Mittelwertberechnung

Ändert sich diese Anzahl während der Laufzeit? Wenn nein, könnte das gut ein const byte sein, oder?
Besser wäre es den Wert aus der Arraydefinition abzuleiten.

const byte zahlAvg = sizeof(dutyWerte)/sizeof(dutyWerte[0]);
byte i = 0; //max. 255

Der Kommentar ist überflüssig, der Name der Variable unglücklich.

int high = 0;
int low = 0;

Die Werte würden gerne uint32_t werden.

Serial.begin(9600);

Wozu dient das Schneckentempo? Hast du noch ein altes Terminal rumstehen?

pinMode(2, INPUT);  // Input PWM

Warum 2 und nicht interruptPin?

Warum gibt es keine vernüftigen Namen für die Pins 7 und 9?

 noInterrupts();
    dutyWerte[i] = ((float)highTime / (float)(lowTime + highTime)) * 100;
    high = highTime;
    low = lowTime;
    highTime = 1;
    lowTime = 1;
    interrupts();
    measTime = millis();
    Serial.println(highTime); Serial.println(lowTime);

Nicht ganz.

 noInterrupts();
    high = highTime;
    low = lowTime;
    interrupts();
    dutyWerte[i] = ((float)high / (float)(low + high)) * 100;
    measTime = millis();
    Serial.println(high); Serial.println(low);

Das rechnet immer noch mit float, hier geht es aber zunächst mal um die Zugriffe/Reihenfolge.
Halte die Zeit mit abgeschalteten Interrupts so kurz wie möglich.

calc = 0;

Das sollte nur von der ISR verwaltet werden. Wenn es im nicht-ISR Teil benutzt wird, muss es volatile werden.

if (i == zahlAvg) {

Warum berechnest du den Mittelwert nicht immer?
Du benutze deinen Wertepuffer zyklisch, es sind immer die letzten x Werte drin.

Du versuchst (z.B. mit calc) einen Ablauf zusammenzunageln, anstatt einfach nur die anfallenden Werte auszuwerten.
Nach jedem ISR Aufruf könntest du einen Messwert in die Liste eintragen,
dazu müsstest du dort nur ein Flag setzen, das beim Auslesen zurückgesetzt wird.
calc ist dafür ungeeignet, da es den Zustand beschreibt, kein Ereignis,
außerdem ist es überflüssig da es den Pin Zustand nachbildet.
Genauso könntest du aus der ISR das Flag nur jedes zweite Mal setzen, falls du auf neue LOW und HIGH bestehst.
Wenn du lieber zu festen Zeitpunkten samplen willst, verknüpfe den Flag-Test mit einem millis() Test.

Lass die ISR einfach laufen, nimm dir einen Satz Werte wenn du sie haben willst.

Du solltest deinen Programmablauf grundsätzlich ändern.

Hallo,

das wäre meine Basis zum testen. Testen kann ich morgen in Ruhe. Dann schauen wir was der Ball macht, lassen dutycycle ändern und schauen wie die high/low Zeiten reagieren … kannst du (Nevo) auch machen … :wink:

// https://forum.arduino.cc/index.php?topic=597321.0

#include <util/atomic.h>    // für cli() und sei() mit SREG Sicherung

const byte pin_interrupt = 2;
const byte pin_pwm_output = 9;
byte duty_cycle = 128;

volatile bool flag_ready = false;
volatile unsigned long highTime = 0;
volatile unsigned long lowTime = 0;



void setup() {
  Serial.begin(250000);

  // Pins definieren
  pinMode(pin_interrupt, INPUT);            // Input PWM
  pinMode(pin_pwm_output, OUTPUT);          // Output PWM zum Testen
  analogWrite(pin_pwm_output, duty_cycle);  // Festlegen des Tastverhältnis
  attachInterrupt(digitalPinToInterrupt(pin_interrupt), pwm_ISR, CHANGE);
}


void loop()
{

  Ausgabe (500);

}


void pwm_ISR()
{
  static unsigned long startmicros = 0;

  if (digitalRead(pin_interrupt) == HIGH) {
    lowTime = micros() - startmicros;
    startmicros = micros();
    //flag_ready = true;
  }
  else {
    highTime = micros() - startmicros;
    startmicros = micros();
  }
}


void Ausgabe (unsigned int interval)
{
  static unsigned long last_ms = 0;
  unsigned long ms = millis();

  if (ms - last_ms <= interval)  return;

  last_ms = ms;

  unsigned long temp_highTime = 0;
  unsigned long temp_lowTime = 0;

  ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
    temp_highTime = highTime;
    temp_lowTime = lowTime;
  }

  Serial.print(temp_highTime);
  Serial.print('\t');
  Serial.println(temp_lowTime);

}

Mit der Methode kann man leider 100% nicht wahrnehmen,
da finden keine Wechsel statt die man timen könnte.
Aber man kann natürlich merken, dass die ISR niemals aufgerufen wurde.

const byte interruptPin = 2;
const byte  pwmPin = 2;
uint16_t dutyWerte[50]; //max. 255 wegen i
const byte zahlAvg = sizeof(dutyWerte) / sizeof(dutyWerte[0]);
uint16_t dutyMax;
uint16_t dutyMin;
uint16_t dutyAvg;
uint32_t high;
uint32_t low;
uint16_t currentPercent;

volatile bool newData;
volatile uint32_t highTime = 0;
volatile uint32_t lowTime = 0;

void setup() {
  Serial.begin(250000);
  pinMode(interruptPin, INPUT);  // PWM
  pinMode(9, OUTPUT); // Output PWM zum Testen
  analogWrite(9, 64); //Festlegen des Tastverhältnis
  attachInterrupt(digitalPinToInterrupt(interruptPin), pin_pwm, CHANGE);
}

void loop() {
  static byte currentSample;
  static uint32_t lastPrint;
  uint32_t topLoop = millis();
  if (newData) {
    noInterrupts();
    high = highTime;
    low = lowTime;
    interrupts();
    newData = false;
    currentPercent = high * 1000 / (low + high);
    dutyWerte[currentSample++] = currentPercent;
    if (currentSample >= zahlAvg) {
      currentSample = 0;
    }
    average();
  }
  if (topLoop - lastPrint > 1000) {
    lastPrint = topLoop;
    Serial.print(F("High "));
    Serial.print(high);
    Serial.print(F(", Low "));
    Serial.print(low);
    Serial.print(F(", "));
    printPercent(currentPercent);
    Serial.print(F("%, Min "));
    printPercent(dutyMin);
    Serial.print(F("%, Avg "));
    printPercent(dutyAvg);
    Serial.print(F("%, Max "));
    printPercent(dutyMax);
    Serial.println();
  }
}

void average() {
  uint32_t sum = 0;
  dutyMax = 0;
  dutyMin = 10000;
  for (byte j = 0; j < zahlAvg; j++) {
    sum += dutyWerte[j];
    dutyMax = max(dutyWerte[j], dutyMax);
    dutyMin = min(dutyWerte[j], dutyMin);
  }
  dutyAvg = sum / zahlAvg;
}

void pin_pwm() {
  static uint32_t startMicros;
  uint32_t topISR = micros();
  if (digitalRead(interruptPin)) {
    lowTime = topISR - startMicros;
  } else {
    highTime = topISR - startMicros;
  }
  startMicros = topISR;
  newData = true;
}

void printPercent(uint16_t what) {
  if (what < 100) {
    Serial.write(' ');
  }
  Serial.print(what / 10);
  Serial.write('.');
  Serial.print(what % 10);
}
High 512, Low 1528, 25.0%, Min 25.0%, Avg 25.0%, Max 25.0
High 512, Low 1528, 25.0%, Min 25.0%, Avg 25.0%, Max 25.0
High 512, Low 1528, 25.0%, Min 25.0%, Avg 25.0%, Max 25.0
High 512, Low 1528, 25.0%, Min 25.0%, Avg 25.0%, Max 25.0
High 512, Low 1528, 25.0%, Min 25.0%, Avg 25.0%, Max 25.0
High 512, Low 1528, 25.0%, Min 25.0%, Avg 25.0%, Max 25.0
High 512, Low 1528, 25.0%, Min 25.0%, Avg 25.0%, Max 25.0
High 512, Low 1528, 25.0%, Min 25.0%, Avg 25.0%, Max 25.0
High 512, Low 1528, 25.0%, Min 25.0%, Avg 25.0%, Max 25.0
High 512, Low 1528, 25.0%, Min 25.0%, Avg 25.0%, Max 25.0
High 512, Low 1528, 25.0%, Min 25.0%, Avg 25.0%, Max 25.0
High 512, Low 1528, 25.0%, Min 25.0%, Avg 25.0%, Max 25.0
High 512, Low 1528, 25.0%, Min 25.0%, Avg 25.0%, Max 25.0
High 512, Low 1528, 25.0%, Min 25.0%, Avg 25.0%, Max 25.0
High 512, Low 1528, 25.0%, Min 25.0%, Avg 25.0%, Max 25.0
High 512, Low 1528, 25.0%, Min 25.0%, Avg 25.0%, Max 25.0
High 512, Low 1528, 25.0%, Min 25.0%, Avg 25.0%, Max 25.0
High 512, Low 1528, 25.0%, Min 25.0%, Avg 25.0%, Max 25.0
High 512, Low 1528, 25.0%, Min 25.0%, Avg 25.0%, Max 25.0
High 512, Low 1528, 25.0%, Min 25.0%, Avg 25.0%, Max 25.0
High 512, Low 1528, 25.0%, Min 25.0%, Avg 25.0%, Max 25.0

An beiden Grenzen der PWM (1 oder 254) reicht die Zeit nicht aus um den kurzen Teil der Sequenz zu ermitteln.
Bei der Ausgabe fehlt am Ende ein %, aber das rüste ich jetzt hier nicht mehr nach.

Beim Testen des dieses Sketches hatte ich zunächst nur ziemlich gleichmäßige Ergebnisse mit einzelnen Ausreißern.

Ungeschickterweise hatte ich ein zweipoliges Kabel zum Verbinden von Pin 2 und 9 genommen, aber auf einer Seite den falschen Pol erwischt.

Die ziemlich guten Phasen waren also einfach über die kurzen parallelen Leitungen eingekoppelt, berührt man die Verbindung mit der Hand, spielen die Werte verrückt.

High 16, Low 2024,  0.7%, Min  0.1%, Avg  0.6%, Max  0.7%
High 16, Low 2024,  0.7%, Min  0.1%, Avg  0.6%, Max  0.7%
High 16, Low 2024,  0.7%, Min  0.1%, Avg  0.6%, Max  0.7%
High 16, Low 2024,  0.7%, Min  0.7%, Avg  0.7%, Max  0.7%
High 16, Low 2024,  0.7%, Min  0.7%, Avg  0.7%, Max  0.7%
High 16, Low 2024,  0.7%, Min  0.2%, Avg  0.6%, Max  0.7%
High 16, Low 2024,  0.7%, Min  0.7%, Avg  0.7%, Max  0.7%
High 16, Low 1596,  0.9%, Min  0.1%, Avg 11.8%, Max 87.3%
High 9600, Low 10008, 48.9%, Min  0.1%, Avg 27.3%, Max 97.7%
High 16, Low 388,  3.9%, Min  0.1%, Avg 31.9%, Max 98.8%
High 9456, Low 744, 92.7%, Min  0.1%, Avg 30.0%, Max 97.9%
High 9600, Low 10368, 48.0%, Min  0.1%, Avg 27.8%, Max 96.4%
High 9616, Low 10020, 48.9%, Min  0.1%, Avg 30.3%, Max 98.9%
High 16, Low 10160,  0.1%, Min  0.1%, Avg 30.0%, Max 98.0%
High 16, Low 544,  2.8%, Min  0.1%, Avg 31.5%, Max 99.0%
High 16, Low 10104,  0.1%, Min  0.1%, Avg 29.9%, Max 98.0%
High 16, Low 2024,  0.7%, Min  0.0%, Avg  0.5%, Max  0.7%
High 16, Low 14264,  0.1%, Min  0.0%, Avg  0.5%, Max  0.7%
High 16, Low 2024,  0.7%, Min  0.1%, Avg  0.5%, Max  0.7%
High 16, Low 2024,  0.7%, Min  0.1%, Avg  0.6%, Max  0.7%
High 16, Low 2024,  0.7%, Min  0.1%, Avg  0.6%, Max  0.7%
High 16, Low 2024,  0.7%, Min  0.1%, Avg  0.6%, Max  0.7%

Hallo,

meine Variante funktioniert auch. :slight_smile:

// https://forum.arduino.cc/index.php?topic=597321.0

#include <util/atomic.h>    // für cli() und sei() mit SREG Sicherung

const byte pin_interrupt = 2;
const byte pin_pwm_output = 9;

byte duty_cycle = 128;                      // Startwert

//volatile bool flag_ready = false;
volatile unsigned long highTime = 0;
volatile unsigned long lowTime = 0;



void setup() {
  Serial.begin(250000);

  pinMode(pin_interrupt, INPUT);            // Input PWM
  pinMode(pin_pwm_output, OUTPUT);          // Output PWM zum Testen
  analogWrite(pin_pwm_output, duty_cycle);  // Festlegen des Tastverhältnis
  attachInterrupt(digitalPinToInterrupt(pin_interrupt), pwm_ISR, CHANGE);
}


void loop()
{
  Ausgabe(500);                 // intervall

  change_DutyCycle(1000, 20);   // intervall, Abstufung
}


void pwm_ISR()
{
  static unsigned long startmicros = 0;

  if (digitalRead(pin_interrupt) == HIGH) {
    lowTime = micros() - startmicros;
    startmicros = micros();
  }
  else {
    highTime = micros() - startmicros;
    startmicros = micros();
  }
}


void Ausgabe (unsigned int interval)
{
  static unsigned long last_ms = 0;
  unsigned long ms = millis();

  if (ms - last_ms <= interval)  return;
  last_ms = ms;

  unsigned long temp_highTime = 0;
  unsigned long temp_lowTime = 0;

  ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
    temp_highTime = highTime;
    temp_lowTime = lowTime;
  }

  Serial.print(temp_highTime);
  Serial.print('\t');
  Serial.print(temp_lowTime);
  Serial.print('\t');
  Serial.print((100.0/(temp_highTime+temp_lowTime))*temp_highTime); Serial.print("%");
  Serial.print('\t');
  Serial.print(1.0/(temp_highTime+temp_lowTime)*1000000); Serial.print("Hz");
  Serial.print('\t');
  Serial.println(duty_cycle);
}


void change_DutyCycle (unsigned int interval, byte add)
{
  static unsigned long last_ms = 0;
  unsigned long ms = millis();

  if (ms - last_ms <= interval)  return;
  last_ms = ms;

  duty_cycle += add;
  analogWrite(pin_pwm_output, duty_cycle);  
}