CTC interrupt timing problem? [BONUS Poetry by Johan!]

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

OK, this is not the first time I have used timers by any means, but for some reason I can't figure this one out. Basically I had a 10MHz crystal hooked up to an ATMega48 and was trying to keep accurate time, but every hour or so I'd be loosing a second. On top of that I want multiple interrupts per second anyway, so I set up timer 1 in CTC mode, enabled interrupts on compare with OCRA1, set up the interrupt etc. Still I lost seconds, so figuring perhaps the crystal was bad I changed it to the only other value I had at hand (because Mouser decided not to send me the other 5 values I had ordered :evil:) which was 20MHz.

When I put the power in the result was to be as expected, one seconds worth of interrupts went by in half that time. So I decided to adjust the timer for the new 20MHz clock and see if I could get things going without loosing time. Here is how my calculations broke down:
Prescale by 8 in hardware fuse is off.
Timer Control prescaler is set to 256, so 20MHz / 256 = means 78125 ticks per second (yes?).
Output Compare set to 625, so for every 625 ticks an interrupt will fire and the Timer cleared such that there are 125 interrupts per second.

Here is how I have set the registers in C, which I believe should reflect the above settings for the ATMega48:

	TCCR1B |= ((1 << WGM12)); //timer 1 CTC mode
	TIMSK1 |= (1 << OCIE1A); //enable intterupt on CTC (Output Compare Interrupt Enable on register 1A (OCR1A))
	OCR1A = 625; //compare value (16 bits)
	TCCR1B |= ((1 << CS12) | (1 << CS10)); //timer start. timer at external 20mhz / 256 (78125 counts per second)

However, counting to 125 and incrementing the seconds results in something like 3 seconds per increment. This is obviously not correct, and just to double check I have switched out clock crystals, uCs etc. So I'm pretty sure I'm doing something stupid and it is not the hardware.

I'm catching TIMER1_COMPA_vect by the way, and yes all variables manipulated in the SPI statement are volatile.

So, any ideas?

Last Edited: Mon. Sep 22, 2008 - 09:02 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Output Compare set to 625, so for every 625 ticks an interrupt will fire

Not quite. Every 626 ticks the timer fires--both 0 and 625 are counted.

But then you should gain time, not lose time.

I think we'd need to see your ISR and other time-keeping code.

I use "avrcalc" to aid in these calculations. kmr has a program as well. For 125 "Hz" w/ 20MHz/256, AVRCALC indeed comes up with 625, so 624 would be the right OCR value.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Mon. Sep 22, 2008 - 05:21 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm not sure why you get the 3 seconds, but I think I know why you were losing the 1 second an hour. The compare value is 1 too high. So in the calculation for 20MHz it would be 624, not 625.

Regards,
Steve A.

The Board helps those that help themselves.

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

624, that makes too much sense.... I've returned the system to 10MHz, prescaler at 64 for 156250 counts per second, output compare register 1A is now at 624, for a total of 250 interrupts per second (correct?).

