ATMEGA328P TIMER2 running 2x slow?

Go To Last Post
12 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

 

First time post here migrating from the Arduino forums (be gentle!).

 

I'm building an alarm clock using the ATMEGA328P running at 8MHz internal clock at 3.3V, using Timer2 with a 32.768kHz watch crystal. Schematic and details here.

 

I'm scratching my head because my clock is almost exactly running at half speed.

 

I'm following AN 1219 which uses the following logic:

 

32.768 kHz = 2^15 Hz

/128 prescaler = 2^7

 

this gives 2^8 = 256 Hz, so an overflow should occur every 1s.

 

Here is the code snippet for setting everything up (full code is here):

ASSR |= (1<<AS2); //set Timer/counter2 to be asynchronous from the CPU clock
TCNT2 = 0; // reset Timer/counter2
TCCR2B = (1<<CS00)|(1<<CS02); //Prescale the timer to be clock source/128
while (ASSR & ((1<<TCN2UB)|(1<<OCR2AUB)|(1<<TCR2AUB)));  //Wait until TC2 is updated
TIMSK2 |= (1<<TOIE2); // enable overflow interrupt
sei(); // enable interrupts

I then use ISR(TIMER2_OVF_vect) as per the application note (increment seconds).

Possible explanations I have tried to rule out:

 

  1. Vcc of 3.3V is an issue (datasheet gives me 0 - 10MHz@2.7 - 5.5.V)
  2. Load capacitance issue of crystal - it has 6pF load capacitance so as per datasheet doesn't need extra capacitors. Also it's almost exactly 2x slow so binary issue IMO
  3. I've messed up the registers (most likely explanation)

 

Any help much appreciated,

Amadeus

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

I didn't look at any of your information, but remember if you tick (toggle) something at 100 Hz you now have 100 edges per second  ....50 rising and 50 falling...that's 50Hz frequency, not 100 Hz, so do keep in mind the difference between toggling and frequency.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Your time struct is shared between isr and mainline code - it must be declared volatile. You also access your time structure without any atomic protection - the isr may change values as you’re reading/writing them.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the suggestions.

 

I'm not entirely sure what was wrong with my implementation, but the following works well:

 

TCCR2B = 0;  //stop Timer 2
TIMSK2 = 0;  // disable Timer 2 interrupts
ASSR = (1<<AS2);  // select asynchronous operation of Timer2
TCNT2 = 0;      // clear Timer 2 counter
TCCR2A = 0;     //normal count up mode, no port output
TCCR2B = (1<<CS22) | (1<<CS20);   // select prescaler 128 => 1 sec between each overflow

while (ASSR & ((1<<TCN2UB)|(1<<TCR2BUB))); // wait for TCN2UB and TCR2BUB to clear

TIFR2 = 0xFF;     // clear all interrupt flags
TIMSK2 = (1<<TOIE2);  // enable Timer2 overflow interrupt
sei(); // enable interrupts

The only difference I can see is the use of = instead of |=, which shouldn't have made a difference as the initial values for the registers are 0s.

 

For the record I have measured ~75ppm accuracy.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

amadeus_z wrote:
The only difference I can see is the use of = instead of |=, which shouldn't have made a difference as the initial values for the registers are 0s.
Not always. For example in Arduino it uses some timers for it's own use so by the time you get to program values into them they may already be "live". Just one of the many reasons that = is preferable over != for first time initialisation. (same true if your code gets back to the 0 entry point for some other reason than power on reset - the peripherals may also be "live" in this case too.

 

BTW as pointed out above the usual reason for a "factor of 2" error when using timers is to recognise that a "cycle" is not just

+----+
|    |
|    |
+    +

but:

+----+
|    |
|    |
+    +----+

so it lasts twice as long as the timing period itself.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

amadeus_z wrote:
I've messed up the registers (most likely explanation)

No - Your TIMER2 configuration code is fine.

 

I looked at your circuit on github where you've swapped out the 16MHz crystal for a watch crystal: Therefore your CPU will run at 32kHz ; whilst this is perfectly feasible you must keep an eye on how long functions take to complete.

 

