Interrupt und analogRead bzw analogWrite

Hallo die Herren,

ich bräuchte einen Tip bezüglich eines Programms.
Im Verdacht habe ich, dass die Interrupt-Routine des
Drehzahlmessers (hier für Anemometer verwendet)
die analogRead Funktion stört.
Ich versuche einen Geschwindigkeitsmesser in das
bestehende Programm zu integrieren.
Die LEDs welche ich über einen MosFet mit PWM
versorge, flackern wie blöde - eine Regelung über Poti
scheitert ebenfalls…
Oder kann es an dem Watchdog liegen?
Ich weis mir ehrlich gesagt nicht zu helfen und
würde mich freuen, wenn ich entsprechende Tips
bekommen könnte !

Liebe Grüße

Moritz Both

// Drehzahlmesser für Anemometer, Lichtsteuerung, ESC Steuerung
// -------------------------------------------------------------------
// Drehzahlmesser Code stammt von von der Seite
//http://forum.zerspanungsbude.net/viewtopic.php?f=99&t=1601
//und wurde von "Karl" erstellt.

// benutzt wird ein Arduino Mega 2560

#include <Servo.h>  // Einbindung Servo Libary
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>


#define Eingang 2                             // Drehzahl-Eingang: Pin2 / Interrupt 0
#define AVG 8                                 // runden über 8 Werte                                
#define REFRESH 300                           // Display refresh alle 200ms
#define SENSORS 10                             // Anzahl Sensoren / Impulse pro U   war vorher 1

// Variablen
uint32_t last=0;                              // Zählerwert beim letzten Interrupt
uint16_t rra[AVG];                            // Array für rolling Average
uint8_t ptr=0;                                // Array Pointer
volatile int32_t prep=0;                      // Avg prep
volatile uint8_t cnt=0;                       // Avg count
volatile uint16_t drehzahl=0;                 // selbstredend
volatile uint16_t timeout=10;                 // dynamic timeout   WAR 10
volatile uint16_t wdog=10;                    // um Stillstand zu erkennen WAR 10
char buf[18];                                 // Pufferstring für sprintf

// Zuweisung der Ein- und Ausgänge
const int DimmerPotiPin = A0;  // Analoger input pin am Poti
const int MotorPotiPin = A1;  // Analoger input pin am Poti
const int LichtOutPin = 7; // Pwm Ausgang pin am Mosfet( Für LEDS)
const int RauchPin = 9;    //PWM Ausgang 9 für Rauch Schalter
const int StartLichtPin = 22; //Lampe von Startknopf auf pin 22
const int StartButtonPin = 23; // StartKnopf Minus schaltend an Pin 23
const int StopLichtPin = 24; //Lampe von Stoptknopf auf pin 24
const int StopButtonPin = 25; // StopKnopf Minus schaltend an Pin 25
const int WahlAutoPin = 26; // DrehschalterRechts an Pin 26
const int WahlManuPin = 27; // DrehschalterLinks an Pin 27
const int WingButtonPin = 44;





// Erzeugen der Variablen für Licht und Motor

int LichtValue = 0;        // Wert Messung vom Lichtdimmer
int MosfetValue = 0;        // Ausgabe am PWM (analog out)
int val = 25;             // Anfangswert Motorsteureung
int RauchValue = 0;       // Anfangswert für Stromstärke am Heizdraht über Mosfet
 
unsigned long startzeit;
unsigned long messzeit = 100000; // Messintervall in Mikrosekunden
 
Servo Schub;  // Erstelle Servo unter Schub 