my ISR in a nutshell:

	SubSeconds++;

	if (SubSeconds >= TOTAL_SUBSECS_PER_SEC)
	{
		Seconds++;
		SubSeconds = 0;
.....

TOTAL_SUBSECS_PER_SEC is currently set to 250. Since 250 is the wrap around and I am incrementing before the compare I am thinking this should be 250 and not 249, but my brain is now doubting me. I'm running this revised setup now to check accuracy. If it is completely accurate that will be the end of it for now, but I am still quite curious as to why the 20MHz setup was so quirky.

Thanks theusch and Koshchi for being better, smarter men than me (I'm assuming you are both men, if not sorry).

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

Quote:

I'm assuming you are both men

Biting my toungue.. Oh, the temptation.. Must resist..

There, managed to hold the really dirty things back. Time for another limerick, though..

Kagetsuki, a freak from Japan
Tried to tell if 'ol Lee was a man
He could do math in hex
but not tell Theusch's sex
That is why he refrained from Lee-san

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

Wouldn't a haiku be more pertinent?

Regards,
Steve A.

The Board helps those that help themselves.

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

I have had pictures posted in my avatar for a number of years, but right now they are lost in the morass of the Forum. No offense one way or the other; I'm comfortable with my sexuality.

Attachment(s): 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Steve wrote:

Wouldn't a haiku be more pertinent?

It was not a matter of pertinece, but of ability. Now stop whining, or I will restart working on "your" Limerick. I didn't get it together properly and let it go, but it's right there on the floor, half-done, just for me to pick up and continue working on... 8)

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

Johan, that was awesome. I've updated the thread title.

Perhaps we should start a thread (in the off-topic forum) for bad poetry and haiku if there isn't one already.

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

Johan,
That limerick was one of the funniest things I have read in a long time. NICE JOB!!

Jim

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

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

Kagetsku, good plan and thanks for updating the thread-title. Would have been a shame to mis it :lol:

Nard

A GIF is worth a thousend words   They are called Rosa, Sylvia, Tricia, and Ulyana. You can find them https://www.linuxmint.com/

Dragon broken ? http://aplomb.nl/TechStuff/Dragon/Dragon.html for how-to-fix tips

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

There once was a young man from Lund
By photo in no way rotund
For Lee a new rhyme
With meaning sublime
Reply is "I've been out-punned!"

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Quote:

Johan, that was awesome.

The punch line wouldnt have come to me if you didn't teach us all about the uses of "-san" and other japanese "politeness suffixes" last week. AVRfreaks is a great well of knowledge and fun!

Quote:
There once was a young man from Lund

For once you've got your facts messed up, but I thank you. I am young at heart, though..

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

I hate to re-rail the thread, but I seem to be loosing about a half a second each hour. Is it normal for a 10MHz crystal to be just a bit off? Have I mis-accounted for something? This particular project is the first one I have had to actually have a working clock that keeps people-time incorporated into, so I'm not sure if this is to be expected with a standard crystal. I supposed I could use an actual dedicated RTC, but if it is not necessary I would like to avoid it.

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

A standard crystal is usually within 60 parts per million. There are about two and a half million seconds in a month, so it should be better than two and a half minutes per month; around five seconds a day.

Twelve seconds a day, hmmm... could be that you're pulling the crystal frequency a *lot*, I suppose. But I'd be suspicious of the code, myself :)

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

barnacle wrote:
A standard crystal is usually within 60 parts per million. There are about two and a half million seconds in a month, so it should be better than two and a half minutes per month; around five seconds a day.

Twelve seconds a day, hmmm... could be that you're pulling the crystal frequency a *lot*, I suppose. But I'd be suspicious of the code, myself :)

I'm personally suspicious of my code myself, but I just wanted to make sure I wasn't pouring over it in vein. I should note that at the moment this is all in a solderless breadboard and I've got a lot of long wires and untrimmed leads, so I guess perhaps I could be getting some interruptions or vibrations from the poor setup. In the end, I'd like to avoid loosing 12 seconds a day if possible. However even if I loose 12 seconds a day it would not be the end of the world. If in the end the 12 seconds a day actually does turn out to be inadequate would you recommend getting an RTC module hooked up to the design to keep time? Would that perform any better?

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

You should be able to determine if the code is the villain here by running the code in the simulator and looking at the stop watch.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

JohanEkdahl wrote:
You should be able to determine if the code is the villain here by running the code in the simulator and looking at the stop watch.

That's an interesting suggestion. Do you recommend a particular simulator? As it is I am just starting the physical system running in sync with a stop watch. Running the code in the simulator would in fact give me an indication as the simulator should be a perfect environment unaffected by things like EMF and resonance in the wires etc. Thanks for the tip!

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

When I played around with my RTC I just tried it out in the Simulator in AVR Studio. Made sure that the simulator knew the actual CPU frequency, placed a breakpoint at the start of the ISR for the timer interrupt. Ran to there. Did a reset on the stop watch and the CPU cycle counter. Let the thing loose again and waited for the breakpoint in the ISR to be hit again. Now I have timing with both microsecond resolution (ie ppm) and CPU cycles for onee timer interrupt period.

