Forum Rule: Always post complete source code & details to reproduce any issue!

# Thread: ADC library, with support for Teensy 4, 3.x, and LC

1. 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?  Reply With Quote

2. Originally Posted by onehorse 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?
Since its a lipo I would try a 10K/10K divider to keep usage down. If you can add the cap in later you can probably get away with the 100K/100K divider (possibly).  Reply With Quote

3. I built up another couple of Teensy flight controllers and used 4K7/4K7 for the voltage divider and now I am getting very stable readings. I have two LiPo batteries, one I measure at 3.78 and the other at 4.18 V with a voltmeter. The Teensy sees 3.84 and 4.24 V, respectively, and this is independent of the slow or fast sample rate and 4x or 8x averaging, etc, just what I expect. The reading is very stable, doesn't change by more that a hundredth of a volt from reading to reading.

With the battery removed I am reading 30 mV, not sure why the offset but it accounts for half of the overage. I measure 3.31 V on the 3V3 spigot so the 3.3 V conversion factor is about right. I could subtract the zero bias and get to 1% error and, of course, I can scale by 0.98 or 0.99 and get to essentially zero percent error if I care to. But 1-2% is great for a simple battery voltage monitor in a robotic application.  Reply With Quote

4. Are you're using digital ground or analog ground? That might explain the 30 mV offset.  Reply With Quote

5. Yes, good point. I went to the trouble to include the analog section in my design but forgot to actually use it when needed! I'll fix this on the next board rev, although i can live with a 30 mV offset.  Reply With Quote

6. If you can add the cap in later you can probably get away with the 100K/100K divider (possibly).
If you use a 100 nF ceramic capacitor in parallel with the bottom 100 kOhm resistor, and a top 100 kOhm resistor, and only read a few times a second, I would expect it would work fine based on other experience.
The time constant on that circuit is only 10 milliseconds, and 100 nF should be sufficient to fill an ADC input stage without noticeable voltage drop. (If you really care about >10 bits, use a 1 uF capacitor.)

I'd also recommend 1% or better resistors to minimize errors. (And the more precise, the better, of course.) Also: what makes you think that your volt meter is the correct value here? :-)

4K7 vs 100K is a difference of 0.45 mA leakage versus 21 uA leakage.  Reply With Quote

7. 4K7 vs 100K is a difference of 0.45 mA leakage versus 21 uA leakage.
which is, of course, why I chose a 100 K resistor all in the first place. I'll try the 100 - 1000 nF cap solution on the next iteration. For quadcopters the difference in current drain is not significant. For other applications, we are struggling to eliminate all sources of extraneous power draw.

My first priority was just getting a sensible result. Now I will try to get a sensible result at a sensible power loss...  Reply With Quote

8. Originally Posted by Pedvide 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?  Reply With Quote

9. Originally Posted by someteen 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?
What I do (learned from work) is when my motor is not enabled(normally at startup) I sample the LEM and average the values over time to set zero. I use the +/- 400A version that sits around 2.5V at 0A but it will vary based on temperature and voltage supplied. This way zero is self calibrated every time.  Reply With Quote

10. I'm monitoring a battery bank current (feeding a power inverter) thus there's no "stand-by" operation available to make the calibration. But I'm also monitoring some PV panels and that's how I noticed the offset (during night time, their output current is zero).

I don't think it's a sensor problem, as I only restart Teensy (after a hex uploading, usually) and the ADC readings already have an offset upon start-up. The readings are very accurate (and stable) but I just don't like to see I have some PV output from the moon light. Btw, I'm using LEM HTFS 200-P (+/-300A) but I'm using a loop to measure twice the operating current.  Reply With Quote

11. 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.  Reply With Quote

12. Originally Posted by Pedvide You should use analog ground AGND.
Thanks, I'm using analog ground, indeed. Originally Posted by Pedvide 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.
Actually, if I upload a new hex every two minutes (during tests) I got different offset at every restart. Sometimes, I have to rewrite the same hex to get a more "convenient" offset (zero). Is there a way to tweak the calibration procedure (at start-up)?  Reply With Quote

13. Agreed with Pedvide on AGND being yours best bet.

Could outside noise be disturbing the calibration? Are you sourcing any current from the Teensy to outside components or on top of a switching power supply?

edit..
Are you directly connected to the LEM or passing it threw another circuit first?

What I use in conjunction with a LEM HAL-400s.
Current measurement and Hardware Over-current, so far I get readings on my breadboard setup within 0.5%   Reply With Quote

14. The measurements are OK. So I have a steady output (constant current); nothing else changed but Teensy restarts (due to hex uploading). Right after restart, the readings (still steady) have a small offset. I might be "lucky" and get the same output. Anyway, after further restarts (uploading the same hex) I got variable offset.

Actually, the offset is quite small (1-2 LSB) but it's always there and constant.

