Timer interrupt

Platform: Arduino Nano with Atmega 168.

I need a timer interrupt to set my system clock (1 ms tick). I try any of 3 timers available - and every time some another function is damaged. My guess is that the timers are used in Serial.x() functions and in delayMicroseconds(). The former are used to communicate with my PC, the latter - with DS18B20.

Where can I find the formal description of Atmega's hardware usage in standard Arduino libraries?
Additionally, are the libraries open? Can I see a source codes?

Thanks!

Can I see a source codes

Yes, of course, they're in the Arduino installation folders.

AWOL: Thanks a lot!

As I can see now, Timer0 is used in millis() and micros(), but not in delayMicroseconds().

SoftwareSerial functions use delayMicroseconds().

So, my USB communications as well as DS18B20 functions are independent of the Timer0 usage. And they are not linked to Timer1 and Timer2.

It leaves my initial question: why my program goes mad when I try to use timers?
You see, I created the sketch and it works well. Now I decided to re-organize the program and added a timer interrupt subroutine. Just to try. But, any timer used, I have one or two of such effects:

  1. Serial communication stops
  2. DS18B20 gives wrong tesults
  3. Program timer based on interrupts (1 ms tick) is not correct

Any suggestions?

Trim down the program to the smallest possible that still has one of those errors. If you still can't see what's wrong, post the source here.

Well, here is my simplified sketch.

I want Timer2 to generate interrupts every 1 ms. The mode of Timer2 - CTC, the TOP code is 250 and operating frequency is 250 kHz.

16,000,000 / 64 = 250,000
250,000 / 250 = 1,000 - it's the interrupt frequency

What I cannot understand is:

  1. TCNT2=0x00;
    The Timer2 resets to 0 in CTC mode. But the interrupts are uncontrollably frequent when I remove this operation.

  2. Interrupts occur not every 1000 us, but slightly more - ab. 1028. I think it's the result of point 1)

#include <avr/interrupt.h>
#include <avr/io.h>

volatile int Sec2 = 0;

ISR(TIMER2_COMPA_vect) {
  static int Millis2 = 1000;
  static byte blink = 0;
  TCNT2=0x00;                // No way without this stupid operation!
  if (--Millis2 == 0) { 
    Millis2 = 1000;  ++Sec2; } 
  blink ^= 1;
  digitalWrite( 13, blink);
}

void setup()
{
  pinMode( 13, OUTPUT);
  Serial.begin(9600);
  Serial.print( "\nThe study of Interrupts\n");

  TCNT2=0x00;
  OCR2A = 250;              // 1 ms @ Fosc = 16 MHz
  TCCR2A=0x02;              // WGM: No wave generation
  TCCR2B=0x04;              // START Timer, prescaler = 64
  TIMSK2 = (1 << OCIE2A);   // Enable interrupt when Timer reaches OCRA
  sei();
  Serial.print( "Timers and Ints are ready\n");
}

int s2, old2 = 0;
void loop()
{
  int stopCounter = 21;
  do
  {
    long T = (long)millis();
    s2 = Sec2;                   // Fix Sec2 (photo shot)
    if( s2 != old2)              // New second
    {
      old2 = s2;
      --stopCounter;  
      Serial.print( T); Serial.print( "  "); 
      Serial.print( s2); Serial.print( "  \n");
    }
  } while( stopCounter > 0);
  while(1) ;             // Endless loop
}

Hmm. That looks right to me. Any improvements of you do a cli() before messing with the timer settings, and also ensure every value you write is a byte ( i.e. b00000010 instead of 0x02, TIMSK2 = (byte)(1 << OCIE2A) ?

Thanks for you answer!

I've changed the type of interrupt

ISR(TIMER2_OVF_vect) {
  static int Millis2 = 1000;
  static byte blink = 0;
  TCNT2=0x06;                // No way without this stupid operation!
  if (--Millis2 == 0) { 
    Millis2 = 1000;  ++Sec2; } 
  blink ^= 1;
  digitalWrite( 13, blink);
}

and this:

//  TIMSK2 = (1 << OCIE2A);
  TIMSK2 = (1 << TOIE2);

Now the timing is right. But why the CTC interrupt doesn't work? Still unknown...

It's not the pure perfectionism.
When I reload Timer in my ISR - the timing is not exact. You see, the timer overflow occurs, then some time passes till CPU enters ISR and the program reload is performed. So. next overflow will happen not exactly in 1000 us after the previous.
On the contrary, hardware reload does not enlarge the time interval.

Timer1 - the same situation. Now I can formulate the main problem:

Timer0 is a sacred cow, don't use it. Now we speak only about Timer1 and Timer2.

Both Timers can be programmed to cause Interrupts:

a) Overflow Ints