BTW: Your ISR contains two 16-bit integer divides this will take an absolute eternity at 32kHz. This is where your fault lies and must be designed out. You either need a faster implementation of IsLeapYear() or you will need to change your fuses to use the internal RC oscillator with external low power watch crystal, which in turn necessitates use of the sleep modes to obtain a decent battery life.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

N.Winterbottom wrote:
where you've swapped out the 16MHz crystal for a watch crystal: Therefore your CPU will run at 32kHz ;
I thought he said he was going to use async on T2 (and the setting of AS2 in ASSR seems to confirm this)? So presumably he's actually got the chip fused for "int RC" (and hence either1MHz or 8MHz depending on CKDIV8).

 

So the "running 2X slow" reported in the thread title is presumably because of the fallback from ext 16Mhz crystal to internal 8MHz ?

 

But wait a mo' - where in this was there ever a mention of 16MHz anyway (apart from post #6) ?

 

EDIT: oh sorry, I see, it is an "arduino project" (complete with INO) so presumably the issue is that it's using an Arduino "core" making a 16MHz assumption but because the Xtal pins have been hijacked for TOSC2 use then the fuses are set to "int RC" and it is that which dropped things form 16MHz to 8 MHz? (and hence X2).

Last Edited: Wed. Feb 10, 2021 - 03:53 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

amadeus_z wrote:
I'm not entirely sure what was wrong with my implementation

I had the same issue as your. Your example works for me as well. By digging more I have founded in atmel datasheet that the issue is with atmega registers, they are corrupted in some cases, I expected there will be same value as after reset, but when I reset registers by myself it "magically" started to works.

 

When the value of AS2 is changed, the contents of TCNT2, OCR2A, OCR2B, TCCR2A and TCCR2B
might be corrupted.

 

22.9. Asynchronous Operation of Timer/Counter2

When TC2 operates asynchronously, some considerations must be taken:
When switching between asynchronous and synchronous clocking of TC2, the registers TCNT2, OCR2x, and TCCR2x might be corrupted. A safe procedure for switching clock source is:

1. Disable the TC2 interrupts by clearing OCIE2x and TOIE2.
2. Select clock source by setting AS2 as appropriate.
3. Write new values to TCNT2, OCR2x, and TCCR2x.
4. To switch to asynchronous operation: Wait for TCN2xUB, OCR2xUB, and TCR2xUB.
5. Clear the TC2 Interrupt Flags.
6. Enable interrupts, if needed. 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What? are you guys assuming registers will have a preset value, even if you don't write them with what you want?

Power reset and physical reset pin activation "should" preset some registers with a default reset value.

The same when switching wide modes of operation, you need to be sure about the registers values, few lines of code may give you peace of mind, always.

Wagner Lipnharski
Orlando Florida USA

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

 

wagnerlip wrote:
are you guys assuming registers will have a preset value

They do - and the datasheet tells you what they are:

 

But, as clawson said, things like Arduino may have got to them first ... 

 

ADDENDUM

 

Although the initial value is very often all zeros (as above), that's not always the case; eg,

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Mon. Oct 11, 2021 - 03:27 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The AVR power-on-reset hardware sequence promotes several actions inside the chip, including setting up registers and counters.

It is expected that it follows the sequence correctly everytime the reset pin goes low for a certain quantity of clock pulses anytime power is stable, or during power-up.

We also expect a lot of other things to happen in life, machinery and electronics, by programming or design, but the fact that it can fail, at some point it will.

A noisy power supply, a bad ground connection, an electric discharge close to the device, a secondary chip unknowingly feeding power to the uC via GPIO, etc, would be enough to cause malfunction in several ways.

Voltage glitching is the most common known way to alter registers by external brute force, hacking proved that many times.

What I said in the previous post, it doesn't hurt to set registers you will use to a known value right at start, so you will not be cough by surprise. 

 

Wagner Lipnharski
Orlando Florida USA

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What I said in the previous post, it doesn't hurt to set registers you will use to a known value right at start, so you will not be cough by surprise. 

 

And in a extra critical app, you might assume you need to set them more than just at power up...you turn on the elevator controller and leave it on for 3 months & can't figure out why you keep ending up on the 10th floor of a 9 story building.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!