By example, the ADC reads "0 Watt" for half an hour. After uploading a hex (thus restarting the Teensy) it keeps reading "3 W" or "-4 W" or even "0 W" (the value seems randomly chosen).

The displayed power ("0 W", "-4 W") is computed by multiplying the sensor current times battery voltage.

I'm not using any conditioning circuit as the sensors have 0-5V output range (I'm only using a resistive voltage divider to get 0-3.3V) but once again, the measurements are quite accurate.  Reply With Quote

15. Hello Members, I do have a problem. I'm using the Teensy 3.2 and want to use the analog differential inputs. I used the library which Pedvide wrote. In the example of "synchronizedMeasurements" I only changed two things.
First one :
Second one:

When I start the program the measurements are going crazy. They are switching between 0 and 65535. It starts when I reach about 25V.

Example of the measurements:

103231701 0 8
103231734 65535 8
103231774 0 8
103231810 65535 9
103231846 0 9
103231882 65535 9
103231920 65535 10

Can someone help me?  Reply With Quote

16. 25 V?? I hope that's a typo!!
In any case, post the complete source code.  Reply With Quote

17. Aww, I forgot to tell you, that my Teensy is connected to a PowerMeter Board. The Teensy is only getting digital signals and switch them to analog.
I allready got some help and its working as wished.

Thanks anyway  Reply With Quote

18. Hello Members, again I have a problem. I want to use the Pin 9 as a starting point for the measurements. Because my Pin 9 is detecting, when the zero point is crossed. I tried to expand the "if(c=='c') " so, the measurements will start, when "c" is pushed and the zero line is crossed. My input is AC , so there are negative and positive outputs.
It would be a great help.

Code:
```#include <ADC.h>
#include <TimerOne.h>

const int measurements = 125;

const int iOffset = +0;
const int uOffset = +3;

volatile int uMeas={0};
volatile int iMeas={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);

adc->setAveraging(1); // set number of averages
adc->setResolution(14); // set bits of resolution

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
{
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())
{
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 ()
{

pointer++;

if (pointer >= measurements)
{
pointer = 0;
}
}```  Reply With Quote

19. So I'm being silly here...

If I do this...

Code:
```ADC::Sync_result result = adc->analogSyncRead(A2, A3);
analogWrite(A14, in);```

The values are right — but they seem to wrap around. Is it because it returns a uint32? I'm not getting it.  Reply With Quote

20. ADC::Sync_result returns int32_t, not uint32_t:
Code:
```struct Sync_result{
};```
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).  Reply With Quote

21. Originally Posted by Pedvide Code:
```struct Sync_result{
};```
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).
Thanks!

I've set it up like this:

Code:
```  adc->setReference(ADC_REF_3V3, ADC_0);

And I had set up my "in" variable as an int32_t.

What I ended up with were values going up to about half way then going to zero.

(for some reason the serial on my Teensy is playing up so I could only observe on the scope *I output the 16 bit value back out the DAC  which was set to 16 bits etc)  Reply With Quote

22. New user here. Is the ADC module verified for the Teensy 3.2?

Update: Looks like everything works. The module is nicely organized and easy to use. Thanks!  Reply With Quote

23. Yes, indeed it does. I have updated the first post to make this clear. Sadly I can't edit the post title.  Reply With Quote

24. ## solo and synchronized reads

Hello -

Newbie here. I have a project where I'd like to simultaneously sample two separate voltage sources. In testing, I ran across two things that I have not been able to understand.

Teensy 3.2, Arduino 1.6.9 and Teenyduino 1.29-beta2. I've reduced circuit and code to barebones for testing.

Teensy is USB powered. A single AA battery positive is connected to pins A10,A12 and negative to pins A11,A13.

Code:
```#include <ADC.h>
#include <RingBuffer.h>
#include <RingBufferDMA.h>

float Vref = 3.31;       // ADC ref from Vin
float Vain = 1.36;       // ADC input voltage
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

//adc->setAveraging(1); // set number of averages
//adc->setResolution(13); // set bits of resolution

// 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

// always call the compare functions after changing the resolution!
// Compare values at 16 bits differential resolution are twice what you write!

// always call the compare functions after changing the resolution!

// You can also try:

Serial.begin(115200);
delay(10000);
}

void loop() {

delay(2000);

// 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
delay(2000);

}```
Here's the output
Code:
```Expected ADC count is somewhere around 26927

1. Why is the output of ADC_1 negative?
2. analongReadDifferential() outputs realistic values, while analogSynchronizedReadDifferential() does not. Though in this particular case, seem suspiciously ~half that of values from the former.

Can someone give me half a clue or point me in a direction to investigate further? I've read through the html documentation from pevide's github without gaining any further insight.  Reply With Quote

25. I'll have a look at this today or tomorrow.  Reply With Quote

#### Posting Permissions

• You may not post new threads
• You may not post replies
• You may not post attachments
• You may not edit your posts
•