I have a 22 uF and 0.1 uF cap on the VBAT line & can't easily add a cap on the A1 input in this version (maybe next one); what resistance value (current) would you recommend for the ADC? 10K? 1K?
If you can add the cap in later you can probably get away with the 100K/100K divider (possibly).
4K7 vs 100K is a difference of 0.45 mA leakage versus 21 uA leakage.
Are you're using digital ground or analog ground? That might explain the 30 mV offset.
So which ground should be used to avoid that 30mV offset?
==
I have another question though. I noticed that every time I upload a new hex (Teensy 3.1), there is a small offset in ADC voltage readings.
I'm using some current sensors (LEM, 200A) and I have calibrated them once (to display "0" when there's no current passing through). Anyway, each time Teensy restarts I've got a variable (small) offset (sometimes is "0" again, as intended).
I've read that there's a calibration procedure regarding the ADC but (as @Pedvide did mention in his library) it is fired up at every start-up or when you change the ADC resolution.
Is there a way to better calibrate the ADC at start-up? Is there any parameters involved?
You should use analog ground AGND.
The calibration is performed at startup and when the reference is changed.
You can use adcX->recalibrate() to calibrate at any point if the conditions have changed significantly. I imagine that cycles of day and night are enough to produce a small error.
That function can take a while to finish, so you can also call calibrate and check that it's done with wait_for_cal.
#include <ADC.h>
#include <TimerOne.h>
ADC *adc = new ADC(); // adc object
ADC::Sync_result result;
const int measurements = 125;
const int iOffset = +0;
const int uOffset = +3;
volatile int uMeas[125]={0};
volatile int iMeas[125]={0};
volatile int pointer =0;
int start_pin = 9;
int t;
float inst_power;
float sum_inst_power;
float real_power;
float inst_voltage;
float square_voltage;
float sum_square_voltage;
float mean_square_voltage;
float root_mean_square_voltage;
float inst_current;
float square_current;
float sum_square_current;
float mean_square_current;
float root_mean_square_current;
float apparent_power;
float power_factor;
char c=0;
void setup()
{
//pinMode(LED_BUILTIN, OUTPUT);
pinMode(start_pin, INPUT);
pinMode(A10, INPUT); //Diff Channel 0 Positive
pinMode(A11, INPUT); //Diff Channel 0 Negative
pinMode(A12, INPUT); //Diff Channel 1 Positive
pinMode(A13, INPUT); //Diff Channel 1 Negative
//Timer1 triggers every 800µs function measure()
Timer1.initialize(800);
Timer1.attachInterrupt(measure);
Serial.begin(57600);
///// ADC0 ////
// reference can be ADC_REF_3V3, ADC_REF_1V2 (not for Teensy LC) or ADC_REF_EXT.
//adc->setReference(ADC_REF_1V2, ADC_0); // change all 3.3 to 1.2 if you change the reference to 1V2
adc->setAveraging(1); // set number of averages
adc->setResolution(14); // set bits of resolution
//Set the speed (ADC_VERY_LOW_SPEED, ADC_LOW_SPEED, ADC_MED_SPEED, ADC_HIGH_SPEED_16BITS, ADC_HIGH_SPEED or ADC_VERY_HIGH_SPEED)
adc->setConversionSpeed(ADC_MED_SPEED); // change the conversion speed
adc->setSamplingSpeed(ADC_MED_SPEED); // change the sampling speed
////// ADC1 /////
adc->setAveraging(1, ADC_1); // set number of averages
adc->setResolution(14, ADC_1); // set bits of resolution
adc->setConversionSpeed(ADC_HIGH_SPEED, ADC_1); // change the conversion speed
adc->setSamplingSpeed(ADC_HIGH_SPEED, ADC_1); // change the sampling speed
adc->startSynchronizedContinuousDifferential(A10, A11, A12, A13);
delay(100);
}
//Voltage factor: Berechnungsvorschrift
float voltage_factor = (440075.0/75.0)*(3.3/(65535.0*8.0)); // ((Spannungsteiler "R14,15,16")/R16)*3.3V "Teensy" / 2^16*Gain "von AMC1100"
//Current factor:
float current_factor = 1/8.0*10.0*0.001; //1/Gain "AMC1100"*10 "Jumper Verst."*SpTeiler "R1,R38,R39"
//float current_factor = 1;
void loop()
{
//Werte auf 0 gesetzt
sum_inst_power = 0.0;
sum_square_voltage = 0.0;
sum_square_current = 0.0;
for (t=0;t<measurements;t++) //Spannungswerte in array eingelesen
{
//Offset dazu addiert
iMeas[t]=(iMeas[t]+iOffset);
uMeas[t]=(uMeas[t]+uOffset);
//Werte um Faktor multipliziert
inst_current = iMeas[t]*current_factor;
inst_voltage = uMeas[t]*voltage_factor;
//Leistungsberechnung
inst_power=inst_voltage*inst_current; //mom. Leistung = mom. Sp * mom. Str
sum_inst_power = sum_inst_power + inst_power; //Summe mom. Leistung
//Stromberechnung
square_current = inst_current*inst_current; //quadr. Str = (mom. Str)^2
sum_square_current += square_current; //Summe aus quadr. Str
//Spannungsberechnung
square_voltage = inst_voltage*inst_voltage; //quadr. Sp = (mom. Sp*mom. Sp)
sum_square_voltage += square_voltage; //Summe aus quadr. Sp
real_power = sum_inst_power/measurements; //tats. Leistung = Summe mom. Leistung / Anzahl Messwerte
mean_square_current = sum_square_current/measurements; //mittlere quadr. Str = Summe quadr. Str / Anzahl Messwerte
root_mean_square_current = sqrt(mean_square_current); //Strom RMS
mean_square_voltage = sum_square_voltage/measurements; //mittlere quadr. Sp = Summe quadr. Sp / Anzahl Messwerte
root_mean_square_voltage = sqrt(mean_square_voltage); //Spannung RMS
apparent_power = root_mean_square_voltage * root_mean_square_current; //Scheinleistung
power_factor = (float) real_power/apparent_power; //Leistungsfaktor = tats. Leistung / Scheinleistung
}
if (Serial.available())
{
c = Serial.read();
if(c=='c') // sobald "c" gedrückt wird
{
noInterrupts(); //Timer(800µs) Anfang
Serial.print("Pointer: ");
Serial.println(pointer);
for (int i=0; i< measurements; i++)
{
inst_current = iMeas[i]*current_factor;
inst_voltage = uMeas[i]*voltage_factor;
//Ausgabe von Werten
/*
Serial.print(iMeas[i]); Serial.println(", ");
Serial.print(uMeas[i]); Serial.println(", ");
*/
/*
Serial.print(iMeas[i]);
Serial.print(", ");
Serial.println(uMeas[i]);
*/
//Serial.print(" Strom: ");
Serial.print(inst_current, 4);
//Serial.print(" A ");
Serial.print(" , ");
//Serial.print("Spannung: ");
Serial.println(inst_voltage, 12);
//Serial.println(" V ");
/*
Serial.println(inst_power);
Serial.println(sum_inst_power);
*/
/*
Serial.print("Power: ");
Serial.println(inst_power);
*/
/*
Serial.print(" A Factor ");
Serial.println(current_factor, 12);
Serial.print("V Factor: ");
Serial.print(voltage_factor, 12);
*/
/*
Serial.print("Mean square current: ");
Serial.println(mean_square_current);
Serial.print("Mean square voltage: ");
Serial.println(mean_square_voltage);
*/
} //for-Schleife Ende
Serial.print("I RMS: ");
Serial.println(root_mean_square_current, 6);
Serial.print("U RMS: ");
Serial.println(root_mean_square_voltage, 4);
Serial.print("Real power: ");
Serial.println(real_power);
Serial.print("Apparent power: ");
Serial.println(apparent_power);
Serial.print("Power factor: ");
Serial.println(power_factor);
Serial.println(" ");
interrupts(); //Timer ende
} //if(c=='c')-Befehl Ende
else
{
Serial.println("Kein Strom");
}
} //if(Serial.available)-Befehl Ende
} //loop Ende
void measure ()
{
result=adc->analogSyncReadDifferential(A10, A11, A12, A13);
iMeas[pointer] = (int)result.result_adc0;
uMeas[pointer] = (int)result.result_adc1;
//uMeas[pointer] = adc->analogReadDifferential(A10, A11, ADC_0); // read a new value, will return ADC_ERROR_VALUE if the comparison is false.
//iMeas[pointer] = adc->analogReadDifferential(A12, A13, ADC_1);
pointer++;
if (pointer >= measurements)
{
pointer = 0;
}
}
ADC::Sync_result result = adc->analogSyncRead(A2, A3);
in = result.result_adc0;
analogWrite(A14, in);
struct Sync_result{
int32_t result_adc0, result_adc1;
};
ADC::Sync_result returns int32_t, not uint32_t:
Code:struct Sync_result{ int32_t result_adc0, result_adc1; };
Also, the actual ADC values are int16_t, so the upper half of each result_adcX is zero.
The behavior of your code will depend on how you defined the variable "in", and the signature of analogWrite (and what it does inside).
adc->setReference(ADC_REF_3V3, ADC_0);
adc->setReference(ADC_REF_3V3, ADC_1);
adc->setResolution(16, ADC_0);
adc->setResolution(16, ADC_1);
adc->setSamplingSpeed(ADC_VERY_HIGH_SPEED);
adc->setConversionSpeed(ADC_VERY_HIGH_SPEED);
#include <ADC.h>
#include <ADC_Module.h>
#include <RingBuffer.h>
#include <RingBufferDMA.h>
float Vref = 3.31; // ADC ref from Vin
float Vain = 1.36; // ADC input voltage
float ADCmax = 65535;
float ADCexpected = ADCmax *(Vain/Vref);
ADC *adc = new ADC(); //create adc object
ADC::Sync_result result; // result struct for synchronous ADC ops
const int LED1 = 13; // LED on digital pin 13
int32_t x0=0, x1=0;
void setup() {
pinMode(LED1,OUTPUT);
pinMode(A10, INPUT); //ADC_0 Diff Channel 0 Positive
pinMode(A11, INPUT); //ADC_0 Diff Channel 0 Negative
pinMode(A12, INPUT); //ADC_1 Diff Channel 1 Positive
pinMode(A13, INPUT); //ADC_1 Diff Channel 1 Negative
///// ADC0 ////
// reference can be ADC_REF_3V3, ADC_REF_1V2 (not for Teensy LC) or ADC_REF_EXT.
//adc->setReference(ADC_REF_1V2, ADC_0); // change all 3.3 to 1.2 if you change the reference to 1V2
//adc->setAveraging(1); // set number of averages
//adc->setResolution(13); // set bits of resolution
// it can be ADC_VERY_LOW_SPEED, ADC_LOW_SPEED, ADC_MED_SPEED, ADC_HIGH_SPEED_16BITS, ADC_HIGH_SPEED or ADC_VERY_HIGH_SPEED
// see the documentation for more information
//adc->setConversionSpeed(ADC_HIGH_SPEED); // change the conversion speed
// it can be ADC_VERY_LOW_SPEED, ADC_LOW_SPEED, ADC_MED_SPEED, ADC_HIGH_SPEED or ADC_VERY_HIGH_SPEED or ADC_HIGH_SPEED_16BITS
//adc->setSamplingSpeed(ADC_HIGH_SPEED); // change the sampling speed
// PGA, use only for signals lower than 1.2 V. Activate the 1.2V reference (ADC_REF_1V2)
// the gain can be 1, 2, 4, 8, 16, 32 or 64
//adc->enablePGA(1, ADC_0);
adc->setReference(ADC_REF_3V3, ADC_0);
adc->setAveraging(16, ADC_0); //0,4,8,16 or 32
adc->setResolution(16, ADC_0); //8,12 or 16
adc->setSamplingSpeed(ADC_HIGH_SPEED_16BITS, ADC_0);
adc->setConversionSpeed(ADC_HIGH_SPEED_16BITS, ADC_0);
// always call the compare functions after changing the resolution!
// Compare values at 16 bits differential resolution are twice what you write!
//adc->enableCompare(1.0/3.3*adc->getMaxValue(ADC_0), 0, ADC_0); // measurement will be ready if value < 1.0V
//adc->enableCompareRange(-1.0*adc->getMaxValue(ADC_0)/3.3, 2.0*adc->getMaxValue(ADC_0)/3.3, 0, 1, ADC_0); // ready if value lies out of [-1.0,2.0] V
////// ADC1 /////
adc->setReference(ADC_REF_3V3, ADC_1);
//adc->enablePGA(1, ADC_1);
adc->setAveraging(16, ADC_1); // set number of averages
adc->setResolution(16, ADC_1); // set bits of resolution
adc->setSamplingSpeed(ADC_HIGH_SPEED_16BITS, ADC_1); // change the sampling speed
adc->setConversionSpeed(ADC_HIGH_SPEED_16BITS, ADC_1); // change the conversion speed
// always call the compare functions after changing the resolution!
//adc->enableCompare(1.0/3.3*adc->getMaxValue(ADC_1), 0, ADC_1); // measurement will be ready if value < 1.0V
//adc->enableCompareRange(-1.0*adc->getMaxValue(ADC_1)/3.3, 2.0*adc->getMaxValue(ADC_1)/3.3, 0, 1, ADC_1); // ready if value lies out of [-1.0,2.0] V
// You can also try:
//adc->startSynchronizedContinuous(readPin, readPin2);
// adc->startSynchronizedContinuousDifferential(A10, A11, A12, A13);
// Read the values in the loop() with readSynchronizedContinuous()
Serial.begin(115200);
delay(10000);
Serial.print("Expected ADC count is somewhere around "); Serial.println(ADCexpected,0);
}
void loop() {
Serial.println("\nCalling analogReadDifferential(...)");
x0 = adc->analogReadDifferential(A10, A11, ADC_0); // read a new value, will return ADC_ERROR_VALUE if the comparison is false.
x1 = adc->analogReadDifferential(A12, A13, ADC_1); // read a new value, will return ADC_ERROR_VALUE if the comparison is false.
Serial.print("ADC_0 count: "); Serial.print(x0); Serial.print(" Diff: "); Serial.println(ADCexpected - x0);
Serial.print("ADC_1 count: "); Serial.print(x1); Serial.print(" Diff: "); Serial.println(ADCexpected - x1);
delay(2000);
Serial.println("\nCalling analogSynchronizedReadDifferential(...)");
result = adc->analogSynchronizedReadDifferential(A10, A11, A12, A13);
// if using 16 bits and single-ended is necessary to typecast to unsigned,
// otherwise values larger than 3.3/2 will be interpreted as negative
//result.result_adc0 = (uint16_t)result.result_adc0;
//result.result_adc1 = (uint16_t)result.result_adc1;
x0 = result.result_adc0;
x1 = result.result_adc1;
Serial.print("ADC_0 count: "); Serial.print(x0); Serial.print(" Diff: "); Serial.println(ADCexpected - x0);
Serial.print("ADC_1 count: "); Serial.print(x1); Serial.print(" Diff: "); Serial.println(ADCexpected - x1);
delay(2000);
}
Expected ADC count is somewhere around 26927
Calling analogReadDifferential(...)
ADC_0 count: 26954 Diff: -27.23
ADC_1 count: -26974 Diff: 53900.77
Calling analogSynchronizedReadDifferential(...)
ADC_0 count: 13501 Diff: 13425.77
ADC_1 count: -13505 Diff: 40431.77
Calling analogReadDifferential(...)
ADC_0 count: 26954 Diff: -27.23
ADC_1 count: -26984 Diff: 53910.77
Calling analogSynchronizedReadDifferential(...)
ADC_0 count: 13502 Diff: 13424.77
ADC_1 count: -13502 Diff: 40428.77
Calling analogReadDifferential(...)
ADC_0 count: 26948 Diff: -21.23
ADC_1 count: -26976 Diff: 53902.77
Calling analogSynchronizedReadDifferential(...)
ADC_0 count: 13500 Diff: 13426.77
ADC_1 count: -13503 Diff: 40429.77
Calling analogReadDifferential(...)
ADC_0 count: 26954 Diff: -27.23
ADC_1 count: -26984 Diff: 53910.77
Calling analogSynchronizedReadDifferential(...)
ADC_0 count: 13501 Diff: 13425.77
ADC_1 count: -13507 Diff: 40433.77