Link here

Frequency Measurement Using Microcontrollers
Use the 9S12 HCS12 counting/timing unit and a C language example program as a frequency counter

Using a microcontroller pulse accumulator

Many embedded applications require that you measure frequency or tally pulses from a sensor or signal source. Microcontrollers often include pulse accumulators that count pulses applied to an input pin without the need for the processor to service an interrupt for each input transition. Generally, you can just read the current pulse tally from a counter register.

The Freescale 9S12/HCS16 MCU includes a particularly useful pulse accumulator as part of its ECT (Enhanced Capture Timer) module. Using it, and a little bit of C code, you can easily build a frequency counter or tally counter capable of counting rates from DC to 5 MHz.

The PDQ Board's operating system includes high level C library functions for accessing the processor's enhanced capture timer and pulse accumulator systems. This webpage provides an example program for accessing those functions for frequency measurement.

This program measures a frequency applied to pin 17 on the Digital IO Header (H8) of the PDQ Board. The maximum voltage allowed on pin 17 is 5.0 volts, the minimum voltage is 0 volts. The signal should be referenced to the ground at pin 1 on H8.

Frequency is measured by using the Pulse Accumulator A hardware in Event counting mode. The Enhanced Capture/Timer #3 hardware is also used to fire an interrupt every 100ms (10Hz). An ISR calculates the difference in pulses counted by the Pulse Accumulator and saves the measured Hz in the global variable fHz. The ISR is allowed to fire ISR_MULTIPLIER times before the frequency is calculated.

Block diagram for the 16-bit pulse accumulator in event counting mode
Fig. 1  Block diagram of the HCS12/9S12 16-bit Pulse Accumulator A connected to the hardware input pin PT7
 

Setting the counter's period

The main parameter in this example ISR_MULTIPLIER. This value sets the measurement period in seconds such that:

period in seconds = (ISR_TIME/ONE_MS) * ISR_MULTIPLIER / 1000

In the existing source code below ISR_MULTIPLIER is set to 10. (ISR_TIME/ONE_MS) is 100, resulting in a 1 second period.

Before adjusting the value of ISR_MULTIPLIER read the "Note about Accuracy" in the source code.

 

C example source code for a frequency counter

Application source code:

