平均ADC读数在图中有奇怪的台阶

问题描述 投票:1回答:2

我正在读取ADC值以测量温度,然后将它们平均。但是从中得出的图显示的是“步长”,但不是从值的实际步长中得出的。有人知道为什么会这样吗?

#include "arduino.h"

#define AVG_COUNT 1300
#define AVG_MAX_COUNT (2147483647/1023)
#define AVG_DURATION_TIME (AVG_COUNT * 150)

#define LOOP_INTERVAL 250

#define LOOP_INTERVAL_X 100

#define E 2.7182818284
#define AREF 4945.0

#define VREF 4945.0 // 3004 at about 22°C and 3012 at about 0°C
//#define S1_R_REF 15000.0
#define S1_R_REF_DIV4 3750.0
#define S1_RM 6260

#define S1_A_1 0.003354016
#define S1_B_1 0.0002744030
#define S1_C_1 0.00000366694
#define S1_D_1 0.000000137549

byte last_ADMUX = 0;
#define ADC_BANDGAP ((1 << MUX3) | (1 << MUX2) |  (1 << MUX1))
#define ADC_RAW_BANDGAP 1110

#define T_B 5.255
#define T_A_x100 0.65

int temperature;

#define SUM_COUNT 10

int getADCRaw(){
    //Start conversion
    ADCSRA |= (1 << ADSC);

    //wait for Measurment to finish
    while(ADCSRA & (1 << ADSC));

    return (ADCL | (ADCH << 8));
}

int getTempX100(int RntcDiv4){
    return (int) (100 / (S1_A_1 + S1_B_1*log(RntcDiv4/S1_R_REF_DIV4) + S1_C_1*pow(log(RntcDiv4/S1_R_REF_DIV4),2) + S1_D_1*pow(log(RntcDiv4/S1_R_REF_DIV4), 3) ));
}


int getTempAvg(int avgCount){
    unsigned long rmSum = 0;

    for(int i = 0; i < avgCount; i++){
        rmSum += (unsigned long) getADCRaw();;
    }

    float rmAvg = mapFloat((float)rmSum / avgCount, 0, 1023, 0, AREF);

    int rntcDiv4 = (int) ((VREF / rmAvg - 1) * S1_RM / 4);

    int tempCelsiusX100 = getTempX100(rntcDiv4) - 27315;

    delay(1);
    return tempCelsiusX100;
}

unsigned long getXBase10(byte exponent){
    unsigned long rValue = 1;

    for(;exponent > 0; exponent--){
        rValue *= 10;
    }
    return rValue;
}

void setup(){
    Serial.begin(57600);
    //Pin A0input
    DDRC &= ~(1 << PC0);

    ADMUX |= (1 << REFS0);  // 5V AREF (ADC0 is 0 by default)
}


unsigned long printTime = 0;
unsigned long nowMillis = 0;
unsigned long measTime = 0;

unsigned long lastPrintTime = 0;

#define INTERVAL 250

void loop(){
  nowMillis = millis();
  int temp = getTempAvg(20);

  measTime = millis() - nowMillis;

  while(millis() - lastPrintTime < INTERVAL)
    delayMicroseconds(333);

  Serial.println(measTime);

  Serial.flush();
  lastPrintTime = millis();
}

通过平均,我想提高分辨率,这样我就不会再走那么大步了。以及更精确的读数。为什么世界上有这些步骤?

big steps and small steps

c++ microcontroller atmega adc
2个回答
0
投票

问题似乎是您的平均值是很短时间内的平均值。假设A2D需要1us完成,那么您要在20us的窗口中获取数据。从那里,例程执行简单的平均(rmSum / avgCount)和查找。从那里开始,您可能会执行一些可能过于复杂的计算以生成单个结果。最终值(tempCelsiusX100和temp)永远不会在下一个周期中用作平均值的一部分。温度变化相对缓慢;连续读取20个读数是可以的,但同样,这实际上是相同的读数。您需要创建一个使用先前值的加权滚动平均值,并在更长的时间内提供平均值。例如,在循环部分中,每隔250毫秒您将获得一个新值,您应将这些值中的10个取平均值,以给出2.5秒的总体平均值。我建议使用加权滚动平均值,其中最新值的加权平均值要高于前一个值。使用10个值的滚动缓冲区限制了过去读数过多的影响。


0
投票

我没有验证所有整数数学以确保您没有舍入问题,但假设您没有,并且0.1度对应于ADC的分辨率,那么...

问题是输入电路中没有足够的噪声。

如果您没有足够的噪声,则ADC将提供最接近实际温度的值。这不会改变,因此,如果将一束平均在一起,则几乎可以获得相同的值。

另一方面,如果您要采样的模拟信号中有少量LSB噪声,则ADC将根据概率分布产生值,并且该分布的中心-平均值-会发生变化以反映实际温度的微小变化。

合适的术语是“抖动”。在图像量化中更常见,但也用于量化一维信号:https://en.wikipedia.org/wiki/Dither

当然,增加输入噪声会使输出的噪声更大,而最初的精度会降低,但是看起来您有很大的空间来增加平均过程的长度来弥补这一点。

最简单的将噪声引入电路的方法可能是自己产生。这里有一篇很好的文章介绍如何使用arduino:https://thecavepearlproject.org/2017/02/27/enhancing-arduinos-adc-resolution-by-dithering-oversampling/

© www.soinside.com 2019 - 2024. All rights reserved.