How to calibrate the error of ADC module inside MCU?
Understanding ADC Error Sources
Before calibration, it's important to understand the common error sources in microcontroller ADCs:
Offset Error: Non-zero output when input is zero
Gain Error: Deviation from ideal slope in transfer function
Non-linearity: Deviation from straight-line transfer function
Noise: Random variations in readings
Reference Voltage Errors: Inaccuracies in voltage reference
Hardware Preparation for Calibration
Precision voltage source (or at least a stable known voltage)
High-quality multimeter (for reference measurements)
Stable power supply (clean power to MCU)
Temperature-controlled environment (if temperature compensation needed)
Basic Calibration Methods
1. Offset Calibration
// Measure with grounded input #define NUM_OFFSET_SAMPLES 100 float adc_calibrate_offset() { uint32_t sum = 0; for(int i=0; i<NUM_OFFSET_SAMPLES; i++) { sum += adc_read(); // Replace with your ADC read function delay(1); } float offset = (float)sum / NUM_OFFSET_SAMPLES; return offset; }
2. Gain Calibration
// Apply known reference voltage (e.g., 2.5V) float adc_calibrate_gain(float known_voltage, float vref) { float offset = adc_calibrate_offset(); // Get offset first uint32_t sum = 0; for(int i=0; i<100; i++) { sum += adc_read(); delay(1); } float raw_avg = (float)sum / 100.0; float expected_raw = (known_voltage / vref) * ADC_MAX_VALUE; float gain_error = (raw_avg - offset) / expected_raw; return gain_error; }
Advanced Calibration Techniques
1. Multi-Point Calibration
typedef struct { float actual_voltage; float raw_adc; } CalibrationPoint; typedef struct { float slope; float intercept; } CalibrationCoeff; CalibrationCoeff calculate_calibration(CalibrationPoint points[], uint8_t count) { float sum_x = 0, sum_y = 0, sum_xy = 0, sum_xx = 0; for(uint8_t i=0; i<count; i++) { sum_x += points[i].raw_adc; sum_y += points[i].actual_voltage; sum_xy += points[i].raw_adc * points[i].actual_voltage; sum_xx += points[i].raw_adc * points[i].raw_adc; } CalibrationCoeff coeff; coeff.slope = (count * sum_xy - sum_x * sum_y) / (count * sum_xx - sum_x * sum_x); coeff.intercept = (sum_y - coeff.slope * sum_x) / count; return coeff; }
2. Temperature Compensation
typedef struct { float temp; float offset; float gain_error; } TempCalPoint; float interpolate_temp_compensation(TempCalPoint cal_table[], uint8_t count, float current_temp) { // Find surrounding points // Perform linear interpolation // Return compensation value }
Implementation in Firmware
1. Storing Calibration Parameters
typedef struct { float offset; float gain; float temp_comp_slope; uint32_t crc; // For data integrity check } AdcCalibrationData; void save_calibration_to_flash(AdcCalibrationData *data) { // Calculate CRC data->crc = calculate_crc(data, sizeof(AdcCalibrationData)-4); // Write to flash flash_write(CALIBRATION_ADDRESS, (uint8_t*)data, sizeof(AdcCalibrationData)); }
2. Applying Calibration
float read_calibrated_adc(float temperature) { static AdcCalibrationData calib; static bool calib_loaded = false; if(!calib_loaded) { load_calibration_from_flash(&calib); calib_loaded = true; } float raw = (float)adc_read(); float temp_comp = calib.temp_comp_slope * (temperature - 25.0); // 25°C as reference // Apply calibration float calibrated = (raw - calib.offset - temp_comp) * calib.gain; return calibrated; }
Practical Calibration Procedure
Warm-up period: Power on system and wait for stabilization (10-15 minutes)
Offset calibration:
Short ADC input to ground
Measure multiple samples and calculate average
Gain calibration:
Apply precise reference voltage (e.g., 80% of full scale)
Measure and compare with expected value
Multi-point verification:
Check at 10%, 50%, 90% of full scale
Adjust calibration if needed
Temperature testing (if applicable):
Characterize at different temperatures
Build temperature compensation table
Special Considerations
Reference Voltage Stability:
Use external reference if internal is unstable
Consider reference voltage measurement via another channel
Averaging for Noise Reduction:
#define ADC_OVERSAMPLE 16 uint32_t read_oversampled_adc() { uint32_t sum = 0; for(int i=0; i<ADC_OVERSAMPLE; i++) { sum += adc_read(); } return sum >> 2; // For 16x oversampling, gives 2 extra bits }
PCB Layout Effects:
Ensure proper grounding
Minimize noise coupling
Use proper bypass capacitors
Validation and Testing
Static Test:
Apply known DC voltages and verify readings
Dynamic Test:
Use function generator for ramp signals
Check linearity across range
Long-term Drift Test:
Monitor over hours/days
Check for environmental effects
评论
发表评论