If you are interrupt driven you can do this with a minimal application with just the ISR and not much else.

Did I make myself understandable?

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

Johan, you were very clear, thank you! Though this particular application is not primarily a clock. It is amongst other things driving a somewhat unusual display and occasionally handling some proprietary serial communication. I'm now starting to think this much time loss may actually be unacceptable and am considering adding a RTC such as the DS1307 to my design. If you were to want to keep accurate time would you do the same? Or would you press forward with my current approach of using a watch crystal and interrupts?

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

I would definitively start with determining if/why your code generates that drift.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

Point taken :) . Thanks!

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

Quote:
If you were to want to keep accurate time would you do the same?
If you want to keep accurate time even a DS1307 would NOT help you, UNLESS you use something like a DS32KHz TXCO to provide accurate, temperature compensated clock.

In fact if you use a DS32KHz TXCO as you timing reference AND your code is correct I would expect very accurate time. I have used a combination of DS1307 and DS32KHz TXCO with good results. My current project uses a DS3232 RTC which has a built in TXCO and also runs with a battery back up. I have had a board off for several months and when turned back on the clock was still deadly accurate.

Of course it depends if your project can afford the cost of the RTC chip.

John Samperi

Ampertronics Pty. Ltd.

https://www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

js, that is very interesting to hear. Thank you very much for the information and your experiences. I'll be picking up a DS32KHz TXCO immediately and, of course, making sure there are no problems with my code. I may throw in the RTC and take some of the load of the uC while adding a date feature as well. Had you not given me the advice you did just now I would have just had an additional date feature with equally bad accuracy, which would have been lame. Thank you thank you thank you.

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

Kagetsuki - all you need is the breakpoint at the start of the interrupt routine, I think f9 at the appropriate cursor position will toggle it for you.

However, there's a gotcha - the interrupt doesn't happen until the current instruction completes, or for four cycles if the processor happens to be asleep. What this means is that the interval between two consecutive interrupts might vary by up to four clock cycles - two hundred nanoseconds at 20MHz (if I got by sums right).

So, an interrupt at (say) 25000 clock cycles (= 1.25ms) might occur at 1.25 +/- 0.0002ms - which will look at first as if your clock has variable timing.

But add them up over ten or a dozen (or better, more) loops and see if they average out. Better, implement the complete code and let the simulator run till a minute or an hour (this might take some time!) has completed.

A way I've used frequently is just to use the master crystal at 8 or 16MHz and divide that down with timer1 in CTC mode - for 20MHz crystal, I'd set the OCRA to 19999 to give a one millisecond tick, prescale the clock by 8 to give an 8 millisecond interrupt, and count 125 ticks in a memory or register location - that gives you a reliable, accurate, and fast interrupt which will hold HMS and ticks. It's as accurate as your crystal.

I've never used the external clock input but it's only a matter of selecting the clock reference, I think, and in this case I'd probably not bother with a prescale, just set OCRA to 32767 (0x7fff) and let it get on with it. One interrupt a second, again, as accurate as the external source.

Neil

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

With apologies to Elizabeth Barrett Browning

Quote:
How do I time thee? Let me count the tics.

I count thee when the over flow doth trip
And interrupts my running code,
And then returns to do its mundane task.

I count thee when you match my preset val
And may or may not start again at zip.

I count thee freely, until my ints may overflow;
I count thee purely, as only nils and ones can do.

I count thee with a passion put to use
In date determination, and to simply know the time.

I count thee with some code that seems to lose
Some minor tics, yet catches most withall.
And, if God choose, I shall now find
These little bugs that bite and fret me still.

Chuck Baird

"I wish I were dumber so I could be more certain about my opinions. It looks fun." -- Scott Adams

http://www.cbaird.org

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

Thanks Chuck. That made my day. :lol: I'm forwarding it to my sister who used to be an English teacher. :roll:

If you think education is expensive, try ignorance.

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

