Arduino Forum

Using Arduino => Programming Questions => Topic started by: boylesg on Aug 20, 2016, 10:16 pm

Title: Timer1 - how do you set a 1 minute timer?
Post by: boylesg on Aug 20, 2016, 10:16 pm
It is apparently not as simple as Timer1.initialize((uint32_t)60000000); because this does not seem to work.

The interval is more like 10s or so and increasing the value of the parameter further has no effect.

Do you have to mess with the clock dividers to get it to work?

I can't seem to find an example of this sort of long period to follow.
Title: Re: Timer1 - how do you set a 1 minute timer?
Post by: Robin2 on Aug 20, 2016, 10:38 pm
I don't understand why you want to use a hardware Timer for a period as long as 1 minute? Why not just use millis()?

The demo Several Things at a Time (http://forum.arduino.cc/index.php?topic=223286.0) illustrates the use of millis() to manage timing.

I don't know if Timer1 can deal with a period as long as 1 minute but you would certainly need to "mess with the clock dividers". All the details are in the relevant Atmel datasheet - you don't say which Arduino you are using.

...R
Title: Re: Timer1 - how do you set a 1 minute timer?
Post by: AWOL on Aug 20, 2016, 10:43 pm
Quote
The interval is more like 10s
About 10.176 seconds. IIRC.
Title: Re: Timer1 - how do you set a 1 minute timer?
Post by: Ps991 on Aug 20, 2016, 10:46 pm
First of all, PLEASE read the How To Use This Forum thread before you post. I had to do alot of googling, which most people WILL NOT DO, simply because you failed to tell us basic things about your problem, such as:
The library you were using, and
Your code, which would have showed me the library you were using

Anyway, here is what I found in the library I ASSUME you are using...
Code: [Select]
 long cycles = (F_CPU / 2000000) * microseconds;                                // the counter runs backwards after TOP, interrupt is at BOTTOM so divide microseconds by 2
  if(cycles < RESOLUTION)              clockSelectBits = _BV(CS10);              // no prescale, full xtal
... //removed because irrelevant
  else        cycles = RESOLUTION - 1, clockSelectBits = _BV(CS12) | _BV(CS10);  // request was out of bounds, set as maximum


RESOLUTION is set to 65536 because Timer1 is a 16bit timer.
cycles = (16,000,000 / 2,000,000) * 60,000,000 = 480,000,000
So, cycles is NOT less than RESOLUTION - 1, therefore it was more than maximum

This code sets the prescaler to 1024, as slow as you can get.
A prescaler of 1024 allows for a maximum time of 8388.608mS or ~8.3 seconds
Code: [Select]
clockSelectBits = _BV(CS12) | _BV(CS10);
Title: Re: Timer1 - how do you set a 1 minute timer?
Post by: Ps991 on Aug 20, 2016, 11:14 pm
Here is a slightly modified program I made to easily set up timed interrupts. I tested its accuracy and it works consistently down to the microsecond.

The interrupt gets called every 1 second and adds 1 to 'seconds'. Simply check if 'seconds' is 60 or whatever, if it is, then execute your code. This program toggles an LED.

Code: [Select]
void setup() {
  int frequency = 1; // in hz
  //Interupt Service Routine and timer setup
  noInterrupts();// kill interrupts until everybody is set up
  //We use Timer 1 b/c it's the only 16 bit timer
  TCCR1A = B00000000;//Register A all 0's since we're not toggling any pins
    // TCCR1B clock prescalers
    // 0 0 1 clkI/O /1 (No prescaling)
    // 0 1 0 clkI/O /8 (From prescaler)
    // 0 1 1 clkI/O /64 (From prescaler)
    // 1 0 0 clkI/O /256 (From prescaler)
    // 1 0 1 clkI/O /1024 (From prescaler)
  TCCR1B = B00001100;//bit 3 set for CTC mode, will call interrupt on counter match, bit 2 set to divide clock by 256, so 16MHz/256=62.5KHz
  TIMSK1 = B00000010;//bit 1 set to call the interrupt on an OCR1A match
  OCR1A  = (unsigned long)((62500UL / frequency) - 1UL);//our clock runs at 62.5kHz, which is 1/62.5kHz = 16us
  interrupts();//restart interrupts

  Serial.begin(115200);
  pinMode(13, OUTPUT);
}

volatile int seconds = 0; //make it volatile because it is used inside the interrupt

void loop() {
 
}

ISR(TIMER1_COMPA_vect){ //Interrupt Service Routine, Timer/Counter1 Compare Match A
  seconds++;
  if(seconds >= 1) { //set to however many seconds you want
    Serial.println(micros());           // This code is what happens
    seconds = 0;                        // after 'x' seconds
    digitalWrite(13, !digitalRead(13)); //
  }
}
Title: Re: Timer1 - how do you set a 1 minute timer?
Post by: MorganS on Aug 21, 2016, 12:31 am
...which is EXACTLY what millis() does, except it uses milliseconds as the unit instead of seconds. You have replaced one problem by exactly the same problem except you have used another precious timer which now cannot be used for PWM or servos.
Title: Re: Timer1 - how do you set a 1 minute timer?
Post by: Ps991 on Aug 21, 2016, 01:50 am
Ok, then please give him an alternative that executes code at exactly 1 minute, which is what I was showing them how to do, because...
Code: [Select]

if(millis() - lastTime >= 60000) {
  lastTime = millis();
   ...
}

does not guarantee execution after 1 minute because other code may take a long time to execute.
Title: Re: Timer1 - how do you set a 1 minute timer?
Post by: larryd on Aug 21, 2016, 01:56 am
As Robin2 said, use millis() techniques.

Or, is there a reason why the OP cannot?


.
Title: Re: Timer1 - how do you set a 1 minute timer?
Post by: larryd on Aug 21, 2016, 02:01 am
Having a hard time thinking what can be so important to happen at 1 uS after a minute.


.
Title: Re: Timer1 - how do you set a 1 minute timer?
Post by: Ps991 on Aug 21, 2016, 02:19 am
I provided a proof of concept using an interrupt, the same timer that OP was attempting to use. I answered their question using their method.

However, OP did not specify whether or not perfect timing was critical. 1 or 2 uS or even mS may not be important, but if they have alot of blocking code and 1 minute is fairly important, then interrupts are the way to go. OP will decide what is right for their code.
Title: Re: Timer1 - how do you set a 1 minute timer?
Post by: saximus on Aug 21, 2016, 02:53 am
If he/she is after simplicity and still wants to use interrupts, would the Timer1 library (http://playground.arduino.cc/Code/Timer1) not be easier?
Title: Re: Timer1 - how do you set a 1 minute timer?
Post by: boylesg on Aug 21, 2016, 03:21 am
I don't understand why you want to use a hardware Timer for a period as long as 1 minute? Why not just use millis()?

The demo Several Things at a Time (http://forum.arduino.cc/index.php?topic=223286.0) illustrates the use of millis() to manage timing.

I don't know if Timer1 can deal with a period as long as 1 minute but you would certainly need to "mess with the clock dividers". All the details are in the relevant Atmel datasheet - you don't say which Arduino you are using.

...R
Because if I did it that way I would be continuously polling for a few different things in my loop() and it may start degrading performance.
If I use a 1 minute interrupt then I am only polling for some of those things in my loop function once per minute as necessary.
Title: Re: Timer1 - how do you set a 1 minute timer?
Post by: Ps991 on Aug 21, 2016, 03:25 am
If he/she is after simplicity and still wants to use interrupts, would the Timer1 library (http://playground.arduino.cc/Code/Timer1) not be easier?
It would be easier, if only it worked past 10 seconds. The Timer1 library is the same library the OP is having problems with. And this, OP, is why you show us your code in the first post.
Title: Re: Timer1 - how do you set a 1 minute timer?
Post by: boylesg on Aug 21, 2016, 03:36 am
Ok, then please give him an alternative that executes code at exactly 1 minute, which is what I was showing them how to do, because...
Code: [Select]

if(millis() - lastTime >= 60000) {
  lastTime = millis();
   ...
}

does not guarantee execution after 1 minute because other code may take a long time to execute.
It would seem I don't have a choice but to do it this way.

The problem I was having was that the too rapid interrupt was interfering with the web server part of my loop()

I was trying to poll for a couple of different things only every minute and the above is effectively polling too.

But hopefully the above code wont slow down my loop() such that it interferes with my web server.

I suppose I could stick my bluetooth polling inside the above check as well, because I don't really need to poll for incoming bluetooth data every X microseconds.

I have never mucked around with the pre-scalers before so I had no clear idea how they work, how to use them or their limitations. Apparently I chose the wrong key words in my google search on how to set long interrupts.
Title: Re: Timer1 - how do you set a 1 minute timer?
Post by: boylesg on Aug 21, 2016, 03:40 am
Roughly how long does

Code: [Select]
if(millis() - lastTime >= 60000) {
  lastTime = millis();


take to execute in loop() compared to just

Code: [Select]
if (boolean flag) in the case of an interrupt?
Title: Re: Timer1 - how do you set a 1 minute timer?
Post by: Ps991 on Aug 21, 2016, 03:43 am
I posted code above showing you how to use the prescalers and that code works if you want it to check every 1 minute. Did you miss that or are you saying that it still doesn't work with your code?
Title: Re: Timer1 - how do you set a 1 minute timer?
Post by: Ps991 on Aug 21, 2016, 03:56 am
The first bit of code takes about 2.3268 uS to execute just the if statement because the condition was always false.

The second bit of code takes about 0.3144 uS to execute.

Code: [Select]

volatile int i = 0;
volatile unsigned long lastTime = 0;
volatile boolean flag = false;
unsigned long startTime = 0;
void setup() {
  Serial.begin(115200);
  startTime = micros();
  for(i = 0; i < 10000; i++) {
    //if(millis() - lastTime > 60000)
    //  lastTime = 0;
    if(flag)
      flag = false;
  }
  Serial.println((micros() - startTime));
}

void loop() {
  // put your main code here, to run repeatedly:

}

//11948 for statement
//15092 boolean flag
//35216 millis - unsigned long
Title: Re: Timer1 - how do you set a 1 minute timer?
Post by: boylesg on Aug 21, 2016, 04:11 am
I posted code above showing you how to use the prescalers and that code works if you want it to check every 1 minute. Did you miss that or are you saying that it still doesn't work with your code?
OK I get what you are doing.

Instead of setting a flag in the ISR you add to a counter, and then just check the value of the counter instead of the value of a flag.

It has not occurred to me to implement INTs like this previously so thanks for the idea - all the examples I have seen involve boolean flags.

But I could do that with my roughly 8 second ISR because I don't need absolute precision.

And I would need to disable INTs if a HTTP request is detected because my web page takes about 10 seconds or so to download.

So which would you suggest is the best solution for the situation below?

Polling millis() as others have suggested or your method?

Code: [Select]
void loop()
{
  nElapsedMillis = millis() - nLastMillis;
  if (nElapsedMillis < 0)
    nElapsedMillis = ((uint32_t)-1 - nLastMillis) + millis();
   
  // If a minute has elapsed...
  if (nElapsedMillis >= 60000L)
  {
    debugS(String((double)nElapsedMillis / 1000));
/*
    noInterrupts();
   
    // Then iterate through all the programs, for all stations and for today's date, and we need to run any of them based on the current time.
    program.run();

    // If start of a new day then....
    if (rtc.getHours() == 0)
    {
      // Read in the program for each station for the new date.
      if (!program.read())
        printError(__FILE__, __LINE__);
    }
    interrupts();
*/
  }
  if (serialHC05.available() > 0)
    processBTData(serialHC05);

  // Listen for incoming clients
  WiFiEspClient WifiClient = WifiServer.getClient();
 
  if (WifiClient)
  {
    WifiServer.processHTTPRequest(WifiClient);
  }
}


Title: Re: Timer1 - how do you set a 1 minute timer?
Post by: Ps991 on Aug 21, 2016, 04:22 am
It still depends, mainly on what you are doing every 1 minute.

If you really want it to poll every 1 minute, or your download has the possibility of taking longer (such as a slower connection or larger file), then use the interrupt.

If using interrupts breaks your code or causes problems in the downloading process, then don't use it.

Another option would be to make the download process non-blocking, but that may be quite a process or even not possible.

Just be aware that interrupts are not made for large chunks of code that take a lot of time, they are made for quick little snippets of code that are generally sensitive to when they are executed.
Title: Re: Timer1 - how do you set a 1 minute timer?
Post by: Robin2 on Aug 21, 2016, 09:51 am
Because if I did it that way I would be continuously polling for a few different things in my loop() and it may start degrading performance.
That implies {a} that you don't know, {b} that it is not very probable and {c} that you have not told us everything.

If it was my project I would wait until I had a problem before I tried to fix it. :)

...R
Title: Re: Timer1 - how do you set a 1 minute timer?
Post by: boylesg on Aug 21, 2016, 01:10 pm
That implies {a} that you don't know, {b} that it is not very probable and {c} that you have not told us everything.

If it was my project I would wait until I had a problem before I tried to fix it. :)

...R
It is not really broken - I had a very specific problem as I posted.

I didn't realise that you couldn't set any timers for 1 minute, at least not directly.

Everything else in the project is working fine and I am merely trying to work out the best strategy to use with my timed (1 minute) function calls in loop().

I.E. Use the interrupt plus counter or simply poll millis() in loop() as has been suggested.

And after just trying the first method it appears that setting an interrupt on TimerOne interferes with WifiESP some how so I can't use that method.
Title: Re: Timer1 - how do you set a 1 minute timer?
Post by: AWOL on Aug 21, 2016, 02:35 pm
Don't forget to factor-in the interrupt pre and post-amble.
Title: Re: Timer1 - how do you set a 1 minute timer?
Post by: Ps991 on Aug 21, 2016, 09:02 pm
The ESP uses Serial, so you have 2 options for interrupts.
1) Make the serial buffer larger, which will take more time to fill it up and you interrupt has more time to execute before it becomes a problem. This method is not recommended b/c it could pose problems in the future
2) After the interrupt happens, check to see if there is 64? bytes of data in the buffer and if there is, then that is most likely an overflow. Throw away that data, empty the buffer, and re-request the data.
Title: Re: Timer1 - how do you set a 1 minute timer?
Post by: MorganS on Aug 22, 2016, 06:50 am
I suppose I could stick my bluetooth polling inside the above check as well, because I don't really need to poll for incoming bluetooth data every X microseconds.
That already sounds like a bad idea. Most Arduino bluetooth devices are plain serial. Even at the relatively pedestrian 9600 baud, you get about 960 characters per second. That means you need to check the serial buffer at least 15 times per second to prevent the buffer overflowing. With a properly constructed loop, it is simple to do this check hundreds or thousands of times per second.

Remember the computers work for us. We don't have to make their job easy. The basic Arduinos have 16 million instructions every second, whether they are doing useful work or just waiting for the next serial character to arrive. "Hey! You! I want you to look at this input 16 million times and let me know if it changes, okay?"
Title: Re: Timer1 - how do you set a 1 minute timer?
Post by: Robin2 on Aug 22, 2016, 09:16 am
If you are receiving Serial data have a look at the examples in Serial Input Basics (http://forum.arduino.cc/index.php?topic=396450.0) - simple reliable ways to receive data. There is also a parse example.

...R