Search code examples
cembeddedfrequencypicfixed-point

Calculating signal Frequency using Fixed-Point rather than Float


Hope someone can help... I'm looking to calculate frequency of captured signal period using fixed-point arithmetic rather than my currently implemented and working Float, as I understand this would be a less resource intensive approach; I'm using the frequency counter to measure audio frequencies. I'm a beginner to PIC programming and very new to the concept of fixed-point, but would appreciate any guidance.

I'm using the PIC18F13K22 with Internal Clock at 4Mhz (other internal clock options: 16Mhz, 8Mhz, etc), in Capture mode, using Timer 3, at 1 Mhz (Fosc/4); therefore each captured count period is 1E-6 Sec. For example: an input frequency of 125Hz = 1E6 / 125 = 8,000 counts.

My code snippet below, using C and XC8 compiler:

volatile char buffer[5];
volatile uint16_t t1;
volatile uint16_t t2;
volatile uint16_t period;
volatile float freq;

void main() {
    
    t1 = 0x0;
    t2 = 0x0;
    period = 0x0;
    freq = 0.0;
    
    int status;
    char *text;

    // Init
    sys_init();
    capture_init();
    LCD_Initialize();
    
    LCDPutStr("Frequency:");
    LCDGoto(0,1);
    LCDPutStr("0");
    LCDGoto(14,1);
    LCDPutStr("Hz");
        
    while(1) {
        TMR3L = 0x00;
        TMR3H = 0x00;
        
        T3CONbits.TMR3ON = 1; // Timer3 on
        while(PIR1bits.CCP1IF==0);  // Wait for first rising edge
        PIR1bits.CCP1IF = 0;
        t1 = CCPR1;
        while(PIR1bits.CCP1IF==0); // Wait for second rising edge
        PIR1bits.CCP1IF = 0;
        t2 = CCPR1;
        T3CONbits.TMR3ON = 0; // Timer3 off
        
        if (t1 < t2) {
            period = t2 - t1;
            freq = 1000000 / period;

            text=ftoa(freq,&status);

            LCDGoto(0,1);
            LCDPutStr(text);
        }
    }
}

Currently the variable freq is defined as a Double data type. Looking to take as efficient approach as possible, and hence interested in calculating and representing frequency in fixed-point; also keen to ensure as accurate as possible whilst maintaining use of internal clock and Capture method.

Thanks, Alex


Solution

  • The idea would be to create a kind of Microclock integer type having a definition greater than the clock. The difference of definition could be a power of two to make the conversion the fastest as possible. Let's give an example with a definition multiplicated by 256 :

    typedef UInt32 Microclock;
    static inline Microclock clock2microclock(UInt16 clock) { return clock << 8; }
    static inline UInt16 microclock2clock(UInt16 clock) { return clock >> 8; }
    static inline Microclock microclock2fractional(UInt16 clock) { return clock  % 256; }
    static const Microclock SecondInMicroclock = 256000000;
    

    then you can do :

    volatile Microclock freq;
    freq = SecondInMicroclock / period;
    

    The result would be equivalent to a float having a fractional part of 256 values. I give you an example :

    1000000 / 421.f = 2375.29688;
    256000000 / 421 = 608076;
    

    You can observe the difference of definition by converting to a float again :

    608076 / 256.f  = 2375.296875;
    

    Although the lost is not very important, you could increase the definition by choosing, for instance, 65536 instead of 256.

    But naturally in codes you will use integers only (but it's difficult to give an example of use without knowing what you're doing with this frequency variable) :

    UInt16 ifreq    = microclock2clock (608076);      // 2375
    Microclock rest = microclock2fractional(608076);  // 76 (76/256)