We have now reached the point of geek-poetry where a pointer to one of the official TCP/IP documents must be linked to. No really, its a genuine RFC, and the morale of this piece of poetry is applicable to the everyday troubles of all 'freaks.

Enjoy: http://www.isi.edu/in-notes/rfc9...

And, Chuck: Though my lack in education when it comes to English/American litterature that was one beautiful thing you created. Are you wasting a talent or do you actually write poetry? If I would fall for an english speaking girl, could I hire you to write the poems to bag her? (Why did Cyrano de Bergerac suddenly come to mind?)

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

Quote:
But add them up over ten or a dozen (or better, more) loops and see if they average out.

No need to do this. Just look at the timer value of two consecutive interrupts and subtract the difference from the cycle count.

Regards,
Steve A.

The Board helps those that help themselves.

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

Chuck, that was genius!

Regards,
Steve A.

The Board helps those that help themselves.

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

Barnacle, thank you, you've given me some ideas and things to think about. In this case the micro is never going to sleep, but it is constantly executing code. I knew the interrupt occurred after the current instruction, and the code I have running has quite a few comparisons and a few 16bit memory reads and writes etc. It would definitely be reasonable to assume the interrupt is hitting these and I'm getting multiple cycles worth of lost time.

Since I pushed the clock back to the 10MHz crystal I'm assuming the cycle time would double from what you stated of 0.0002ms to 0.0004. I have 250 interrupts per second on the current setup, so that's something like 0.1ms of lost time per second. That's 6ms per minute, 360ms per hour. I seem to be loosing just a little under a half second per hour, and 360ms feels like it would be fitting just around there. This calculation assuming you would only be loosing time, not gaining it. You stated +/- 0.0002ms which I'm currently having trouble wrapping my brain around as I'm not able to fathom how you would get a negative delay between the interrupt fire and the SPI (I must be thinking about this wrong?).

I'm still blaming my code until I can scientifically prove otherwise, so I intend to run it all through the simulator as soon as I can. My notebook blew a fan out and is in for warranty repair, the box I'm using now is actually a 12 year old server. Once I get my notebook back I'll be able to run the simulator and will report on my findings in detail, and hopefully be able to find anything in my code that is causing un-needed delay.

Other than that, in a nutshell what you are suggesting I do (assuming I have not misunderstood) is to use the faster 20MHz crystal, which will have less real loss time if there is a delay before the interrupt fire, and decrease the amount of interrupts as much as is reasonably possible to decrease the amount of chances of loss per second. Then, in addition to this add up the amount of counts at each actual firing and see how all the +s and -s average out and if that is a significant trend one way or the other. Then, use supplemental code to artificially fix that trend (maybe?). Had you not pointed it out I would not have considered it. I will now proceed to pound my head against my desk to ensure the concept packed in there. Thank you :) .

Last Edited: Tue. Sep 23, 2008 - 10:14 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Are you wasting a talent or do you actually write poetry?
No
No

The original:

Quote:
XLIII. "How do I love thee? Let me count the ways..."
by Elizabeth Barrett Browning (1806-1861)

How do I love thee? Let me count the ways.
I love thee to the depth and breadth and height
My soul can reach, when feeling out of sight
For the ends of Being and ideal Grace.
I love thee to the level of everyday's
Most quiet need, by sun and candle-light.
I love thee freely, as men strive for Right;
I love thee purely, as they turn from Praise.
I love thee with a passion put to use
In my old griefs, and with my childhood's faith.
I love thee with a love I seemed to lose
With my lost saints, --- I love thee with the breath,
Smiles, tears, of all my life! --- and, if God choose,
I shall but love thee better after death.

Chuck Baird

"I wish I were dumber so I could be more certain about my opinions. It looks fun." -- Scott Adams

http://www.cbaird.org

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

Koshchi wrote:
Quote:
But add them up over ten or a dozen (or better, more) loops and see if they average out.

No need to do this. Just look at the timer value of two consecutive interrupts and subtract the difference from the cycle count.

