I need some advice on a project I am working on where I got stuck.
I need to measure the duty cycle for a signal with a frequency between 1-4 kHz. Previously I have done this by sampling "signal high" and "signal low" for a large number of cycles. This works fine and is a solution I can use now as well as a last resort. But I thought about using the external interrupt and a timer instead just because they are available and I really would like to know if imy initial thinking works.
I use an 18F258 running at 40MHz using HS-PLL (10MHZ resonator) with the signal connected to INT0
This is my thinking:
For timing my worst case scenario is a 4 kHz signal giving me 1250 instruction cycles between interrupts. This sounds workable as the interrupt only need to read and store a timer value.
By changing the trigger flank for the INT0 IRQ in the interrupt routine I can get interrupts both on the rising and the falling edge. I set up timer0 as a timebase read the timer content on rising and falling edges of the signal. Now I have something like 1250/2 cycles between worst case interrupt timing which I feel should be OK.
If I now set a high prescaler on timer0 this works fine. The problem is that if I use a high prescaler value I get to bad resolution. So I lower the prescaler and also count timer owerflows. Now I just get very strange and random results.
I can't figure out what I am doing wrong. The overflow interrupt from timer 0 will add to the interrupt frequency but not to bad.
If anyone have a suggestion or can point out an error in my thinking above it would be very much appreciated.
> If I now set a high prescaler on timer0 this works fine. The problem
> is that if I use a high prescaler value I get to bad resolution. So I
> lower the prescaler and also count timer owerflows. Now I just get
> very strange and random results.
Are you clearing TMR0 and the roll-over counter at each IRQ ?
> I need to measure the duty cycle for a signal with a
> frequency between 1-4 kHz...
> I use an 18F258 running at 40MHz using HS-PLL (10MHZ resonator) with
> the signal connected to INT0
>
> This is my thinking:
>
> For timing my worst case scenario is a 4 kHz signal giving me 1250
> instruction cycles between interrupts. This sounds workable as the
> interrupt only need to read and store a timer value.
But you also have the "timer0 overflow interrupts" to take are of, right ?
> By changing the trigger flank for the INT0 IRQ in the
> interrupt routine I can get interrupts both on the rising and the
> falling edge. I set up timer0 as a timebase read the timer
> content on rising and falling edges of the signal.
Do you use 8 or 16 bit mode ?
Why not clear the timer on one of the edges, then you don't have
to subtract the two to get the time for (at least) one of the edges.
> Now I have something like 1250/2 cycles between worst
> case interrupt timing which I feel should be OK.
>
> If I now set a high prescaler on timer0 this works fine.
So all edges falls within one timer0 cycle, right ? That is, well
between two overflows ?
> The problem is that if I use a high prescaler value I get
> to bad resolution. So I lower the prescaler and also count timer
> owerflows.
How ? Using the timer0 overflow interrupt ?
> Now I just get very strange and random results.
How do you calculate the result ? How do you add the
count of overflows to the total ?
It should be something like :
<timer value> + X * <number of overflows>
X = 2**8 or 2**16 depending on if you use 8-bit or 16-bit
mode in tmr0.
> I can't figure out what I am doing wrong. The overflow interrupt from
> timer 0 will add to the interrupt frequency but not to bad.
And what do you *do* when the tmr0 overflow interrupt happens ?
>>If I now set a high prescaler on timer0 this works fine. The problem
>>is that if I use a high prescaler value I get to bad resolution. So I
>>lower the prescaler and also count timer owerflows. Now I just get
>>very strange and random results.
>>
>>
>
>Are you clearing TMR0 and the roll-over counter at each IRQ ?
>
>
>
Yes I do that on the falling edge.
>Ake Hedman wrote :
>
>
>
>>I need to measure the duty cycle for a signal with a
>>frequency between 1-4 kHz...
>>
>>
>
>
>
>>I use an 18F258 running at 40MHz using HS-PLL (10MHZ resonator) with
>>the signal connected to INT0
>>
>>This is my thinking:
>>
>>For timing my worst case scenario is a 4 kHz signal giving me 1250
>>instruction cycles between interrupts. This sounds workable as the
>>interrupt only need to read and store a timer value.
>>
>>
>
>But you also have the "timer0 overflow interrupts" to take are of, right ?
>
>
>
>
>>By changing the trigger flank for the INT0 IRQ in the
>>interrupt routine I can get interrupts both on the rising and the
>>falling edge. I set up timer0 as a timebase read the timer
>>content on rising and falling edges of the signal.
>>
>>
>
>Do you use 8 or 16 bit mode ?
>
>Why not clear the timer on one of the edges, then you don't have
>to subtract the two to get the time for (at least) one of the edges.
>
>
>
16-bit mode and I clear both the overflow counter and the timer on the falling edge.
>>Now I have something like 1250/2 cycles between worst
>>case interrupt timing which I feel should be OK.
>>
>>If I now set a high prescaler on timer0 this works fine.
>>
>>
>
>So all edges falls within one timer0 cycle, right ? That is, well
>between two overflows ?
>
>
>
yes!
>>The problem is that if I use a high prescaler value I get
>>to bad resolution. So I lower the prescaler and also count timer
>>owerflows.
>>
>>
>
>How ? Using the timer0 overflow interrupt ?
>
>
>
Yes! I increase a value representing the MSB third byte of the counter value on each interrupt so i get
0verflow << 16 + timerh << 8 + timerl
>>Now I just get very strange and random results.
>>
>>
>
>How do you calculate the result ? How do you add the
>count of overflows to the total ?
>It should be something like :
>
><timer value> + X * <number of overflows>
>
>X = 2**8 or 2**16 depending on if you use 8-bit or 16-bit
>mode in tmr0.
>
>
>
>
See above
>>I can't figure out what I am doing wrong. The overflow interrupt from
>>timer 0 will add to the interrupt frequency but not to bad.
>>
>>
>
>And what do you *do* when the tmr0 overflow interrupt happens ?
>
>
>
Just increase the overflow counter ans server other interrupts if there flags are active.
>Regards,
>Jan-Erik.
>
>
>
>
Some extra info. If I use the ICD and check the interrupt sequence it looks like the INT0 interrupt is always active. I reset the flag by reading PORTB and have checked that it is reset. But I suspect this being a problem with the flag being set when the debugger has hit the breakpoint. Anyway if I step the interrupt routine after an overflow interrupt the INT0 flag is always active also.
General:
I am self employed and I often miss the thing you Jan-Erik and Jinx give now. You state your problem and someone ask intelligent questions giving a differnet view into the problem.. This is always much better then a strait answere.
> >Are you clearing TMR0 and the roll-over counter at each IRQ ?
> >
> Yes I do that on the falling edge.
Hmmm. OK, you think the values you get are "random". But are
they really - is there any kind of pattern or similarity ? Have you
looked at the difference between consecutive readings ? Is there
any change if you put TMR0 in 16-bit mode ? INT0 and TMR0
IRQ bits are all in INTCON - no mix-up with bit numbers ?
>>>Are you clearing TMR0 and the roll-over counter at each IRQ ?
>>>
>>>
>>>
>>Yes I do that on the falling edge.
>>
>>
>
>Hmmm. OK, you think the values you get are "random". But are
>they really - is there any kind of pattern or similarity ? Have you
>looked at the difference between consecutive readings ? Is there
>any change if you put TMR0 in 16-bit mode ? INT0 and TMR0
>IRQ bits are all in INTCON - no mix-up with bit numbers ?
>
>
>
Good (and helpful) questions. I shall do a double check on these parameters. You guys gave me some energy now again :-)
> So I lower the prescaler and also count timer owerflows.
> Now I just get very strange and random results.
Now, how do you know they are "strange and random" ?
Or in other words, how are you "reading" those results ?
On an LCD ? Dumped on a serial line ?
Could it be some conversion problem between the internal
binary format and whatever "display" format you use now
that you get larger results ?
>Ake Hedman wrote :
>
>
>
>>So I lower the prescaler and also count timer owerflows.
>>Now I just get very strange and random results.
>>
>>
>
>Now, how do you know they are "strange and random" ?
>Or in other words, how are you "reading" those results ?
>On an LCD ? Dumped on a serial line ?
>Could it be some conversion problem between the internal
>binary format and whatever "display" format you use now
>that you get larger results ?
>
>
>
I have an ICD 2 and set a breakpoint in the main loop where I do a calculation from a running mean of the sampled values every second. The results a "strange and random" (probaly not when I know why as always :-) ) as they are not related to the actual signal in any way (as seen on the scope and expected result).
But I will go over the code again (round 3275-- ;-) . Actually your posts have helped me a lot already by not finding a big flaw in my thinking keeping my spitit up.
Does any one know if IRQ flags freezes when a breakpoint is hit using the ICD-2 debugger? This is what I would expect but practical results indicate that its not the case.
> I have an ICD 2 and set a breakpoint in the main loop where I do a
> calculation from a running mean of the sampled values every
> second. The results a "strange and random" (probaly not when
> I know why as always :-) ) as they are not related to the actual
> signal in any way (as seen on the scope and expected result).
OK. I see. I've never use any ICDx, so I've no idea what happens to
the timers and other "free-running" stuff when it hits a breakpoint.
I think I have seen something about problems to "debug" I2C and
similiar when using breakpoints and the ICDx, but I'm not sure.
Or was it interrupts ? Never mind...
But, I would guess that that info (that you use the ICD2 to read out
the values) could have been a great value to others if it had been
known earlier in the thread.
> But I will go over the code again (round 3275-- ;-).
What *I* would do in this case, would be to write up a short
test-case that only (more or less) has the duty cycle calculation.
Or are all those 3275 lines (or words) part of the calculation ?
>Ake Hedman wrote :
>
>
>
>>I have an ICD 2 and set a breakpoint in the main loop where I do a
>>calculation from a running mean of the sampled values every
>>second. The results a "strange and random" (probaly not when
>>I know why as always :-) ) as they are not related to the actual
>>signal in any way (as seen on the scope and expected result).
>>
>>
>
>OK. I see. I've never use any ICDx, so I've no idea what happens to
>the timers and other "free-running" stuff when it hits a breakpoint.
>I think I have seen something about problems to "debug" I2C and
>similiar when using breakpoints and the ICDx, but I'm not sure.
>Or was it interrupts ? Never mind...
>
>But, I would guess that that info (that you use the ICD2 to read out
>the values) could have been a great value to others if it had been
>known earlier in the thread.
>
>
>
>>But I will go over the code again (round 3275-- ;-).
>>
>>
>
>What *I* would do in this case, would be to write up a short
>test-case that only (more or less) has the duty cycle calculation.
>Or are all those 3275 lines (or words) part of the calculation ?
>
>
>
You misunderstood me here. Probably my fault... What I tried to say (write!) was that I have checked the code 3275 times all ready...
But as you say it's time to take it all apart and do check 3276... ;-)
Thanks for your kind help. Let you know what a I find....
>>I have an ICD 2 and set a breakpoint in the main loop
>>
>>
>
>Can't help with any ICD2 problems, but how about collecting
>a block of values and then writing them to EEPROM ?
>
>
>
Very good idea. Haven't thought about that. Great. Thanks
>OK. I see. I've never use any ICDx, so I've no idea what happens to
>the timers and other "free-running" stuff when it hits a breakpoint.
>I think I have seen something about problems to "debug" I2C and
>similiar when using breakpoints and the ICDx, but I'm not sure.
>Or was it interrupts ? Never mind...
Strange things do happen to the hardware peripherals when you stop the code
with an ICD. Interface things like I2C and UARTS send/receive corrupted
values, and timers keep running while the ICD code runs, resulting in weird
values in the timer if you are trying to set up specific intervals. Also if
you are single stepping, an interrupt will not fire off - you have to set a
breakpoint and let the program free run to allow the interrupt to happen.
To do reasonable debugging with an ICD, I found it good practice to set up
some labels specifically for setting breakpoints on the ICD, at points where
I knew that I/O things had properly completed, and one at a suitable point
inside the interrupt routine so I could see what was happening here. When
debugging inside the interrupt you can single step OK, and return to the
mainline code alright, but another interrupt will not fire off until you let
the processor free run again.
>>OK. I see. I've never use any ICDx, so I've no idea what happens to
>>the timers and other "free-running" stuff when it hits a breakpoint.
>>I think I have seen something about problems to "debug" I2C and
>>similiar when using breakpoints and the ICDx, but I'm not sure.
>>Or was it interrupts ? Never mind...
>>
>>
>
>
>
>Strange things do happen to the hardware peripherals when you stop the code
>with an ICD. Interface things like I2C and UARTS send/receive corrupted
>values, and timers keep running while the ICD code runs, resulting in weird
>values in the timer if you are trying to set up specific intervals. Also if
>you are single stepping, an interrupt will not fire off - you have to set a
>breakpoint and let the program free run to allow the interrupt to happen.
>
>To do reasonable debugging with an ICD, I found it good practice to set up
>some labels specifically for setting breakpoints on the ICD, at points where
>I knew that I/O things had properly completed, and one at a suitable point
>inside the interrupt routine so I could see what was happening here. When
>debugging inside the interrupt you can single step OK, and return to the
>mainline code alright, but another interrupt will not fire off until you let
>the processor free run again.
>
>
>
Good info. Thanks Alan. Helps me a lot when I move further into this problem.
I find the ICD2 very useful most of the time and well worth its low cost. The lack of multiple breakpoints is the worst limitation in my opinion. Knowing things like this also helps.
The problem was me nulling TMR0L before TMR0H on one of the edges.
I still don't understand what the differens is but reversing the order resolves my problem. If anyone know why this is like this I'm very interested to learn why!?
> The problem was me nulling TMR0L before TMR0H on one of the edges.
>
> I still don't understand what the differens is but reversing
> the order resolves my problem. If anyone know why this is
> like this I'm very interested to learn why!?
You should write the *high* byte before the *low* byte.
See the data sheet for details (section 11.4, page 111).
(And read the *low* byte before the *high*, if reading...)
The high TMR0 byte is actualy a "shadow" register
that is read/written to the real TMR0 high byte
whenever the *low* byte is read/written.
So if you write the low byte first, the high byte will
be written with whatever was left over in the
shadow regsiter since last time. The current
write to the high byte will just update the shadow
register (and maybe be used as the high TMR0
value *next* time you update TMR0).
In the same way, you should *read* the *low*
byte first.
All this is to be able to make a full 16-bit read or
write at the same moment (while TMR0 is running).
That is, in short, what section 11.4 in the data sheet says...
Jan-Erik.
Jan-Erik Soderholm wrote :
> You should write the *high* byte before the *low* byte.
>Ake Hedman wrote :
>
>
>
>>The problem was me nulling TMR0L before TMR0H on one of the edges.
>>
>>I still don't understand what the differens is but reversing
>>the order resolves my problem. If anyone know why this is
>>like this I'm very interested to learn why!?
>>
>>
>
>You should write the *high* byte before the *low* byte.
>See the data sheet for details (section 11.4, page 111).
>
>(And read the *low* byte before the *high*, if reading...)
>
>Jan-Erik.
>
>
>
>
>
I will never forget that again... Promise... (I must admit there is a small memory fragment of it somewhere deep inside ) ;-) As usual there is a simplistic solution to a problem that have taken a lot of time from the project. And in this case just RTFM.... Strange really why one sell dome spend more then budgeted time with the hard parts but underestimate the easy parts...
>He he, that was a bit short answer, wasn't it :-)
>
>A better answer :
>
>The high TMR0 byte is actualy a "shadow" register
>that is read/written to the real TMR0 high byte
>whenever the *low* byte is read/written.
>
>So if you write the low byte first, the high byte will
>be written with whatever was left over in the
>shadow regsiter since last time. The current
>write to the high byte will just update the shadow
>register (and maybe be used as the high TMR0
>value *next* time you update TMR0).
>
>In the same way, you should *read* the *low*
>byte first.
>
>All this is to be able to make a full 16-bit read or
>write at the same moment (while TMR0 is running).
>
>That is, in short, what section 11.4 in the data sheet says...
>
>Jan-Erik.
>
>
>
>Jan-Erik Soderholm wrote :
>
>
>
>>You should write the *high* byte before the *low* byte.
>>
>>
>
>
>
>
>
No need to look up the section then. Thanks. Now I understand why and also why it makes sense.