Nano Timer 1 with ISR 2 Circuit

Hi Guys,

Was hoping someone could point me in the right direction with my code.
Basically what i am trying to do is time the time taken between when i trigger the event (HIGH pin 13) and when i get the interrupt on pin 2. On Digital Pin 2. I am expecting between a few micro seconds and 900 micro seconds.

I wanted to go this route (not use Micros() ) because i wanted more than 4 micro seconds precision plus its a chance to learn something new :smiley:

I started with Nick Gammon's tutorial on timers here (Gammon Forum : Electronics : Microprocessors : Timers and counters) and really thought i understood what i need to do for my code...
:frowning: apparently not

I was expecting either the "Time: xx uS" event or else the comment "Crashed and Burned"
Not sure why i am getting both.

The response i am getting
"
Starting Stopwatch...
Crashed and Burned
Time: 1 uS.
"

//assume I status Register is automatically enabled by Arduino IDE
// Timer 1 is a 16 bit timer that we can use for our stopwatch (65,536)
const byte interruptPin = 2;

volatile boolean TimedOut = false;
volatile boolean Success = false;

void Input_1_ISR()
{
  TCCR1B =  0x00; // first thing we do is stop counting on timer1 by setting prescaler to 0
  detachInterrupt(digitalPinToInterrupt(interruptPin));
  Success = true;
}

void startTiming ()
{

  // reset Timer 1
  TCCR1A = 0; // Houses compare settings plus how it counts, here we want to count 0 - max (normal mode )
  TCCR1B = 0;

  // We are stopping interrupts on timers 0 and 2 so other timing functions will not work but they will not interrupt us (millis etc)
  TIMSK0 = 0x00;   // Turn off all interrupts on Timer 0
  TIMSK2 = 0x00;   // Turn off all interrupts on Timer 2

  TIMSK1 = 0x01;   // Turn On Overflow interrupt on Timer 1 (16 bit timer) so i know if i missed my pulse

¬† // Timer 1 - our stopwatch , 16 MHz clock (62.5 ns per tick) - prescaled by 8, counter increments by 1 every 0.5 ¬Ķs.

  TCNT1 = 0;     // sets Timer1 Counter to zero

  // Reset prescalers
  GTCCR = bit (PSRASY);        // reset prescaler - seen other people doing it not sure why? data sheets has it greyed out???

  // start Timer 1
  TCCR1B =  0x02;  // Prescaler of 8 - so 1 tick is 62.5ns * 8 = 0.5 micro seconds
  digitalWrite(13, HIGH); //starts output signal to time the response


}  // end of startTiming


//******************************************************************
//  Timer1 Overflow service routine will trigger 16Mhz / (8 ticks per count due to prescaler) * (65536 number of ticks in 16 bit register) = 32.768ms (no prescaler would cause it to trigger every 4.096ms)
//  16Mhz / 128 / 125 = 1000 Hz
ISR (TIMER1_OVF_vect)
{
  TimedOut = true;
  TCCR1B =  0x00; // sets prescaler to 0 stoping timer1
}

void setup ()
{
  attachInterrupt(digitalPinToInterrupt(interruptPin), Input_1_ISR, RISING);

  Serial.begin(9600);
  Serial.println("Starting Stopwatch...");
}

void loop ()
{
  startTiming();

  while (!TimedOut || !Success) // Wait here until we either time out or we have a successful timed event
  { }

  //displaying the results
  if (TimedOut)
  {
    Serial.println ("Crashed and Burned");
  }
  if (Success)
  {
    Serial.print ("Time: ");
    Serial.print ((unsigned long) TCNT1 / 2); // need to divide TCNT1 by 2 because each tick is 0.5uS
    Serial.println (" uS.");
  }

  digitalWrite(13, LOW); //Stops output signal
  while (1); // just do this once for now
}

Would appreciate any guidance on where i should be looking.

P.S. Sorry for all the comments its all pretty new to me

//while (!TimedOut || !Success) // Wait here until we either time out or we have a successful timed event
  while (!TimedOut && !Success)

You will only leave the while loop when both are true. Use && to exit when either one is true.

Firstly thanks for your time, really appreciate your help.