Interesting, but I'm a bit confused. I thought barnacle was suggesting I add up all the gains and losses in time to see if they would actually be detracting from my clock performance significantly and by taking that average gain or loss I could try and artificially supplement the timing procedure to make it "sort of" more accurate.

Thinking about this, in CTC mode, when the interrupt flag is set, is the counter set back to 0 immediately or is it set to 0 when the SPI is processed? If it gets set to 0 when the flag gets set and counting begins immediately we could take in that count and and subtract the difference with the previous count from the counter, but if the SPI is always being executed and ending between counts anyway it seems like we'd be pointlessly subtracting. The problem here is then not that we are loosing time between the interrupt firing and the SPI, but that the interrupt does not fire exactly on the compare value in some cases as the compare occurs after the current instruction finishes (correct?). So if we have a compare value of 20, and there is an instruction taking 3 cycles that just started, the compare may not occur till 22, which would then fire the interrupt and set the counter back to 0 despite the fact we just lost 2 cycles... I think? I'm now very confused, I'll go bury my head in the data sheets for a while and see if I can sort this out.

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

AH! I just got it. Start two timers, have the primary one set to interrupt on compare. At the beginning of the SPI copy the value in timer #2, then clear timer #2. Use the value I just derived and stored to either make an average for comparison or check against the last value to determine how many cycles worth of time were lost going to interrupt.

Now I think I understand the methodology Barnacle and Koschi were trying to present to me. You guys are awesome.

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

I may have been overcomplicated... what I was trying to say is that the interrupt will fire at slightly different times because of the delay, but that the number of ticks will still be correct *provided you have correctly calculated the interval*.

YOU NEVER NEED TO ADJUST THE BASIC TIMER ROUTINE. Apologies if I have caused confusion here.

Think of a clock. The second hand goes around reliably and accurately, once a minute. But the minute hand is pushed by some mechanical process which jitters. Every time the second hand reaches 12 the minute hand is advanced, but it might happen a couple of seconds late. It's *never* more than a couple of seconds wrong, no?

With the timer (10MHz, now) in CTC mode, remember that it will count up to the value in OCRA and reset in the *next* clock cycle - so if you have the OCRA set to '5', it will count 0-1-2-3-4-5-0-1-2-3-4-5 etc.

So to calculate the clock, first decide how many ticks you want in a second - easiest if you pick a number between 1 and 255. Let's stick with 125; it's a nice round number.

So, 125 ticks a second is an interval of 4,000 microseconds; at 10MHz crystal that's 40,000 ticks. That's a smaller number than the size of the timer one register on most (all?) AVRs, so you can set the prescaler to 1 (that is, no division of the system clock) and put 39,999 in OCR1A. You'll need to set TIMSK and TCCR to arrange interrupts and CTC counter mode.

For investigation, all your interrupt routine needs to do is return; for an actual clock you would basically say in the interrupt code:

ticks++;
if (ticks == 125)
{
  ticks = 0;
  seconds++;
  if (seconds == 60)
  {
    seconds = 0;
    minutes++;
  // you can see where this is going!
  }
}

where ticks, minutes, and seconds are global volatile variables.

This will *work*. The only place where it won't work is if you get yourself into a place where the interrupts are disabled for longer than one tick - basically, in another interrupt. You should almost never let yourself get stuck in an interrupt for that length of time; but that's another story. If the timer is the only interrupt going, or your other interrupts are well managed, then the worst that happens is that the time gets delayed on one tick and catches up later.

If you *still* have a timing error then it's down to the timing reference, not the code.

Neil

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

Barnacle, I have only one interrupt going, and the code snippet you provided looks almost exactly like what I have for comparisons only I am using prescalers, have 250 interrupts per second, and compare with >= instead of == ... you know, just in case. Also, I capitalize the first letter of global variables, and I named my variable "ticks" "SubSeconds".

Also, we use the exact same indenting style and I put spaces before the parenthesis on statements as well. I think we would get along very well in a coding team.