b) CTC Ints.

And:
Both kinds of Ints are invoked in my experiments. I make ISR with appropriate vectors, program TIMSK1 register with TOIE1 or OCIE1A bits - and I see the result (oscillogram at BLINK port and increasing of Sec counter).

But:
Timers are not reseted by hardware in CTC mode! In both kinds of Ints I have to set TCNT to relevant values (~16000 or ~250 in OVF Ints or ZERO in CTC Ints).

Why are not timers reseted in CTC mode?

Tried the CTC version without resetting TCNT2 in the ISR.
It is interesing that T, the millis()-value printed is the same on every line. At 9600 bps, it ought to take 1 millisecond for every character printed, so why doesn't millis advance between printed lines?
Does the timer2-interrupt come so often that the timer0-interrupts that would advance millis can't get a word in edgeways?

Tried the CTC version without resetting TCNT2 in the ISR.

And? Have you noticed the effect I'm speaking about? Without resetting TCNT2 in the ISR the interrupts are very frequent. With resetting - the value of OCR2A does not matter...

Yes, I noted the strange effect. In CTC mode, it shouldn't matter whether you reset TCNT2 or not. I suspect all the serial printing might disturb things, so I'll test by storing the timings in an array first and defer all the printing until after the timing checks are completed.
(I'm currently developing a sketch that depends on CTC working as advertised, hence my interest in this bug)

Here is the sketch without Print from ISR:

ISR(TIMER2_COMPA_vect) {
  static int Millis2 = 1000;
  static byte blink = 0;
  TCNT2=0x00;
  blink ^= 1;
  digitalWrite( 13, blink);
}
void setup()
{
  pinMode( 13, OUTPUT);
  Serial.begin(9600);

  TCNT2=0x00;
  OCR2A = 50;              // 1 ms @ Fosc = 16 MHz
  TCCR2A=0x02;              // WGM: No wave generation
  TCCR2B=0x07;              // START Timer, F = 15625 Hz
  TIMSK2 = (1 << OCIE2A);   
  sei();
  Serial.print( "Timers and Ints are ready\n");
}

void loop()
{
  int stopCounter = 20;
  do
  {
    delay( 1234);
    --stopCounter;  
    Serial.print( TCNT2, DEC); Serial.print( "  \n");
  } while( stopCounter > 0);
  noInterrupts(); 
  while(1) ;             // Endless loop
}

I increased prescaler to feel free inside ISR. With the frequency of counting 15625 Hz the overflow of 8-bit counter must happen once in 16.4 ms. So it is! No COMPARE and CLEAR. I have 2 proves to it:

  1. Port 13 is toggled once in 16.4 ms
  2. Printed 20 codes of TCNT2 show all values from 0 to 255

Besides, OCR2A = 50; does nor change anything

And when I eliminate resetting of TCNT2 in ISR - interrupts become very frequent.

I have now tried everything else I could think of:

  • defining a value for OCR2B as well to ensure that it will not cause any disturbing timer compare matches
  • writing to ASSR so we are not using external clocks
  • clearing out old interruptflags from TIFR2
  • Not doing anything to Serial, not even Serial.begin(), until all tests are finished, in case serial communication might disturb the timers.
  • Verifying that main() does absolutely nothing between the calls to setup() and loop()

None of those precautions helped though.

To recapitulate:
The code posted below tries to setup timer2 in CTC mode at 1000Hz and will print 21 lines with two numbers each.
The first number is millis since startup (counted by hopefully undisturbed timer0 interrupts).
The other number is the number of seconds passed, as counted by the supposedly 1000Hz timer2 interrupt.
The expected output is that the first column increases by roughly 1000 between lines, and the second column by one.

Since CTC mode means TCNT2 should auto-reset to zero on every OCR2A match/TIMER2_COMPA interrupt - why is the TCNT2 = 0 line required in the ISR for the output to look sane? Any forum gurus listening? ::slight_smile:

#include <avr/interrupt.h>
#include <avr/io.h>

volatile int Sec2 = 0;

ISR(TIMER2_COMPA_vect) {
  static int Millis2 = 1000;
  static byte blink = 0;
  // TCNT2 = B00000000;             // Why does this matter?
  if (--Millis2 == 0) {
    Millis2 = 1000;  ++Sec2; }
  blink ^= 1;
  digitalWrite( 13, blink);
}

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

  cli();
  ASSR  = B00000000;   // make sure we use internal clocks
  TCNT2 = B00000000;
  OCR2A = B11111010;   // 250, for 16e6 / 64 / 250 = 1000Hz interrupts
  OCR2B = B11111111;   // 255, so there will be no disturbing OCR2B timer compare matches
  TIFR2 = B00000000;   // clear flags for previous interrupts
  TCCR2A= B00000010;   // CTC mode, supposed to autoreset TCNT2 to 0 when it reaches OCR2A
  TCCR2B= B00000100;   // prescaler = 64
  TIMSK2= B00000010;   // Enable interrupt when Timer reaches OCR2A
  sei();
}

