我正在为一个小型8位微控制器编写一个软件,在C中,部分代码是读取电流互感器(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然而,它有许多浮点计算。这给我的小型单片机增加了很大的负担。我发现sqrt()函数在我的编译器中不起作用。
有什么代码可以运行得更快吗?
发布于 2015-03-02 14:32:32
当您需要在缺乏FPU的处理器上获得更快的速度时,最好的选择是用不动点代替浮点计算。将此与joop的建议(一牛顿-拉斐逊平方米)相结合,您将得到如下内容:
#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样本即可。为了获得更高的分辨率,您可以在这里和那里添加几个位移位,但是您必须小心,不要溢出变量。我怀疑你真的需要额外的决心。
过滤器的输出与其输入在同一个单元中。在这种情况下,它是你的模数转换器的单位:2.5V/1024的副≈,2.44mV。如果您可以在以后的计算中保留此单元,则可以通过避免不必要的转换来节省周期。如果你真的需要电压值(这可能是I/O的要求),那么你必须转换成浮点。如果您想要毫伏,您可以停留在整数域:
uint16_t rms_in_mV = rms_filter(raw_sample) * 160000UL >> 16;发布于 2015-03-02 11:17:12
由于您的平方和值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备注:
float更改为double (这在通用计算机/桌面上是正常的),如果double在您的微型计算机上非常昂贵,您可以将它们改回来。static,因为我不知道代码是来自函数还是循环。static强制它内联。如果编译器没有内联静态函数,则应手动将其内联。发布于 2015-03-02 10:39:22
希望您的项目是测量“大”交流电压(而不是像9v的电机控制)。)如果碰巧是这种情况,那么您就可以作弊,因为您的错误可以在可接受的范围内。
以整数计算所有数学,并为sqrt操作使用一个简单的查找映射。(您可以在启动时预先计算,如果您正在执行3个阶段,那么您只需要大约600个奇数。)
这也引出了一个问题,你是否真的需要VAC、RMS或其他一些衡量能力的指标?(例如,你能用一个简单的盒子-平均计算法-逃脱吗?)
https://stackoverflow.com/questions/28807537
复制相似问题