Just to keep topic alive, I'd like to share my code. It works pretty ok (looks a little ugly however), so I don't feel that I would change anything.
Attached is main routine include file, ADCCal.h.
The library is very simple and only does 2-point linear calibration, so corrects for shift and gain. It does not include any nonlinear or table lookup routines. Library outputs the data even beyond that points. So if you calibrate "min" value as 0.5V, it will give you 0.1V, but will set the flag that the value is outside the calibration boundaries and unreliable.
How to use:
- Declare everything. In my case there is an array of 2 calibrations, one for input voltage (ADC), second for output current (DAC):
#define CALDATA_VOLTAGE 0
#define CALDATA_DAC_CURRENT 1
ADC_DAC_CALIBRATION ADCcalData[2];
- Load/Save from eeprom (thanks to robtillaart). Here with unnecessary Serial output of loaded parameters:
for (int i=0;i<1;i++)
{
EEPROM_readAnything(i*sizeof(ADC_DAC_CALIBRATION)+1, ADCcalData[i]); //don't save to "0" position
Serial.print(F("READ CAL DATA:"));
Serial.println(i);
Serial.print(F("MIN: Analog:"));
Serial.print(ADCcalData[i].m_iMinAnalogVal);
Serial.print(F("MIN: Digital:"));
Serial.println(ADCcalData[i].m_iMinDigitalVal);
Serial.print(F("MAX: Analog:"));
Serial.print(ADCcalData[i].m_iMaxAnalogVal);
Serial.print(F("MAX: Digital:"));
Serial.println(ADCcalData[i].m_iMaxDigitalVal);
}
Serial.println(F("DONE LOADING EEPROM"));
- You need some calibration routine to get the data. That's the most weird code. In my device, it's made by interacting with user:
while(!keyboard.available()){}; keyboard.read();
Serial.println("CONNECT 5.0V PSU, set 5.00V, and press any key");
while(!keyboard.available()) {}; keyboard.read();
unsigned long ulADCData=0;
for (int i=0;i<1000;i++)
{
ulADCData+=analogRead(VOLTAGE_ADC_PIN); //Reading voltage for 5 secs.
delay(5);
}
ADCcalData[CALDATA_VOLTAGE].m_iMaxAnalogVal= 5000; //5000mV is 5.00V
ADCcalData[CALDATA_VOLTAGE].m_iMaxDigitalVal = ulADCData/1000; //in ADC reads, that's what we've read.
The same calibration should be done for "low" ranges.
So, to do the full calibration, user should fill in 4 variables:
m_iMaxAnalogVal = 5000; //5.00V is the max what we sense. Struct is in millivolts, so 5000mV.
m_iMaxDigitalVal = ADCData; //Whatever ADC read when 5.00V was connected.
m_iMinAnalogVal= 500; //0.5V is our min range that we sense. 500mV is 0.5V.
m_iMinDigitalVal = ADCData; //Whatever ADC read when 0.5V was connected.
For the DAC data, all is pretty the same: you output some digital (PWM,DAC,...) signal by Arduino, and measure it with scope, ruler or DMM.
Both for "High" range of signal and "Low" range of signal. Then you fill in MaxDigital and MinDigital with known output data, and fill in MaxAnalog and MinAnalog with what you've measured.
Saving to EEPROM is straightforward:
Serial.println(F("SAVING CAL DATA TO EEPROM"));
for (int i=0;i<1;i++)
{
EEPROM_writeAnything(i*sizeof(ADC_DAC_CALIBRATION)+1, ADCcalData[i]); //don't save to "0" position
}
Serial.println(F("DONE"));
And now the usage of lib. Simple and easy.
For ADC:
unsigned int iADC0=analogRead(VOLTAGE_ADC_PIN); //Read Analog voltage.
Serial.println(ADCcalData[CALDATA_VOLTAGE].GetAnalog(iADC0));
For DAC:
unsigned int iDischargeCurrent = 10000; //it's 10A analog value
DACOutput(ADCcalData[CALDATA_DAC_CURRENT].GetDigital(iDischargeCurrent));
I would be happy if this code would be useful for someone, and appreciate if someone makes a candy from that 
ADCCal.h (5.21 KB)