C example frequency counter

  1: #include <mosaic\allqed.h>     // include all of the QED and C utilities
  2:
  3: // *********************** Measure Frequency Example ***********************
  4: //
  5: // This program measures a frequency applied to pin 17 on the Digital IO Header (H8)
  6: // of the PDQ Board.  The maximum voltage allowed on pin 17 is 5.0 volts,
  7: // the minimum voltage is 0 volts.  The signal should be referenced to pin 1 on H8.
  8:
  9: // Frequency is measured by using the Pulse Accumulator A hardware in "Event
 10: // counting mode".  The Enhanced Capture/Timer #3 hardware is also used to
 11: // fire an interrupt every 100ms (10Hz).  An interrupt service routine (ISR)
 12: // calculates the difference in pulses counted by the Pulse Accumulator and
 13: // saves the measured Hz in the global variable 'fHz'.  The ISR is allowed
 14: // to fire ISR_MULTIPLIER times before the frequency is calculated.  This gives
 15: // us the ability to measure pulses over a period of time longer than the
 16: // maximum time for a single ISR.
 17:
 18:
 19:
 20: // ************************** Warning about Rollovers **************************
 21: // The maximum value an unsigned 16bit integer can hold is 65535.  There are
 22: // two separate values in this program which need to be designed with this
 23: // overflow limit in mind.
 24: //
 25: // The first value is the ISR period:
 26: // By default each tick of TCNT is 1.6 microseconds.  This means that the longest
 27: // period for an ISR is 65535 * 1.6 microseconds or 104.8576 ms.  This program
 28: // uses an even period of 100ms for the rollover.
 29: //
 30: // The second value is the number of pulses:
 31: // The number of pulses that can be counted in a 16 bit number limits the period
 32: // we can count over.  If the highest frequency we want to measure is "fMax", the
 33: // longest period we can measure is:
 34: //
 35: //      longest period in seconds = 65536 / fMax
 36: //
 37: // fMax should be chosen to give you some headroom above the highest frequency
 38: // you think you will be measuring.
 39:
 40:
 41: // **************************** Note about Accuracy ****************************
 42: // The accuracy of your measurement is determined by the measurement period.
 43: // A longer period results in more accuracy (but a lower maximum frequency).
 44: // The period is set by
 45: //
 46: //    period in seconds = (ISR_TIME/ONE_MS) * ISR_MULTIPLIER / 1000
 47: //
 48: // You should change the ISR_MULTIPLIER value to adjust the period.  Setting
 49: // the ISR_TIME lower will increase the load on the CPU.
 50: //
 51: // The accuracy of your measurement is given by
 52: //
 53: //    (+/- accuracy in Hz) = 1 / period in seconds
 54: //
 55: // For a period of 2 seconds, the accuracy is +/- 0.5 Hz.
 56:
 57:
 58:
 59: // ********** Interrupt Service Routine for Timed Function Calling **********
 60: // The TCNT counter in the Enhanced Capture Timer (ECT) system is a free-running
 61: // counter.  The TCNT frequency is 20 MHz divided by n, and the corresponding
 62: // TCNT period is 50 nanoseconds times n.
 63: // Valid values for the prescaler n are decimal 1, 2, 4, 8, 16, 32, 64, 128;
 64: // see ECTPrescaler()
 65:
 66: // We leave the prescaler at decimal 32,
 67: // yielding a 1.6 microsecond TCNT period, and a 104.8576 ms rollover (overflow) time.
 68:
 69:
 70:
 71:
 72:
 73: #define ONE_MS  625     //  625 counts of 1.6 microsecond TCNT = 1 ms
 74:
 75: // Specifies time in TCNT periods between calls to FunctionTimer()
 76: // This will execute FunctionTimer() once every 100 milliseconds (that is, every 62500 TCNT periods)
 77: // Note, any value for ISR_TIME that is greater than 65535 is invalid
 78: // (unsigned) is extremely important here, otherwise this will result in a negative number
 79: #define ISR_TIME  ((unsigned)ONE_MS * 100)
 80:
 81:
 82: // This is the number of times the ISR fires before we make a measurement
 83: // Change this value to change the period of the measurement.  Read notes above!
 84: #define ISR_MULTIPLIER 20
 85:
 86:
 87: // ********** Global variables **********
 88: uint timea; // previous value of TCNT timer
 89: uint timeb; // current value of TCNT timer
 90:
 91:
 92: uint pulsesa; // previous value of PULSE_A
 93: uint pulsesb; // current value of PULSE_A
 94:
 95: uint timex; // the scheduled TCNT value of the previous ISR was
 96: uint timey; // the scheduled TCNT value of the current ISR
 97:
 98: float fHz;  // Read this from your main() or anywhere to get
 99:             // the frequency in Hz.