Back to the subject, I have another question but this one has no actual direct impact to what I am doing at the moment: you said interrupts are disabled during other interrupts, this does not mean a counter or something like that would be disabled correct? In other words if you had timer 2 fire an interrupt and while you are processing that timer interrupt 1 fired the interrupt 1 flag would be set and timer 1 would begin counting from 0 again while interrupt 2 was still being processed... yes? Then interrupt 1 would be picked up when the ISR for interrupt 2 ended... correct?

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

Quote:
this does not mean a counter or something like that would be disabled correct?

No, the device is not disabled, only the global interrupt flag is cleared during an ISR.

Quote:
In other words if you had timer 2 fire an interrupt and while you are processing that timer interrupt 1 fired the interrupt 1 flag would be set and timer 1 would begin counting from 0 again while interrupt 2 was still being processed... yes? Then interrupt 1 would be picked up when the ISR for interrupt 2 ended... correct?

Yes and yes.

Regards,
Steve A.

The Board helps those that help themselves.

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

As Steve says. The interrupts will be dealt with in order of priority as each interrupt returns and automatically re-enables the interrupts. Which is why it's only a problem if another interrupt holds things up for more than one tick period.

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

Excellent, got it. Thanks.

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

Quote:

considering adding a RTC such as the DS1307 to my design. If you were to want to keep accurate time would you do the same? Or would you press forward with my current approach of using a watch crystal and interrupts?

Unless you take the extra step of a [more expensive] "cooked" oscillator, and have some sort of temp comp scheme, and/or use an RTC chip that has a "cal" function, and/or tweak your load caps there should be no significant difference in accuracy among watch crystal on AVR, watch crystal on DS13xx, or main AVR crystal. Sure, you may get a "sweet" unit that is nearly perfect with inexpensive components; you can also get an outlier with any of the three approaches.

Obviously, once set up properly the battery-backed RTC doesn't need other power. But if you care to do the design a modern P or V series AVR can also be set up the same way.

With an 8-bit AVR timer and common master crystal frequencies, 10ms is a nice comfortable application tick. That's my default in my apps.

With CTC mode and 10ms, you can then have interrupt latency of up to 20ms and still keep perfect time with a simple ISR of "tick_flag = 1;" and all other work in the mainline. The only time that I might not get around that fast in a main loop would be with a bulk EEPROM write sequence, such as "restore factory defaults".

That can simply be dodged by having the ISR increment an 8-bit or 16-bit tick counter. Now it would have to be the interrupt latency of 20ms or longer--unheard of in my apps or any serial comms would fail miserably.

My DS1305/6 apps with watch crystal I check for "minute a month", or about 2 seconds per day. Many are closer but there seems to be a bell curve with the outliers being at about that level. I count that "good enough" for my apps.

Why did I go through all this? If you are in fact off as much as you said, I'd look at your setup and there is no need to go to the 2nd or 3rd tier -- if the 20ppm is good enough then any AVR running on a properly-loaded normal crystal of a few MHz should be able to do that. Either you are missing a tick here and there, or you are off a CTC count, or your cap isn't properly loaded.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

...and then there is Daylight Saving Time..... :evil: Is DST used in Japan? Do you nedd to provide automatic changeover?

John Samperi

Ampertronics Pty. Ltd.

https://www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Lee - I'm blaming the inaccuracy on my code until I can prove otherwise.

The thing is if I were to be making this device strictly a clock (which it is not) would it be possible of replicating the accuracy of the DS3232. I think I've conceived a way of nullifying the effects extra cycles that may cause slight delays before an interrupt, but I have yet to test it.

As for accuracy, it seems most vanilla crystals have at least 10ppm of drift. I personally feel 10ppm would be acceptable, that comes out to 10ppm at 20MHz = 1/2000000th of a second drift + or -, so if even the drift only went one way that would be 0.0018 seconds per hour, 0.0432 seconds per day, close to 1.3 seconds per month, and about 12 seconds per year. As it is I'm getting 12 seconds of drift on a bad day, and always lag (did someone scream bad code?), but 12 seconds a year is no sweat. As js just pointed out many places use daylight savings time, so that's at least two times a year people would be resetting their clocks anyway.

