Teensy 3.1 Voltage sensing and low battery alert

Status
Not open for further replies.
@Manitou, Thanks for jumping in. Did I misinterpret the datasheet? Or is it just that VREFH is not used by default on the Teensy LC? Is there any particular reason this is preferred to the reference voltage as we used on Teensy 3.x?

The bandgap channel you propose should work of course, as long as there's a low - regulated - voltage that can be compared against Vin this method to calculate the input voltage should work.

No VREFH is not an internal reference voltage.

If you look in chapter 3.7 of ref manual for teensy 3, 3.1 and LC you will find a table of ADC channel assignments. For 3 and 3.1 there is a channel for "VREF Output" (internal 1.195v), and that is what channel 39 is mapped to. For the LC, alas, there is no "VREF output", but there is an internal Bandgap channel for LC (and 3 and 3.1). So for the LC, analog.c maps "39" to the Bandgap channel (internal 1.0v). I assume one can derive the fitting equation ...
 
Last edited:
No VREFH is not an internal reference voltage.
If you look in chapter 3.7 of ref manual for teensy 3, 3.1 and LC you will find a table of ADC channel assignments. For 3 and 3.1 there is a channel for "VREF Output" (internal 1.195v), and that is what channel 39 is mapped to. For the LC, alas, there is no "VREF output", but there is an internal Bandgap channel for LC (and 3 and 3.1). So for the LC, analog.c maps "39" to the Bandgap channel (internal 1.0v)
Ah I see, thanks for clearing that up.

I assume one can derive the fitting equation ...
Definitely! We just need some measurements, in post #15 I detailed the measurement methodology used last time. If anyone else feels like taking some measurements I'd gladly contribute to fitting the polynomial and creating a nice function. If I have to perform the measurements it might be a few more months ;)
 
First of all, good job on all this!

I know this seems like a totally dumb question, but I was wondering how exactly to use this function. I am very new to arduino and am not sure exactly how the return function works.

Code:
  void setup() {
  Serial1.begin(115200);
  analogReference(EXTERNAL);
  analogReadResolution(12);
  analogReadAveraging(32); // this one is optional.
  uint32_t bat;
  }
  
  void loop() {
  bat=getInputVoltage();
  Serial.print(bat);
  }
uint32_t getInputVoltage(){ // for Teensy 3.1, only valid between 2.0V and 3.5V. Returns in millivolts.
    uint32_t x = analogRead(39);
    return (178*x*x + 2688743864 - 1182047 * x) / 371794;
}

That is my whole sketch. I'm sure I'm just being very ignorant but cannot figure out how to use it. Any help would be greatly appreciated.
 
The structure of your sketch is fine. But it will have a compilation error, you define 'uint32_t bat' in the setup() function, so this variable won't be available in the scope of the loop() function. Also, you initialize Serial1, whereas you print on the USB Serial port (Serial), you should initialize and print on the same one.

If you move the 'bat' variable declaration and change the Serial port print (also made it a println, such that you get a number per line), you end up with the following code, I haven't tested it, but it should work. If you want the output to go over the USB serial port, change the Serial1 into Serial without a number in both the setup() and loop() function.
Code:
void setup() {
  Serial1.begin(115200);
  analogReference(EXTERNAL);
  analogReadResolution(12);
  analogReadAveraging(32); // this one is optional.
}

void loop() {
  uint32_t bat;
  bat=getInputVoltage();
  Serial1.println(bat);
}

uint32_t getInputVoltage(){ // for Teensy 3.1, only valid between 2.0V and 3.5V. Returns in millivolts.
    uint32_t x = analogRead(39);
    return (178*x*x + 2688743864 - 1182047 * x) / 371794;
}
 
Thanks! I got it working. Feel kind of silly for missing that one. I had it on Serial1 because I am printing it to an SD card. Ive ran it for about 12 hours or so and I still am getting values around 3534. Maybe my understanding is skewed. Is this supposed to measure the battery level of a lipo plugged into Vin?
 
