'Measuring pulse width with 16C84'
Hi, I just joined this group yesterday. I've got a problem measuring pulse
widths and was wondering if anyone else has experince with this.
I wrote code to use TMR0 and the port B change interrupt to measure a pulse
that varies between 1ms and 2ms in length. No matter what I do, there is an
error in the measurement of the width if the measurement is near a TMR0
Specifics are: PIC16C84 running at 8Mhz. TMR0 using internal instruction
clock with a 1:2 prescale (increments every 1us).
The approaches I've tried:
1. Use interrupts for both Port B change and TMR0 overflow. You start
measuring the pulse width by taking the "time stamp" of when the pin goes
high, then the "time stamp" of when the pin goes low. The time stamp is a
16-bit value which is simply the 8-bits of TMR0 as the low value and an
incrementing counter that is bumped every time TMR0 overflows. The problem
with this approach is that you may be servicing a TMR0 overflow interrupt
when the Port B pin actually goes low, and that interrupt won't get serviced
until the TMR0 interrupt has returned.
2. Use interrupts for Port B change and poll for TMR0 overflow. Disable
TMR0 interrupts. Basically wait for high transition on Port B pin to enter
interrupt service routine (ISR), then sit in a tight loop waiting for the
T0IF flag to get set indicating a TMR0 overflow. When it overflows, bump
the high counter (described in approach #1).
Meanwhile, interrupts are re-enabled in the ISR so that we can get nested
interrupts (yes, I handle saving the context off for each nested interrupt
correctly) to get the Port B change interrupt.
The loop loops something like this:
BTFSS INTCON, T0IF ; Clock overflow again?
GOTO PWM0 ; Loop
BTFSC SERVO_STAT, SERVO_RDY ; Did we get the down pulse?
GOTO B_DONE ; Then get out of here
INCF SERVO_W_H,F ; Bump top of servo count
BCF INTCON, T0IF ; Clear the TMR0 overflow
SUBLW 9h ; Over 2.3ms long?
; error, pulse exceeded 2.3ms long
Note that in the above code, the SERVO_RDY flag bit gets set by the ISR when
the Port B pin goes low. I didn't include that code in the above snippet.
The problem seems to be that you can get a Port B change interrupt between
the time you detect the T0IF flag set and the actual increment of the top
3. I've even tried polling both bits T0IF and RBIF, but this runs into the
same basic problem as in #2.
You either really need a 16-bit counter (so that it can count 1000 1us
steps), or you need a way to atomically update a counter whenever TMR0
overflows, or you need a capture/compare/PWM module which the 16C84 doesn't
I've decided to try the 16C72 which has a capture/compare/PWM module. Its
too bad. I really liked the 16C84. Also note that the HC11 can do this
If anyone has a way of measuring pulse widths accurately, please let me know.
|> From: Anil Patel <AUTODESK.COM> anil.patel
> If anyone has a way of measuring pulse widths accurately, please let me know.
The '84 does have a 16-bit counter, i.e. the prescaler (which is not
directly accessible) and TMR0. The trick is to somehow find out
the contents of the prescaler at a particular point. Although I have
not tested the following idea, it should work. Polling is used
rather than interrupts because:
. interrupt latency is relatively tricky to deal with
. since this is a servo application, there's plenty of
time between pulses to accomplish something else.
Set the prescaler to 1:256 so that TMR0 increments every 128us. Now
poll the input until it goes high. Clear TMR0 (which also clears the
prescaler). Poll input until it goes low. The combination of
prescaler/TMR0 holds the low and high byte of the count (units of
0.5us). Save the high byte (TMR0), then poll TMR0 until it changes --
you can just check its LSB. While polling, an auxiliary counter is
incremented to get an estimate of the remaining count left in the
prescaler. If the complete polling loop is N instruction cycles, then
the aux count is multiplied by N to get the prescaler remainder, then
subtracted from 256 to estimate the prescaler count at the time the
input went low -- this becomes the low byte of the overall timer
In reality, you will need a few other corrections to account for
differing code path delays etc., however you should be able to get a
resolution of about 3us.
Just wanted to say thanks Steve. Your approach did the trick nicely.
I did one thing differently. When the pin goes low indicating we want to
read the TMR0 and prescaler values, I switch the TMR0 to increment from the
PA4/T0CKI pin instead of the internal CLKOUT clock. This allows me to clock
the PA4/T0CKI pin manually to determine the count remaining in the prescaler
without having to have a tight counting/polling loop.
At 12:00 PM 2/12/97 EST, you wrote:
> From: Anil Patel <AUTODESK.COM> anil.patel
> Just wanted to say thanks Steve. Your approach did the trick nicely.
> I did one thing differently. When the pin goes low indicating we want to
> read the TMR0 and prescaler values, I switch the TMR0 to increment from the
> PA4/T0CKI pin instead of the internal CLKOUT clock. This allows me to clock
> the PA4/T0CKI pin manually to determine the count remaining in the prescaler
> without having to have a tight counting/polling loop.
Actually, I was going to suggest this but refrained because I
couldn't remember whether reassigning the clock source would
disturb the counter. However, it is gratifying to know that
it really works.
More... (looser matching)
- Last day of these posts
- In 1997
, 1998 only
- New search...