And js: Japan recently tried daylight savings time this past year, perhaps for energy reasons. I am actually not sure why, but I remember being surprised about it and showing up at the office an hour early 3 days in a row.

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

Quote:

I remember being surprised about it and showing up at the office an hour early 3 days in a row.

Plenty of times we've made international calls and the office there hasn't opened yet. :P

I work with a value of 20ppm for regular crystals.

In your case there could be two sources of error.
1. The code itself. Count the number of cycles needed to count up to 1 second.
If you are using the prescaler, make sure nothing is resetting the prescaler and causing you to lose counts.

2. Some external interrupt is taking so long to get serviced that you are missing some timer overflows. Try with a debug version where you disable all external interrupts and just have the clock running.

If you think education is expensive, try ignorance.

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

Crisp autumn leaves fall,
Time is divisor plus one:
Check OCRA

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

barnacle wrote:
Crisp autumn leaves fall,
Time is divisor plus one:
Check OCRA

That was gorgeous.

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

I think I have it working now, or at least the simulator seems to think so. I'll be stress testing the physical model again. Now I feel somewhat confident in my code (thought perhaps not my clock source). This project wasn't initially intended to be a clock, though clock functionality was part of it (because it will look cool). Now I know the clock will be just about as accurate as I can make it with cheap oscillators. I can't thank you guys enough!

Attachment(s): 

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

Maybe you could take the "Cap'n Crunch" method of programming. Add a second to the real-time result every two hours.

This reference is to John T. Draper, who got a reputation in the 1970's as an 'illegal' explorer of the Bell telephone system when he discovered that a whistle in a breakfast cereal box was the exact frequency used by the phone system to defeat the toll collection on long-distance calls.

He later became an 8080 assembly-language programmer who wrote the 'Electric Pencil' program, one of the first word-processors in the early 1980s.

He would astound other programmers by not tracking down the cause of bugs in his code, but would rather paste in a crude kluuge patch around the bug to correct it. I did that last week after being unable to track down the cause of a minor bug even with hours of ICE debugging.

If the code is consistently losing 12 seconds a day, try adding a second every two hours and see if this keeps correct time over a long period. Also try it on another prototype board. At worst, this may help determine if the cause is a hardware or software issue.

Using a DS1307 and 32KHz crystal is probably a good idea, since so many other people use it as a dependable real-time solution. It's possible that many people adopt the DS1307 solution after they too have spent long hours trying to determine why their system-clock approach doesn't give the needed precision.

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

Hum, forgot about this thread... I wrote a world's simplest clock program but never got around to posting it as I hadn't put it on silicon for final proof... and I don't have time to now - anyone want to play? It's for the mega48 with an external 8MHz crystal, and outputs the time on the normal TX out serial port (hopefully!).

Note there's a bug in my old compiler which has since been corrected; it will compile correctly with either, I think.

// the world's simplest AVR clock
// (c) nailed_barnacle 2008
//
// released LGPL - use the net for details
//
// runs on ATMega48, uses an external 8MHz crystal (so *should*
// work from 1.8v upwards. Don't forget to set the fuses for external
// full range crystal...
//
// this is intended only to prove the accuracy of the internal clock
// it provides no means of setting the clock - this is left, as they
// say, as an exercise for the student.
//
// a seconds count (0-59) is provided in binary on portC0-5
// the time in hh:mm:ss format is transmitted from TXD using the uart
// at 9600 baud, 8 bit, one stop, no parity, no flow control, every second
// immediately after the LEDs are updated. We also generate a 100Hz
// output on OC1A.
//
// this is free software and worth every penny!
//
// the method involves using timer 1 (sixteen bits) in CTC mode to divide
// the master clock by 40,000 to give an interrupt every 5ms - two hundred
// ticks per second. No prescaler is used.
// at each interrupt, the tick, second, minute, and hour ticks are incremented
// as required; if the second count is incremented, then it is also output to
// the port.
//
// the main routine simply observes the seconds count, and if it has changed
// since the last observation, packages the time and outputs it.

