Interruptgesteuerter Frequenzteiler Fehlerhaft

Hallo,

habe versucht einen /4 Frequenzteiler mit dem Arduino Mega zu bauen.
Interrupt gesteuert (RISING) wird ein Counter erhöht, bis er 4 erreicht, dann der Ausgang invertiert und Counter auf 0 gestzt. Die Main ist leer.

Leider nimmt die ISR mal 4 mal 3 mal 2 Pulse "mit"! (vgl. Screenshot Oszzi)

Code:

/*
  Pin IN - CH - ISR - Pin OUT
  18     - A  - 5   - 8        //Test LED Pin 13
  19     - B  - 4   - 9
*/
//--- Includes -------------------------------
//--- Defines --------------------------------
//--- FCN ------------------------------------
//--- Globale Variables ----------------------
int pinAin              = 18;      // ISR 4 Pin 19
int pinAout             = 8;       // Pin A out (8 oder 13 LED)
volatile boolean stateA = 1;       // Out State A  
volatile int cntA       = 0;       // Counter CH A

void setup(){ //--- Setup --------------------
  pinMode(pinAout, OUTPUT);            // Outputs                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         pinMode(pinA, OUTPUT);
  attachInterrupt(5, isrA, RISING );    // Pin 18
 
}//END void setup()


//--- MAIN loop -------------------------------
void loop(){ //--- Main Loop ------------------
//nix  

}//END void loop()


//--- ISR A -----------------------------------
void isrA(){
    
  if(cntA > 3){
    stateA = !stateA;
    digitalWrite(pinAout, stateA);
    cntA = 0;
    }//END if(cntA > 3)
    cntA++;  

}//END void isrA()

Screenshot Oszzi:

Was mache ich falsch? Oder ist 1kHz input bzw. 125Hz output schon zu viel?

Ich würde das vollständig ohne Interrupts lösen. Einfach Timer 2 mit externem Takt versorgen und die Teilung /4 von der Hardware erledigen lassen.

Dein Code ist OK würde ich sagen. Und 1 kHz ist laaangsaaaam, also kein Problem. Die Frage ist: bist Du Dir sicher, daß das Signal wirklich so aussieht wie auf dem Bild? Zieh mal die Zeitachse weit auseinander und schau mal ob die Flanken wirklich sauber sind oder ob der Controller nicht doch mehr Pulse bekommt als Du glaubst. Insbesondere das Überschwingen am Anfang / Ende der Flanken ist ja doch sehr deutlich. Probier mal einen 2kHz RC Tiefpass vor den Controller zu schalten und schau mal was dann passiert.

jim_beam:
Was mache ich falsch? Oder ist 1kHz input bzw. 125Hz output schon zu viel?

Dein Eingangssignal scheint ja nur eine Spannung von ca. 3 Volt zu haben. Das dürfte wohl zusammen mit den flatternden Flanken des Signals zu so einer Art Prellen des Signalpegels führen.

Warum hat Dein Eingangssignal keine sauberen 5 Volt?
Oder wenigstens 3.3 Volt mit sauberen und nicht flatternden Flanken?

Da wohl jede Flanke mindestens einmal, teils mehrmals erkannt wird, könntest Du ggf. das Signal innerhalb der ISR softwaretechnisch entprellen. Dazu müßtest Du aber festlegen können, wie hoch die Eingangsfrequenz maximal sein darf, um eine sinnvolle Prellzeit zum Entprellen festlegen zu können.

Oder vielleicht per Optokoppler, Komparator, Transistor oder HCT IC 5V draus machen.

Dein Eingangssignal scheint ja nur eine Spannung von ca. 3 Volt zu haben. Das dürfte wohl zusammen mit den flatternden Flanken des Signals zu so einer Art Prellen des Signalpegels führen.

Danke, das war das Problem! Verdammt. Ich nutze Sweepgen um das Signal mit der Soundkarte zu erzeugen und habe nicht auf die Signalhöhe geachtet.

DANKE! :slight_smile:

Die Idee mit dem ext. Tackt auf Time 2 ist natürlich genial einfach! Danke.

Ich würde das vollständig ohne Interrupts lösen. Einfach Timer 2 mit externem Takt versorgen und die Teilung /4 von der Hardware erledigen lassen.

Schöne Idee, nur wenn ich das http://forum.arduino.cc/index.php?topic=108434.msg814269#msg814269 richtig verstehe, können nur Timer 1 und 5 auf dem Mega ext. mit clock source versorgt werden, was ja für mich auch ok wäre.

Ziel des ganz ist, die Frequenz eines AB Encoder Signals herrunter zu teilen von max 50kHz um Faktor 4 oder 5 auf 12,5kHz bzw. 10kHz. Dabei muss die Phasenverschiebung zw. Kanal A und B von 90Grad eingehalten werden.

