[CLOSED] reading 16-bit timer register correctly, and the timer mode

I want to get an interrupt exactly all 20ms. I found Arduino Playground - Timer1 and I think, this should be the right library for me.

Only I wonder, in which mode the timer will run, if I use the attachInterrupt method. Mode 4, the CTC mode, should be the right one for periodic interrupts, isn't it? Or will one of the pwm modes, which I suspect the library to be using, will be as good too?

Also, during my ISR I want to check the already spent time, so I must read the TCNTn register. In the atmega 2560 datasheet I read about the low byte/high byte stuff for the 16-bit registers. Now I wonder, how I can be sure that the high byte and low byte are read in the correct order.

Thank you for answers,
Thomas

Are you saying to want to create a pulse that is exactly 20mS?
Why not try a simpler approach to start, and measure for 20,000 microseconds, see if that will meet your needs.

The datasheet should explain that the C compiler accessing "16-bit" registers in the correct order. The only thing you need to worry about is the "TEMP" register being overridden if an interrupt occurs. Disable interrupts while accessing the "16-bit" registers.

#define BEGIN_ATOMIC  \
{ unsigned char oldSREG = SREG; cli(); {
#define END_ATOMIC    \
} SREG = oldSREG; }

CrossRoads:
Are you saying to want to create a pulse that is exactly 20mS?

Oh no, I don't want to create any pulses. I want to start doing something exactly all 20 ms.

Of course, after the job is done, I could just poll the millis(), until the next 20 ms are gone, but this would be a bit unpreciese. I would prefer using an interrupt. Do you have any answers to some of my questions above?

mkwired:
The datasheet should explain that the C compiler accessing "16-bit" registers in the correct order.

Ahh, good, so this is standardized. Actually I was hoping to get an answer like this.

mkwired:
The only thing you need to worry about is the "TEMP" register being overridden if an interrupt occurs. Disable interrupts while accessing the "16-bit" registers.

Good hint, thanks a lot.

There's no way to get exactly a 20mS delay. There will be always a small error. What do you need +/-1uS, +/-1nS, or what?

For an interrupt every 20ms, yes, I'd use CTC mode. Nothing special is needed to access 16-bit registers in an ISR, assuming interrupts have not been re-enabled within the ISR. In the main program, disable interrupts while accessing 16-bit registers. I haven't used that library, certainly not saying anything against it, but setting a timer up in this way just involves setting a few registers, so I'd just do that directly.

I don't think that if the ISR reads the TCNTn register that it will find anything interesting. It should always be the top value that was set up or thereabouts.

No, I've not needed to create a software interrupt; nor with that kind of precision.

Ups - now you sayed something I haven't thought of. In my ISR I intended to do major stuff, like using hwserial and ethernet. So when I get called-back from the Timer-lib, I would have to re-enable interrupts at the beginning, not really knowing, what other side effects this may have to the timer lib.

I looked into the datasheet, and decided, that it is too much for me :-] but as we are here now: What does this _BV(...) methods do, that are used in the timer1 lib and in the servo lib?

But if you could be so kind to post me the right setup for 16-bit timer3 with mode 4, prescale 8 and OCR 40000(dec) I would really appreciate it.

I understood it the way, that the timer starts counting from 0 again right away, so if you check he value at some point in the ISR, it should tell you, how long the ISR took until then. Isn't it?

About the precision: Actually I don't care for some ten microseconds up or down. I only want to make sure that errors do not accumulate over time. On the other hand, having a drift of some ten microseconds per day would not be so dramatic.

ADDITION: No the millis are not preceise enough - deleted the last sentence.

I haven't tested this code.

void setup()
{
   // CTC mode with clk/8 prescaler
   TCCR1A = 0;
   TCCR1B = 1<<WGM12 | 1<<CS11;
   // 
   OCR1A = 40000;
}

void loop()
{
   // wait for timer
   while (!(TIFR1 & 1<<OCF1A));
   // clear timer
   TIFR1 = 1<<OCF1A;
   
   // do your serial and ethernet stuff here
}

I've created some helper functions for timers on the Atmeg328 here:

The 2560 will be a bit different.

I've got example of timer interrupts here:

In my ISR I intended to do major stuff, like using hwserial and ethernet. So when I get called-back from the Timer-lib, I would have to re-enable interrupts at the beginning, not really knowing, what other side effects this may have to the timer lib.

I would advise against lengthy ISR processing. You certainly would need to re-enable interrupts, and if - for some reason - the timer was called again before finishing this "major stuff" you would have big problems.

Actually I don't care for some ten microseconds up or down. I only want to make sure that errors do not accumulate over time.

