Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 25 of 25

Thread: How measure battery voltage?

  1. #1

    How measure battery voltage?

    Hi,

    I'd like to show battery charge status on screen of a device.

    I have Teensy 3.5.

    I found this blog for Arduino
    http://jeelabs.org/2012/05/04/measur...gap/index.html
    http://jeelabs.org/2012/05/12/improved-vcc-measurement/

    I like the idea to use internal reference.

    I am trying to repeat it for Teensy, but it does not compile.
    symbols ADMUX,ADCSRA, ADSC, ADC are not defined.

    Example from here https://www.pjrc.com/teensy/adc.html
    also does not work.

    which file needs to be included for ADMUX,ADCSRA, ADSC, ADC symbos defined?

    including these
    #include <avr/io.h>
    #include <avr/pgmspace.h>
    does not help.

    I've connected LiIo battery 4.2V to pin A0. analogRead(0) shows 1023.
    I try to change AD reference to INTERNAL. But it always shows 1023
    Code:
    void setup()
    {                
      Serial.begin(38400);
      //analogReference(DEFAULT);
      analogReference(INTERNAL);
      //analogReference(EXTERNAL);
    }
    
    int val;
    void loop()                     
    {
      val = analogRead(0);
      Serial.print("analog 0 is: ");
      Serial.println(val);
      delay(250);
    }
    what is wrong?
    Any ideas?
    Thanks!

  2. #2
    Senior Member Theremingenieur's Avatar
    Join Date
    Feb 2014
    Location
    Colmar, France
    Posts
    834
    Oh man, the analog pins of the Teensy 3.x processors are all only 3.3V tolerant (read the fu...ing reference manual). That means that all voltages of 3.3V or above (the latter risk to kill the Teensy) will read 1023 with the 10 bit default resolution. Thus, you'll have to connect your LiIo battery through a voltage divider and then do a little arithmetic in your code to do the correct scaling.

    And you'll have to understand that the Teensy 3.x processors are NOT 8bit AVR processors, but 32bit ARM processors, thus, most Arduino code which directly addresses AVR hardware registers can not work on a Teensy 3.x since these have different hardware registers. Reading manuals broadens the mind...
    Last edited by Theremingenieur; 04-17-2017 at 10:44 PM.

  3. #3
    Quote Originally Posted by Theremingenieur View Post
    Oh man, the analog pins of the Teensy 3.x processors are all only 3.3V tolerant (read the fu...ing reference manual).
    Of course I read.
    https://www.pjrc.com/store/teensy35.html - All digital pins are 5 volt tolerant.

    Quote Originally Posted by Theremingenieur View Post
    That means that all voltages of 3.3V or above (the latter risk to kill the Teensy) will read 1023 with the 10 bit default resolution. Thus, you'll have to connect your LiIo battery through a voltage divider and then do a little arithmetic in your code to do the correct scaling.
    That is what I try to avoid. I would not want that the divider works all time. It will draw power.
    So I will need to add MOSFET that turns on this circuit only for couple of microseconds for measuring.

    If it is possible to get V from analog pin that is connected with battery, this would simplify the thing.

    Quote Originally Posted by Theremingenieur View Post
    And you'll have to understand that the Teensy 3.x processors are NOT 8bit AVR processors, but 32bit ARM processors, thus, most Arduino code which directly addresses AVR hardware registers can not work on a Teensy 3.x since these have different hardware registers. Reading manuals broadens the mind...
    This ARM Cortex M4 has: Dual 1Msps, 16-bit ADCs of up to 24 channels with analog front end, offering offset error correction and gain control. Also includes 2-channel, 1Msps, 12-bit DAC
    Not 32-bit ADCs.
    Anyway I tried to set analogReadRes(12); and to 16. It does not really help.

    If you could point me to Teensy manuals where it is described how to reference vs internal, I'd be happy.
    Last edited by mm7; 04-17-2017 at 11:40 PM.

  4. #4
    Senior Member
    Join Date
    Jan 2014
    Posts
    109
    1. Read the sage advice (again) of Mr. Theremingenieur. Pins configured for analog input should NOT exceed the ADC's reference V, or the Vmax for the particular model.

    2. At best, is seems that 11.4 ENOB is to expected for the T3.x ADCs where not doing differential stuff. So configure for 12b unless you are running windowed averages of many samples of filtered data.

    3. The example at ../teensy/adc.html is for an AVR chip. Use the wonderous lib by Pedvide, as discussed in this thread:
    forum.pjrc.com/threads/25532-ADC-library-update-now-with-support-for-Teensy-3-1

    4. Have some T3.5s myself; one of which runs my hearing aid. Vbat is monitored by a R divider that is buffered by a lo-pwr op amp with an enable pin.

  5. #5
    Senior Member Constantin's Avatar
    Join Date
    Nov 2012
    Location
    In the yard with a 17' Dia. Ferris Wheel
    Posts
    1,408
    Yup. The add library makes life easier, Paul has also published some examples how to read analog values.

    It might be as simple as a voltage divider and capacitor from analog input to agnd. Something in the 0.01uF range as recommended by OEM or higher.

    Check out the voltage divider calculator over at Rarltron among other places.

  6. #6
    I've changed battery to 3v one.
    trying ADC lib. It is wonderful!
    running readAllPins...
    However even without V applied all pins show some voltage around 3V
    Code:
    A0: 2.97. A1: 3.07. A2: 3.05. A3: 3.05. A4: 3.04. A5: 3.05. A6: 3.05. A7: 3.05. A8: 3.06. A9: 3.04. A10: 1.07. A11: 0.94. A12: 2.99. 
    A13: 3.00. A14: 3.04. A15: 3.05. A16: 3.00. A17: 3.01. A18: 2.98. A19: 3.01. A20: 3.02. A21: 1.49. A22: 1.29. A23: 2.98. A24: 3.02. 
    Differential pairs: 0: -0.16. 
    Temperature sensor (approx.): : 30.73 C.
    Here the battery is connected only to only A0. Why others show voltage?

    My understanding that differential can measure double voltage ~7V.
    This covers LI battery fully charged voltage.
    What about a crazy idea to connect battery to differential (A10,A11) ?
    However I do not know how it will work when battery(-) will be connected to GND,
    battery(+) to Vin and A11 and battery(-) again to A10. What will go to Analog GND?
    Are GND and Analog GND disconnected galvanically?

  7. #7
    Senior Member
    Join Date
    Apr 2013
    Posts
    1,463
    If the other pins are not connected they'll float to some random voltage, in your case looks like they've all floated up. If you want a sensible read, you need to give them something to read. If this is just for testing you can enable the internal pullup/pulldowns on the pins and watch them change.

    See the Teensy schematic
    https://www.pjrc.com/teensy/schematic.html
    for your last question, and the answer is 'no'.

    And pretty sure you can't get the differential input above 3.3V.

    You are way overthinking this - look up resistive dividers as suggested above, put in a 1/2 or 1/3 and go to it.
    Last edited by GremlinWrangler; 04-18-2017 at 09:24 AM. Reason: Spelling

  8. #8
    Thank you guys!
    I see that no embedded "magic" is possible here. Only R-divider will help.
    What Rs will be good? If I will use 1.2v ref, will be 200KOhm and 1MOhm good?
    Or this resistance is too high?
    Also, to avoid leaking through this divider I should add a switch there, what would be better a MOSFET or opamp or something else? Better it to be as small as possible, because it will be in handheld device.

  9. #9
    Senior Member Theremingenieur's Avatar
    Join Date
    Feb 2014
    Location
    Colmar, France
    Posts
    834
    If you take a high R voltage divider, it will load less the battery. But it risks also to give wrong readings because little load might show better battery state than real. And the delta-sigma conversion of the analog inputs does also represent a varying load (see reference manual). Thus I'd go for a 39k / 10k divider with a 10nF capacitor in parallel with the 10k resistor since that is the recommended source impedance by the manufacturer.

    A p-MOSFet will do as a switch. An OP amp draws too much current for nothing.

    Thus the 39k resistor goes from the battery to the source of the p-mosfet. Another 39k resistor will go from the gate to the source of the mosfet as a discharge resistor for the gate capacitance. The gate goes to your control GPIO pin which is high in the inactive times. The drain goes to the 10k resistor, the 10nF capacitor and to the analog input. The other side of the 10k and the 10nF go to AGND.

    Symbolic code is:

    digitalWrite(controlPin, LOW); //switch the mosfet on
    delay(20); //allow the 10nF cap to charge
    int rdx = analogRead(analogPin); //read the input voltage
    delay(5); // just to be sure
    digitalWrite(controlPin, HIGH); //switch the mosfet off

    and then do whatever you want with the rdx value, scaling, offset correction, etc.
    Last edited by Theremingenieur; 04-18-2017 at 03:11 PM.

  10. #10
    THANK you Theremingenieur!

  11. #11
    Senior Member
    Join Date
    Jan 2013
    Posts
    843
    You are measuring an extremely slow signal. A 200kOhm / 50kOhm divider with a larger cap (e.g. 0.1uF) will work well.

    I'm not convinced powering down the voltage divider is worth it. How are you powering down the Teensy? E.g. with Snooze hibernate, I still have 45uA power consumption with Teensy 3.5, which is FAR more than the divider.

  12. #12
    Senior Member Theremingenieur's Avatar
    Join Date
    Feb 2014
    Location
    Colmar, France
    Posts
    834
    toi, the application note for the ADCs of these processor states clearly that the source impedance should be <= 10kOhm...

  13. #13
    Senior Member
    Join Date
    Jan 2013
    Posts
    843
    Quote Originally Posted by Theremingenieur View Post
    toi, the application note for the ADCs of these processor states clearly that the source impedance should be <= 10kOhm...
    The application note is irrelevant for the huge 0.1uF buffer cap I mentioned. That is 10'000x the 10pF of the SAR sampling capacitor and for practical purposes, the output impedance of that buffer cap is 0.

    Yes, I have practically tested this.

  14. #14
    Quote Originally Posted by tni View Post
    You are measuring an extremely slow signal. A 200kOhm / 50kOhm divider with a larger cap (e.g. 0.1uF) will work well.

    I'm not convinced powering down the voltage divider is worth it. How are you powering down the Teensy? E.g. with Snooze hibernate, I still have 45uA power consumption with Teensy 3.5, which is FAR more than the divider.
    Thanks tni!

    LTSpice shows I just 17uA for 200K/50K divider with 0.1uF cap.
    In this case it does not make sense to add a switch.
    I'll definitely try this.

  15. #15
    Senior Member
    Join Date
    Mar 2013
    Posts
    578
    The 10nF is all you need on your analog input, as long as you don't go crazy with your divider, the difference between the 10nF and the 100nF is about 700uV with your divider. Keep in mind that your values will always be a little lower then if you used a low resistance source like an opamp, but opamps add there own gotchas and increase part count.

    Here is a SAR simulation I created for the Teensy 3 series based on the Electrical Specs. The timings are based on conjecture and details gleaned from the datasheets, I sent a message to Pedvide a while back to see if he had details on the actual sample timings I could use. I was hoping to create a guide for beginners that would make it more apparent what they can and cant do with the Teensy ADC's but I have been kept busy with work.

    For some strange reason I cant attach the .zip or .asc file into the thread, instead I will just drop the contents of the .asc here and you can past it into a blank .asc file and open it with LTspice.

    Code:
    Version 4
    SHEET 1 2312 700
    WIRE -624 -1296 -800 -1296
    WIRE -352 -1296 -624 -1296
    WIRE -800 -1264 -800 -1296
    WIRE -624 -1136 -624 -1296
    WIRE -528 -1136 -624 -1136
    WIRE -528 -1120 -528 -1136
    WIRE -272 -1104 -400 -1104
    WIRE -352 -1072 -352 -1296
    WIRE -400 -1056 -400 -1104
    WIRE -384 -1056 -400 -1056
    WIRE -272 -1040 -272 -1104
    WIRE -272 -1040 -320 -1040
    WIRE -224 -1040 -272 -1040
    WIRE -96 -1040 -96 -1072
    WIRE -96 -1040 -144 -1040
    WIRE 32 -1040 -96 -1040
    WIRE 160 -1040 112 -1040
    WIRE 272 -1040 240 -1040
    WIRE 416 -1040 272 -1040
    WIRE -528 -1024 -528 -1040
    WIRE -384 -1024 -528 -1024
    WIRE -96 -1024 -96 -1040
    WIRE 416 -1024 416 -1040
    WIRE -528 -1008 -528 -1024
    WIRE 272 -1008 272 -1040
    WIRE 512 -1008 464 -1008
    WIRE 480 -960 464 -960
    WIRE 480 -944 480 -960
    WIRE 272 -912 272 -944
    WIRE 416 -912 416 -944
    WIRE 416 -912 272 -912
    WIRE 224 -848 224 -992
    WIRE 224 -848 144 -848
    WIRE 256 -848 224 -848
    WIRE 512 -848 512 -1008
    WIRE 512 -848 320 -848
    WIRE -624 -592 -624 -1136
    WIRE -464 -592 -624 -592
    WIRE -208 -592 -464 -592
    WIRE -208 -560 -208 -592
    WIRE -208 -464 -208 -480
    WIRE -112 -464 -112 -496
    WIRE -112 -464 -208 -464
    WIRE 32 -464 -112 -464
    WIRE 160 -464 112 -464
    WIRE 272 -464 240 -464
    WIRE 416 -464 272 -464
    WIRE -208 -448 -208 -464
    WIRE 416 -448 416 -464
    WIRE -112 -432 -112 -464
    WIRE 272 -432 272 -464
    WIRE 512 -432 464 -432
    WIRE 480 -384 464 -384
    WIRE 480 -368 480 -384
    WIRE 272 -336 272 -368
    WIRE 416 -336 416 -368
    WIRE 416 -336 272 -336
    WIRE 144 -272 144 -848
    WIRE 224 -272 224 -416
    WIRE 224 -272 144 -272
    WIRE 256 -272 224 -272
    WIRE 512 -272 512 -432
    WIRE 512 -272 320 -272
    WIRE -464 16 -464 -592
    WIRE -224 16 -464 16
    WIRE -224 32 -224 16
    WIRE -224 128 -224 112
    WIRE -112 128 -112 96
    WIRE -112 128 -224 128
    WIRE 32 128 -112 128
    WIRE 160 128 112 128
    WIRE 272 128 240 128
    WIRE 416 128 272 128
    WIRE -224 144 -224 128
    WIRE 416 144 416 128
    WIRE 272 160 272 128
    WIRE 512 160 464 160
    WIRE 480 208 464 208
    WIRE 480 224 480 208
    WIRE 272 256 272 224
    WIRE 416 256 416 224
    WIRE 416 256 272 256
    WIRE 144 320 144 -272
    WIRE 144 320 96 320
    WIRE 224 320 224 176
    WIRE 224 320 144 320
    WIRE 256 320 224 320
    WIRE 512 320 512 160
    WIRE 512 320 320 320
    WIRE 96 384 96 320
    FLAG 480 224 0
    FLAG 176 176 0
    FLAG 96 464 0
    FLAG -800 -1184 0
    FLAG 416 256 0
    FLAG 480 -368 0
    FLAG 176 -416 0
    FLAG -112 -368 0
    FLAG 416 -336 0
    FLAG 480 -944 0
    FLAG 176 -992 0
    FLAG 416 -912 0
    FLAG -352 -1008 0
    FLAG -224 224 0
    FLAG -208 -368 0
    FLAG -528 -928 0
    FLAG -96 -960 0
    FLAG 272 128 A
    FLAG 272 -464 B
    FLAG 272 -1040 C
    FLAG -112 96 AIN
    FLAG -112 -496 BIN
    FLAG -96 -1072 CIN
    SYMBOL cap 256 160 R0
    SYMATTR InstName CADINA
    SYMATTR Value 10p
    SYMATTR SpiceLine Rser=3
    SYMBOL res 128 112 R90
    WINDOW 0 0 56 VBottom 2
    WINDOW 3 32 56 VTop 2
    SYMATTR InstName RADINA
    SYMATTR Value 5K
    SYMBOL sw 416 240 R180
    WINDOW 0 24 16 Invisible 2
    WINDOW 3 24 96 Invisible 2
    SYMATTR InstName S1
    SYMATTR Value mySW
    SYMBOL sw 144 128 R270
    WINDOW 0 24 16 Invisible 2
    WINDOW 3 24 96 Invisible 2
    SYMATTR InstName S2
    SYMATTR Value mySW
    SYMBOL Digital\\inv 256 256 R0
    SYMATTR InstName A1
    SYMBOL res -208 128 R180
    WINDOW 0 36 76 Left 2
    WINDOW 3 36 40 Left 2
    SYMATTR InstName R2
    SYMATTR Value 10K
    SYMBOL voltage 96 368 R0
    WINDOW 123 0 0 Left 2
    WINDOW 39 0 0 Left 2
    SYMATTR InstName V1
    SYMATTR Value PULSE(0 3.3 0.001 10n 10n 800n 250u 10)
    SYMBOL voltage -800 -1280 R0
    WINDOW 123 0 0 Left 2
    WINDOW 39 24 124 Left 2
    SYMATTR SpiceLine Rser=0
    SYMATTR InstName V2
    SYMATTR Value 3.3
    SYMBOL cap 256 -432 R0
    SYMATTR InstName CADINB
    SYMATTR Value 10p
    SYMATTR SpiceLine Rser=3
    SYMBOL res 128 -480 R90
    WINDOW 0 0 56 VBottom 2
    WINDOW 3 32 56 VTop 2
    SYMATTR InstName RADINB
    SYMATTR Value 5K
    SYMBOL sw 416 -352 R180
    WINDOW 0 24 16 Invisible 2
    WINDOW 3 24 96 Invisible 2
    SYMATTR InstName S3
    SYMATTR Value mySW
    SYMBOL sw 144 -464 R270
    WINDOW 0 24 16 Invisible 2
    WINDOW 3 24 96 Invisible 2
    SYMATTR InstName S4
    SYMATTR Value mySW
    SYMBOL Digital\\inv 256 -336 R0
    SYMATTR InstName A2
    SYMBOL Opamps\\opamp2 -352 -1104 R0
    SYMATTR InstName U2
    SYMATTR Value LM324
    SYMBOL cap 256 -1008 R0
    SYMATTR InstName CADINC
    SYMATTR Value 10p
    SYMATTR SpiceLine Rser=3
    SYMBOL res 128 -1056 R90
    WINDOW 0 0 56 VBottom 2
    WINDOW 3 32 56 VTop 2
    SYMATTR InstName RADINC
    SYMATTR Value 5K
    SYMBOL sw 416 -928 R180
    WINDOW 0 24 16 Invisible 2
    WINDOW 3 24 96 Invisible 2
    SYMATTR InstName S5
    SYMATTR Value mySW
    SYMBOL sw 144 -1040 R270
    WINDOW 0 24 16 Invisible 2
    WINDOW 3 24 96 Invisible 2
    SYMATTR InstName S6
    SYMATTR Value mySW
    SYMBOL Digital\\inv 256 -912 R0
    SYMATTR InstName A3
    SYMBOL res -128 -1056 R90
    WINDOW 0 0 56 VBottom 2
    WINDOW 3 32 56 VTop 2
    SYMATTR InstName R6
    SYMATTR Value 1.5K
    SYMBOL cap -128 -432 R0
    SYMATTR InstName C2
    SYMATTR Value 0.01
    SYMATTR SpiceLine V=16 Irms=647m Rser=0.0322889 Lser=0 mfg="KEMET" pn="C0201C103K4PAC" type="X5R"
    SYMBOL res -208 240 R180
    WINDOW 0 36 76 Left 2
    WINDOW 3 36 40 Left 2
    SYMATTR InstName R7
    SYMATTR Value 10K
    SYMBOL res -192 -464 R180
    WINDOW 0 36 76 Left 2
    WINDOW 3 36 40 Left 2
    SYMATTR InstName R4
    SYMATTR Value 10K
    SYMBOL res -192 -352 R180
    WINDOW 0 36 76 Left 2
    WINDOW 3 36 40 Left 2
    SYMATTR InstName R8
    SYMATTR Value 10K
    SYMBOL res -512 -1024 R180
    WINDOW 0 36 76 Left 2
    WINDOW 3 36 40 Left 2
    SYMATTR InstName R9
    SYMATTR Value 10K
    SYMBOL res -512 -912 R180
    WINDOW 0 36 76 Left 2
    WINDOW 3 36 40 Left 2
    SYMATTR InstName R10
    SYMATTR Value 10K
    SYMBOL cap -112 -1024 R0
    SYMATTR InstName C4
    SYMATTR Value 0.01
    SYMATTR SpiceLine V=16 Irms=647m Rser=0.0322889 Lser=0 mfg="KEMET" pn="C0201C103K4PAC" type="X5R"
    TEXT 112 544 Left 2 !.model mySW SW(Ron=0.1 Roff=10Meg Vt=.3)
    TEXT -352 568 Left 2 !.tran 0 0.001001 0.0009999
    TEXT 1736 -112 Left 1 !* LM324      QUAD LOW POWER OP AMP/BUFFERS\n* connections:   non-inverting input\n*                | inverting input\n*                | | positive power supply\n*                | | | negative power supply\n*                | | | | output\n*                | | | | |\n.subckt LM324    1 2 3 4 5\n*\n  c1   11 12 2.887E-12\n  c2    6  7 30.00E-12\n  dc    5 53 dx\n  de   54  5 dx\n  dlp  90 91 dx\n  dln  92 90 dx\n  dp    4  3 dx\n  egnd 99  0 poly(2) (3,0) (4,0) 0 .5 .5\n  fb    7 99 poly(5) vb vc ve vlp vln 0 21.22E6 -20E6 20E6 20E6 -20E6\n  ga    6  0 11 12 188.5E-6\n  gcm   0  6 10 99 59.61E-9\n  iee   3 10 dc 15.09E-6\n  hlim 90  0 vlim 1K\n  q1   11  2 13 qx\n  q2   12  1 14 qx\n  r2    6  9 100.0E3\n  rc1   4 11 5.305E3\n  rc2   4 12 5.305E3\n  re1  13 10 1.845E3\n  re2  14 10 1.845E3\n  ree  10 99 13.25E6\n  ro1   8  5 50\n  ro2   7 99 25\n  rp    3  4 9.082E3\n  vb    9  0 dc 0\n  vc    3 53 dc 1.500\n  ve   54  4 dc 0.65\n  vlim  7  8 dc 0\n  vlp  91  0 dc 40\n  vln   0 92 dc 40\n.model dx D(Is=800.0E-18 Rs=1)\n.model qx PNP(Is=800.0E-18 Bf=166.7)\n.ends\n*$
    TEXT 688 216 Left 7 ;A
    TEXT 680 -376 Left 7 ;B
    TEXT 680 -936 Left 7 ;C
    LINE Normal 0 400 0 -16 2
    LINE Normal 0 400 0 400 2
    LINE Normal 0 400 0 400 2
    LINE Normal 0 400 0 400 2
    LINE Normal 0 400 0 400 2
    LINE Normal 656 496 0 496 2
    LINE Normal 656 -16 656 400 2
    LINE Normal 0 -16 656 -16 2
    LINE Normal 0 -16 0 -16 2
    LINE Normal 0 -16 0 -16 2
    LINE Normal 0 496 0 400 2
    LINE Normal 656 496 656 400 2
    LINE Normal 0 -192 0 -608 2
    LINE Normal 0 -192 0 -192 2
    LINE Normal 0 -192 0 -192 2
    LINE Normal 0 -192 0 -192 2
    LINE Normal 0 -192 0 -192 2
    LINE Normal 656 -96 0 -96 2
    LINE Normal 656 -608 656 -192 2
    LINE Normal 0 -608 656 -608 2
    LINE Normal 0 -608 0 -608 2
    LINE Normal 0 -608 0 -608 2
    LINE Normal 0 -96 0 -192 2
    LINE Normal 656 -96 656 -192 2
    LINE Normal 0 -768 0 -1184 2
    LINE Normal 0 -768 0 -768 2
    LINE Normal 0 -768 0 -768 2
    LINE Normal 0 -768 0 -768 2
    LINE Normal 0 -768 0 -768 2
    LINE Normal 656 -672 0 -672 2
    LINE Normal 656 -1184 656 -768 2
    LINE Normal 0 -1184 656 -1184 2
    LINE Normal 0 -1184 0 -1184 2
    LINE Normal 0 -1184 0 -1184 2
    LINE Normal 0 -672 0 -768 2
    LINE Normal 656 -672 656 -768 2

  16. #16
    Junior Member
    Join Date
    Jan 2017
    Posts
    14
    You may not need a cap, when your frequency is low. If your resolution is 12 BIT or lower you can set the ADC clock to 1MHZ. I figued this out with a 100K/100K (R1/R2) voltage divider. When everything is fine I would measure a resistance of 100K. As you can see with a 100K the resluts for high frequencies are far away from reality. Voltage is underestimated because of incomplete loading of internal cap. For lower frequencies (and therefore longer sampling times) they come closer to the expected value.


    Click image for larger version. 