LiquidCrystal_I2C lcd(0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Setze die I2C Adresse Fest

void setup() {
  Serial.begin(9600);     // Starte Kommunikation auf Seriellem Port
  startzeit = micros();   // Lese Internen Timer aus
  Schub.attach(6);        // Servosteuerung auf Pin 6
  Schub.write(25); //Wird zum Initialisieren des ESC benötigt
  delay(2000);
 
  pinMode(RauchPin, OUTPUT);                   // Für Rauch zuständigen Pin als Ausgang
  pinMode(LichtOutPin, OUTPUT);               // Für LEDs zuständigen Pin als Ausgang
  pinMode(Eingang, INPUT_PULLUP);              // Anemometer Eingang und Pullup zuweisen
  pinMode(StartLichtPin, OUTPUT);               // Startknopf Licht an 
  pinMode(StartButtonPin,INPUT_PULLUP);                   // Startknopf eingang und Minusschaltend deklariert
  pinMode(StopLichtPin, OUTPUT);                   // Stopknopf Licht an
  pinMode(StopButtonPin, INPUT_PULLUP);                   // Stopknopf eingang und Minusschaltend deklariert
  pinMode(WahlAutoPin, INPUT_PULLUP);
  pinMode(WahlManuPin, INPUT_PULLUP);
  pinMode(WingButtonPin, INPUT_PULLUP);

  digitalWrite(StartLichtPin, HIGH);
  digitalWrite(StopLichtPin, HIGH);
 

  lcd.begin(16, 2);                           // Display hat 2 Zeilen a 16 Zeichen
  lcd.home();                                 // cursor an den Anfang der 1. Zeile
  lcd.print("-=- Drehzahl -=-");              // Überschrift ausgeben
  lcd.setCursor(0, 1);                        // cursor an den Anfang der 2. Zeile
  lcd.print("** ???? U/min **");              // 2. Zeile ausgeben
  attachInterrupt(0, readmicros, FALLING);    // Interrupt 0 auf Routine readmicros setzen

}

void loop() {                                 // Hauptprogramm
Anemometer();
Licht();                           // Aufruf Unterfunktion Licht
  if ((micros()- startzeit) >= messzeit){
 Motor();                         // Aufruf Unterfunktion Motor
    }
}


void Anemometer()
{ if (wdog <= timeout) {                      // dynamic timeout
    wdog++;
  } else {
    cli();                                    // kein Impuls erhalten
    drehzahl = 0;                             // alle Werte auf 0
    cnt = 0;
    prep = 0;
    last = 0;
    sei();
  }
  drehzahl = myround(drehzahl);               // runden auf 5 / 10 / 50 / 100 - optional
  sprintf(buf, "%5u ", drehzahl);             // als 5stellig formatierte Zahl in den Puffer 
  lcd.setCursor(2, 1);                        // cursor an Position bringen
  lcd.print(buf);                             // Puffer ausgeben
  delay(REFRESH);                             // etwas warten
}

void readmicros() {                           // Interrupt-Routine
  uint32_t m, dur;
  uint16_t rpm;
  m = micros();                               // Microsekundenzähler auslesen
  dur = m - last;                             // Differenz zum letzten Durchlauf berechnen
  if (dur > 5000L) {                          // plausibel? Drehzahl bis max 12000 U/min
    if (last > 0) {                           // ersten Messzyklus ignorieren
      rpm = (60000000L / SENSORS) / dur;      // Drehzahl ausrechnen
      if (cnt < AVG) {                        // |
        cnt++;                                // |
      } else {                                // |
        prep -= rra[ptr];                     // |
      }                                       // | rolling Average
      rra[ptr++] = rpm;                       // |
      prep += rpm;                            // |
      ptr %= AVG;                             // |
      drehzahl = prep / cnt;                  // |
    }
    last = m;                                 // letzten Wert merken
    timeout = dur / (REFRESH * 500L);         // timeout nach 2 fehlenden Impulsen
  }
 wdog = 0;                                   // timeout watchdog
}

uint16_t myround(uint16_t value) {            // Gewichtete Rundung
  uint16_t rto;
  if (value > 10000) {                        // Rundungswert bestimmen  
    rto = 100;
  } else if (value > 3000) {
    rto = 50;
  } else if (value > 500) {
    rto = 10;
  } else if (value > 100) {
    rto = 5;
  } else {
    return (value);
  }
  return (_myround(value, rto));
}

uint16_t _myround(uint16_t value, uint8_t roundto) { // Rundung Hilfsroutine
  value += (roundto >> 1);                    // halben roundto Wert addieren
  value /= roundto;                           // integer division  
  value *= roundto;                           // integer multiplikation
  return (value);
}


void Licht()

{
  // Erfassen des analogen Wert des DimmerPotis:
  LichtValue = (analogRead(DimmerPotiPin)- 20);    // Zuweisung Wert an Variable
  
  MosfetValue = map(LichtValue, 0, 1003, 0, 255); // ReichweitenAnpassung:
  // change the analog out value:
 Serial.println(MosfetValue);
  analogWrite(LichtOutPin, MosfetValue); // Pulsen PwmAusgang am LichtOutpin(~7) mit Wert Mosfet
 }

 void Motor()
 {
    val = map(analogRead(A1), 0, 1023, 25, 175);  // Potiwert in ESCPulse mappen
    Schub.write(val);                             // ESCPulse ausgeben
    startzeit = micros();

  }

void Rauch(){
analogWrite(RauchPin, 10); // Pulsen PwmAusgang für Rauchdraht

}

Im Verdacht habe ich, dass die Interrupt-Routine des Drehzahlmessers (hier für Anemometer verwendet) die analogRead Funktion stört.

readmicros() ist schon eine relativ umfangreiche ISR, auch wenn ich darin keinen Fehler sehen kann.

  LichtValue = (analogRead(DimmerPotiPin)- 20); 

MosfetValue = map(LichtValue, 0, 1003, 0, 255); // ReichweitenAnpassung:

Verstehe ich nicht ganz. Was soll bei LichtValue < 0 passieren?
Was kommt denn bei der Serial.println Testausgabe raus?

  if ((micros()- startzeit) >= messzeit){

Motor();                        // Aufruf Unterfunktion Motor
  }

Um Verwirrung kleiner zu halten, entweder das if komplett innerhalb Motor(), oder die Zeile
startzeit = micros(); von Motor () hierher verlegen.
Das hat aber nur mit dem einfacheren Verständnis zu tun…

Hallo michael_x,
Danke für Deine Hilfestellung.

Quote

Code: [Select]

LichtValue = (analogRead(DimmerPotiPin)- 20);

MosfetValue = map(LichtValue, 0, 1003, 0, 255); // ReichweitenAnpassung:

Verstehe ich nicht ganz. Was soll bei LichtValue < 0 passieren?
Was kommt denn bei der Serial.println Testausgabe raus?

Ähmm… der Poti ist leider Hardwaremäßig defekt. Er fängt bei einem (analogRead)Wert von 20 - 21
an und erreicht nie Null. Bis die neuen Potis da sind, habe ich den Fehler wie oben abgefangen.

Um Verwirrung kleiner zu halten, entweder das if komplett innerhalb Motor(), oder die Zeile
startzeit = micros(); von Motor () hierher verlegen.
Das hat aber nur mit dem einfacheren Verständnis zu tun…

Stimmt… prinipiell “Motor()” anspringen lassen, und dort entscheiden ob der
Code ausgeführt werden soll oder nicht…
Danke, werd ich änden.

Ich habe in den englischen Foren nachgeschlagen…
es scheint prinzipiell Probleme mit Interruptroutinen im Zusammenhang
mit analogRead zu geben. (oder umgekehrt?)

Ich muss ja weder die Windgeschwindigkeit “ständig” angezeigt bekommen,
noch muss die Geschwindigkeitsänderung des Motors bei jedem Durchlauf
durchgeführt werden. Geschweige denn, die Dimmfunktion des Lichts.

Wie kann ich denn eine Art “Verteiler” im Loop() anlegen, der die Interuptroutine
“totlegt”, wenn ich analogRead benutze?

Moritz

mit einem Merker, je nac Inhalt des Merkers machst du das eine, das andere oder nichts.

und alle x ms änderst du den Merker

... indem du keine Interrupt-Routine verwendest.

Im einfachsten Fall pulseIn() mit timeout.
Das legt zwar für die Dauer zwischen zwei Pulsen loop lahm, lässt aber die Interrupts offen.

Oder in der ISR nur Pulse zählen. Und in loop daraus ganz gemütlich Pulse/Zeit berechnen.

Danke für die Hilfe.
Ich hab den Zeitverteiler jetzt "irgendwie "
hingekommen. Noch mal nach der Schule
mit Motor testen, aber den Dimmer alle 500 mills ()
abzufragen hat schon mal geholfen.
Für den Motor brauchte ich erst wieder volle
Akkus. Der loop lief aber ebenfalls schon.
Ich poste, wenn ich fertig bin.

Moritz