# Measuring a period of a square wave with averaging

#### spinnaker

Joined Oct 29, 2009
7,835

They have a section Measuring a period of a square wave with averaging that I am not understanding.

1. Configure control bits CCPxM3:CCPxM0
(CCPxCON<3:0>) to capture every 16th
rising edge of the waveform.
2. Configure the Timer1 prescaler so Timer1 will
run 16 TMAX(1) without overflowing.
3. Enable the CCP interrupt (CCPxIE bit).
4. When a CCP interrupt occurs:
a) Subtract saved captured time (t1) from
captured time (t2) and store (use Timer1 interrupt flag as overflow indicator).
b) Save captured time (t2).
c) Clear Timer1 flag if set.
d) Shift value obtained in step 4.a right four
times to divide by 16 – this result is the
period (T).

Steps 1-4c are easy enough. They lose me on 4d. I understand the dividing part but wouldn't you need 16 samples to average and divide by 16?

They go on to say the advantages are:

Fewer CCP interrupts to disrupt program flow

How is this possible?

#### MrChips

Joined Oct 2, 2009
24,207
The objective is to measure the period of one full cycle of a square wave.
They are measuring the time over 16 full cycles. Hence the result is 16 times too large.
This is where the averaging comes into play.

To obtain the proper result, they divide by 16 by shifting the integer value 4 bits to the right. This truncates the result. If they wanted to round the result to the nearest integer they ought to add 8 to the result before shifting 4 bits to the right.

#### spinnaker

Joined Oct 29, 2009
7,835
Sorry I still don't get it. Sorry for being dense. If they are measuring time over 16 cycles don't they need to keep track of all 16 samples, add them up and then divide? Or at the very keep adding to a total till they get 15 samples then divide?

#### MrChips

Joined Oct 2, 2009
24,207
Suppose you want to determine the average weight of 16 dollar coins.
You can weight each coin. Record the value of each coin. Add the 16 values and divide by 16.

Or weight all 16 coins together all at once and divide the total weight by 16.

They are doing the latter method.

#### JohnInTX

Joined Jun 26, 2012
4,554
Agreed.

• They're setting the CCP mode to capture TIMER1 only after detecting 16 cycles (rising edges). It does not capture nor interrupt on all rising edges, just every 16th one.
• TIMER1 is set to run at a rate that is less than 2^16 counts over the maximum time expected for 16 cycles.
• On the 16th cycle, the CCP will capture TIMER1 (0 - 2^16 -1) and interrupt the processor.
• Since TIMER1 is free running you detect the first capture and save the captured value. Then you wait for the next one, 16 cycles later. The difference between the two captured values is the number of counts over 16 cycles i.e. 16 rising edges.
• Since TIMER1 is free running, you have to account for roll-over. They suggest using TMR1IF (with TMR1IE disabled) as an overflow detection bit that would let you know how to adjust the result of the subtraction. Works for me but be sure to clear TMR1IF before starting.
• The resulting number of counts between the two interrupts represents the time over 16 periods so you divide by 16 to get one period.

Hope that helps!

#### spinnaker

Joined Oct 29, 2009
7,835
Agreed.

• They're setting the CCP mode to capture TIMER1 only after detecting 16 cycles (rising edges). It does not capture nor interrupt on all rising edges, just every 16th one.
• TIMER1 is set to run at a rate that is less than 2^16 counts over the maximum time expected for 16 cycles.
• On the 16th cycle, the CCP will capture TIMER1 (0 - 2^16 -1) and interrupt the processor.
• Since TIMER1 is free running you detect the first capture and save the captured value. Then you wait for the next one, 16 cycles later. The difference between the two captured values is the number of counts over 16 cycles i.e. 16 rising edges.
• Since TIMER1 is free running, you have to account for roll-over. They suggest using TMR1IF (with TMR1IE disabled) as an overflow detection bit that would let you know how to adjust the result of the subtraction. Works for me but be sure to clear TMR1IF before starting.
• The resulting number of counts between the two interrupts represents the time over 16 periods so you divide by 16 to get one period.

Hope that helps!
That makes more sense.

