PIC TMR0 adjustment - Trap for young players? - Page 1
Forum section: electronics / general

author: igendel
author_rank: Frequent Contributor
timestamp: August 21, 2016, 11:10:49 am
content: Hi,While writing this, I have a feeling that the PIC programmer folk are snickering behind by back, but...I wanted to generate a 104us periodic interrupt on an 8-bit PIC (e.g. PIC12F675) using Timer0. The clock is the internal oscillator, effectively 1MHz (1us per instruction). Now, Timer0 can only generate an interrupt when its 8-bit counter, TMR0, overflows from 0xFF to 0x00 - meaning once per 256us, which is too slow for my requirements.However, as the datasheet heartily suggests, we can write new values directly to TMR0. If we set TMR0 at the beginning of the ISR function to 152, it will only need 104 more cycles* to reach 0x00 and I'll have our desired 104us period.* Yes, I know about the two-cycle count inhibition... let's ignore that for nowBut here's the trap: try the above and you won't get 104us. It'll be closer to 125us. My understanding is that it's because there's a time gap between the interrupt event and the setting of TMR0: The MCU needs time to enter the ISR and run the instructions until the new value is in place. Makes sense, right? What's really weird for me is that I found no reference to this anywhere. Every online/offline "timer calculator" I tried, even Microchip's own "MPLAB Code Configurator" that generates code for you, makes the mistake of ignoring this time gap (I tested it on a PIC12F1840). Is everyone's timing off by ~20 clock cycles? Can anyone provide some feedback on this finding?Thanks!

author: ludzinc
author_rank: Unknown
timestamp: August 21, 2016, 11:25:36 am
content: YepBeen there, empirically done that.There's overhead to account for. Either calculate or measure.I measured it, found it repetable across multiple devices (100 or so) and called it done.

author: dannyf
author_rank: Super Contributor
timestamp: August 21, 2016, 11:30:28 am
content: QuoteSomething is at work here.Generally, don't write to TMR0.If you have to, use increments:Code:TMR0+=-PERIOD; //load offsetHowever, you will likely see a small error that way, as the value of TMR0 will continue to increment as its value is being modified.As to ISR overhead, it only impacts the very first period. After that, the same overhead is applied to consecutive ISR invocation so your timing (since the last invocation) is the same.

author: igendel
author_rank: Frequent Contributor
timestamp: August 21, 2016, 11:48:15 am
content: Quote from: dannyf on August 21, 2016, 11:30:28 amQuote from: dannyf on August 21, 2016, 11:30:28 amThat does make sense.Quote from: dannyf on August 21, 2016, 11:30:28 amI'm not sure I understood this one... indeed there's the same overhead each time, so each and every period is off by that overhead; if I set TMR0 to 152, the next interrupt will be triggered after 104us, but will still require the extra time until I can actually set TMR0 again.

author: dannyf
author_rank: Super Contributor
timestamp: August 21, 2016, 12:00:46 pm
content: let's say the ISR overhead is 20ticks / 20us.So 20 ticks after the TMR0 overflows, your ISR is executed; In your case, another 104us - 20us later, your TMR0 overflows, and 20us later, your ISR gets executed again.The time between the two invocations is 104us - 20us + 20us = 104us.The ISR overhead doesn't matter.Going back to TMR0 offset, if you really want to get your timing right, do this:Code:TMR0+=-PERIOD+TMR0_ERROR;where TMR0_ERROR accounts for the time elapsed in modifying TMR0. Usually it is 2.

author: dannyf
author_rank: Super Contributor
timestamp: August 21, 2016, 12:03:08 pm
content: QuoteThe problem gets more interesting when a prescaler is used on a fast signal.

author: igendel
author_rank: Frequent Contributor
timestamp: August 21, 2016, 12:07:11 pm
content: Quote from: dannyf on August 21, 2016, 12:00:46 pmRight - if I adjust for PERIOD as you suggested. I thought you meant the setup I described at the beginning.We should also be careful if we change the ISR (add another interrupt-source check before checking the Timer0 flag), or if we change the compiler (e.g. from free to pro).

author: igendel
author_rank: Frequent Contributor
timestamp: August 21, 2016, 12:13:50 pm
content: Quote from: dannyf on August 21, 2016, 12:03:08 pmHeh, "Interesting" may not be the most accurate adjective hereWhat I still don't get is why this matter isn't mentioned anywhere (that I could find so far, anyway). It really is a trap, for instance if someone trusts the MPLAB Code Composer blindly.

author: dannyf
author_rank: Super Contributor
timestamp: August 21, 2016, 12:15:27 pm
content: QuoteShort of a caveat, none of that matters to the approach I gave earlier.Where it fails is if the timer has advanced so much and / or the value of PERIOD is so short so that the operation caused an overflow.

author: dannyf
author_rank: Super Contributor
timestamp: August 21, 2016, 12:16:47 pm
content: QuoteIt is well documented in the datasheet.QuoteWell, that's the fault of the person who trusts blindly, either MPLAB Code Composer or anything else.

author: igendel
author_rank: Frequent Contributor
timestamp: August 21, 2016, 12:25:34 pm
content: Quote from: dannyf on August 21, 2016, 12:15:27 pmThe approach will still work,but the PERIOD value probably won't be the same. We'll have to measure/cycle-count it again, right?Edit: No, I get what you're saying nowQuote from: dannyf on August 21, 2016, 12:16:47 pmBut in entirely separate sections, not as a single topic/discussion as far as I can see.Quote from: dannyf on August 21, 2016, 12:15:27 pmYeah well, of course I won't build an application without testing each and every part of it - that's how I found this issue - but still, one would expect some minimal correlation between MCC-generated code and the user requirements...

author: dannyf
author_rank: Super Contributor
timestamp: August 21, 2016, 01:31:17 pm
content: QuoteNot sure. I would say generally no: the configurator's job is to provide you with building (code) blocks that you can assemble together for your application. It has no knowledge of your application and the responsibility has to reside with the user of such configurators to make sure that the generated code works to that specific application.Putting too much faith in the configurator to get it right for your application is risky, as you have shown here.

author: Bruce Abbott
author_rank: Frequent Contributor
timestamp: August 24, 2016, 12:55:47 am
content: Quote from: igendel on August 21, 2016, 11:10:49 amIt's not a mistake. It's a well documentedlimitationof Timer0 that it cannot generate periodic interrupts with arbitrary timing. You can emulate it in software, but of course that will result in timing variations caused by the software overhead.Is it a trap for young players? Yes, but only for those who think they can get away with programming a chip without knowing its features and limitations. The first step in working withanyMCU should be to read the datasheet, so you know what it is capable of and how to use it.QuoteIf they use that method then yes, their timing will be off. You can tweak the count to get close to the timing you want, but there is no guarantee that it will be single cycle accurate. In many cases that is good enough. When it isn't you use a different method, eg. change the system clock to a frequency that gives the correct timing with a count of 256, or use a PIC which has a more advanced timer.

author: Mechatrommer
author_rank: Super Contributor
timestamp: August 24, 2016, 11:38:27 am
content: what compiler? have you looked at disassembled code? tracing from interrupt vector to where you set the TMR0?