Great point however that does raise two more questions,

  1. in the code posted how can i have both success and a timedout event since both ISR and overflow interrupt both stop the timer... so how can they ever both be true... thinking... so if the timer was to overflow, it would set the TOIE1 =1 and then continue counting. So that is why i was getting both 'Success' and 'TimedOut'?

  2. Second question is this line
    "
    GTCCR = bit (PSRASY);
    "

In the datasheet it is greyed out so can we still write to it?
I am guessing the reason why you would set this bit is so all prescalers are set to zero essentially stopping all timers which would mean no interrupts and not timing unless specifically turned on?

Thanks again for the help

  1. in the code posted how can i have both success and a timed out event since both ISR and overflow interrupt both stop the timer... so how can they ever both be true... thinking... so if the timer was to overflow, it would set the TOIE1 =1 and then continue counting. So that is why i was getting both 'Success' and 'TimedOut'?

You make an excellent point. In fact, when I tested your code I actually got neither "success" nor "timeout" printed output. This is what I expected with the || used.
With the && I can get either one.

Try with the output on some pin other than 13 for output, as the on board led can affect things with some boards. Second, set the pinMode explicitly to OUTPUT.

I am not clear on the settings in the GTCCR register. I have seen two different data sheets for the 328. One is greyed out like yours, and the other shows the quote below.

19.9.4. General Timer/Counter Control Register
When addressing I/O Registers as data space using LD and ST instructions, the provided offset must be
used. When using the I/O specific commands IN and OUT, the offset is reduced by 0x20, resulting in an
I/O address offset within 0x00 - 0x3F.
Name: GTCCR
Offset: 0x43
Reset: 0x00
Property:
When addressing as I/O Register: address offset is 0x23
Bit 7 6 5 4 3 2 1 0
TSM PSRASY PSRSYNC
Access R/W R/W R/W
Reset 0 0 0
Bit 7 ‚Äď TSM: Timer/Counter Synchronization Mode
Writing the TSM bit to one activates the Timer/Counter Synchronization mode. In this mode, the value
that is written to the PSRASY and PSRSYNC bits is kept, hence keeping the corresponding prescaler
reset signals asserted. This ensures that the corresponding Timer/Counters are halted and can be
configured to the same value without the risk of one of them advancing during configuration. When the
TSM bit is written to zero, the PSRASY and PSRSYNC bits are cleared by hardware, and the Timer/
Counters start counting simultaneously.
Bit 1 ‚Äď PSRASY: Prescaler Reset Timer/Counter2
When this bit is one, the Timer/Counter2 prescaler will be reset. This bit is normally cleared immediately
by hardware. If the bit is written when Timer/Counter2 is operating in asynchronous mode, the bit will
remain one until the prescaler has been reset. The bit will not be cleared by hardware if the TSM bit is
set.
Bit 0 ‚Äď PSRSYNC: Prescaler Reset
When this bit is one, Timer/Counter1 and Timer/Counter0 prescaler will be Reset. This bit is normally
cleared immediately by hardware, except if the TSM bit is set. Note that Timer/Counter1 and Timer/
Counter0 share the same prescaler and a reset of this prescaler will affect both timers.

'Comment Removed' - premature excitement :slight_smile:

Looks like i got ahead of myself, at first it looked like everything was working, consistent timing etc...

but the timing values seemed a bit off.

So i plugged Pin 11 straight into Digital Pin 2 (ISR) so there is no hardware delay and its outputting 7 uS.
As far as i am aware I am basically timing my ISR response with the following code and for an ISR to take 7 uS ( 7 * 2 * 8 = 112 clock cycles) seems too long?

My goal is to get 0.5uS or better timing resolution with an ISR

Again any help greatly appreciated :slight_smile:

Code:

//assume I status Register is automatically enabled by Arduino IDE
// Timer 1 is a 16 bit timer that we can use for our stopwatch (65,536)
const byte interruptPin2 = 2;

volatile boolean TimedOut = false;
volatile boolean Success = false;
volatile boolean TimerStarted = false;

void Input_1_ISR()
{
 TCCR1B =  0x00; // first thing we do is stop counting on timer1 by setting prescaler to 0
 detachInterrupt(digitalPinToInterrupt(interruptPin2));
 Success = true;
}

