Timer1 and duration of instructions

Hello.
I'm in the middle of some project, where I must measure time between start of measurement and interrupt. Since Arduino UNO is running on 16MHz and there are a lot of single-cycle instructions, this should give me about 62.5ns resolution or so.

So my thinking was: I will use timer1 with no prescaller, start measurement, set timer to zero and than wait until hardware interrupt (low state on pin 2 or 3). In that ISR I should read the timer1 value and subtract time, that interrupt call needs. Or something similar, isn't really important, since values so far are clearly wrong or I'm really wrong somewhere in my thinking.

I make some experiment with timer1 and there are some strange values coming out of serial print. Here is the code:

const int LED = 13;
const int testPIN = 2;
void setup()
{
  Serial.begin(9600);
  pinMode(LED, OUTPUT);
  pinMode(testPIN, INPUT);
  digitalWrite(testPIN, HIGH);
  digitalWrite(LED, LOW);
  
  noInterrupts(); 
  TCCR1A = B00000000;  //timer control register A - default
  TCCR1B = B01000001;  //prescaler = 1; 
  TCCR1C = B00000000;  //timer control register C - default
  TCNT1 = 0; //inicitialize timer - set it to tero
  interrupts(); 
}

unsigned int timerOne1 = 0;
unsigned int timerOne2 = 0;
unsigned int test = 0;
int timeBasic = 0;