Thanks! I got it working. Feel kind of silly for missing that one. I had it on Serial1 because I am printing it to an SD card. Ive ran it for about 12 hours or so and I still am getting values around 3534. Maybe my understanding is skewed. Is this supposed to measure the battery level of a lipo plugged into Vin?
Glad to hear the code is working. It indeed attempts to measure the input voltage on Vin. It can only measure once the level drops below 3.5v-ish volt, see the figures in post #15, before that the input voltage of the Teensy is regulated to 3.3v and the readings are constant at approximately the 3534 you are seeing.
Depending on the LiPo cell you are using, it can take quite some time before the voltage drops below 3.5v; the Teensy draws something like 20 mA, so divide the battery's capacity by that to get an estimate of the duration. My tip is to check whether everything is working using a variable voltage power supply, for example put only 3.0v or 2.5v on Vin and check what voltage the serial port reports.
 
Glad to hear the code is working. It indeed attempts to measure the input voltage on Vin. It can only measure once the level drops below 3.5v-ish volt, see the figures in post #15, before that the input voltage of the Teensy is regulated to 3.3v and the readings are constant at approximately the 3534 you are seeing.
Depending on the LiPo cell you are using, it can take quite some time before the voltage drops below 3.5v; the Teensy draws something like 20 mA, so divide the battery's capacity by that to get an estimate of the duration. My tip is to check whether everything is working using a variable voltage power supply, for example put only 3.0v or 2.5v on Vin and check what voltage the serial port reports.

Ok so I tried the power supply and had some interesting results. The SD card reader I am using doesn't reliably run below 3.5V. I went up to 4.5V with it and it was working properly but I didn't go any higher cause I didn't want to damage the Teensy or the reader. It stays aroudn 3536 until I drop below ~3.7V. It will continue to drop until I get to 3.5V before it shuts off. Around 3.5V it is 3474. Below that it will not record any results. Is this expected? I just looked up the datasheet for the card reader and it says it operates at 3.3V-12V. Does that mean, in theory, if my Lipo falls under 3.3 (actually 3.5) the card reader will stop recording but the Teensy will continue to run? From my understanding of this thread the Teensy runs from 2.0-3.7.
 
It stays aroudn 3536 until I drop below ~3.7V. It will continue to drop until I get to 3.5V before it shuts off. Around 3.5V it is 3474. Below that it will not record any results. Is this expected?
Yes, this behaviour is exactly as expected. In the range from approximately 5.5v to 3.5v the readings are practically identical, with the polynomial I calculated this becomes the 3536 you see. I made an image in to hope to clear things up:
to_Vin.png

The getInputVoltage function works by using the ADC to calculate the value of the internal 1.2v reference (analogRead(39)), this value is calculated in respect to the external reference, which is the voltage on which the Teensy runs (Vdd).
In the orange area, so the part from Vin:3.5v-5.5v, all readings are approximately on a vertical line. The input voltage is high enough so that the regulator which creates the 3.3v Vdd voltage level works well. The ratio between analogRead(39) and Vdd remains constant, at about 1.2v/3.3v = 0.3636, with a 12 bits ADC this results in the value: 4096 * 0.3636 = 1489 for readings of analogRead(39), this matches the position on the horizontal axis. This means that despite the change in the voltage, the change in our measured value is very little.
Once we get below ~3.5v, the Vdd voltage level starts dropping as the regulator is not able to maintain 3.3v anymore, this means that the ratio between analogRead(39) and Vdd starts changing. This is the green area in the graph. In this part, we can calculate a function which relates the value on the horizontal axis (analogRead(39)) with the value of the vertical axis (Vdd).