void startTiming ()
{

 // reset Timer 1
 TCCR1A = 0; // Houses compare settings plus how it counts, here we want to count 0 - max (normal mode is WGMxx bits set to 0 - WGM10, WGM11 are in TCCR1A, WGM12, WGM13 are in TCCR1B
 TCCR1B = 0;

 // We are stopping interrupts on timers 0 and 2 so other timing functions will not work but they will not interrupt us (millis etc)
 TIMSK0 = 0x00;   // Turn off all interrupts on Timer 0
 TIMSK2 = 0x00;   // Turn off all interrupts on Timer 2

 TIMSK1 = 0x01;   // Turn On Overflow interrupt on Timer 1 (16 bit timer) so i know if i missed my pulse

¬†// Timer 1 - our stopwatch , 16 MHz clock (62.5 ns per tick) - prescaled by 8, counter increments by 1 every 0.5 ¬Ķs.

 TCNT1 = 0;     // sets Timer1 Counter to zero

 // Reset prescalers
 GTCCR = bit (PSRASY);        // reset prescaler - seen other people doing it not sure why? data sheets has it greyed out???

 // start Timer 1
 TCCR1B =  0x02;  // Prescaler of 8 - so 1 tick is 62.5ns * 8 = 0.5 micro seconds
 digitalWrite(11, HIGH); //starts output signal to time the response


}  // end of startTiming


//******************************************************************
//  Timer1 Overflow service routine will trigger 16Mhz / (8 ticks per count due to prescaler) * (65536 number of ticks in 16 bit register) = 32.768ms (no prescaler would cause it to trigger every 4.096ms)
ISR (TIMER1_OVF_vect)
{
 TCCR1B =  0x00; // sets prescaler to 0 stoping timer1
 TimedOut = true;
}

void setup ()
{
 attachInterrupt(digitalPinToInterrupt(interruptPin2), Input_1_ISR, RISING);
 pinMode(11, OUTPUT); // used to manually test / debug

 Serial.begin(9600);
 Serial.println("Starting Stopwatch...");
}

void loop ()
{

 startTiming();
 
 while (!TimedOut && !Success) // Wait here until we either time out or we have a successful timed event
 {}
 //  displaying the results
 if (TimedOut)
 {
   Serial.println ("Crashed and Burned");
 }
 if (Success)
 {
   Serial.print ("Time: ");
   Serial.print ((unsigned long) TCNT1 / 2); // need to divide TCNT1 by 2 because each tick is 0.5uS
   Serial.println (" uS.");
 }
 digitalWrite(11, LOW); //Stops output signal

 while (1) // debug only do this once
 {
 }
}

Thanks

As far as i am aware I am basically timing my ISR response with the following code and for an ISR to take 7 uS ( 7 * 2 * 8 = 112 clock cycles) seems too long?

This seems reasonable considering what you are doing.

First issue is the use of digitalWrite(11,HIGH) after the timer has been started in startTiming(). This command can take between 3-4 microseconds on a non pwm pin, and slightly more on a pwm pin. You should replace this with a direct port manipulation.

D11 is PortB bit3. You can will find stated in the Atmel docs, that writing a bit high via PINx register TOGGLES that bit's state. So, if you just want to flip an output bit you can write to the input port

Since you know when pin 11 is low and high you can use this to turn it on and off

PINB = 0b00001000; // toggle bit 3

A second issue is the interrupt latency and time spent in the ISR itself. There is a good discussion of this in Nick Gammons tutorial on interrrupts Gammon Forum : Electronics : Microprocessors : Interrupts

Entering the ISR for an external interrupts takes something like 3 microseconds.

If you also read his tutorial on the timers you can see that there are faster methods using the input capture register.
https://www.gammon.com.au/timers

What exactly are you trying to do with your program? What do you need half microsecond timing for? You may need to consider using a faster processor.

For some reason I read 'pin change interrupts' in your comment (read about it elsewhere as well) and I did whole test on pin change it is WAY slower 76 uS instead of the 3uS using the 'old school' ISR. (FYI anyone out there wondering). I am guessing this is correct?

But I did learn more about PCINT's and how to use them.

Moving on to input capture...

Hello again,