#include 
#include 
#include 
#include 
#include 

// clock times
static volatile uint8_t ticks;			// 200 of these make a second
static volatile uint8_t seconds;
static volatile uint8_t minutes;
static volatile uint8_t hours;			
uint8_t old_seconds;								// what was the time when we last looked


// I believe this is no longer the approved method of defining interrupts in GCC-AVR
// however, it works on my version of AVR studio and WinAVR (4.13/528 and 20040720,
// apparently) and the main point to note is that as interrupts are not explicitly
// enabled in the interrupt routine, they are not responded to until the routine returns.
// if you should try and execute code within the interrupt that takes longer than the 
// interval until the next interrupt event, it will be responded to on the return from
// the current interrupt. Eventually you will reach a stage where two or more interrupts
// occur within the current interrupt process and one of them will be silently lost.
// This is not a Good Thing[tm] so unless you have very specific needs, keep your
// interrupt routines as short and as fast as possible.

SIGNAL (SIG_OUTPUT_COMPARE1A)
{
	// the interrupt signal on a compare for the timer 1
	// we will have set the timer in CTC/OCR1A mode, and arrive here every 5ms
	
	ticks++;
	if (ticks == 200)
	{
		ticks = 0;
		PORTC = seconds;							// output the seconds count
		seconds++;
		if (seconds == 60)
		{
			seconds = 0;
			minutes ++;
			if (minutes == 60)
			{
				minutes = 0;
				hours ++;
				if (hours == 24)
				{
					hours = 0;
				}
			}
		}
	}
}

int main (void)
{
	// first set up the appropriate registers for the uart
	// with a 8MHz clock, using the standard mode, the correct
	// divisor for 9600 baud is 51, with an error of 0.2% - well
	// within the limits for RS232 data
	// it is not entirely clear from the datasheet that although
	// the M48 has a single USART, it is referred to as 'USART0'
	// and so the digit 0 should be inserted in place of 'n' in
	// all register and bit names.

char time[16];
int q;
	
	// select standard speed usage, single processor mode
	UCSR0A = 1<<U2X0;

	// enable the transmitter, but no interrupts or fancy stuff
	UCSR0B = 1<<TXEN0;

	// async uart, no parity, one stop bit, 8 bits
	UCSR0C = 1<<UCSZ01 || 1<<UCSZ00;

	// and finally the baud rate
	UBRR0H = 0;
	UBRR0L = 51;					// 9600 baud

	// now we can set up the timer for CTC 1
	// as above, the divide ratio is 40,000 which means that
	// we need to set the counter to 39,999 because it resets
	// to *zero*
	
	// set CTC mode 4, toggle output on OCR1A
	TCCR1A = 1<<COM1A1;

	// set CTC mode 4, no prescaler
	TCCR1B = 1<<WGM12 | 1<<CS10;

	// and write the output compare registers
	OCR1AH = 39999 >> 8;
	OCR1AL = 39999 & 0xff;

	// enable the ocr1a interrupt
	// TIMSK1 = 1<<OCIE1A; // <--- this doesn't work; equates to 0x04. BUG!
	TIMSK1 = 0x02;

	// clear the time stores
	ticks = 0;
	seconds = 0;
	minutes = 0;
	hours = 0;

	// make the lower six bits of portc outputs
	DDRC = 0x3f;

	// and finally, enable global interrupts
	sei();

	// now, the main loop where we sit forever
	while (1)
	{
		// we read the seconds count and keep it handy
		old_seconds = seconds;
		
		// then we spin in a loop until it changes
		while (old_seconds == seconds)
		{
			// do nothing
		}

		// if it's changed, we need to emit the current time
		// we will do this the easy way and use sprintf to format it
		// we could couple the serial port directly to printf but
		// I've never tried this; this method is portable
		sprintf(time,"%02d:%02d:%02d\n",hours,minutes,seconds);

		// now we can spit out the formatted time, one byte at a time
		for (q=0; q

All the usual warnings, caveat emptor etc, free software, worth every penny... :mrgreen:

Neil