void loop()
{
  timeBasic = 0;
  cajt = 0;
  TCNT1 = 0;
  timerOne1 = TCNT1;
/*
  int x = 0;
  int y = 1;
  int z = 2;
  x = y + z;
  y = z + x;
  z = y + x;
  x = y + z;
  y = z + x;
  z = y + x;
  */
  timerOne2 = TCNT1;
  timeBasic = timerOne2 - timerOne1;
  Serial.println(timerOne1);
  Serial.println(timerOne2);
  Serial.print("Length of read 16bit register: ");
  Serial.println(timeBasic);

So, there is the strange thing.
The output (serial monitor) is:

1
9
Length of read 16bit register: 8

So, the first read after TCNT1 = 0 command length is 1, then the next one is 8 cycles later.

Notice the commented variable declarations of x, y and z. They don't have any special functions, but when they are uncommented, the output is the same. I thought TCNT1 would have greater value since uC is doing some instructions between two reads. Well it doesn't. I tried a bunch of other things with no success, so I hope here is someone who could explain me what is going on.

btw.: I read atmega328p datasheet and I know that TCNT1 is 16bit register combined from two 8bit registers TCNT1H and TCNT1L, which could be read separately. I did this too, with no succes.

Thanks.

Try this thread, I believe the need is similar:

http://forum.arduino.cc/index.php?topic=185297.0

Thanks, but I've already read it and it didn't resolve my problem. Actually, it is not the project itself I'm trying to solve, it is the fact that I clearly don't know enaugh to explain these wierd numbers.
For now I'm determined to measure clock cycles for basic functions like(read register, digitalRead, digitalWrite, analogRead, analogWrite, If, while, ....). I know how to use input/output compare registers and overflows, but I would like to do this without it. Timer1 should have a range of 4ms or so before overflow, so I think any simple instruction should be measured with this timer with no prescaler.

I would try reading TCNT1 several times in succession before printing all the results.

If you are not getting incrementing values the timer isn't running.

You could try putting a microsecond delay between reads.

...R

schperplata:
So my thinking was: I will use timer1 with no prescaller, start measurement, set timer to zero and than wait until hardware interrupt (low state on pin 2 or 3). In that ISR I should read the timer1 value and subtract time, that interrupt call needs.

Or, go whole hog and use Input Capture.

Notice the commented variable declarations of x, y and z. They don't have any special functions, but when they are uncommented, the output is the same.

They are so not-special that the optimizer completely removes them.

schperplata:
So my thinking was: I will use timer1 with no prescaller, start measurement, set timer to zero and than wait until hardware interrupt (low state on pin 2 or 3). In that ISR I should read the timer1 value and subtract time, that interrupt call needs. Or something similar, isn’t really important, since values so far are clearly wrong or I’m really wrong somewhere in my thinking.

Timer1 can do all that in hardware without the overhead of an interrupt. Look at “input capture” in the datasheet.

I would try reading TCNT1 several times in succession before printing all the results.
If you are not getting incrementing values the timer isn't running.
You could try putting a microsecond delay between reads.

Yes, I tried that jet, but there is always 8 cycles between each reading. Like:

  TCNT1 = 0;
  timerOne1 = TCNT1;
  timerOne2 = TCNT1;
  test = TCNT1;
  timeBasic = TCNT1;
  cajt = TCNT1;
  timerOne1 = TCNT1;
  timerOne2 = TCNT1;
  timeBasic = timerOne2 - timerOne1;

timeBasic always return 8. That is what is bothering me. I want to read in one/two cycles as it says in datasheet, or I'm missing something. If I put digitalRead between readings, which needs a lot more time, timer increments to 59.

So my thinking was: I will use timer1 with no prescaller, start measurement, set timer to zero and than wait until hardware interrupt (low state on pin 2 or 3). In that ISR I should read the timer1 value and subtract time, that interrupt call needs.

Or, go whole hog and use Input Capture.

Yes of course. :slight_smile:

Notice the commented variable declarations of x, y and z. They don't have any special functions, but when they are uncommented, the output is the same.

They are so not special that the optimizer completely removes them.

Oh, that is silly. But anyway. In real code on microcontroller, they must be declared as it is in code?

  TCNT1 = 0;
  timerOne1 = TCNT1;
  int x = 0;
  int y = 1;
  int z = 2;
/* some nonsense calculation with those variables */
  timerOne2 = TCNT1;

That means, there should be some couple of cycles after reading timerOne2 = TCNT1 or what?

As I said previously, I can't understand why there is 8 cycles between two readings. First reading is always after one cycle.

Moderator edit: tags corrected

schperplata:
Oh, that is silly. But anyway. In real code on microcontroller, they must be declared as it is in code?

That means that in real code on the microcontroller, the instructions simply do not exist, because the optimiser has determined that they have no consequence, and completely removed them from your code. That is (amongst other things) what an optimiser does, believe it or not.

This has interesting implications, because the same "optimiser" has now been discovered to remove deliberate security code from networking applications across the Internet in the same manner because it determined the code had no "actual effect", consequently causing major security breaches.

  TCNT1 = 0;
 11c:	e4 e8       	ldi	r30, 0x84	; 132
 11e:	f0 e0       	ldi	r31, 0x00	; 0
 120:	11 82       	std	Z+1, r1	; 0x01
 122:	10 82       	st	Z, r1
  timerOne1 = TCNT1;
 124:	60 81       	ld	r22, Z
 126:	71 81       	ldd	r23, Z+1	; 0x01
 128:	70 93 31 01 	sts	0x0131, r23
 12c:	60 93 30 01 	sts	0x0130, r22
  timerOne2 = TCNT1;
 130:	80 81       	ld	r24, Z
 132:	91 81       	ldd	r25, Z+1	; 0x01
 134:	90 93 33 01 	sts	0x0133, r25
 138:	80 93 32 01 	sts	0x0132, r24

Notice that there are 4 two clock cycle instructions between the reads. 4x2=8clock cycles.

On the other hand, if you ditch the global variables 'timerOne1' and 'timerOne2' and make them local variables to the loop function you get:

  TCNT1 = 0;
 120:	e4 e8       	ldi	r30, 0x84	; 132
 122:	f0 e0       	ldi	r31, 0x00	; 0
 124:	11 82       	std	Z+1, r1	; 0x01
 126:	10 82       	st	Z, r1
  timerOne1 = TCNT1;
 128:	60 81       	ld	r22, Z
 12a:	71 81       	ldd	r23, Z+1	; 0x01
  timerOne2 = TCNT1;
 12c:	e0 80       	ld	r14, Z
 12e:	f1 80       	ldd	r15, Z+1	; 0x01

Now there are only 2 two clock cycle instructions, so a delay of 4 clocks.

Furthermore, if millis() just so happened to execute in between the two reads (or a character is received via serial, or any other interrupt), then it will take much longer.

schperplata:
Yes, I tried that jet, but there is always 8 cycles between each reading. Like:

  TCNT1 = 0;

timerOne1 = TCNT1;
 timerOne2 = TCNT1;
 test = TCNT1;
 timeBasic = TCNT1;
 cajt = TCNT1;
 timerOne1 = TCNT1;
 timerOne2 = TCNT1;
 timeBasic = timerOne2 - timerOne1;



timeBasic always return 8. That is what is bothering me. I want to read in one/two cycles as it says in datasheet, or I'm missing something. If I put digitalRead between readings, which needs a lot more time, timer increments to 59.

It looks like this is not what I had in mind. Your code seems to read the data into the same variables several times thus overwriting the earlier values. I mean something like this

timerOne1 = TCNT1;
timerOne2 = TCNT1;
timerOne3 = TCNT1;
timerOne4 = TCNT1;
timerOne5 = TCNT1;

and then just printing the raw values - not calculated values.

...R

An alternative approach would be:

TCCR1B = 0;  //stop the clock; 
  TCNT1=0;
  cli(); //disable interrupts
  TCCR1B = 0b01000001;  //start the clock;
  
  // Code to time:
  
  TCCR1B = 0; //stop the clock straight after
  sei(); //reenable interrupts
  unsigned int cycles = TCNT1;
  cycles -= 2; //account for time taken to stop the clock
  Serial.print("Clock cycles taken: ");
  Serial.print(cycles);

Tom, thanks! This explains everything. So, the lesson is: check the assembler code.
Then, the values were all right, except, why do I get value of timer

  TCNT1 = 0;
  timerOne1 = TCNT1;
  timerOne2 = TCNT1;

timerOne1 is always 1,
timerOne2 is then as it should be 9. The diference between 2-1 is 8, what is OK. But, why is timerOne1 = 1?

st ← upper 8bit stored to temp register
st ← lower 8bit written timer. upper 8bit written to timer also
lds ← upper 8bit loaded into temp register. Lower 8bit returned by lds
lds ← upper 8bit returned by lds

After the second clock cycle of the second st instruction the 16 bit register is written.
After the first clock cycle of the first lds instruction, the 16bit register is copied

So the difference is 1 clock cycle.