Habe es bisher in Software mit ISR so gelöst:

/*
  Pin IN - CH - ISR - Pin OUT
  18     - A  - 5   - 3        //Test LED Pin 13
  19     - B  - 4   - 4
*/


//--- Includes -------------------------------
#include <Arduino.h>

//--- Defines --------------------------------
#define SP  Serial.print
#define SPL Serial.println
#define DEBUG SP(F("Debug Line: "));SP(__LINE__);SP(F(" us: "));SPL(micros());

//--- FCN ------------------------------------
void looped(void);


//--- Globale Variables ----------------------
//const byte teiler       = 2;        // Define FRQ Teiler 2/4/6/8/10! 

//const byte divider      = teiler-1; // teiler -1 für if <
unsigned int lop        = 0;        // Loop count
byte pinAin             = 18;       // ISR 4 Pin 19
byte pinBin             = 19;       // ISR 4 Pin 19
byte pinAout            = 3;       // Pin A out (8 oder 13 LED)
byte pinBout            = 4;        // Pin B out 
byte pinFlt             = 2;        // Filter/Verrieglung Pin: LOW = aktiv

volatile boolean stateA = 1;        // Out State A  
volatile boolean stateB = 0;        // Out State B
volatile int cntA       = 0;        // Counter CH A
volatile int cntB       = 0;//teiler/2; // Counter CH A
volatile boolean doneA  = 0;        // Verrieglung, A darf zuerst
boolean noFlt;                      // is HIGH if Filter is off           
volatile unsigned long td = 0;      // isrA delay us
volatile unsigned long t1 = 0;      // isrA start us


volatile unsigned long cntFrq = 0;  // Frequenzzähler
unsigned long last      = 0;        // Zeitmerker für FRQ
unsigned long FRQ       = 0;        // FRQ in Hz
unsigned long cntFrq2   = 0;        // lokale Kopie von cntFrq

void setup(){ //--- Setup --------------------
  Serial.begin(115200);              // Start Rs232 @ BaudRate
  SPL();SPL(__FILE__ " " __DATE__ " " __TIME__);SPL(); // Filename, Datum, Zeit
  
  SPL(" AB Encoder Frequenzteiler 4:1");
  SPL(" A_in = Pin 18 * A_out = Pin 3");
  SPL(" B_in = Pin 19 * B_out = Pin 4");
  SPL();
  SPL(" Pin 2 = LOW @ Powerup: Filter/Verriegelung aktiv, LED13 on");
  SPL(" Info: Pin2 has internal PullUp");
  SPL();
  
  
  pinMode(pinAin,  INPUT);             // Inputs
  pinMode(pinBin,  INPUT);
  pinMode(pinAout, OUTPUT);            // Outputs                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         pinMode(pinA, OUTPUT);
  pinMode(pinBout, OUTPUT);
  pinMode(13, OUTPUT);                 // LED13
  pinMode(pinFlt, INPUT_PULLUP);       // FilterInput (Pullup!)
  
  attachInterrupt(5, isrA, RISING );    // Pin 18
  
  if(digitalRead(pinFlt)){              // Verriegelung nur aktivieren wenn FilterPin LOW
    attachInterrupt(4, isrB, RISING );    // Pin 19
    digitalWrite(13, HIGH);               // LED13 an, filter aktiv
    doneA = 1;        // Verrieglung A
    noFlt = 0;        // is LOW if Filter is on
    SPL("Filer/Verriegelung aktiv! (Pin 2 LOW)");
  }
  else{
   digitalWrite(13, LOW);            // LED13 aus, filter deaktiv
   noFlt = 1;         // is LOW if Filter is on
   SPL("Filer/Verriegelung NICHT aktiv! (Pin 2 HIGH  bzw. int. Pullup)");
   }
   SPL();
   
}//END void setup()

void loop(){ //--- Main Loop ------------------
//  looped();                          //Debug loop cnt and ms
  
  //cntFrq = 50000L; //Debug
  
  cntFrq2 = cntFrq;                  // lokale Kopie
  cntFrq = 0;
  unsigned long delta_us = (millis() - last);
  last = millis();
  FRQ = ((cntFrq2 * 1000L) / delta_us );  // FRQ in Hz
  
  

  //SP(F("cntA: "));SP(cntA);SP(F(" doneA: "));SP(doneA);SP(F(" stateA: "));SP(stateA);SPL();
  //SP(F("cntB: "));SP(cntB);SP(F(" doneB: "));SP(doneB);SP(F(" stateB: "));SP(stateB);SPL();
  SP(F("[Hz]: "));SP(FRQ);
  //SP(F("[us]: "));SP(td);
  //SP(F(" Counts: "));SP(cntFrq2);SPL();
  //SP(F("delta ms: "));SP(delta_us);
  SPL();
 
  //DEBUG                              //Debug Line and ms
   
  delay(500);                        //Delay ms
   
}//END void loop()