I just looked up the datasheet for the card reader and it says it operates at 3.3V-12V. Does that mean, in theory, if my Lipo falls under 3.3 (actually 3.5) the card reader will stop recording but the Teensy will continue to run? From my understanding of this thread the Teensy runs from 2.0-3.7.
I am not familiar with that OpenLog card reader, but it seems to include an Atmega328, and 3.3v regulator and an SD card holder. That means the Atmega328 is running on 3.3v, when the input voltage is lowered this Atmega will stop working quite quickly. (Even 3.3v and a clock of 16MHz is outside specifications?).
So yes the Teensy is probably still running (I remember the datasheet saying something like 1.8v as minimal Vdd voltage, don't pin me down on it though). You could try to connect the SD card directly to the Teensy and see at what voltage the Teensy + SD card combination stops working.
 
@iwanders, your polynomial fitting equations are good, but we know the relationship is
Vcc = VREF*4096/analogRead(39).
Using a multimeter, I measure Vcc and then using the relationship above, I "calibrate" what VREF is for my chip. For your 3.3v in data, I calculate VREF (1474*3.33/4096 = 1.198344 v). So I plot Vcc=1.198344*4096/analogRead(39) for your data and get a nice fit
vlc.png
X axis is ADC value, and Y axis is Vcc (volts)

Arguably, a least-squares fit might be safer if VREF changed as Vcc varies, but the anecdotal data that I have observed suggests the simpler equation is effective.
 
Last edited:
Manitou, I like the idea and tried it: I used the same method you proposed to calculate the VREF first at 3v3 and then use the equation 'Vcc = VREF*4096/analogRead(39)' to provide the fit. I obtained the following three images:

to_3v3.pngto_Vin.pngto_Vin_T3.1.png

So the result is indeed very good for the input on the 3v3 pin, but with voltage applied to Vin the results do not align very well at the lower voltages, probably due to the change in VREF you stated.
 
If you look in chapter 3.7 of ref manual for teensy 3, 3.1 and LC you will find a table of ADC channel assignments. For 3 and 3.1 there is a channel for "VREF Output" (internal 1.195v), and that is what channel 39 is mapped to. For the LC, alas, there is no "VREF output", but there is an internal Bandgap channel for LC (and 3 and 3.1). So for the LC, analog.c maps "39" to the Bandgap channel (internal 1.0v). I assume one can derive the fitting equation ...

Re: LC bandgap voltage

I varied the voltage to the 3.3v pin of teensy LC and measured the voltage (DMM) and reported the ADC value from channel "39".

Code:
1170 3.481
1188 3.423
1235 3.301
1287 3.173
1330 3.007
1425 2.863
1518 2.684
1673 2.444
1736 2.35
1873 2.186

Below is data plotted with curve Vcc = 1.0*4096/analogRead(39)
vLC.png
 
Yes, this behaviour is exactly as expected. In the range from approximately 5.5v to 3.5v the readings are practically identical, with the polynomial I calculated this becomes the 3536 you see. I made an image in to hope to clear things up:
View attachment 4001

The getInputVoltage function works by using the ADC to calculate the value of the internal 1.2v reference (analogRead(39)), this value is calculated in respect to the external reference, which is the voltage on which the Teensy runs (Vdd).
In the orange area, so the part from Vin:3.5v-5.5v, all readings are approximately on a vertical line. The input voltage is high enough so that the regulator which creates the 3.3v Vdd voltage level works well. The ratio between analogRead(39) and Vdd remains constant, at about 1.2v/3.3v = 0.3636, with a 12 bits ADC this results in the value: 4096 * 0.3636 = 1489 for readings of analogRead(39), this matches the position on the horizontal axis. This means that despite the change in the voltage, the change in our measured value is very little.
Once we get below ~3.5v, the Vdd voltage level starts dropping as the regulator is not able to maintain 3.3v anymore, this means that the ratio between analogRead(39) and Vdd starts changing. This is the green area in the graph. In this part, we can calculate a function which relates the value on the horizontal axis (analogRead(39)) with the value of the vertical axis (Vdd).


I am not familiar with that OpenLog card reader, but it seems to include an Atmega328, and 3.3v regulator and an SD card holder. That means the Atmega328 is running on 3.3v, when the input voltage is lowered this Atmega will stop working quite quickly. (Even 3.3v and a clock of 16MHz is outside specifications?).
So yes the Teensy is probably still running (I remember the datasheet saying something like 1.8v as minimal Vdd voltage, don't pin me down on it though). You could try to connect the SD card directly to the Teensy and see at what voltage the Teensy + SD card combination stops working.


Thank you for clearing everything up for me iwanders. One question I have. I know that these few lines of code:

analogReference(EXTERNAL);
analogReadResolution(12);
analogReadAveraging(32);

change the analog reading. I'm wondering how exactly it effects it. Previously the readings were originally from 0-1023. Now I'm not sure what max and min values are but they are ~2000. If I can figure out the min/max I will just map it back to 0-1023.
 
Thank you for clearing everything up for me iwanders. One question I have. I know that these few lines of code:

analogReference(EXTERNAL);
analogReadResolution(12);
analogReadAveraging(32);

change the analog reading. I'm wondering how exactly it effects it. Previously the readings were originally from 0-1023. Now I'm not sure what max and min values are but they are ~2000. If I can figure out the min/max I will just map it back to 0-1023.

analogReference(EXTERNAL); //The Teensy will use an external source for the ADC reference voltage (i.e. from the AREF pin)
analogReadResolution(12); //Enables a 12-bit scale for the ADC, i.e. values from 0-4095
analogReadAveraging(32); // means that the ADC will read the pin 32 times and average the results. This helps reduce spurious noise.

So if you want 10 bit resolution on your signal (i.e. 0-1023 values from the ADC), use analogReadResolution(10);

HTH:p

(Just noticed that I carry the mark of the beast today, this being my 666th post)
 
Last edited:
analogReference(EXTERNAL); //The Teensy will use an external source for the ADC reference voltage (i.e. from the AREF pin)
analogReadResolution(12); //Enables a 12-bit scale for the ADC, i.e. values from 0-4095
analogReadAveraging(32); // means that the ADC will read the pin 32 times and average the results. This helps reduce spurious noise.

So if you want 10 bit resolution on your signal (i.e. 0-1023 values from the ADC), use analogReadResolution(10);

HTH:p

(Just noticed that I carry the mark of the best today, this being my 666th post)

It helps alot! Thanks and sorry for the noobie question.
 
I recently purchased a Teensy LC and performed the analysis I have also done for the previous versions. Once again, credit for Manitou for figuring out how to do this on the Teensy LC (Your suggestion from #36 looks particularly good on these :) ). Details on the 'how' and the measurement data can be found in the updated gist, or just read the previous posts of this thread.

Images of the data and fits:
to_3v3_LC.pngto_Vin_LC.png

The copy-paste-able integer polynomials.
Code:
/*
    Be sure to put the following in your setup:
        analogReference(INTERNAL); // use internal 3.3v reference.
        analogReadResolution(12);
        analogReadAveraging(32);
        PMC_REGSC |= PMC_REGSC_BGBE; // 39=bandgap ref (PMC_REGSC |= PMC_REGSC_BGBE)

*/
uint32_t getInputVoltage(){ // for Teensy LC, Vin, only valid between 1.73V and 3.3V. Returns in millivolts.
    uint32_t x = analogRead(39);
    return (255*x*x + 2435256940 - 1377272 * x) / 338460;
}
uint32_t getInputVoltage(){ // for Teensy LC, 3v3 input, only valid between 1.65V and 3.5V. Returns in millivolts.
    uint32_t x = analogRead(39);
    return (266*x*x + 2496026531 - 1431005 * x) / 342991;
}
 
Just so I understand this entire discussion as it pertains to the LC, when we measure the 1.0 V internal reference (39) using the INTERNAL Analog Reference (which is Vcc), we are really measuring the the value of Vcc, since the 1.0V (39) does not change but Vcc may change.

I assume that we could hook a voltage up to the external Analog Reference, use EXTERNAL in these sketches to arrive at an accurate value of the external AD reference voltage. We could then use this sensed value of the external AD reference and it would make our AD conversions more accurate.

Is that correct?

Matt
 
Just so I understand this entire discussion as it pertains to the LC, when we measure the 1.0 V internal reference (39) using the INTERNAL Analog Reference (which is Vcc), we are really measuring the the value of Vcc, since the 1.0V (39) does not change but Vcc may change.
This is indeed correct, this is exactly how we determine the input voltage. Although I believe it's not available as analog reference, so calling it a reference would be wrong. It's called the bandgap channel in the datasheet, but what it's used for is beyond me.

I assume that we could hook a voltage up to the external Analog Reference, use EXTERNAL in these sketches to arrive at an accurate value of the external AD reference voltage. We could then use this sensed value of the external AD reference and it would make our AD conversions more accurate.
I'll leave the answer to this question to the real analog guru's, but I would guess their answer is something in the lines of; an external reference should provide a static reference voltage that is known beforehand. In theory you could calculate the value of the external reference using the difference between it and the 1.0v bandgap, but the error you make in this estimation is likely far larger than the deviation a dedicated analog reference IC should have.
 
This is indeed correct, this is exactly how we determine the input voltage. Although I believe it's not available as analog reference, so calling it a reference would be wrong. It's called the bandgap channel in the datasheet, but what it's used for is beyond me.


I'll leave the answer to this question to the real analog guru's, but I would guess their answer is something in the lines of; an external reference should provide a static reference voltage that is known beforehand. In theory you could calculate the value of the external reference using the difference between it and the 1.0v bandgap, but the error you make in this estimation is likely far larger than the deviation a dedicated analog reference IC should have.

Thanks! That makes good sense. Any recommendations on good dedicated analog reference ICs?

matt
 
I like the LM4040, which works out of the box with the 3.x series. For the LC, you would have to supply a current-limiting resistor. Presumably, you could use the same 470 Ohm resistance value as with the 3.x series. (see the schematic). For extra credit, you could even consider putting a ferrite between 3.3V and the resistor to help kill any noise in the ADC supply.

Note: Unlike the Teensy 3.x series, the LC does not feature a AGND pin. What is a AGND pin on the 3.x series is merely GND with the Teensy. You likely still would gain some benefit from bringing in all sensors to one GND pin, however (it could act as a quasi-star ground). So I would treat that pin like AGND even though it isn't.
 
Last edited:
Cool, thanks for sharing that Constantin, I did not know that type of device. I have used the REF19x series from Analog Devices in the past, but the part you propose seems to be a lot easier to work with.
 
Hi all, I realise this is an old thread but it is spot on target for my current project.

Reading from above and quoting from #iwanders code:

// for Teensy LC, Vin, only valid between 1.73V and 3.3V. Returns in millivolts.
// for Teensy LC, 3v3 input, only valid between 1.65V and 3.5V. Returns in millivolts.

What is the maximum voltage we can safely apply to the 3.3V input? It would be nice to get the greater range of 1.65V to 3.5V but can the 3.3V input take the fully charged voltage of 4.2V for a 3.7V single cell Lipo?

I hope someone can help.
 
no, the i/o pins are not 5 V tolerant. i guess, max is 3.3..3.5 V

perhaps use a voltage-divider ? (two resistors)
 
Hi Frank, thanks for the reply. This is not about the I/O pins more about what voltage can be safely applied to the 3.3V pin. I guess your answer is still correct though, looking at the graph in post 40. I think I will just go with Vin for safety's sake at this point and see if I can get the functions I need to work that way first before looking in to this further as I now think I can make it work that way.
 
VIN is 5 V (or more!)

It is working fine powered by the Lipo on Vin, not sure how far down this will work but all indications, from reading this thread, are that it should work right down to the 3V cut off level of the Lipo without the USB function.
 
Status
Not open for further replies.
Back
Top