Name:	Plot1b.png 
Views:	25 
Size:	49.2 KB 
ID:	10368

    In the first case there was only a small sample time selected (6 ADC clock cycles) . But sample time can be increaed to a maximum value of 24 ADC clock cycles. This is shown in the second plot. For a sampling time of 24 ADC clock cycles and a ADC clock frequency of 1.5 MHZ (CPU clock @ 24 MHZ) the mean measured value of R2 is 99901 which is very close to 100K.




    Click image for larger version. 

Name:	Plot2.png 
Views:	34 
Size:	30.9 KB 
ID:	10366
    Last edited by tvetter; 04-21-2017 at 11:53 AM.

  17. #17
    Senior Member
    Join Date
    Mar 2013
    Posts
    578
    That's about a 16uS sample time.
    Oddly enough just looking at my LTspice model it should realistically only take about 8uS with a 100K/100K divider to basically reach the max value based on total system resistance.

  18. #18
    currently not having a 0.1uF cap, I've just inserted 47K/100K divider into my breadboard.
    All works. Voltage of 3.14V battery shows up as 0.99V, USB voltage (5V) shows as 1.98V.

    If it works without a cap, what cap gives? Smoothing? But if port is averaging is smoothing required?

  19. #19
    Senior Member defragster's Avatar
    Join Date
    Feb 2015
    Posts
    4,803
    AFAIK - noted above there is an internal cap within the Teensy that must be charged to the level of the signal to be measured. That cap it used to complete the internal multi sample measurement of the signal.

    The voltage divider used to drop voltage and limit current drain will also limit the ability of that internal cap to be fully charged to the actual signal level as it is internally discharged during sampling.

    The external cap being larger than the internal cap will over time match the signal level to be measured and when the Teensy starts a sample and charges the internal cap it will have that larger external cap to draw from in a way that will not affect the signal to be measured as shown in the post #16 images where rapid samples indicate a lower than true signal value.

  20. #20
    Senior Member
    Join Date
    Apr 2013
    Posts
    1,463
    Cap is there to get a 'correct' response from the ADC. As noted above the ADC isn't magic, and has a non zero current consumption during conversion and using very high value resistors (manual says max 10k) the measured result will skew, I think downward if I'm understanding the ADC design right. So you can't just use the ADC value * divisor factor to get a voltage. Adding the capacitor means that during non sampling time the capacitor charges slowly through the resistors (slowly in electronic terms anyway), then when ADC starts the cap provides the current need to accurately measure the voltage present. Note this means you can't do high speed reads since you need to wait for the cap to stabilise again.

    Without the cap the results should be consistent, just not the actual voltage present. So if you don't want to add it you should be able to manually input known voltages (or use another Teensy DAC) and then map the ADC output numbers to your measured voltage and make up some form of conversion. If all you want is 'full/half/empty indicators then you can just find out what the AnalogRead for those levels are and call it done. Gets trickier if you want to actually display bat voltage somewhere.

    If you want a starting point to do your own reading, since this will impact other things down the road
    https://en.wikipedia.org/wiki/Input_impedance
    https://en.wikipedia.org/wiki/Analog...ital_converter

  21. #21
    Senior Member
    Join Date
    Mar 2013
    Posts
    578
    Im sorry mm7 but your information does not make sense. Here is why. I have a Teensy 3.1 on my desk with 3.3V - 47K - Teensy - 100K - GND. Im running 96MHz(12MHz ADC Clock) with ADC sampling and conversion set to default speeds(medium), and averages set to 1. The values i'm getting are extremely close considering i'm not using a cap and i'm using DMA to consecutively read the pins. Even reducing sample time and conversion time to its lowest setting still provides a value closer then what your reporting.

    You need to provide a picture of what your doing and a sample of the code that shows the issue because what your saying does not make sense.

    Here is the code i'm using, it samples 8 pins using 2 DMA channels, each pin is sampled roughly 48000 times a second.

    Code:
    // Converted to only 1 buffer per ADC, reduced to a small example.
    //Based on Example by SaileNav
    
    
    #include "DMAChannel.h"
    #include "ADC.h" 
    
    #define BUF_SIZE 256
    
    ADC *adc = new ADC(); // adc object
    
    DMAChannel* dma0 = new DMAChannel(false);
    DMAChannel* dma1 = new DMAChannel(false);
    DMAChannel* dma2 = new DMAChannel(false);
    DMAChannel* dma3 = new DMAChannel(false);
    
    
    //ChannelsCfg order must be {CH1, CH2, CH3, CH0 }, adcbuffer output will be CH0, CH1, CH2, CH3
    //Order must be {Second . . . . . . . . First} no matter the number of channels used.
    const uint16_t ChannelsCfg_0 [] =  { 0x47, 0x4F, 0x44, 0x46 };  //ADC0: CH0 ad6(A6), CH1 ad7(A7), CH2 ad15(A8), CH3 ad4(A9)
    const uint16_t ChannelsCfg_1 [] =  { 0x45, 0x46, 0x47, 0x44 };  //ADC1: CH0 ad4(A17), CH1 ad5(A16), CH2ad6(A18), CH3 ad7(A19)
    
    const int ledPin = 13;
    
    DMAMEM static volatile uint16_t __attribute__((aligned(BUF_SIZE+0))) adcbuffer_0[BUF_SIZE];
    DMAMEM static volatile uint16_t __attribute__((aligned(BUF_SIZE+0))) adcbuffer_1[BUF_SIZE];
    
    volatile int d2_active;
    
    void setup() {
      // initialize the digital pin as an output.
      pinMode(ledPin, OUTPUT);
      delay(500); 
    
      d2_active = 0;
      
      pinMode(2, INPUT_PULLUP);
      pinMode(4, OUTPUT);
      pinMode(6, OUTPUT);
    
      attachInterrupt(2, d2_isr, FALLING);
      
      Serial.begin(9600);
      
      // clear buffer
      for (int i = 0; i < BUF_SIZE; ++i){
          adcbuffer_0[i] = 50000;
          adcbuffer_1[i] = 50000;
        
      }
       
      setup_adc();
      setup_dma(); 
      
    }
    elapsedMillis debounce;
    
    
    void loop() {
    
      while(1){
        uint8_t pin = 0;
        if(d2_active) pin=1;
    
        if(pin>0){
          
          for (int i = 0; i < BUF_SIZE; i = i + 4){
            int a = adcbuffer_0[i];
            Serial.print(a);
            Serial.print(".");
            int b = adcbuffer_0[i+1];
            Serial.print(b);
            Serial.print(".");
            int c = adcbuffer_0[i+2];
            Serial.print(c);
            Serial.print(".");
            int d = adcbuffer_0[i+3];
            Serial.print(d);
            Serial.print("...");                
            int e = adcbuffer_1[i];
            Serial.print(e);        
            Serial.print(".");
            int f = adcbuffer_1[i+1];
            Serial.print(f);        
            Serial.print(".");
            int g = adcbuffer_1[i+2];
            Serial.print(g);        
            Serial.print(".");                        
            int h = adcbuffer_1[i+3];
            Serial.println(h);
    
          }
    
          digitalWrite(ledPin, HIGH);   // set the LED on
          delay(50);                  // wait for a second
          digitalWrite(ledPin, LOW);    // set the LED off
    
          d2_active = 0;     
        }
      }
    
      digitalWrite(ledPin, HIGH);   // set the LED on
      delay(100);                  // wait for a second
      digitalWrite(ledPin, LOW);    // set the LED off
      delay(100);   
    }
    
    void setup_dma() {
      dma0->begin(true);              // allocate the DMA channel 
      dma0->TCD->SADDR = &ADC0_RA;    // where to read from
      dma0->TCD->SOFF = 0;            // source increment each transfer
      dma0->TCD->ATTR = 0x101;
      dma0->TCD->NBYTES = 2;     // bytes per transfer
      dma0->TCD->SLAST = 0;
      dma0->TCD->DADDR = &adcbuffer_0[0];// where to write to
      dma0->TCD->DOFF = 2; 
      dma0->TCD->DLASTSGA = -2*BUF_SIZE;
      dma0->TCD->BITER = BUF_SIZE;
      dma0->TCD->CITER = BUF_SIZE;    
      dma0->triggerAtHardwareEvent(DMAMUX_SOURCE_ADC0);
      dma0->disableOnCompletion();    // require restart in code
      dma0->interruptAtCompletion();
      dma0->attachInterrupt(dma0_isr);
      
      dma1->begin(true);              // allocate the DMA channel 
      dma1->TCD->SADDR = &ChannelsCfg_0[0];
      dma1->TCD->SOFF = 2;            // source increment each transfer (n bytes)
      dma1->TCD->ATTR = 0x101;
      dma1->TCD->SLAST = -8;          // num ADC0 samples * 2
      dma1->TCD->BITER = 4;           // num of ADC0 samples
      dma1->TCD->CITER = 4;           // num of ADC0 samples
      dma1->TCD->DADDR = &ADC0_SC1A;
      dma1->TCD->DLASTSGA = 0;
      dma1->TCD->NBYTES = 2;
      dma1->TCD->DOFF = 0;
      dma1->triggerAtTransfersOf(*dma0);
      dma1->triggerAtCompletionOf(*dma0);
    
      dma2->begin(true);              // allocate the DMA channel 
      dma2->TCD->SADDR = &ADC1_RA;    // where to read from
      dma2->TCD->SOFF = 0;            // source increment each transfer
      dma2->TCD->ATTR = 0x101;
      dma2->TCD->NBYTES = 2;     // bytes per transfer
      dma2->TCD->SLAST = 0;
      dma2->TCD->DADDR = &adcbuffer_1[0];// where to write to
      dma2->TCD->DOFF = 2; 
      dma2->TCD->DLASTSGA = -2*BUF_SIZE;
      dma2->TCD->BITER = BUF_SIZE;
      dma2->TCD->CITER = BUF_SIZE;    
      dma2->triggerAtHardwareEvent(DMAMUX_SOURCE_ADC1);
      dma2->disableOnCompletion();    // require restart in code
      dma2->interruptAtCompletion();
      dma2->attachInterrupt(dma2_isr);
      
      dma3->begin(true);              // allocate the DMA channel 
      dma3->TCD->SADDR = &ChannelsCfg_1[0];
      dma3->TCD->SOFF = 2;            // source increment each transfer (n bytes)
      dma3->TCD->ATTR = 0x101;
      dma3->TCD->SLAST = -8;          // num ADC1 samples * 2
      dma3->TCD->BITER = 4;           // num of ADC1 samples
      dma3->TCD->CITER = 4;           // num of ADC1 samples
      dma3->TCD->DADDR = &ADC1_SC1A;
      dma3->TCD->DLASTSGA = 0;
      dma3->TCD->NBYTES = 2;
      dma3->TCD->DOFF = 0;
      dma3->triggerAtTransfersOf(*dma2);
      dma3->triggerAtCompletionOf(*dma2);
    
      dma0->enable();
      dma1->enable();
      
      dma2->enable();
      dma3->enable();
      
    } 
    
    void setup_adc() {
      //ADC0
      adc->setAveraging(1, ADC_0); // set number of averages
      adc->adc0->setResolution(12); // set bits of resolution
      adc->setConversionSpeed(ADC_MED_SPEED, ADC_0); // change the conversion speed
      adc->setSamplingSpeed(ADC_MED_SPEED, ADC_0); // change the sampling speed
      //adc->adc0->setReference(ADC_REF_1V2);
      
      //ADC1
      adc->setAveraging(1, ADC_1); // set number of averages
      adc->adc1->setResolution(12); // set bits of resolution
      adc->setConversionSpeed(ADC_MED_SPEED, ADC_1); // change the conversion speed
      adc->setSamplingSpeed(ADC_MED_SPEED, ADC_1); // change the sampling speed
      //adc->adc1->setReference(ADC_REF_1V2);`
      
      ADC1_CFG2 |= ADC_CFG2_MUXSEL;
      
      adc->adc0->enableDMA(); //ADC0_SC2 |= ADC_SC2_DMAEN;  // using software trigger, ie writing to ADC0_SC1A
      adc->adc1->enableDMA();
      
    } 
    
    void d2_isr(void) {
      if(debounce > 200){
        d2_active = 1;
        debounce = 0;
        }
        else{return;}
    }
    
    void dma0_isr(void) {
        dma0->TCD->DADDR = &adcbuffer_0[0];
        dma0->clearInterrupt();
        dma0->enable();
        digitalWriteFast(4, HIGH);
        digitalWriteFast(4, LOW);
    }
    
    void dma2_isr(void) {
        dma2->TCD->DADDR = &adcbuffer_1[0];
        dma2->clearInterrupt();
        dma2->enable();
        digitalWriteFast(6, HIGH);
        digitalWriteFast(6, LOW);
    }
    Last edited by Donziboy2; 04-23-2017 at 12:25 PM.

  22. #22
    Junior Member
    Join Date
    Jan 2017
    Posts
    14
    Quote Originally Posted by mm7 View Post
    currently not having a 0.1uF cap, I've just inserted 47K/100K divider into my breadboard.
    All works. Voltage of 3.14V battery shows up as 0.99V, USB voltage (5V) shows as 1.98V.

    If it works without a cap, what cap gives? Smoothing? But if port is averaging is smoothing required?
    At what frequency are you running your teensy? And the ADC-resolution? 10 BIT?

  23. #23
    Currently I'm experimenting and run Teensy 3.5 on default frequency.
    However in future, I'll decrease F to go low power. May be even will change Teensy model to 3.2 or even LC.
    It is battery based app - GPS logger to SD, however it supposed to log at 10Hz (10-12 messages/sec), so it is about 1-2KB/sec.
    Also I am thinking to send some data via Bluetooth BLE to a smart watch. Like to show speed/odo-meters, GPS/battery status, etc...

  24. #24
    Quote Originally Posted by Donziboy2 View Post
    Im sorry mm7 but your information does not make sense. Here is why. I have a Teensy 3.1 on my desk with 3.3V - 47K - Teensy - 100K - GND. ...
    Hi Donziboy2, thanks for your advises!

    Actually my circuit is 3.3V - 100K - Teensy:A0 - 47K - GND.

    Your sketch does not print anything. d2_active is always 0.



    GremlinWrangler, thanks for explanation. It is getting clear.
    Last edited by mm7; 04-23-2017 at 03:52 PM.

  25. #25
    Senior Member
    Join Date
    Mar 2013
    Posts
    578
    That divider explains your 3.14V reading but the 5V reading is way off(should be near 1.599V for 100K/47K).
    A source like batteries will tend to be fairly clean but with a high divider it causes issues with noise, the higher the resistance used the easier noise can cause fluctuation. You can sample/average it a lot which chews up CPU time or use a capacitor to buffer the ADC input, the size of the capacitor determines how much noise can be filtered out. 0.01 - 0.1uF tends to be the sweet spot for most Teensy users.
    Slower CPU Clock frequency's will increase the overall accuracy provided the FBus also goes down with it.

    The sketch, yep, sorry you have to touch a jumper wire/button on pin 2 to GND to trigger the interrupt that lets it print, it only prints the 2*256 buffers then waits for the pin to trigger again. The DMA runs in the background constantly, you can put a Scope on pin 4 or 6 to see when the interrupts for the DMA's trigger.

Posting Permissions

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