小编典典

用C可以更快地计算RMS值吗?

algorithm

我正在用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

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

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


阅读 596

收藏
2020-07-28

共1个答案

小编典典

当您需要在缺乏FPU的处理器上更快运行时,最好的选择是用定点替换浮点计算。结合使用joop的建议(一个牛顿-拉夫森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;
}

只需通过该滤波器运行原始ADC采样即可。您可能会在此处和此处添加一些移位以获取更高的分辨率,但是您必须注意不要使变量溢出。我怀疑您是否真的需要额外的分辨率。

滤波器的输出与输入的单位相同。在这种情况下,它是ADC的单位:2.5 V / 1024≈2.44
mV。如果您可以在以后的计算中保留此单位,则可以避免不必要的转换,从而节省周期。如果您确实需要以伏特为单位的值(可能是I /
O要求),则必须转换为浮点数。如果需要毫伏,则可以停留在整数域中:

uint16_t rms_in_mV = rms_filter(raw_sample) * 160000UL >> 16;
2020-07-28