//--- looped FCN ------------------------------
void looped(void){ //--- Main Loop ------------
  SPL();SP(F("   Loop: "));SP (lop++);SP(F(" ms: "));SPL(millis());SPL();
}//END void looped(void)



//--- ISR A -----------------------------------
void isrA(){
  t1 = micros();                         // dt in us DEBUG 
  
  if(noFlt==0){                          //filter aktiv
    attachInterrupt(4, isrB, RISING );   // enable isrB
    detachInterrupt(5);                  //disable isrA  
    }
    
    switch (cntA){
      
      case 0:
        stateA = !stateA;
        stateB = digitalRead(pinBin);
        digitalWrite(pinAout, stateA);
        digitalWrite(pinBout, stateB);
      break;
        
      case 1:
        stateB = !stateB;
        digitalWrite(pinBout, stateB);
      break;
      
      case 2:
        stateA = !stateA;
        digitalWrite(pinAout, stateA);
      break;
        
      case 3:
        stateB = !stateB;
        digitalWrite(pinBout, stateB);
      break;
      
      default:
      break;  
      
      }//switch
     
    cntA++;
    cntFrq++;              // für Frequenzmessung in Main Loop
    if(cntA>3){ cntA =0;}  // Überlauf abfangen
   
   td = micros() - t1;     // dt in us DEBUG 
 }//END void isrA()


//--- ISR B -----------------------------------
void isrB(){             // Filter / gegenseitige Verriegelung Pin A und B 
  attachInterrupt(5, isrA, RISING );   // enable isrA
  detachInterrupt(4);                  //disable isrB  
}

Mal mit nur einer ISR, mal mit einer Art Filter / Verriegelung, bei der erst der A Kanal getriggert werden kann, wenn B getriggert wurde usw.

Da ich kein Frequenzgenerator mit 2x 90Grad Phasenverschoben Ausgängen habe, teste ich einfach mit einem Kanal. Bis 34kHz stimmt auch die berechnung der Frequenz in der loop() noch, dadrüber nicht mehr. Haut dann nur die Berechnung nicht mehr hin, oder schafft der Mega die ISR nicht mehr? Kann leider zur Zeit nicht mit einem echten Encoder testen.

Software entprellen währe ja auch möglich, bei 50kHz ist eine Periode t=20us, d.h. ich müsste mit 16us entprellen, da us nur in 4us Schritten läuft? Würde dass der Mega noch hinbekommen? Bei 16MHz hat er nur 320 Tackte in 20us?

Wie könnte man die Aufgabe einen AB Encoder Frequenzteiler bis 50kHz eingangs FRQ mit dem Mega am besten/elegantesten/oder überhapt lösen?

Vielen Dank!!

jim_beam:
Wie könnte man die Aufgabe einen AB Encoder Frequenzteiler bis 50kHz eingangs FRQ mit dem Mega am besten/elegantesten/oder überhapt lösen?

Im Zweifelsfall läßt Du die Encoder-Impulse wahrscheinlich besser von einem darauf spezialisierten Chip zählen, von dem Du dann einfach den Zählerstand ablesen kannst.

Mal kurz gegoogelt, das hier könnte sowas sein. Keine Ahnung, ob genau das Teil passt, aber es gibt genau auf den Anwendungsfall "Quadrature Decoder" abgestimmte Chips fertig zu kaufen, die Du an einen Microcontroller anschließen kannst, der mit seinen Interrupts geschwindigkeitsmäßig nicht mehr mithalten kann, um das Zählen mal so nebenbei mit Interrupts zu erledigen.

Timer1 geht auf dem Mega nicht, da T1 nicht herausgeführt ist. Timer2 lässt sich sowie ich das verstehe nur mit einem externen Quarz versorgen.

Eine andere einfache Option ist externe Hardware. z.B. 2 D-Flip-Flops (CD4013) für /4 oder ein Binär-Teiler (74HC4040)

Der beste Weg einen Endoder auszulesen ist per Timer-Interrupt regelmäßig zu Pollen:

Ich weiß aber nicht wie schnell das geht. Normalerweise reichen da 1-2ms

Wenn man aber für das Auslesen direkt die I/Os anspricht statt digitalRead() zu verwenden (was selbst ein paar µs dauert!), sollte das eigentlich recht schnell gehen. Oder die DigitalWriteFast Bibliothek verwenden. Deine zeitlichen Anforderungen sind aber schon etwas extrem, daher kann es auch sein dass das nicht mehr geht.