1.4.2 CLC + NCO + TimerX
Modules | Limit High | Resolution |
---|---|---|
CLC | 65.54 ms | 62.5 ns |
NCO | ||
TimerX |
This method uses the CLC to clock the NCO accumulator when the pulse is high. Any timer can be used to increment when the other modules are running. For best accuracy, this should be used to measure duty cycles of a waveform over an extended period of time of at least 100 periods.
OVERVIEW
When the timer is enabled, immediately enable the NCO to start accumulating with an increment value of one. When the waveform is high, the NCO will increment with every clock edge until the waveform goes low.
The NCO is disabled to conclude the measurement and can be initiated in multiple ways such as an interrupt on the timer or NCO accumulator overflow. If an overflow has occurred, the maximum value of that register should be used and not its current value since the interrupt would not stop it from incrementing from zero.
It is important that the same clock source (FOSC) is used between the CLC and timer to avoid post processing. For example, Timer2 can only utilize FOSC/4 as its clock input. Two left shifts on the resultant timer value can compensate for this by multiplying its value by 4.
The duty cycle is subsequently the 20-bit NCO value divided by the timer value (assuming both the timer and NCO increment by 1 on the same clock source with a 1:1 ratio).
One important consideration is the timer rollover length. The timer must not expire before the waveform makes a complete period.
SETUP
- Set up the NCO in FDC or PFC mode with CLC1OUT as the clock source
- Set up CLC as a 4-input AND gate
- Inputs 1 and 2 set to VDD (invert Gate 1 and 2 output)
- Input 3 is the clock source (HFINTOSC)
- Input 4 is the Pulse
CLC NCO Operation Code
NCO1ACCU = NCO1ACCH = NCO1ACCL = 0x00; // Clear NCO Accumulator TMR1H = 0x00; // Clear Timer1 H/L Bytes TMR1L = 0x01; PIR1bits.TMR1IF = 0; // Clear Timer1 Flag PIR2bits.NCO1IF = 0; // Clear NCO1 Flag uint16_t cycles = 0; // Clear the cycles count reset_CLC2_CLC3(); T1CONbits.TMR1ON = 1; // Enable TMR1 to start counting NCO1CONbits.EN = 1; // Enable NCO1 to start counting if (PIR1bits.TMR1IF || cycles > 99){ NCO1CONbits.EN = 0; // Disable NCO1 to stop counting T1CONbits.TMR1ON = 0; // Disable TMR1 to stop counting } else { cycles++; // Increment cycles for every cycle through the code. } // Eventually enough cycles occur that // NCO1 and Timer1 will stop. // Then measurements can be taken and // a duty cycle can be calculated.
Figure 3 shows a timing diagram as an implementation of Figure 1.
The measured duty cycle is then:
There is uncertainty associated with this measurement due to when the timer starts and stops incrementing relative to when the waveform period starts and ends. This uncertainty fluctuates with the placement of the timer overflow. If the timer is disabled just before the start of the second pulse (not pictured), then the duty cycle would appear to be much less than it is.
For this reason, this method should be employed to be counting during as much of the waveform as possible. As an example, a 50 kHz square wave that goes through 100 periods, will have an accuracy of one percent, or rather one period length. This would require that the timer be counting for at least 2 ms.