PaulStoffregen / TimerOne

TimerOne Library with optimization and expanded hardware support
http://www.pjrc.com/teensy/td_libs_TimerOne.html
470 stars 209 forks source link

TCNT1 = 0 causes unwanted interrupt #9

Open Swap-File opened 8 years ago

Swap-File commented 8 years ago

Every call to start(); appears to fire the interrupt when resetting the timer to zero on the Arduino (AVR).

This seems to have been mentioned in the code:

TCNT1 = 0;      // TODO: does this cause an undesired interrupt?

As a quick and dirty fix, I changed this to reset the timer to 1 instead of zero. This may not work for every application, but it seems like the original TimerOne hosted on google did something similar

https://code.google.com/archive/p/arduino-timerone/downloads

Around line 151, "wait until timer moved on from zero - otherwise get a phantom interrupt"

samcarson commented 8 years ago

Even though the data sheet does not explicitly state that starting the timer with 0x0000 in TCNT1 will set the overflow flag if does state the overflow flag will be set when TCNT1 = 0 while counting. Apparently the overflow flag will be set ANY time TCNT1 contains zero and the counter is running, i.e. any of the clock source control bits (CS10, CS11, CS12) are set and mode 8 is used. It would be nice if the datasheet stated this.

I can see no way other than to start TCNT1 at 0x0001. The first timer interval will be short 1 clock cycle, all after will be correct. The overflow flag should also be cleared by writing a 1 into it (I know this sounds funny, blame Atmel).

Other mode count up only. This would half the maximum possible interval and quite possibly screw up the PWM functions.

samcarson commented 8 years ago

Setting TCNT1 to 1 will not fix the problem. If the timer was previously counting down, setting the TCNT1 to 1 will count DOWN to 0 after 1 count - causing the TOV1 to be set after 1 timer tick. The data sheet does not specify how to guarantee the TCNT1 counts up when the timer is started. Simply setting the prescaler bits (thus starting the timer) doesn't do it.

jcottrell-ellex commented 5 years ago

This was a pretty big issue for me today. I wanted to get a simple one-shot timer interrupt working without digging too deep, but was stuck for a while because the first (and only) interrupt always happens immediately. I came up with the same "TCNT1 = 1" solution as Swap-File, but yes, that has its own issues. Maybe a different counting configuration is needed.

SteveGuidi commented 1 year ago

I too am trying to "extend" the length of a timer by calling start() from within the ISR. This issue is a real pain in the rear to deal with, especially for those (like me) who are just learning how to use timers. I've gone as far as manipulating the registers for timer operation directly and can repro the issue as I'm eventually setting TCNT1 = 0 from my "start" routine.

For reference, I looked at the implementation of the MsTimer2 library, which has a similar start/stop/init interface. This library will set the TCNT vector to the following expression in its start() method, which is generally never zero. Is it sensible to use the same technique for TCNT1 for Timer1?

tcnt2 = 256 - (int)((float)F_CPU * 0.001 / prescaler);

In other words:

TCNT1 = 65536 - (int)((float)F_CPU * 0.001 / prescaler);

The Timer libraries do a great job abstracting the meticulous register and bit manipulation required for timer use and it would be great if we can fix this.