Indefinate timer

Hi
I have the code below and i was hoping someone could just have a quick check through it and see if it does what I think it does.
I basically want to record a maximum of ten timings when a user presses a button. I want to store those timings in positions 1 - 10 of an array, positions 0, 11 - 12 are for other things.
I think the code below should do that ( i want the timings compounded on the last one):

void data_collector()
{
	unsigned int datatable[30][12];
	int i, j;
	for (i = 0;i<30;i++)
	{
	for (j=0;j<12;j++)
	{
	datatable[i][j] = 0;//Filling all array posiitons with 0
	}
	}
	for (i = 0;i<30;i++)
	{
	//function to begin collecting data
	datatable[i][0] = i+1; //Column 1 of table is index number
	
		while (PIND&(1<<PD6));  // loop here until first button press
   			TCNT0 = 0;
   			timer_tick=0;// here is where we start timing
    		_delay_ms(10);  // delay for 1 ms to debounce button press
						
   		while (!(PIND&(1<<PD6)));  // loop here until button is released
   			_delay_ms(10);  // delay for 10 ms to debounce button release
   
   for (j=0;j<10;j++)//Filling columns 1 - 10
	{
   		while (PIND&(1<<PD6));  // loop here until second button press
   			datatable[i][j+1] = timer_tick;      //get the value of the millisecond counter
   			_delay_ms(10);  // delay for 1 ms to debounce button press
		while (!(PIND&(1<<PD6)));  // loop here until button is released
   
  			 _delay_ms(10);  // delay for 10 ms to debounce button release
	
	}
//	datatable[i][11] = //to be completed
	//datatable[i][12] = //to be completed
}
}

Does that make sense?
I am worried about the delays for debounces - are these an effective way to avoid debouncing? Will they mess up my timings?
If the user wants to pull out before 10 timings have been taken, (by pressing another button say) can I just use an if break; command within the for loop?
Lastly, it is waiting for the button release, for accurate timings though everything should happen on the button press right?
I basically want to know if you guys would go about this the same way?
Thanks guys

Hello.

Before I spend too much time looking at your code, can you tell me what microcontroller/board you are programming? Are you using an Orangutan?

Can you define what you mean by “timings” (e.g. what event is supposed to start the timer and what event stops it?) and let us know what level of timing accuracy is needed?

You don’t update the value of timer_tick anywhere, so I assume it is getting set by an ISR? If so, and the data type is an int or long (i.e. if it is more than one byte), you are not using it in an interrupt-safe way. That is because the interrupt that increments the tick value could occur while you are reading or writing to the variable, which would put it in a corrupted state. I suggest you make read and write accessor functions that temporarily disable either all interrupts or just the appropriate Timer 0 interrupts while you are reading or writing to the variable. Also, you should make sure there is no risk of timer_tick overflowing over the longest possible period you need to be able to time.

- Ben

Thanks for your reply Ben, yeah Orangutan Mega8.
I basically want to record the time between consecutive button presses. So the user will press a button 11 times, the microcontroller will time and record all the times between the button presses - i.e 10 readings. I would like to have it accurate to 3 dp if possible, I am going to use an external crystal because that makes the timings more accurate.
Yeah sorry I wasnt sure at the time of the post how it was understanding to update the value of timer_tick. But i do have the ISR code in there too:

ISR(TIMER0_OVF_vect)
{
   timer_tick++;
   TCNT0 = ~(125);         //preload counter to overflow after 1 ms (125 counts)
}

yeah it is being saved into an int, however I dont think I have overflow issues because the times Im talking about wont be more than about 30 seconds, however I am interested in what you are saying about it being interupt safe?
And that 125 in the line TCNT=~(125), does that number change depending on the frequency of the chip, i.e 8MHz or 16MHz?
Apart from the points you made is the method of saving the times to the array correct(ish)?
Thanks Ben
Alex

It’s probably not that important, and I think I know what you mean, but just to give you some feedback, this statement is frustratingly imprecise given the level of precision you want in your measurements. For example, do you care about the time from the release of one button to the press of another, or do you want the timer value to be captured every time the button makes initial contact?

I assume that “dp” means “decimal places”? Without specifying units, this is not a meaningful measure of accuracy. I’m guessing that you mean you want to measure the time in seconds to three decimal places (i.e. you want to measure the time to the nearest millisecond)?

Do you actually know how to clock the Orangutan from an external crystal? Please note that if you incorrectly change the clock fuse bits, you could permanently disable your Orangutan.

This seems more like a statement than a question, so I’m not sure what you’re trying to ask. I explained in my previous post how to make your code interrupt safe. If you don’t understand my explanation, please be more specific about what you don’t understand so I know what to rephrase or elaborate on.

Yes, the timer tick rate is a function of the CPU clock frequency.

I’ll try to remember to look at it more closely tomorrow, but at a quick glance it seems somewhat reasonable. Your questions make me think that someone else wrote this; is that the case? Have you actually tried it to see if it does what you want?

- Ben

Sorry Ben, I saw your reply and I was a work when I replied, rushing my answers. I can reply with more detail now.
I want the time between initial presses of the of the button. This is quite hard to ‘word’ but I dont want it to start timing when the button is released, I want it to start timing the moment the button is pressed, and to stop timing (and start the second timing) the moment the next time the button is pressed. - Like a reflex tester or something. By 3 dp yeah I would like to be able to measure millisecond accuracy, and I had been informed that using the internal oscillator is not accurate to milliseconds.
yeah I know how to wire up a crystal and adjust the fuses to use it. However I could see the pin connection on the orangutan, so I will keep the fuse as the internal for now, until I make up my circuit on a bread board in a week or so. However the reason I asked about the ~125 is because I want to use a mega168 when I move to a breadboard with a 16MHz crystal.

Sorry about the ‘interupt safe’ comment, I hadnt heard of this before and yeah the code was originally written by someone else, I just tried to modify it and understand it - I have tested it, and it works but I am not sure of its accuracy and I wanted to check with you guys here.
Could you explain how the 125 is calculated. I am assuming it is 1(second)/8(as in 8MHz) which gives the 125, however that would mean for 16MHz the value would be 62.5 which I dont think is right considering it is stored as an integer.

Does this mean you understand how to implement it now? If you care about accuracy of your data, this is an important change to make. I’m happy to explain it more you don’t understand, but I’d like you to give me more feedback about what is still confusing.

There are a lot of problems with what you’re doing here, and I strongly urge you to try to avoid this kind of general sloppiness as it will get you in trouble when writing programs and constructing circuits (I’m sorry if that sounds harsh, but I think developing good engineering practices will help you a lot in the long run). For one, 1/8 is not 125, and for another, you can’t just randomly combine units like that! All of these should be big red flags that you have no idea of the underlying process that is going on, and when you are in a state like that, you should seek to understand how it works rather than making assumptions (by the way, I do recognize that your asking questions here is an attempt to gain that understanding, but you can also find the necessary information in the ATmega8 datasheet).

In general, you need to figure out the time per timer tick, and then use that to compute how many ticks it takes to get one millisecond. If you know the timer frequency, you can compute the time per tick by taking the reciprocal. For example, if the timer ticks at 8 MHz (or 8 million cycles per second), the time per tick is 1 / (8000000 cycles/second) = 1.25e-7 seconds/cycle (or 12.5 microseconds/cycle). When you are doing calculations like this, it is really important that you do not get sloppy with your units. Unfortunately, you didn’t post the part of your code that initializes Timer 0, so I don’t know what rate it ticks at, which makes explaining this slightly harder, but I can work the math backwards from the 125 in your code to determine that it ticks once for every 64 ticks of the CPU clock, and, not surprisingly, it has a frequency of 8 MHz / 64 = 125 kHz.

To compute ticks per millisecond from the timer frequency, you need to solve the equation:

ticks per millisecond = timer frequency (in Hz, or ticks/s) / 1000 ms/s

If you change your CPU clock to 16 MHz, the timer frequency becomes 16 MHz / 64 = 250 kHz, and the ticks per millisecond becomes 250. This should be intuitively obvious to you. If you switch to a clock that ticks twice as fast, you get twice as many ticks over the same time period.

Your code looks to me like it will do what you want. One way you can test it is by replacing your button with an I/O line from another microcontroller that simulates button presses at precise intervals (i.e. goes low for 100 ms, goes high for 900 ms, repeat) and see (a) how consistent your timings are and (b) how closely they match the value you expect (1000 ms). Keep in mind that the factory calibration of the internal oscillator on the ATmega8 has an accuracy of ±10% and is both voltage- and temperature-dependent (so if you time a 1 second pulse while using the internal oscillator, your answer could be off by ±100 ms). You can calibrate the internal oscillator to get that accuracy to ±1%, but a crystal is probably the way to go if you want the kind of accuracy you’re talking about.

- Ben

Thanks Ben. Its late now, but I’ll read about the “interupt safe” in the morning.

When i stated 1/8 and 125 I meant that the two values have some relationship but I know 125 is not = 1/8. I find when I know the question and I have the answer I try to find some sort of relationship to see if I can work out how the answer came about. I’m asking the question here because I didnt get there. Thank you for the equation (and the solution…)

I have wired up a crystal, Im not gonna fiddle with trying to improve the internal oscillator. I’ll get back to you if I undertand or not about interupt safe…

Right New Day and a fresh coffee.
Interupt-Safe.
I understand that the timer ‘interupts’ whatever the microcontroller is doing every clock tick. When the button is pressed the timer begins. The one part of my code is I am not sure what line says begin interupting, however I understand that when it writes the value of the time to the variable, if the timer then interupts in the middle of that, the variable will contain corrupted data. After reading around I think I am supposed to have cli() before writing the variable and sei() after writing the variable?
The timer will still tick right in this time, As in saving the first value to the array and disabling interupts while this is happening will not affect the value of the second timing??
Does this line start the interupt timer?:

TCNT0 = 0;

And lastly for this to work sei() must be in the code somewhere to enable global interupts??
Am I close?

By the way this is where I initiialize my timer:

 TCNT0=~(124);//preload counter to overflow after 1 ms (125 counts)
   TIMSK=(1<<TOIE0);//enable interrupt on timer0 overflow
   TCCR0=(1<<CS01)|(1<<CS00);//set timer to normal mode with CPU clock/64 (125 kHz)
   

You’re mostly on the right track. This kind of stuff can be a little difficult to grasp if you aren’t familiar with what the AVR is doing at the instruction level. I can elaborate on this more if you are interested, but for now I’ll keep my response higher-level.

Yes, in order to make sure that the timer interrupt does not corrupt the value of timer_tick, you need to make sure it does not occur in the middle of a variable read or write, and that can be done by disabling interrupts before the read and re-enabling them after. For example:

void set_timer_tick(unsigned int value)
{
  cli();  // disable interrupts
  timer_tick = value;
  sei();  // re-enable interrupts
}

unsigned int get_timer_tick()
{
  unsigned int value;
  cli();  // disable interrupts
  value = timer_tick;
  sei();  // re-enable interrupts
  return value;
}

Then you would replace lines like:

timer_tick = 0;

datatable[i][j+1] = timer_tick;

with

set_timer_tick(0);

datatable[i][j+1] = get_timer_tick();

If you were to leave interrupts disabled for too long, you would start to miss timer_tick updates and your timer would become inaccurate. However, “too long” is the time it takes for two interrupts to occur, which in this case is more than two milliseconds. You are only disabling interrupts for perhaps a few hundred nanoseconds. If a timer update interrupt occurs while they are disabled, execution will jump into the ISR and the update will be performed as soon as you re-enable them.

I still haven’t seen the portion of your code that declares your timer_tick variable, but make sure you use the “volatile” attribute:

volatile unsigned int timer_tick;

“Volatile” tells the compiler to load the variable from RAM every time it is read and to write it to RAM every time it is written. If you have a variable that is written to in an ISR and read by your main program, it is very important that you let the compiler know by declaring it as volatile.

No, all that does is clear the timer counter. The timer is started by this line:

TCCR0=(1<<CS01)|(1<<CS00);//set timer to normal mode with CPU clock/64 (125 kHz)

And the timer overflow interrupt is enabled by this line:

TIMSK=(1<<TOIE0);//enable interrupt on timer0 overflow

However, the timer overflow interrupt will not occur until you also enable the global interrupt flag, which can be done by calling sei().

- Ben

Cheers Ben, I’ll try that in a bit.
one other question I had is that the times are stored as integers in milliseconds right? What if you want to do ‘maths’ on the times. SI units are in seconds, but I cant divide by 1000 because that would make it a double.

Yes, your code is storing the time as an integer number of milliseconds. The way you deal with it is by doing all your math with integer times in units of milliseconds, not seconds. You can use any units you want; you are not restricted to SI units.

- Ben