Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 5 of 5

Thread: micros() within Interrupt possible?

  1. #1

    micros() within Interrupt possible?

    Hi,
    I want to use interrupts to measure pulse lengths with a Teensy 3.2. The time between interrupts shall be measured by micros().
    But anywhere I read that using micros() within interrupts might cause problems. I can't remember where I read this.

    Therefore my question ist: can I call micros() within an interrupt and what has to be considered when using it?

  2. #2
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    10,075
    Indeed micros() isn't a clean/easy function on T_3.x's.
    Better to use the CPU cycle counter - T_LC doesn't have one but T_3.x's do though not enabled by default, it is on T4 though:

    This test seems to compare and initialize when needed - place in setup():
    Code:
    	if ( ARM_DWT_CYCCNT == ARM_DWT_CYCCNT ) {
    		// Enable CPU Cycle Count
    		ARM_DEMCR |= ARM_DEMCR_TRCENA;
    		ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
    	}
    On T_3.2 the cycles per second is known with :: F_CPU

    So tracking :: uint32_t countNow = ARM_DWT_CYCCNT

    Is a simple read of the running count - like micros() - except instead of 1M/sec it will change at 96M/sec when running at F_CPU==96 MHz.

    So much better resolution and much less overhead.

  3. #3
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,773
    You probably saw that on Arduino's site. On Teensy 3.x, micros() does support use from within interrupts. For many of Arduino's boards, micros() should not be used from interrupts.

    And yes, Defragster is right, the ARM DWT cycle counter is the most precise and lowest overhead way. Just remember it will overflow after 44.7 seconds when counting at 96 MHz, or only 7.15 seconds if you someday step up to Teensy 4.0 which runs at 600 MHz. Best to use the cycle counter only when you know the period to be measured will be fairly short.

  4. #4
    Quote Originally Posted by PaulStoffregen View Post
    You probably saw that on Arduino's site. On Teensy 3.x, micros() does support use from within interrupts. For many of Arduino's boards, micros() should not be used from interrupts.
    The background for my question was, that a code which successfully runs on an Arduino Nano makes problems when run on T3.2.
    The sketch is decoding a Manchester coded signal from a 433 MHz receiver. Both edges of the pulses from the receiver are triggering an interrupt. For decoding, the pulse width shall be measured which is done by calling micros() - on Nano and on T3.2.
    This works on the Nano. But on T3.2 there are a lot of unknown messages ("unbekanntes Signal") and only sometimes (every 3 to 6 times) the signal is decoded correctly.

    Here is the code of the sketch:
    Code:
    /* Test der Decodierung der Oregon-Daten auf einem Teensy 3.2   */
    
    // Decodierung Oregon-Daten:
    #include <Arduino.h>
    #include <stdio.h>
    #include <math.h>
    #include "OregonDecodeV3_2.h"
    #include "digitalWriteFast.h"
    
    #include <util/atomic.h>
    
    // Decodierung Oregon-Daten:
    #define SYNCPIN 5   // zum Triggern Oszi
    #define MARKSENT   A1  // zeigt 1/2 Sek. lang an LED an, dass Datenblock gesendet wurde
    //#define HOT       // HOT fuer WDFA
    //#define MEAN_SPEED  // gemittelte Windgeschw. anstatt aktueller
    
    OOK_OregonDecodeV3_2 orscV3;
    boolean SerMonitor;
    
    uint8_t   fstat;
    elapsedMicros blinkPrint=0;
    uint16_t looping=0;
    byte isr_disen =0;
    
    // Decodierung Oregon-Daten:
    bool tglbit=false;
    unsigned long tm; // Zeit für einen loop-Durchlauf
    // Winddata:
    word wv;
    byte wdir, lowbatt;
    // for temperature data:
    int temp_act;
    byte rh_act;
    
    // Parameter in loop():
    byte n, dtyp;
    static word Scount=0;
    static short int v16;
    static byte v8;
    static char outstr[12];
    static byte newdata=0;
    unsigned long tm_wd;  // Zeit zwischen WGR800-Winddaten
    byte norecdat;        // Luecke zwischen Winddaten in 14sec-Einheiten
    char ic='n';
    byte respos_cpy, respos_cold;
    
    // Parameter, die von der Komparator-Interrupt-Routine veraendert werden koennen:
    volatile byte res_valid[N_DATASETS];
    volatile byte res_pos=0, dbcount;
    volatile byte dtmin=255, dtmax=0;
    volatile word pulse, tot_p;
    //volatile byte Wdone=1;
    
    void waitSM(const char* msg) {
      Serial.println(msg);
      while (Serial.available()>0) Serial.read();
      while (Serial.available()==0);
      while (Serial.available()>0) Serial.read();
    }
    
    // Interrupt Routine
    
    void ext_int_2(void) {  // ohne Comparator
        static unsigned long last, pw, t00; // mit static muss Speicher für die Parameter
                                           //   nur einmal reserviert werden
        static byte dt;
    
        // determine the pulse length in microseconds, for either polarity
        t00 = micros();   // nur zum Testen
        pw = micros() - last;
        last += pw;
        if (pw < 65536) pulse=(word)pw;
        else pulse=65535;
    
        // OregonDecoder
        if ( (orscV3.total_bits > 0) || (pulse > PW1) ) {
          if (orscV3.nextPulse(pulse)==1) {
            dbcount = orscV3.pos;  // pos retten, ehe es zu 0 wird
            orscV3.resetDecoder ();
            res_valid[res_pos]=1;  // valid Flag
            res_pos++;
            if (res_pos > (N_DATASETS-1)) res_pos = 0;
            orscV3.setdb(res_pos);  // für nächsten Datensatz
            tot_p = orscV3.anz_p;  // Wert retten
          }
        }
        dt=micros()-t00;
        if (dt<dtmin) dtmin=dt;
        if (dt>dtmax) dtmax=dt;
    }
    
    byte readRFData(byte r_pos, short int* val16, byte* val8, byte* lowBatt, boolean mean_speed) {
      // liest Daten des Empfaengers aus dem Datensatz r_pos und liefert sie in val16 und val8 zurück;
      //  mean_speed==true: gemittelte Windgeschwindigkeit
      //  mean_speed==false: aktuelle Windgeschwindigkeit
      //            WGR800        THGN          TFA    
      // val16    Geschw.*10    Temperatur*10  Geschw.*10 simuliert
      // val8     Richtung      Luftfeuchte    Richtung simuliert
      // return     1             0             2
      // 
      volatile const byte* data = orscV3.getData(r_pos);
      static int  wv=36, tmp;   // für Geschw.,  Temp. 
      byte wd, rh;              // für Richtung, rel. Luftfeuchte
      byte retv=0;
    
    //Serial.print("von Datensatz ");Serial.print(r_pos);Serial.print(" ");
      if ( (data[0] == 26) & (data[1] == 137) ) { // WGR800: 1A89
        if (mean_speed)
          wv = (data[8] >> 4)*100 + (data[8] & 0x0F)*10 + (data[7] >> 4);
        else
          wv = (data[7] & 0x0F)*100 + (data[6] >> 4)*10 + (data[6] & 0x0F);
        Serial.print(": ");Serial.print(wv);Serial.print("m/s*10 ");
    //    wv = word(round(wv * 3.6));   // kmh*10
        wd = (data[4] >> 4);
    
        *val16 = wv;
        *val8 = wd;
    
        if (data[4] & 0x04)
          *lowBatt = 4;   // battery low Windsensor
        else
          *lowBatt = 0;
          
        retv = 1;
      }
      else if ( (data[0] == 250) && (data[1] == 40) ) {  // THGR810: FA28
        tmp = (data[5] >> 4)*100 + (data[5] & 0x0F)*10 + (data[4] >> 4);
        if (data[6] & 0x0F) tmp = -tmp;
        rh = (data[7] & 0x0F)*10 + (data[6] >> 4);
    
        *val16 = tmp;
        *val8 = rh;
        
        if (data[4] & 0x04)
          *lowBatt = 5;   // battery low Temperatursensor
        else
          *lowBatt = 0;
          
        retv = 0;
      }
      else {  // unbekannte Daten
        retv = 2;
      }
    
      return(retv);
    }
    
    void mark_dataReceived() {
      digitalWrite(MARKSENT,HIGH);
      delay(500);
      digitalWrite(MARKSENT,LOW);
      delay(200);
    }
    
    
    void setup() { // -------------------------------------------------------------
      uint32_t read_time;
      int16_t anz_errors;
      
      analogWriteResolution(8);
      pinMode(A14, OUTPUT);
    
      Serial.begin(38400);
      while (!Serial) {
        ; // wait for serial port to connect. Needed for native USB port only
      }
      Serial.println(F("<<< Sprache + Oregon-Decodierung >>>"));
    
      SerMonitor=true;
      
      // Vorbelegungen ohne cli() ok, weil erst
      //  anschließend die Interrupt-Routine scharf wird
        for (byte nn=0; nn<N_DATASETS; nn++)
          res_valid[nn]=0; // Flags der Datenbaenke
        res_pos=0;
        respos_cpy=respos_cold=0;
        orscV3.setdb(0);
    
      // Empfaenger-Interrupt aktivieren, Pin 23
        attachInterrupt(digitalPinToInterrupt(23), ext_int_2, CHANGE);
    
        Serial.println("es ist ein Teensy");
    
    #ifdef WGR800
      Serial.println("Pulssequenzen wie WGR800");
    #else
      Serial.println("Pulssequenzen wie TFA-Sensor");
    #endif
    
      tm_wd=millis();
      tm=tm_wd;
    }
    
    void loop() { // ---------------------------------------------------------
      // to read a variable which the interrupt code writes, we
      // must temporarily disable interrupts, to be sure it will
      // not change while we are reading.  To minimize the time
      // with interrupts off, just quickly make a copy, and then
      // use the copy while allowing the interrupt to keep working
      boolean dvalid;
      byte dtmin_c, dtmax_c;
      
      if (Serial.available()) {
        ic=Serial.read();
        while (Serial.available()) Serial.read();
      }
    
      // --------------------------------------
      // Trigger-Sync:
      digitalWrite(SYNCPIN,HIGH);
      digitalWrite(SYNCPIN,LOW);
    
        delay(200);
    
          for (n=0; n<N_DATASETS; n++) {
              ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
              {
                dvalid = res_valid[n] == 1; 
              }
              if (dvalid)  {  // Daten sind verfuegbar
    #ifdef MEAN_SPEED
                dtyp = readRFData(n, &v16, &v8, &lowbatt, true);
    #else
                dtyp = readRFData(n, &v16, &v8, &lowbatt, false);
    #endif
                switch (dtyp) {
                  case 0:  // Temperaturdaten
                    sprintf(outstr,"n=%d dbcnt=%d T%04dH%02dB%d",n,dbcount,v16,v8,lowbatt);
                    Serial.print(outstr);
                    mark_dataReceived();
                    break;
                  case 1: // Winddaten WGR800
    #ifndef HOT
                    Serial.print(millis()-tm_wd);Serial.print(": ");
                    tm_wd=millis();
      
                    sprintf(outstr,"n=%d dbcnt=%d W%03dR%02dB%d",n,dbcount,v16,v8,lowbatt);
                    Serial.print(outstr);
      
    #ifdef MEAN_SPEED
                    Serial.print(F(" mean="));
                    Serial.print(v16);
    #endif
                    
    #else
                    sprintf(outstr,"n=%d W%03dR%02dB%d",n,v16,v8,lowbatt);
                    Serial.println(outstr);
    #endif
                    mark_dataReceived();
                    break;
                  case 2:  // unbekanntes Signal
                      Serial.print(F("unbekanntes Signal: n="));Serial.print(n);
                      Serial.print(F(", dbcnt="));Serial.print(dbcount);
                } // switch
      
                Serial.print(F(" Interrupt-Dauer von "));
                ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
                {
                  dtmin_c = dtmin;
                  dtmax_c = dtmax;
                  dtmin=255;
                  dtmax=0;
                }
                Serial.print(dtmin_c);
                Serial.print(" bis ");Serial.println(dtmax_c);
      
                res_valid[n] = 0;   // wenn es 1 war, dann wurde in der Interrupt-Routine
                                    // bestimmt nicht daran gearbeitet
              } // if (res_valid..)
              
              Scount++;
              tm=millis();
          } // for n
    //    }
    
    }
    Code of class OOK_OregonDecodeV3_2:
    Code:
    #ifndef _myOregonDecoderV3_2_
    #define _myOregonDecoderV3_2_
    
    // 2012-06-21 - Oregon V3 decoder revisited - Dominique Pierre
    // New code to decode OOK signals from weather sensors, etc.
    // 2010-04-11 <jcw@equi4.com> http://opensource.org/licenses/mit-license.php
    // $Id: ookDecoder.pde 5331 2010-04-17 10:45:17Z jcw $
    
    #include <Arduino.h>
    
    #define WGR800    // auskommentiert heißt TFA-Sensor
    
    #ifdef WGR800
    #define PW1 350     // Original 200
    #define PW2 720     // Original 700
    #define PW3 1150     // Original 1200
    #define FLIP 32
    #define TOTALB 80
    #define N_DATASETS 4  // 1 mehr als TFA für Temperatursensor
    #else       // TFA
    #define PW1 200
    #define PW2 750
    #define PW3 1200
    #define FLIP 5
    #define TOTALB 104
    #define N_DATASETS 6  // TFA-Windsensor sendet Daten 3 mal
    #endif
    
    // 433 MHz Oregon-decoder:
    
    class OOK_OregonDecodeV3_2 {
      // Die Klassen DecodeOOK und OregonDecoderV3 sind hier zusammengefasst
    
    protected:
        byte bits, flip, state;
        volatile byte data[N_DATASETS][15];
        // 15 reicht für TFA und WGR800
        char dc;
    public:
        byte total_bits, pos;  // hier wegen Direktzugriff
        volatile byte* data_pnt; // wird auf eine der N_DATASETS Datenbänke zeigen
        word anz_p=0;   // zählt die Pulse pro Datensatz
        enum { UNKNOWN, T0, T1, T2, T3, OK, DONE };
    
        OOK_OregonDecodeV3_2 () { resetDecoder(); }
    
        bool isDone () {
          return state == DONE;
        }
       
        // Pointer auf einen der Datenblöcke setzen:
        void setdb(byte rpos) {
          data_pnt = data[rpos];
        }
        
        volatile const byte* getData (byte rpos) const {
            return data[rpos]; 
        }
       
        void resetDecoder () {
            total_bits = bits = pos = flip = 0;
            anz_p = 0;
            state = UNKNOWN;
        }
        
        // add one bit to the packet data buffer (Methode V3)
        void gotBit (char value) {
            data_pnt[pos] = (data_pnt[pos] >> 1) | (value ? 0x80 : 00); // LSB comes first
            total_bits++;
            pos = total_bits >> 3;
            if (pos >= sizeof data) {
                resetDecoder();
                return;
            }
            state = OK;
        }
        
        // store a bit using Manchester encoding
        void manchester (char value) {
            flip ^= value; // manchester code, long pulse flips the bit
            gotBit(flip);
        }
    
        void done () {
            while (bits)
                gotBit(0); // padding
            state = DONE;
        }
        
        bool nextPulse (word width) {
            if (state != DONE) {
    
                anz_p++;
    //            switch (decode(width)) {
    //                case -1: resetDecoder();  break;  // ergibt Fehlermeldung mit
                                                        //  Teensy Compiler
    //                case 1:  done(); break;
    //            }
                dc=decode(width);
                if (dc==-1) resetDecoder();
                else if (dc==1) done();
            }
            return isDone();
        }
    
        char decode (word width) {
            if (PW1 < width && width < PW3) {
                byte w = width >= PW2;
                switch (state) {
                    case UNKNOWN:
                        if (w == 0)
                            ++flip;
                        else if (FLIP <= flip) {
                            flip = 1;
                            manchester(1);
                        } else
                            return -1;
                        break;
                    case OK:
                        if (w == 0)
                            state = T0;
                        else
                            manchester(1);
                        break;
                    case T0:
                        if (w == 0)
                            manchester(0);
                        else
                            return -1;
                        break;
                }
            } else 
                return -1;
    
            return total_bits == TOTALB ? 1: 0;
        }
    
    };
    
    #endif
    The interrupt is filling one of 4 buffers, data[][], with the received data and then sets a flag res_valid[].
    In loop() the flag is checked and if set the data are interpreted and output on Serial Monitor.

    The true Manchester signal is generated and sent from a test setup in my lab. Of course the 433 MHz receiver is seeing also signals from sensors outside my lab sending any Manchester codes. But the Nano is decoding just the true signals and the T3.2 is reporting many false signals ("unbekanntes Signal") and only a few of the true signals.

    I would be happy to get some ideas why T3.2 is performing so different and poor.

  5. #5
    Senior Member
    Join Date
    Feb 2017
    Posts
    320
    Without thinking about it too much, it seems you'd be better off using one of the K20's FTMs set up for Input Capture mode.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •