C中任何更快的RMS值计算?

eep*_*pty 7 c algorithm performance microcontroller

我正在用C编写一个小型8位微控制器的软件.部分代码是读取电流互感器(ZCT)的ADC值,然后计算RMS值.流过ZCT的电流是正弦曲线但可能会失真.我的代码如下:

float       adc_value, inst_current;
float       acc_load_current;           // accumulator = (I1*I1 + I2*I2 + ... + In*In)
double      rms_current;

// Calculate the real instantanous value from the ADC reading
inst_current = (adc_value/1024)*2.5;    // 10bit ADC, Voltage ref. 2.5V, so formula is: x=(adc/1024)*2.5V                           

// Update the RMS value with the new instananous value:
// Substract 1 sample from the accumulator (sample size is 512, so divide accumulator by 512 and substract it from the accumulator)
acc_load_current -= (acc_load_current / 512);       
inst_current *= inst_current;          // square the instantanous current
acc_load_current += inst_current;      // Add it to the accumulator

rms_current = (acc_load_current / 512);  // Get the mean square value. (sample size is 512)
rms_current = sqrt(rms_current);         // Get RMS value

// Now the < rms_current > is the real RMS current
Run Code Online (Sandbox Code Playgroud)

但是,它有许多浮点计算.这给我的小型MCU增加了很大的负担.我发现该sqrt()函数在我的编译器中不起作用.

有没有可以运行得更快的代码?

Edg*_*net 7

当您需要在没有 FPU 的处理器上提高速度时,最好的办法是用定点代替浮点计算。将此与 joop 的建议(一个 Newton-Raphson sqrt)结合起来,您会得到如下结果:

#define INITIAL 512  /* Initial value of the filter memory. */
#define SAMPLES 512

uint16_t rms_filter(uint16_t sample)
{
    static uint16_t rms = INITIAL;
    static uint32_t sum_squares = 1UL * SAMPLES * INITIAL * INITIAL;

    sum_squares -= sum_squares / SAMPLES;
    sum_squares += (uint32_t) sample * sample;
    if (rms == 0) rms = 1;    /* do not divide by zero */
    rms = (rms + sum_squares / SAMPLES / rms) / 2;
    return rms;
}
Run Code Online (Sandbox Code Playgroud)

只需通过此过滤器运行您的原始 ADC 样本即可。您可以在这里和那里添加一些位移以获得更高的分辨率,但您必须小心不要溢出您的变量。我怀疑你真的需要额外的分辨率。

滤波器的输出与其输入处于同一单位。在这种情况下,它是您的 ADC 的单位:2.5 V / 1024 ? 2.44 毫伏。如果您可以在后续计算中保留此单位,您将通过避免不必要的转换来节省周期。如果您确实需要以伏特为单位的值(这可能是 I/O 要求),那么您将不得不转换为浮点数。如果你想要毫伏,你可以留在整数领域:

uint16_t rms_in_mV = rms_filter(raw_sample) * 160000UL >> 16;
Run Code Online (Sandbox Code Playgroud)


joo*_*oop 6

由于您的平方和值acc_load_current在迭代之间变化不大,因此其平方根将几乎不变.Newton-Raphson sqrt()函数通常仅在几次迭代中收敛.通过每步使用一次迭代,计算被抹掉.

static double one_step_newton_raphson_sqrt(double val, double hint)
{
double probe;
if (hint <= 0) return val /2;
probe = val / hint;
return (probe+hint) /2;
}

static double      acc_load_current = 0.0;           // accumulator = (I1*I1 + I2*I2 + ... + In*In)
static double      rms_current = 1.0;
float       adc_value, inst_current;
double      tmp_rms_current;

// Calculate the real instantanous value from the ADC reading
inst_current = (adc_value/1024)*2.5;    // 10bit ADC, Voltage ref. 2.5V, so formula is: x=(adc/1024)*2.5V                           

// Update the RMS value with the new instananous value:
// Substract 1 sample from the accumulator (sample size is 512, so divide accumulator by 512 and substract it from the accumulator)
acc_load_current -= (acc_load_current / 512);
inst_current *= inst_current;          // square the instantanous current
acc_load_current += inst_current;      // Add it to the accumulator

tmp_rms_current = (acc_load_current / 512);
rms_current = one_step_newton_raphson_sqrt(tmp_rms_current, rms_current);         // Polish RMS value

// Now the <rms_current> is the APPROXIMATE RMS current
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 我将一些数据类型更改floatdouble(这在通用机器/桌面上是正常的)如果double在您的微型计算机上非常昂贵,您可以将其更改回来.
  • 我还补充说static,因为我不知道代码是来自函数还是来自循环.
  • 我做了static强制它内联的功能.如果编译器没有内联静态函数,则应手动内联.

  • 尼斯.但是:*Ralfston*?;-) (2认同)