使用MCU時常會有不準確的問題,造成量測不精準的狀況,可以透過兩點式校正ADC的方法,將ADC的Gain error及Offset error校正回來,取得需要的精確度。
要進行ADC兩點校正方法不難,首先在MCU的部分需要有VREF輸入可以使用,在不透過人工使用Calibration Source的情況下,利用ATL431B Grade(0.5%)輸出2.5V(VREF)做ADC參考電壓,然後再利用ATL431輸出的2.5V電壓做90%*VREF及10%*VREF兩個電壓點分別進入AN0與AN1,利用AN0、AN1兩點電壓做校正,假設ADC的解析度為12 bit,2點校正實際方式如下:
/*
VREF = 2.5V
The two calibration points are chosen at V1
= 2.5*10k/(10k+100k); V2 = 2.5*100k/(10k+100k)
The ideal ADC output values at V1 and V2
are represented as Ci1 and Ci2.
Ci1 = V1 * 4096 / 2.5 = 372; Ci2 = V2 *
4096 / 2.5 = 3724
Assume Ca1 = 404, Ca2 = 3847
The actual ADC output values at V1 and V2 are represented as Ca1 and Ca2.
The gain and offset error will be calculated using the equation of a straight line y = mx + b, Where m is the slope of the line and b is the offset.
The gain error can be calculated as the
slope of the actual ADC output divided by the slope of the ideal ADC output.
Gain Error = (Ca2 – Ca1) / (Ci2 – Ci1) =
(3847 – 404) / (3724 – 372) = 1.027(positive gain error)
b = y – mx
Offset error = Cal – (Gain error * Ci1) = 404 – (1.027 * 372) = 22(positive offset error)
GAINCORR(Gain correction) = 4096 / Gain
error = 4096 / 1.027 = 3988
OFFSETCORR = offset error = 22
Adc result = (Conversion value –
OFFSETCORR) * (GAINCORR/4096)
Ca1 = (404 – 22) * (3988 / 4096) = 372
Ca2 = (3847 – 22) * (3988 / 4096) = 3724
*/
#define CI1 372
#define CI2 3724
long int Ci1 = CI1, Ci2 = CI2;
unsigned int GainError;
int OffsetError;
Ca1 = Adc_Read(ACS_AN1, VBG_DIS);
Ca2 = Adc_Read(ACS_AN2, VBG_DIS);
/* y = mx + b, m = slope = (y2-y1) /
(x2-x1), y2 = Ca2, y1 = Ca1, x2 = Ci2, x1 = Ci1*/
GainError = (unsigned int)(((Ca2-Ca1) << 12) / (Ci2 - Ci1)); // scale to 4096
/* b = y - mx */
OffsetError = (int)(Ca1 - (Ci1 * GainError
>> 12));
OffsetCorr = OffsetError;
/* GainCorr = 4096 * (1/GainError) = 4096 *
4096 / GainError */
GainCorr = (unsigned int)(4096ul * 4096 / GainError);
void MeasureVoltage(void)
{
static
unsigned int SumOfMovAvg = 0;
static
unsigned int MovAvg_Buffer[8] = {0}; //
Moving average buffer
static
unsigned char Index = 0; // Moving average index
unsigned
char cnt = 0;
unsigned
long int Temp;
unsigned int result;
MovAvg_Buffer[Index++]
= Adc_Read(ACS_AN0, VBG_DIS);
if(Index
== 8)
{
Index = 0;
FLAGbits.MovingAvgOutput = 1;
}
SumOfMovAvg
= 0;
if(FLAGbits.MovingAvgOutput)
{
for(cnt = 0; cnt < 8; cnt++)
SumOfMovAvg +=
MovAvg_Buffer[cnt];
Temp = (SumOfMovAvg >> 3);
}
else
{
for(cnt = 0; cnt < Index; cnt++)
SumOfMovAvg +=
MovAvg_Buffer[cnt];
Temp =
MovAvg_Buffer[Index-1]; //Temp = (SumOfMovAvg / Index);
}
result
= (unsigned int)((unsigned long int)(Temp - OffsetCorr) * GainCorr >>
12);
//
result is the ADC output after two point calibration
}