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,588
- 1
- 12
- 17
- 81
- 4
-
1You 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 04:44
5 Answers
The whole idea is sound, but I would make a few changes:
- as explained by Jot in a comment, you have to update
previousMillisinside theifstatement. You have two choices:- set it to
currentMillis, thenOffTimewill 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
- set it to
- 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;
}
}
};
- 43,033
- 4
- 38
- 76
-
1For 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
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 );
}
};
//----------------------------------------------------------
- 43,033
- 4
- 38
- 76
- 81
- 4
-
Your test
if (lateMillis + delta >= 0)is always true. Thevolatilekeyword is useless here. – Edgar Bonet Jul 06 '17 at 20:19
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
}
- 2,770
- 10
- 13
-
-
@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
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.
- 15,246
- 3
- 23
- 51
-
Nested functions are not include in C or C++,... it is compiler specific.
– dannyf Jul 01 '17 at 14:36 -
-
-
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 02 '17 at 12:47
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
- 43,033
- 4
- 38
- 76
- 81
- 4
-
1Your 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 01 '17 at 13:01