In that case you are better off just setting a flag in a timer (or use millis() itself). The millis() function has a slight error component because the millis() interrupt actually fires every 0.00124 of a second. However there is compensation built in so this doesn't accumulate.

What I would be doing is making sure that you check for when the last event should have happened, plus 20 mS (not when it actually happened). That way a slight error amount in starting processing doesn't accumulate.

mkwired:
I haven't tested this code.

   OCR1A = 40000;

Don't forget these counters are zero relative. Assuming everything else is right, you would want 39999.

mkwired:
I haven't tested this code.
...

Thanks for that code. Unfortunately I cannot read it well. But if I got the main point, you suggest to use the timer for counting, but not for calling an ISR. Instead to poll the timer in the main loop. This sounds good to me.

But wouldn't the timer be reset automatically (as it is CTC mode, at least the comment says so), so clearing it in the main loop is not necessary?

Thomas33:
But wouldn't the timer be reset automatically (as it is CTC mode, at least the comment says so), so clearing it in the main loop is not necessary?

My "clear timer" comment was intentionally ambiguous. It doesn't set the timer counter to zero. It clears the Output Compare A Match Flag (see documentation).

Ok, now i think i can put it all together. Thanks to you all.

@Nick: many thanks for your interrupt and counter tutorials. You really address the critical stuff. For example the "queued" interrupt events. Very detailed, very good!

So if i am right, the code line
TCCR1B = 1<<WGM12 | 1<<CS11;
is equivalent to
TCCR1B = _BV(WGM12) | _BV(CS11);

But what is meant with "normal operation" in the code lines
TCCR1A = 0; // normal operation

In the datasheet the "timer mode 0" is named "normal" mode . But as this code line is together with setups for CTC or PWM modes, so what is meant with "normal" in this comment?

Here's a tested example:

/* ---------------------------------------------------------------
   Timer 1 setup
   --------------------------------------------------------------- */
  
namespace Timer1 
  {
  // TCCR1A, TCCR1B
  const byte Modes [16] [2] = 
    {
    
    { 0,                         0 },            // 0: Normal, top = 0xFFFF
    { _BV (WGM10),               0 },            // 1: PWM, Phase-correct, 8 bit, top = 0xFF
    {               _BV (WGM11), 0 },            // 2: PWM, Phase-correct, 9 bit, top = 0x1FF
    { _BV (WGM10) | _BV (WGM11), 0 },            // 3: PWM, Phase-correct, 10 bit, top = 0x3FF
    { 0,                         _BV (WGM12) },  // 4: CTC, top = OCR1A
    { _BV (WGM10),               _BV (WGM12) },  // 5: Fast PWM, 8 bit, top = 0xFF
    {               _BV (WGM11), _BV (WGM12) },  // 6: Fast PWM, 9 bit, top = 0x1FF
    { _BV (WGM10) | _BV (WGM11), _BV (WGM12) },  // 7: Fast PWM, 10 bit, top = 0x3FF
    { 0,                                       _BV (WGM13) },  // 8: PWM, phase and frequency correct, top = ICR1    
    { _BV (WGM10),                             _BV (WGM13) },  // 9: PWM, phase and frequency correct, top = OCR1A    
    {               _BV (WGM11),               _BV (WGM13) },  // 10: PWM, phase correct, top = ICR1A    
    { _BV (WGM10) | _BV (WGM11),               _BV (WGM13) },  // 11: PWM, phase correct, top = OCR1A
    { 0,                         _BV (WGM12) | _BV (WGM13) },  // 12: CTC, top = ICR1    
    { _BV (WGM10),               _BV (WGM12) | _BV (WGM13) },  // 13: reserved
    {               _BV (WGM11), _BV (WGM12) | _BV (WGM13) },  // 14: Fast PWM, TOP = ICR1
    { _BV (WGM10) | _BV (WGM11), _BV (WGM12) | _BV (WGM13) },  // 15: Fast PWM, TOP = OCR1A
    
    };  // end of Timer1::Modes
    
  // Activation
  // Note: T1 is pin 11, Arduino port: D5
  enum { NO_CLOCK, PRESCALE_1, PRESCALE_8, PRESCALE_64, PRESCALE_256, PRESCALE_1024, T1_FALLING, T1_RISING };
  
  // what ports to toggle on timer fire
  enum { NO_PORT = 0, 
  
         // pin 15, Arduino port: D9
         TOGGLE_A_ON_COMPARE  = _BV (COM1A0), 
         CLEAR_A_ON_COMPARE   = _BV (COM1A1), 
         SET_A_ON_COMPARE     = _BV (COM1A0) | _BV (COM1A1),
         
         // pin 16, Arduino port: D10
         TOGGLE_B_ON_COMPARE  = _BV (COM1B0), 
         CLEAR_B_ON_COMPARE   = _BV (COM1B1), 
         SET_B_ON_COMPARE     = _BV (COM1B0) | _BV (COM1B1),
     };
  
  // choose a timer mode, set which clock speed, and which port to toggle
  void setMode (const byte mode, const byte clock, const byte port)
    {
    if (mode < 0 || mode > 15)  // sanity check
      return;
  
    // reset existing flags     
    TCCR1A = 0;
    TCCR1B = 0;

    TCCR1A |= (Modes [mode] [0]) | port;  
    TCCR1B |= (Modes [mode] [1]) | clock;
    }  // end of Timer1::setMode
    
  }  // end of namespace Timer1 
  
