I have to read the input value from an external source that is a balance using the processor STM32F107. This balance is external to the board that contains the processor and communicates with it via PA4.
Here is my first attempt to read the input from the balance.
I use this function to setup the 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));
}
And I use this function to get the input:
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);
}
The problem is that in N measurements of the same weight, I get N different results. For example, the weight is 70kg and the output of the readADC1(ADC_Channel_4) is 715,760,748,711,759 etc.
What am I doing wrong?
Edit. I have added this function (that simulates a lp filter) to stabilize the input and it works fine. The problem is how to convert the value returned form this function to kilograms. Using a costant coefficient (determined by measuring a known object.) gives a growing error proportional to increasing weight of the input. Any suggest to have a better conversion?
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;
}
It is possible either that the input is inherently noisy or that noise is introduced by other equipment. It is worth observing the the signal with an oscilloscope to determine the kind of interference or noise on the signal since that may affect the best solution.
If possible you should eradicate any source of external interference, then external analogue signal condition should be applied, ideally by a low-pass filter with a cut-off frequency of half your intended sample rate or less. Then you are in a position to apply filtering in the digital domain if necessary. For static signals (voltage levels), a simple block average will suffice, for faster moving signals a moving average (boxcar filter) would be better. For complex signals from which you need to extract certain frequencies, more complex filters are needed, but that is probably not the case in this instance.