100:
101: uint isrRollover;
102: uint isrDelay;
103: ulong deltaPulses;
104:
105:
106:
107:
108: // Uncomment this to enable an output pin which toggles
109: // every time the ISR fires.  This helps determine the frequency
110: // for the ISR
111: //#define DEBUG_ISR
112:
113:
114:
115: // OC3-based ISR, updates the 'fHz' global variable
116: _Q void FunctionTimer(void)
117: {
118:     //always add one to this rollover count
119:     isrRollover++;
120:
121:     //isrRollover is always a value between 0 and ISR_MULTIPLIER-1 inclusive
122:     isrRollover = isrRollover % ISR_MULTIPLIER;
123:
124:     // if the rollover is 0, we update the frequency
125:     if( isrRollover == 0 )
126:     {
127:         // read the TCNT and PULSE_A variables as soon as possible
128:         timeb = timea;
129:         pulsesb = pulsesa;
130:         timea = TCNT;
131:         pulsesa = PulseRegRead( PULSE_A );
132:
133:         // read the scheduled ISR time
134:         timey = timex;
135:         timex = TC3;
136:
137:         // calculate delay between ISR firing and code execution, if any
138:         isrDelay = timeb-timey-(timea-timex);
139:
140:         // calculate frequency
141:         deltaPulses = (long)ONE_MS*(pulsesa-pulsesb);
142:         fHz = (float)deltaPulses / (isrDelay+(float)ISR_MULTIPLIER*ISR_TIME);
143:         fHz = fHz * 1000;
144:     }
145:
146:     // If defined this will toggle all pins on PortT which are configured
147:     // as outputs.  PortT is configured in main()
148: #ifdef DEBUG_ISR
149:     PORTT = ~PORTT;
150: #endif
151:
152:
153:     TC3 += ISR_TIME;    // set OC3 count for next interrupt.
154:                         // A slower way to do this is: OCRegWrite(TCNT+ISR_TIME, 3);
155:                         // Because we executed ECTFastClear(), this access to TC3
156:                         // automatically resets the OC3 interrupt flag
157: }                       // so that new OC3 interrupts will be recognized.
158:
159: // This is crucial to the operation of the interrupt.
160: MAKE_ISR(FunctionTimer);        // Make the function into an ISR
161:
162:
163:
164: _Q void StopFunctionTimer(void)
165: {
166:     ECTInterruptDisable(3);     // locally disable OC3, same as: TIE &= ~OC3_MASK;
167: }
168:
169: // inits variables and locally enables OC3 interrupt;
170: // does not globally enable interrupts!
171: _Q void StartFunctionTimer(void)
172: {
173:     StopFunctionTimer();         // locally disable OC3 while we set it up
174:     ATTACH(FunctionTimer, ECT3_ID);  // post the interrupt service routine
175:     ECTFastClear();              // allows the ISR to omit calling ECTClearInterruptFlag()
176:     OCAction(OC_NO_ACTION, 3);   // confirm that no automatic pin action occurs on PT3
177:     OutputCompare(3);            // set channel 3 as output compare
178:     OCRegWrite(TCNT+ONE_MS, 3);  // starts in 1 ms, same as: TC3 = TCNT + ONE_MS;
179:     ECTClearInterruptFlag(3);    // clear flag, same as: TFLG1 = OC3_MASK;
180:     ECTInterruptEnable(3);       // locally enable OC3, same as: TMSK1 |= OC3_MASK;
181:     isrRollover = 0;             // initialize our rollover counter
182: }
183:
184:
185:
186:
187: // ********************* main ***********************************
188:
189: int main( void )
190: {
191:     InitElapsedTime();      // init TIMESLICE_COUNT
192:
193:     StartFunctionTimer();   // enable OC3 interrupt to calculate frequency
194:
195:     // This configures the Pulse A accumulator
196:     // The first parameter,  0, instructs the hardware to not fire an interrupt on every edge
197:     // The second parameter, 0, prevents an interrupt from firing every time the accumulator overflows
198:     // The third parameter,  PULSE_A_RISING_EDGE, only counts rising edges on pin 17 of H8
199:     // The final parameter,  1, configures the timer in 16 bit mode.  A 0 would setup 2 timers in 8 bit mode
200:     PulseASetup ( 0, 0, PULSE_A_RISING_EDGE, 1 );
201:
202:     // only enable interrupts after pulse accumulator is running and ISR is configured
203:     ENABLE_INTERRUPTS();
204:     //StartTimeslicer();      // start timeslicer; also calls ENABLE_INTERRUPTS
205:
206:     #ifdef DEBUG_ISR
207:     // if this is enabled
208:     // pin 24 on H8 is an output
209:     // it will toggle every time the Output Compare fires
210:     PORTT_DIRECTION = 0x01;
211:     #endif
212:
213:     // This loop runs forever, and prints the frequency measured on pin 17 of H8
214:     while( 1 )
215:     {
216:         // Delay for 65535 * 5 Microseconds.
217:         // This delay slows down printing to the serial port.  Printing faster,
218:         // or at full speed, can overwhelm the Mosaic Terminal
219:         MicrosecDelay(65535);
220:         MicrosecDelay(65535);
221:         MicrosecDelay(65535);
222:         MicrosecDelay(65535);
223:         MicrosecDelay(65535);
224:         printf("Frequency in Hz = %.3f\n", fHz );
225:     }
226:
227:     return 0;
228: }



See also → How to Measure Analog Distance Sensor

 
This page is about: Measuring Frequency Using 9S12 HCS12 68HCS12 Microcontroller Pulse Accumulator, Microcontroller Frequency Counter, Pulse Counter, Tally Counter, Frequency Measurement Microcontroller Project – Frequency measurement counting using the Freescale 9S12 HCS12 MC68HCS12 microcontroller's ECT (Enhanced Capture Timer) counting/timing unit. C example program for a 5 MHz (Megahertz) frequency counter, pulse counter using the HCS12 MCU pulse accumulator and software overflow interrupt. electronic tally counter, programming microcontroller interrupt service routines (ISR), 9S12 HCS12 hardware and software interfacing
 
 
Navigation