A few questions if you don't mind.

  1. Now a side question - In my datasheet i cannot find any reference to the ISR (PCINT1_vect), i am sure Mr Nick knows what he is talking about, but still would like to see it in the datasheet. I am using the spark fun atmega 328 datasheet from here (https://www.sparkfun.com/datasheets/Components/SMD/ATMega328.pdf)

  2. I am measuring the time difference between a single point pulse and two physically separated sensors. 3uS - might be just fast enough for my application but if i can get to 1uS or better with the pin change interrupts that would be ideal.

  3. can i subtract the constant delay since i know that the Overhead (finishing current code line, then jumping to ISR etc) takes around 3uS with my current code. Would this dramatically change with code or stay reasonable constant?

Thanks again for the help, will finish my trial on the input capture registers tomorrow and report back (after work :frowning: )

P.S. I did order 4 x ESP8266 E12 with their 80-160Mhz clock that i intend to play on the Arduino IDE but with amazon shipping being what they are i decided to give the little Nano a fair shot at it :smiley:

For some reason I read 'pin change interrupts' in your comment (read about it elsewhere as well) and I did whole test on pin change it is WAY slower 76 uS instead of the 3uS using the 'old school' ISR. (FYI anyone out there wondering). I am guessing this is correct?

I'm not certain what you did, but pin change interrupts typically execute faster than external interrupts, but finding out which pin changed does add some overhead.

  1. Now a side question - In my datasheet i cannot find any reference to the ISR (PCINT1_vect), i am sure Mr Nick knows what he is talking about, but still would like to see it in the datasheet. I am using the spark fun atmega 328 datasheet from here (https://www.sparkfun.com/datasheets/Components/SMD/ATMega328.pdf)

I'm not certain what you are asking, but in your data sheet you can see the interrupt vectors listed in 9.4 Interrupt Vectors in ATmega328P Table 9.6

Those vectors get recognized in the Arduino environment by the code which is part of the "core" code which controls the processor. Deep in the weeds is an included file called interrupt.h which has this path in Windows

C:\Program Files (x86)\Arduino\hardware\tools\avr\avr\include\avr\interrupt.h

How it works and the syntax for calling the interrupt vectors is described here
https://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html

Interresting, so it would appear that i messed up somewhere then if the pin change interrupts should be faster.

... I did not really change much, just followed Mr Nick's tutorial on the pin change interrupts from the website you shared on interrupts... hmmm

Also since i am not using A0-A5 i don't have to figure out which one caused the interrupt because only one pin in the mask has PCINT enabled (A0)...

Not seeing where i messed up then.

Just note the output is now in nS. FIgured if we are getting lower it might pay just to use a prescaler of 1

  /*
    // Timer 1 is a 16 bit timer that we can use for our stopwatch (65,536)
    // Works down to 3 uS using conventional ISR's

    pin change interrupts
    registers:  PCICR (Pin Change Interrupt Control Register) - The pin change interrupt PCI2 will trigger if any enabled PCINT23..16 pin toggles.
                                                              The pin change interrupt PCI1 will trigger if any enabled PCINT14..8 pin toggles.
                                                              The pin change interrupt PCI0 will trigger if any enabled PCINT7..0 pin toggles.
              PCIFR (Pin Change Interrupt Flag Register) = bits 0,1,2 enables the interrupts in their 'groups'
              PCMSK (Pin Change Mask Register 0,2,3) --- PCMSK0 (0-7), PCMSK1 (8-14), PCMSK2 (16-23) - The PCMSK2, PCMSK1 and PCMSK0 Registers control which pins contribute to the pin change interrupts.

    // using http://gammon.com.au/interrupts --- Very Handy Table!!!

    D0    PCINT16 (PCMSK2 / PCIF2 / PCIE2)
    D1    PCINT17 (PCMSK2 / PCIF2 / PCIE2)
    D2    PCINT18 (PCMSK2 / PCIF2 / PCIE2)
    D3    PCINT19 (PCMSK2 / PCIF2 / PCIE2)
    D4    PCINT20 (PCMSK2 / PCIF2 / PCIE2)
    D5    PCINT21 (PCMSK2 / PCIF2 / PCIE2)
    D6    PCINT22 (PCMSK2 / PCIF2 / PCIE2)
    D7    PCINT23 (PCMSK2 / PCIF2 / PCIE2)
    D8    PCINT0  (PCMSK0 / PCIF0 / PCIE0)
    D9    PCINT1  (PCMSK0 / PCIF0 / PCIE0)
    D10   PCINT2  (PCMSK0 / PCIF0 / PCIE0)
    D11   PCINT3  (PCMSK0 / PCIF0 / PCIE0)
    D12   PCINT4  (PCMSK0 / PCIF0 / PCIE0)
    D13   PCINT5  (PCMSK0 / PCIF0 / PCIE0)
    A0    PCINT8  (PCMSK1 / PCIF1 / PCIE1)
    A1    PCINT9  (PCMSK1 / PCIF1 / PCIE1)
    A2    PCINT10 (PCMSK1 / PCIF1 / PCIE1)
    A3    PCINT11 (PCMSK1 / PCIF1 / PCIE1)
    A4    PCINT12 (PCMSK1 / PCIF1 / PCIE1)
    A5    PCINT13 (PCMSK1 / PCIF1 / PCIE1)
  */

  const byte interruptPin2 = 2;

volatile boolean TimedOut = false;
volatile boolean Success = false;
volatile boolean TimerStarted = false;

ISR (PCINT1_vect) // handle pin change interrupt for A0 to A5 here
{
  TCCR1B =  0x00; // first thing we do is stop counting on timer1 by setting prescaler to 0
  Success = true;
}

void startTiming ()
{

  // reset Timer 1
  TCCR1A = 0; // Houses compare settings plus how it counts, here we want to count 0 - max (normal mode is WGMxx bits set to 0 - WGM10, WGM11 are in TCCR1A, WGM12, WGM13 are in TCCR1B
  TCCR1B = 0;

  // We are stopping interrupts on timers 0 and 2 so other timing functions will not work but they will not interrupt us (millis etc)
  TIMSK0 = 0x00;   // Turn off all interrupts on Timer 0
  TIMSK2 = 0x00;   // Turn off all interrupts on Timer 2

  TIMSK1 = 0x01;   // Turn On Overflow interrupt on Timer 1 (16 bit timer) so i know if i missed my pulse

¬† // Timer 1 - our stopwatch , 16 MHz clock (62.5 ns per tick) - prescaled by 8, counter increments by 1 every 0.5 ¬Ķs.

  TCNT1 = 0;     // sets Timer1 Counter to zero
  TIMSK1 = 0x01;   // Turn On Overflow interrupt on Timer 1 (16 bit timer) so i know if i missed my pulse

  // Reset prescalers
  GTCCR = bit (PSRASY);        // reset prescaler - seen other people doing it not sure why? data sheets has it greyed out???

  // start Timer 1

  TCCR1B =  0x02;  // Prescaler of 1 - so 1 tick is 62.5ns
  PINB = 0b00001000; // toggle PortB bit 3 = D11


}  // end of startTiming


//******************************************************************
//  Timer1 Overflow service routine will trigger 16Mhz with no prescaler, overflow would cause it to trigger every 4.096ms
ISR (TIMER1_OVF_vect)
{
  TCCR1B =  0x00; // sets prescaler to 0 stoping timer1
  TimedOut = true;
}

void setup ()
{
  pinMode(11, OUTPUT); // used to manually test / debug

  // Setting up Pin CHange Interrupts
  PCMSK1 |= bit (PCINT8);  // want pin A0
  PCIFR  |= bit (PCIF1);   // clear any outstanding interrupts
  PCICR  |= bit (PCIE1);   // enable pin change interrupts for A0 to A5

  Serial.begin(9600);
  Serial.println("Starting Stopwatch...");
}

void loop ()
{

  startTiming();

  while (!TimedOut && !Success) // Wait here until we either time out or we have a successful timed event
  {}
  //  displaying the results
  if (TimedOut)
  {
    Serial.println ("Crashed and Burned");
  }
  if (Success)
  {
    Serial.print ("Time: ");
    Serial.print ((unsigned long) TCNT1 * 62.5); // each TCNT1 tick is 62.5 nS
    Serial.println (" nS.");
  }

  // i know i am creating a pin change interrupt here but for now its ok as it will only reset the prescaler to 0 and success to true
  PINB = 0b00001000; // toggle PortB bit 3 = D11

  while (1) // debug only do this once
  {
  }
}

well i am surprised!
Was able to Knocked it down to 1.5uS after a minor tweak to the code above.

Was neat to add another line of code (PIND = 0b00100000;) before i stopped my timer in PCINT1_vect and see how much longer it takes to execute, turns out it this takes two clock cycles so the output time increased by 2 * 62.5 = 125ns so the print out was 1.625uS :smiley:

Thanks again for the help :smiley:
Really learned a lot from your guidance