1
class Pseudo_Interrupt {
  //only cummulative time is important, exact interval is not
  // Class Member Variables
  // These are initialized at startup
  long OffTime = 1000; // milliseconds of off-time
  // These maintain the current state
  unsigned long previousMillis; // will store last time updated

  void Update() {
    // check to see if it's time for an Interrupt
    unsigned long currentMillis = millis();
    if ((currentMillis - previousMillis >= OffTime) {
      //Time to execute the one second code
    }
  }
}
  • 1
    You are almost there. You have to set previousMillis to currentMillis (inside the if-statement) for the next interval. The previousMills and currentMillis must be unsigned long. The 'Offtime' can be a signed integer, or signed long, but if you make that a unsigned long as well, then everything is unsigned long which makes the code more clear and the compiler doesn't have to convert variables. – Jot Jun 29 '17 at 4:44
3

The whole idea is sound, but I would make a few changes:

  • as explained by Jot in a comment, you have to update previousMillis inside the if statement. You have two choices:
    • set it to currentMillis, then OffTime will be the minimum time between the pseudo-interrupts
    • increment it by a constant amount, which I would call period, then this will be the average time between pseudo-interrupts; this is more similar to how real timer interrupts work
  • add a constructor for:
    • making the period user-selectable
    • making the action of the timer user-selectable via a callback
    • initializing previousMillis

With those changes, the class will look like:

class Pseudo_Interrupt {
    unsigned long period;
    unsigned long previousMillis;
    void (*callback)();

public:
    Pseudo_Interrupt(void (*callback)(), unsigned long period)
        : period(period), previousMillis(millis()), callback(callback)
    { }

    void update()
    {  
        if (millis() - previousMillis >= period)
        {
            callback();
            previousMillis += period;
        }
    }
};
  • 1
    For a steady period (increment previousMillis with a constant), it can create a burst, for example when setup takes a long time. In that case I set previousMillis to millis at the end of setup. It prevents a burst when the sketch starts and setup takes a long time. – Jot Jun 29 '17 at 22:57
1

A function called by your program can do some, but not all, of what an interrupt function does. If you call it frequently enough, you can get timely execution of your "one second code". How frequently you must call it depends on how precisely an execution interval you need. That may require your mainline code to spend more time polling the timer than you can afford; it depends on your particular circumstances.

A true interrupt function will be called nearly immediately on the occurrence of whatever event is set to trigger it. Until then, no time will be taken from your mainline code.

Whether that is that is an effective substitute depends on your particular situation.

The code you supplied is not syntactically correct and does not compile. Nested functions are not include in C or C++, and in the 'if' statement, the parentheses are unbalanced.

  • > Nested functions are not include in C or C++,... it is compiler specific. – dannyf Jul 1 '17 at 14:36
  • Regardless, the code did not compile in the Arduino IDE. – JRobert Jul 1 '17 at 19:29
  • that still doesn't make your original statement correct – dannyf Jul 2 '17 at 1:49
  • No, it is not compiler specific; it is standards specific. Some compilers may implement extensions to the language. That does not make them part of the language. – JRobert Jul 2 '17 at 12:47
1

Latest Version: 7/6/17

//--------------------------------------------------------
class Pseudo_Interrupt {

{   // These maintain the current state of the timer
public: 
volatile unsigned long previousMillis;   // will store last time updated   
volatile unsigned long lateMillis = 0;       // how late was it
volatile bool CallOneMinute = false; //facilitates while loops, 
}
// to prevent a callback set previousMillis = millis(); periodically within the 'period'
// to start the callback set previousMillis = millis() - period; lateMillis = 0;
volatile unsigned long currentMillis; 
static const unsigned long period = 1000;     // milliseconds of off-time delay
volatile unsigned long delta;
    void (*callback)();
public:
    Pseudo_Interrupt(void (*callback)(), unsigned long period)
        : period(period), previousMillis(millis()), callback(callback)
    { }

    void update()
    {       currentMillis = millis(); 
            CallOneMinute = false
            delta = currentMillis - previousMillis - period
        if (lateMillis + delta >= 0)                         
            lateMillis = delta;
            CallOneMinute = true             
        {
            callback();   // with correction to keep net correct cumulative time
            previousMillis += ( period - lateMillis ); 
    }
};
//----------------------------------------------------------
  • Your test if (lateMillis + delta >= 0) is always true. The volatile keyword is useless here. – Edgar Bonet Jul 6 '17 at 20:19
1

Yes, if you advance previous millis by 1000 once that comparison is true.

Setting previous millis to current millis is the wrong approach as it will accumulate timing errors.

edit:

here is a simple piece that hopefully will do what you are trying to do.

//pseudo timer isr
//return 1 if the timer overflows MYTMR_PR
uint8_t mytmr_ovf(void) {
    static uint32_t last_time=0;        //previous time
    uint32_t current_time;

    current_time = millis();            //take current time
    if (current_time - last_time >= MYTMR_PR) {
        last_time += MYTMR_PR;          //update last time
        return 1;                       //indicate overflow
    }
    return 0;                           //indicate no overflow
}

as I suggested earlier, the code is written so that the timer is long-term accurate.

Hope it helps.

edit 2: here is the above code running on a PIC24F, with MYTMR_PR set to 100 (=100ms)

//user loop
void loop(void) {
    //looping around
    if (mytmr_ovf()) digitalWrite(LED, !digitalRead(LED));   //flip the led
}

enter image description here

  • I'm guessing you typed this on a cell phone.... – Johnny Mopp Jun 29 '17 at 11:20
  • @dannyf, I just read the "only cummulative time is important, exact interval is not". You are right, increment with constant will make it work with the same accuracy as the crystal of the microcontroller/processor. – Jot Jun 29 '17 at 23:00
0

I have since put the following in a continuous fast loop. When the program gets to it, it executes. The one second is a little erratic, but the total cumulative is approximately correct.

long OffTime = 1000;     // milliseconds of off-time

// These maintain the current state of the timer
long previousMillis;   // will store last time updated   
long lateMillis;       // how late was it

//----------------------------------------------------------------------

currentMillis = millis();     //correct for millis() overflow
if (currentMillis - previousMillis < 0 )
    currentMillis = currentMillis + 32768;
{
    if (currentMillis - previousMillis >= OffTime - lateMillis )

        // Assume the difference is longer than 1000 milliseconds
        // because the sample time will be late but not by much
        lateMillis = currentMillis - previousMillis - OffTime;

        // Thus, cumulative time is essentially correct 
        //Time to execute the one second code here
        do some code
    else
        return;
    }
// ---------------------------------------------------- 
}  //  End Of Run Repeatedly
  • 1
    Your handling of the millis() overflow is misguided. You should just let it roll over through zero, and make all your timing variables unsigned. Actually, the way you avoided having to handle this in the code from your question was correct. – Edgar Bonet Jul 1 '17 at 13:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.