如何修复STM32F107读取的模拟输入振荡

时间:2013-07-31 21:38:21

标签: embedded microcontroller processor stm32 analog-digital-converter

我必须使用处理器STM32F107从外部源读取输入值。这种平衡在包含处理器的电路板外部,并通过PA4与之通信。

这是我第一次尝试从天平读取输入。

我使用此功能设置ADC:

void ADC_Configuration(void) {

    ADC_InitTypeDef ADC_InitStructure;
   /* PCLK2 is the APB2 clock */
   /* ADCCLK = PCLK2/6 = 72/6 = 12MHz*/
   RCC_ADCCLKConfig(RCC_PCLK2_Div6);
   /* Enable ADC1 clock so that we can talk to it */
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
   /* Put everything back to power-on defaults */
   ADC_DeInit(ADC1);

   /* ADC1 Configuration ------------------------------------------------------*/
   /* ADC1 and ADC2 operate independently */
   ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
   /* Disable the scan conversion so we do one at a time */
   ADC_InitStructure.ADC_ScanConvMode = DISABLE;
   /* Don't do contimuous conversions - do them on demand */
   ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
   /* Start conversin by software, not an external trigger */
   ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
   /* Conversions are 12 bit - put them in the lower 12 bits of the result */
   ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
   /* Say how many channels would be used by the sequencer */
   ADC_InitStructure.ADC_NbrOfChannel = 1;

   /* Now do the setup */
    ADC_Init(ADC1, &ADC_InitStructure);
   /* Enable ADC1 */
   ADC_Cmd(ADC1, ENABLE);
   /* Enable ADC1 reset calibaration register */
   ADC_ResetCalibration(ADC1);
   /* Check the end of ADC1 reset calibration register */
   while(ADC_GetResetCalibrationStatus(ADC1));
   /* Start ADC1 calibaration */
   ADC_StartCalibration(ADC1);
   /* Check the end of ADC1 calibration */
   while(ADC_GetCalibrationStatus(ADC1));
}

我使用此函数来获取输入:

u16 readADC1(u8 channel) {

   ADC_RegularChannelConfig(ADC1, channel, 1, ADC_SampleTime_1Cycles5);

   // Start the conversion
   ADC_SoftwareStartConvCmd(ADC1, ENABLE);
   // Wait until conversion completion
   while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
   // Get the conversion value

   return ADC_GetConversionValue(ADC1);
}

问题是在相同重量的N次测量中,我得到N个不同的结果。例如,重量为70kg,readADC1(ADC_Channel_4)的输出为715,760,748,711,759等。

我做错了什么?

编辑。我添加了这个函数(模拟lp过滤器)来稳定输入,它工作正常。问题是如何将从此函数返回的值转换为千克。使用恒定系数(通过测量已知对象确定)给出与增加的输入重量成比例的增长误差。有人建议有更好的转换吗?

double fix_oscillations(){
    int i;
    double LPOUT=0,LPACC=0;
    int K = 5000;

    for(i=0;i<5000;i++){
       LPACC = LPACC + readADC1(ADC_Channel_4) - LPOUT;
       LPOUT = LPACC / K;
    }
    return LPOUT;
}

4 个答案:

答案 0 :(得分:2)

输入本身可能存在噪声或其他设备引入噪声。值得用示波器观察信号,以确定信号上的干扰或噪声类型,因为这可能会影响最佳解决方案。

如果可能,应根除任何外部干扰源,然后应采用外部模拟信号条件,理想情况下应使用截止频率为预期采样率的一半或更低的低通滤波器。然后,您可以根据需要在数字域中应用过滤。对于静态信号(电压电平),简单的块平均就足够了,对于更快的移动信号,移动平均值(boxcar滤波器)会更好。对于需要从中提取某些频率的复杂信号,需要更复杂的滤波器,但在这种情况下可能不是这种情况。

答案 1 :(得分:1)

  

编辑。我添加了这个函数(模拟lp过滤器)来稳定输入,它工作正常。问题是如何将从此函数返回的值转换为千克。使用恒定系数(通过测量已知对象确定)给出与增加的输入重量成比例的增长误差。有人建议有更好的转换吗?

这可能值得发布一个单独的问题,因为它与问题标题没有明确的关联。

如果输出是非线性的,则可以使用多个校准点,在点之间进行线性插值,但很可能曲线可以用方程表征。在感兴趣的范围内进行多次测量,然后在电子表格工具(如Excel或OpenOffice.org Calc)中绘制这些点。图表工具包括各种类型的“趋势线”绘图,并且可以显示曲线的等式。选择最合适的曲线类型。如果需要使用具有两个以上项的多项式,请确保显示具有足够小数位的方程项,因为这些可能是关键的。您可以通过使用公式生成曲线并查看它与趋势线的匹配程度来测试您是否具有足够的精度。绘制方程可能是任何曲线的一个好主意,作为足够精度的测试。编写代码时,请确保使用的数据类型也足够精确。

关于该行的说明:

LPOUT = LPACC / K;

通过采集5000个样本,您有效地将ADC分辨率提高了大约12位(以采样时间为代价),但除以K后,您已经不必要地丢失了这个精度,并且它也是一个截断的分界。最好在转换为Kg时直接使用未分割的总和。我意识到分割使得值看起来“稳定”,但它与信号有关:噪声比不是绝对噪声幅度。转换为Kg并显示为所需的实际精度将具有“稳定”结果但具有较小累积误差的相同效果。

答案 2 :(得分:1)

您是否看过STM32应用笔记以提高ADC精度? 还有一个用于提高分辨率的应用笔记,以及其他一些应用笔记。

您应该查看此链接here。 在滚动框中搜索“ADC”页面。

STMicro的应用说明通常很有用,因为他们希望尽早将产品推出,然后在文字/支持后添加文字/支持。

答案 3 :(得分:0)

您可能会发现这是一种有用的平均方式:

float FilteredValue;
#define TIME_CONSTANT 100
FilteredValue += ((float)ADCreading - FilteredValue)/TIME_CONSTANT;

这实现了一个真正的低通滤波器,其时间常数为TIME_CONSTANT x采样频率。 如果ADCreading中有步骤更改,FilteredValue将逐渐更改为新值。理论上它永远不会达到它,因为它实现了一个逆指数滤波器。 TIME_CONSTANT的值越大,噪声抑制越好,但稳定所需的时间越长。