int s2, old2 = 0;
void loop()
{
  long Tarr[25];
  int s2arr[25];
  int stopCounter = 21;
  do
  {
    long T = (long)millis();
    s2 = Sec2;                   // Fix Sec2 (photo shot)
    if( s2 != old2)               // New second
    {
        old2 = s2;
        --stopCounter;
        Tarr[stopCounter] = T;
        s2arr[stopCounter] = s2;
    }
  } while( stopCounter > 0);

  // not using serial until now so that it couldn't possibly disturb the timers.
  Serial.begin(9600);
  Serial.print( "\nThe study of Interrupts\n");
  Serial.print( "Timers and Ints are ready\n");

  for (int i=20; i>=0; i--) {
    Serial.print(Tarr[i]); Serial.print("  ");
    Serial.print(s2arr[i]); Serial.print( "  \n");
  }

  while(1) ;             // Endless loop
}

Hello, my friend drhex! We are nicked alike and we have the same problem:

CTC mode is not working properly

Forget interrupts! The problem is in Timer itself (or in our understanding, I hope).
Here is a sketch without interrupts:

#include <avr/io.h>

void setup()
{
  pinMode( 13, OUTPUT);
  Serial.begin(9600);
  TCNT2 = 0;
  OCR2A = 100;              // It DOES NOT MATTER !!!
  TCCR2A=0x02;              // CTC mode
  TCCR2B=0x07;              // START Timer, prescaler = 1024
  Serial.print( "\nTimer ready\n");
}

void loop()
{
  int stopCounter = 50;
  do
  {
    --stopCounter; 
    byte b = TCNT2;
    Serial.print( b, DEC); Serial.print( "  \n");

    [glow]TCNT2 = 0;[/glow]                            // That insane op...

    delay( random( 200));
  } while( stopCounter > 0);
  while(1) ;             // = STOP
}

You see, the Timer is set in CTC mode and it freely runs without any interrupts.
Sometimes I fetch TCNT2 and print it.
The result depends on the insane operation:

  1. without it we see ONLY ZEROES exept the very first time. (if we make Timer to run faster, PRESCALER = 64, we will not see NOZERO even the first time).
    So, the Timer starts, comes to zero and stops forever

  2. When we put that TCNT2 = 0 on the stage - we do see several NOZEROES. Timer runs now!
    By the way, prescaler matters, So try with the slowest possible frequency of Timer - prescaler 1024.

I fixed the problem with PWM phase correct mode. But it would be great to understand my mistakes with the CTC...

You say

Any forum gurus listening?

My love Deep Purple (Pictures Of Home):

Only my own words return
Nobody's up there
It's a deception
When will I ever learn?

:slight_smile:

Got it!

Apparently, writing to the TCCR2*-registers will clear OCR2A. It all works if one writes that register last!

Great! You are right!

Say, what are these datasheets for? They repeat the same things about 3 timers, but forget quite a serious details :-X

Don't want to write a note to Atmel? :wink:

Thank you for a tremendous work! I'll write the result to another (Russian) forum - they also don't know the TRUTH.

My best wishes from Kiev!
/sorry for my English/

There are more funny things in the datasheet.
E.g. 13.4.4 (page 92) says that all the bits of PINB are read-only, but 13.2.2 (page 77) says you can write to PINB to toggle outputs...

There are more funny things in the datasheet.

NO!

Read it again. Yes you can toggle the pins of port B BUT not through the PINB register. that register is only for reading the port pins. PORTB section 13.4.2 shows you the register to use for reading and writing.

Reading again at 13.2.2 "Toggling the pin"

  • Writing a logic one to PINxn toggles the value of PORTxn*

Worked fine when testing!

you can toggle the pins of port B BUT not through the PINB register

Indeed you can and it is also a useful function as you can change the state of a single bit with a single instruction.

Example:

  // toggle PB5 output (digital pin 13)
  PINB = _BV(PINB5);