I should be working right now so I will need to check the data later. But is there a setting to capture the time after 16 events? Or do I need to keep a counter?

#### JohnInTX

Joined Jun 26, 2012
4,554
That makes more sense.
I should be working right now so I will need to check the data later. But is there a setting to capture the time after 16 events? Or do I need to keep a counter?
In the context presented in post #1, no, you don't have to keep a counter to average 16 periods. Setting CCPxCON[CCPxM3:CCPxM0] (the mode bits) to 0111 configures the ECCP to capture and interrupt on every 16th rising edge i.e. one capture each 16 cycles.

This is a cheap way to average 16 periods on the fly. You only get interrupted when 16 cycles have been captured. Nothing is free, of course. Setting it up this way means that whatever time it takes to rotate the wheel 16 times has to fit into 16 bits. That makes the actual period number more coarse than your previous approach where you interrupted on every cycle. Using that approach, 16 bits covers one rev instead of 16 meaning a finer resolution. Whether you need that much precision is up to you and a few jabs at the ol' calculator.

Either way, as before, I would calculate the number of counts between period measuring events (16 per capture or one per capture) in the interrupt service routine and post that number with a NewPeriodPosted flag and leave any further processing to the main routine. That way, the period measurement just free runs, spitting out valid periods over and over. When main decides it wants to know the period, it just reads the last one posted by the interrupt-driven measurement and proceeds apace.

Have fun!

#### spinnaker

Joined Oct 29, 2009
7,835
In the context presented in post #1, no, you don't have to keep a counter to average 16 periods. Setting CCPxCON[CCPxM3:CCPxM0] (the mode bits) to 0111 configures the ECCP to capture and interrupt on every 16th rising edge i.e. one capture each 16 cycles.

This is a cheap way to average 16 periods on the fly. You only get interrupted when 16 cycles have been captured. Nothing is free, of course. Setting it up this way means that whatever time it takes to rotate the wheel 16 times has to fit into 16 bits. That makes the actual period number more coarse than your previous approach where you interrupted on every cycle. Using that approach, 16 bits covers one rev instead of 16 meaning a finer resolution. Whether you need that much precision is up to you and a few jabs at the ol' calculator.

Either way, as before, I would calculate the number of counts between period measuring events (16 per capture or one per capture) in the interrupt service routine and post that number with a NewPeriodPosted flag and leave any further processing to the main routine. That way, the period measurement just free runs, spitting out valid periods over and over. When main decides it wants to know the period, it just reads the last one posted by the interrupt-driven measurement and proceeds apace.

Have fun!

Ah I see it now. Thanks so much for the explanation. I am thinking this might not be useful for my application but it is nice to know it exists.

I am thinking I will need every CCP because the CCP will double as an external interrupt. The interrupt will trigger the home position for each rotation of th edisk.

#### JohnInTX

Joined Jun 26, 2012
4,554
Ah I see it now. Thanks so much for the explanation. I am thinking this might not be useful for my application but it is nice to know it exists.

I am thinking I will need every CCP because the CCP will double as an external interrupt. The interrupt will trigger the home position for each rotation of the disk.
Yeah, that makes sense. You'd need to have that beginning point in any case to know when to start emitting the columns.

Arguably you could use a separate external interrupt for the rotation sensor to index the columns but the CCP will easily do double duty as an index and a period measurement. The idea of simply posting raw readings with a 'new' flag still makes sense. If you still wanted to average periods, you could just sum successive readings and emit the sum with the 'new' flag in the interrupt routine for later processing by main. You always want to keep the processing in a PIC interrupt to a minimum so I would just emit the sum and let main compute the average.

If averaging, keep in mind that you don't necessarily have to divide to get the average right away. You can combine the average count constant with any others in your RPM formula. For example if you are averaging 16 readings for a period and maybe dividing some other value by N to scale to RPM, you can do one division as /(N*16). Simplify the equation as much as you can on paper to save computing time. Another trick is to use some convenient internal period or RPM representation that more directly corresponds to the task. Calculating RPM may not be as useful as columns per system tick for example - or cubits per nano fortnight for that matter. Whatever fits the system.

Sounds like you have it well in hand!