const byte LED = 8;
volatile boolean fired = false;

ISR(TIMER1_COMPA_vect)
{
  fired = true;
}  // end of TIMER1_COMPA_vect


 void setup() 
   {
   pinMode (LED, OUTPUT); 

   // set up Timer 1
  Timer1::setMode (4, Timer1::PRESCALE_64, Timer1::NO_PORT);
  TCNT1 = 0;         // reset counter
  OCR1A =  4999;       // compare A register value
  TIFR1 |= _BV (OCF1A);    // clear interrupt flag
  TIMSK1 = _BV (OCIE1A);   // interrupt on Compare A Match  
     
   }  // end of setup

void loop ()
{
  
 if (fired)
   {
   digitalWrite (LED, HIGH);  
   digitalWrite (LED, LOW);  
   
   // do your stuff here
   
   fired = false;  
   }
} // end of loop

This uses my Timer1 "helper" namespace and function to set up the timer. What it is doing is setting up Timer 1 with a prescaler of 64 and a counter of 5000 (zero relative, 4999). This gives a period of:

62.5 * 64 * 5000 = 20000000 nS

That is, 20 mS.

Then it uses the compare A interrupt to set a flag. In loop you just wait for the flag to be set. Then you process, and clear the flag. Measuring the time between pulses on pin 8, I got 20.004 mS, which is pretty close.

The same code runs on the Mega2560 and the Atmega328.

There might be a slight delay (a microsecond or two) before the flag is noticed, but since the hardware timer is clocking out the interrupts, the delay will not be cumulative.

Thomas33:
@Nick: many thanks for your interrupt and counter tutorials. You really address the critical stuff. For example the "queued" interrupt events. Very detailed, very good!

Thanks! After being called names in another thread, it is nice to be appreciated. :slight_smile:

Thomas33:
So if i am right, the code line
TCCR1B = 1<<WGM12 | 1<<CS11;
is equivalent to
TCCR1B = _BV(WGM12) | _BV(CS11);

Yes, exactly. Slightly easier to read. It is saying "set that bit".

Thomas33:
But what is meant with "normal operation" in the code lines
TCCR1A = 0; // normal operation

In the datasheet the "timer mode 0" is named "normal" mode . But as this code line is together with setups for CTC or PWM modes, so what is meant with "normal" in this comment?

From the datasheet:

Normal port operation, OC1A/OC1B disconnected.

That is, not fiddling with turning a pin on or off.

I hope that didn't happen on this forum. I would expect that from the avrfreaks forum.

Wow - now all my questions are answered. Many thanks, Nick!

Unfortunately my time is up for today. It became already much to late again. I will post the final code, just to make sure that I haven't overseen anything, but it may take a few days until I can split of some time again.

Glad I could help. To make things clearer I moved the "standard" timer setup into a library, so the actual sketch looks simpler.

The library is at:

http://gammon.com.au/Arduino/TimerHelpers.zip

The sketch is now just:

#include <TimerHelpers.h>

const byte LED = 8;
volatile boolean fired = false;

ISR(TIMER1_COMPA_vect)
{
  fired = true;
}  // end of TIMER1_COMPA_vect

void setup() 
  {
  pinMode (LED, OUTPUT); 

  // set up Timer 1
  Timer1::setMode (4, Timer1::PRESCALE_64, Timer1::NO_PORT);
  TCNT1 = 0;         // reset counter
  OCR1A =  4999;       // compare A register value
  TIFR1 |= _BV (OCF1A);    // clear interrupt flag
  TIMSK1 = _BV (OCIE1A);   // interrupt on Compare A Match  
  }  // end of setup

void loop ()
  {
  if (fired)
    {
    digitalWrite (LED, HIGH);  
    digitalWrite (LED, LOW);  

    // do your stuff here

    fired = false;  
    }
  } // end of loop