使用定点而不是浮点计算信号频率

问题描述

希望有人能提供帮助...我正在寻找使用定点算法而不是我目前实施和工作的 Float 来计算捕获信号周期的频率,因为我知道这将是一种资源密集程度较低的方法;我正在使用频率计数器来测量音频频率。我是 PIC 编程的初学者,对定点概念非常陌生,但希望得到任何指导。

我使用的是 PIC18F13K22,内部时钟频率为 4Mhz(其他内部时钟选项:16Mhz、8Mhz 等),在捕获模式下,使用定时器 3,频率为 1 Mhz (Fosc/4) ;因此每个捕获的计数周期为 1E-6 秒。例如:125Hz = 1E6 / 125 = 8,000 计数的输入频率。

我的代码片段如下,使用 C 和 XC8 编译器:

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);
        }
    }
}

目前变量 freq 被定义为 Double 数据类型。希望采用尽可能有效的方法,因此对计算和表示定点频率感兴趣;还热衷于确保尽可能准确,同时保持使用内部时钟和捕获方法

谢谢, 亚历克斯

解决方法

这个想法是创建一种定义大于时钟的微时钟整数类型。定义的差异可能是 2 的幂,以使转换尽可能快。让我们举一个定义乘以 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;

然后你可以这样做:

volatile Microclock freq;
freq = SecondInMicroclock / period;

结果将等同于具有 256 个值的小数部分的浮点数。我给你举个例子:

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

您可以通过再次转换为浮点数来观察定义的差异:

608076 / 256.f  = 2375.296875;

虽然丢失不是很重要,但您可以通过选择增加定义,例如选择65536而不是256。

但自然在代码中你将只使用整数(但如果不知道你用这个频率变量做什么,很难给出一个使用示例):

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