66 posts / 0 new

## Pages

Author
Message

Hello Guys,

Does anyone of you have experience with battery monitoring ?
Which one is good for monitoring 12V battery ?
I saw DS248....what do you reckon ?

I want to monitor my battery with it and display the voltage status on LCD..

Any clues will be very appreciated,

Thank you

Quote:

I saw DS248

???

The most straightforward way is to use a resistor divider. Make it high ohms so that the draw on the battery is minimized. E.g. 12000 ohms is 1mA. 120000 ohms is 100uA. 1200000 ohms is 10uA.

So if you have ~megaohm divider, you want a small cap near the AVR's pin to give the ADC something to "sip" from when it takes a reading.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

I'd divide 15V down to 5V. I guess thats a 3:1 divider. Know what Rs to use?

Imagecraft compiler user

bianchi77 wrote:
Hello Guys,

Does anyone of you have experience with battery monitoring ?
Which one is good for monitoring 12V battery ?
I saw DS248....what do you reckon ?

I want to monitor my battery with it and display the voltage status on LCD..

Any clues will be very appreciated,

Thank you

Is this an automotive battery or a Gell cell type battery, an 12 VDC N size ???

You did not specify what kind of accuracy or resolution you needed or how critical knowing what the actual battery voltage is.

While someone suggested scaling to 15 VDC, this will spread 1023 ADC counts over the 15 VDC range or a resolution of 0.014663 volts (14.663 mV) per ADC step. An automotive battery is legally dead at 12.0 VDC, though, it will still supply enough power to get the job done down to about 10.5 VDC, in most cases.

Measuring in the range between 0 VDC and 15 VDC, you will be wasting about 60 percent of the ADC range over battery voltages you will probably never get down to - like, between 0 and 10.5 VDC.

If you use the differential measuring mode of the AVR ADC, using two ADC channels, you can scale the reference ADC channel (ADC channel 0) at say, 10.5 VDC and then scale the measuring ADC channel (ADC channel 1) to say, 13.8 VDC.

This approach will allow the full 1023 ADC count to be scaled to the usable battery operating range between say, 10.5 VDC and 13.8 VDC. This provides much better resolution of the battery voltage measurement.

That is, differentially speaking, the resolution for the battery range I've proposed would be:

```    (13.8 VDC - 10.5 VDC)
V = --------------------- = 0.003226 volts
1023```

Interestingly, the subtraction takes place automatically within the ADC logic.

So then, a measurement taken over the smaller, more usable battery operating range by the differential measurement method will provide a battery voltage resolution of 3.226 mV per ADC step, as opposed to 14.663 mV per ADC step using a full scale single-ended measurement of 15.0 VDC.

Then, again, it simply might not make any difference in your particular application.

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

bobgardner wrote:
I'd divide 15V down to 5V. I guess thats a 3:1 divider. Know what Rs to use?

Let me give a shot 10K and 20K ....or 100K and 200K ?

Thank you very much for the calculation and consideration....I'll do my experiment and see what happens guys

Quote:
The most straightforward way is to use a resistor divider. Make it high ohms so that the draw on the battery is minimized. E.g. 12000 ohms is 1mA. 120000 ohms is 100uA. 1200000 ohms is 10uA.

So if you have ~megaohm divider, you want a small cap near the AVR's pin to give the ADC something to "sip" from when it takes a reading.

how about it 50K to 100K level? is it good enough for the battery? I have 12V 1.3Ah battery...
do I need 5V1 zener diode ?
http://i129.photobucket.com/albu...

No zener needed.

If you use such high resistors, then the over-voltage protection diode at the input pin will save you. I, personally, would add another small diode (Schottkey, preferred) from the ADC pin to Vcc. It will take any over-voltage current before the inside diode does.

Jim

Until Black Lives Matter, we do not have "All Lives Matter"!

To : microcarl,

I have created :

```adc_result=read_adc1(); //read the voltage from ADC1

{
}```

do you have a function for calculating the voltage ?
thanks

I've done :

```void adc_init()
{
// enable ADC, select ADC clock = F_CPU / 128 (i.e. 125 kHz)

//Do a conversion to get rid of rubbish

//Internal VCC Voltage Reference
//Internal VCC Voltage Reference

}

{
}

{

double t;
return t;
}```
```	adc_result=read_adc1(); //read the voltage from ADC1

{
sprintf(voltage,"%.2f",d);
lcd_string(voltage);
}```

Please correct me if I missed something here,
thanks

Your code makes little sense. I'd suggest you return to basics and work up from there. No point trying to run before you can walk.

ka7ehk wrote:
No zener needed.

If you use such high resistors, then the over-voltage protection diode at the input pin will save you. I, personally, would add another small diode (Schottkey, preferred) from the ADC pin to Vcc. It will take any over-voltage current before the inside diode does.

Jim

If the OP ends up with high value resistors in the divider (>100k range), then adding a schottky diode from ADC to VCC is not a good idea... Consider the reverse leakage current (especially versus temperature) of the typical schottky... Schottky diode typically have quite high reverse leakage currents, especially at higher temperatures.

Any diode that is added as a clamp needs to be carefully chosen to minimise reverse current leakage.

One commercial product I designed is a battery voltage monitoring devices (2 channels for a main battery + optional aux battery) in automotive/motorcycle applications. Using a divider in the 300k/100k range provides sufficiently high impedance that the internal protection diodes are more than adequate. Combine that with a small value (0.1uf typical) RC cap on the bottom of the divider to absorb any fast transients and to provide 'storage' for the S/H cap inside the AVR while changing channels.

I also perform a calibration after initial programming to an accurately set input voltage to allow the AVR to measure any offset errors and variations in the divider network and voltage regulator (also used as the reference). By doing this AND choosing a stable (over wide temperature range) regulator I easily maintain much better than 1% accuracy from 10 - 16V over 0C to 100C across a wide sampling of units (several hundred).

cheers,
george.

You can always look into my LIPO project, where I'm using three ADC channels to measure on each cell (3).

https://www.avrfreaks.net/index.p...

Its complete with code, schematics and everything...

You just need to do the adaptation for the mega128, but that should not be a huge step...

There I measure to control that the cells do not go below 3 Volts. When the ADC value tells me that I'm at 3 Volts on 1 cell or all I shut down the output.

You could easily adapt this for your own project and instead of shutting output down display a bar of diodes or put the value on an LCD display, whatever...

I also go by the alias jsvens64,

BR
JOhan

Well you need also to adapt the voltage devider of course.
BR

JOhan

Ok, thanks Johan,
Here's my init, is it right ?
I want to use 2 channel of ADC with 125Khz clock

```void adc_init()
{
// enable ADC, select ADC clock = F_CPU / 128 (i.e. 125 kHz)

//Do a conversion to get rid of rubbish

//Internal VCC Voltage Reference
//Internal VCC Voltage Reference

}```

I tested my code and got ?????????? on my LCD, is the conversion already running but can not be displayed properly ?

Thanks

Quote:

I tested my code and got ?????????? on my LCD

Are you linking with the correct printf() library?

Especially the part starting with "Since the full implementation of all the mentioned features becomes fairly large [...]".

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

bianchi77 wrote:
I tested my code and got ?????????? on my LCD, is the conversion already running but can not be displayed properly ?

`			sprintf(voltage,"%.2f",d);`

vfprintf() and friends come in three flavours. Here's an excerpt from the AVR Libc manual:

Quote:
Since the full implementation of all the mentioned features becomes fairly large, three different flavours of vfprintf() can be selected using linker options. The default vfprintf() implements all the mentioned functionality except floating point conversions. A minimized version of vfprintf() is available that only implements the very basic integer and string conversion facilities, but only the # additional option can be specified using conversion flags (these flags are parsed correctly from the format specification, but then simply ignored). This version can be requested using the following compiler options:
`   -Wl,-u,vfprintf -lprintf_min`

If the full functionality including the floating point conversions is required, the following options should be used:

`   -Wl,-u,vfprintf -lprintf_flt -lm`

Limitations:

The specified width and precision can be at most 255.
Notes:
For floating-point conversions, if you link default or minimized version of vfprintf(), the symbol ? will be output and double argument will be skiped. So you output below will not be crashed. For default version the width field and the "pad to left" ( symbol minus ) option will work in this case.
JJ

 "Experience is what enables you to recognise a mistake the second time you make it." "Good judgement comes from experience.  Experience comes from bad judgement." "Wisdom is always wont to arrive late, and to be a little approximate on first possession." "When you hear hoofbeats, think horses, not unicorns." "Fast.  Cheap.  Good.  Pick two." "We see a lot of arses on handlebars around here." - [J Ekdahl]

I used vprintf, there's no more ?????????? but now I got only a cursor without anything...
Do I miss something else here ?
thank you

where can I put this option on atmel studio 6.1 ?
compiler options:
Code:
-Wl,-u,vfprintf -lprintf_min

If you are trying to get floats to show as numbers rather than ? why would you think -lprintf_min was the answer? The one you want is -lprintf_flt.

Anyway in the Solution Explorer in AS6.1 right click "Libraries" and use "Add Library". You should see that libprintf_flt.a is one of the things available there - just tick it. That will add the -lprintf_flt for you.

For the -Wl,-u,vfprintf look at the last "General" option under AVR/GNU Linker options in the project options. There's a tick box there that, if ticked, will pass that command.

I did :

```adc_result=read_adc1(); //read the voltage from ADC1
lcd_string(voltage);```

Quote:

I used vprintf, there's no more ?????????? but now I got only a cursor without anything...

Where did anyone say you should use vprintf() ?

Did you read and understand the documentation? The reason I was linking to it was for you to understand that there are three different builds of of the system libraries, and you need to pick the one that implements handling of floating point conversions. You do not need to call any other function - you need to switch to a library that implements handling of floats also.

Quote:

where can I put this option on atmel studio 6.1 ?
compiler options:
Code:
-Wl,-u,vfprintf -lprintf_min

:shock: You are already having problems with prinf() not handling floats. Why would you then select the minimal implementation, with even less functionality? If there was something you didn't understand in the documentation, please ask. My suspicion is that you didn't read, or read sloppily.

So..., how would you do what you want, e.g.

`-Wl,-u,vfprintf -lprintf_flt -lm`

?

I don't have that in my head, but the answer is in several old posts here at AVRfreaks.

Just use the search function here, feed it "printf_flt" and be sure to select to search only the AS5/6 forum.

Yes, I could do that search for you, but how about you giving it a try first? :wink:

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

now it's showing result but strange, 0.000.000.00 ??

```code :
while(1)
{
//==============
//test
lcd_string("Voltage:");

{
lcd_string(voltage);

}
}```

I'm using this circuit :

Input connected to 5V, output connected to PF1 (ADC 1 )

And the definition of the variable voltage?

```char voltage[50];
```

voltage looks OK (but very large for this).

adc_result is declared as an int, but you tell sprintf it's a floating point type (didn't you get a warning?). That will not work.

what do you suggest to fix it ? thanks

like this ?

`double adcA,d,adc_result;`

Quote:

like this ?

I really strongly suggest you take a few steps back and return to a PC only environment and learn the basics of C before trying to use it in anger on an AVR. You are making a lot of very basic and naive mistakes and just guessing at what might work is not the way to go about learning. Get a book and learn the theory, then you can put it into practice.

it gives me a result already but keep coming up from 45 to 255....any ideas ?

Ok, I guess it's not easy on learning C on avr by asking to other people with experience....
thanks anyway...

bianchi77 wrote:
Ok, I guess it's not easy on learning C on avr by asking to other people with experience....
thanks anyway...
bianchi... if you give a man a fish, he'll have a feast for a night. But, if you TEACH a man to fish, he'll eat the rest of his life.

What you are asking for is a 'feast for a night'. I am also a newbie, the experienced programmers here have 'taught me to fish'.

You will be far better served if you 'learn to fish'. This means Follow Instructions! Just because you don't like the answers you get, doesn't mean they are WRONG.

LET THE 'FREAKS' TEACH YOU TO FISH!

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. Antoine de Saint-Exupery (1900 - 1944)

Quote:

Ok, I guess it's not easy on learning C on avr by asking to other people with experience....

The thing is that you can not do it by only asking "other people with experience". You also need to work through things yourself. Reading. Thinking in a structured way. Putting time into it. When you react to help and hints by repeatedly shooting from the hip those "other people" start questioning why they should put energy into trying to help - it might appear as wasted energy to them. When you constantly fail to put energy into reading documentation etc they might feel like they are acting as a private tutor for a lazy person.

Here's an exercise you might try: Whenever you get an answer here, work from that yourself for two hours before asking a follow-up question.

In the end, it is always you that have the ultimate responsibility for your learning.

Having said that, hoping you might take some of it to your heart, here's the thing with calculating the voltage from the ADC reading:

Then you use that raw to compute the voltage by scaling it. THAT computation should result in a floating point value.

Here, you are only trying to output the raw value:

`sprintf(voltage,"%.2f",adc_result);`

And as a float value, to boot. That will not produce any meaningful results.

What you could to to get a better understanding is to actually start by outputting the raw value, AS AN INTEGER VALUE. Then write that down on a piece of paper with a pencil. Then put the AVR and the PC away, and use paper and pencil to compute the voltage by hand. This should hopefully provide some insight into what the computation in C code should look like. If you are not able to do do the computation on paper you will not be able to write code for it anyway.

I will not answer any follow up questions on this post of mine post tonight.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

Antonius, you're making a nuisance of yourself. I got tired of spoonfeeding you, seems others are too. Trying to strike our egos is a waste of time. It usually only works once.

The way to troubleshoot is the binary search technique. Divide and conquer. Bianchi wants to read an a/d channel, and display volts on an lcd. He wants to use the free compiler. Someone needs to write a 1 page program that reads the a/d, and a 1 page program that prints 1.234 on a text lcd. Is that simple enough to get going with?

Imagecraft compiler user

And as a float value, to boot. That will not produce any meaningful results.

What you could to to get a better understanding is to actually start by outputting the raw value, AS AN INTEGER VALUE. Then write that down on a piece of paper with a pencil. Then put the AVR and the PC away, and use paper and pencil to compute the voltage by hand. This should hopefully provide some insight into what the computation in C code should look like. If you are not able to do do the computation on paper you will not be able to write code for it anyway.

I will not answer any follow up questions on this post of mine post tonight.

Am I using the right formula ?

The Value you get back is proportional to the reference that you are using.

10 bits = 1024 steps (0 to 1023)

So the calculation to work out the voltage for your result is:

Result_mV = ( Reference in mV / 1023) * Result

So for a reference of 3.3V (e.g. using Vdd/Vss as ref) and ADC result of 250:
Result = (3300/1023) * 250 = 806mV

bianchi77 wrote:
Am I using the right formula ?

The Value you get back is proportional to the reference that you are using.

10 bits = 1024 steps (0 to 1023)

So the calculation to work out the voltage for your result is:

Result_mV = ( Reference in mV / 1023) * Result

So for a reference of 3.3V (e.g. using Vdd/Vss as ref) and ADC result of 250:
Result = (3300/1023) * 250 = 806mV

Close.

Divide by 1024, not 1023.

Also, do the multiply before the divide, or precision will be lost to integer truncation. Be sure to specify the reference constant as a wide type to avoid overflow in the calculation.

```#define REFERENCE_IN_MV 3300UL
uint16_t result_mv;
result_mv = (REFERENCE_IN_MV * ADC) / 1024;```

You can handle "banker's rounding":

`result_mv = (((2 * REFERENCE_IN_MV * ADC) / 1024) + 1) / 2;`

JJ

 "Experience is what enables you to recognise a mistake the second time you make it." "Good judgement comes from experience.  Experience comes from bad judgement." "Wisdom is always wont to arrive late, and to be a little approximate on first possession." "When you hear hoofbeats, think horses, not unicorns." "Fast.  Cheap.  Good.  Pick two." "We see a lot of arses on handlebars around here." - [J Ekdahl]

Quote:
Am I using the right formula ?

After the 1023->1024 correction suggested by Joey, that will be the voltage at the ADC pin (ADCin), of-course.

For the battery voltage (Vin) you will also have to scale that result to take the voltage divider into consideration.

How are your paper calculations going? If you not only pick the raw ADC value, but also get a reading off the ADC pin with a volt-meter you can check out if your paper calculation comes close. Measure the battery voltage also, and you will have something to check the next step in the calculation against.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

Here's the circuit I use :

The calculation:

So :

Vin = V ADC / 0.1515

Please correct me if I'm wrong,
Thanks

I gave input 5 V but the output gave me 6.73V....is my constant on the calculation missed ?

I change it to d = (adcA / 0.203)/1000; //Vin in Volt

Vin = adcA/0.203.....it's nearly 5V, 5.02V

so there's a gap between my paper and in practice
0.203 - 0.1515 = 0.05148

It's not 0 V when I pulled out the input...??
should I set PF1 as input ? or it sets already by the time I initialize ADC ?

```DDRF = 0xFC;                 // Set PF0 and PF1 as input
```

thanks

It's done, thank you very very much folks

hei-
I see at you want use a Zener 5V1
zener 5V1 begin to conduct from 4V ++ than you will have a parasite "resistance" parallel and make your R bridge wrong.
Just control this.
Thierry

Thierry Pottier

do you mean I make a reverse R bridge ?

Using a zener is fraught with problems. Just look at the data sheet for your particular device. A 400mW device is very different to a 2W device.

ADCW = 1024 * Vmeasure / Vref

Vref is your 'unknown voltage' AVCC (1<<REFS0)

Voltage = 1024 * 1.23V / ADCW

You can either use f-p and have zero brain-ache:

`float Voltage = 1259.5 / ADCW;`

Or you can work in integers e.g. millivolts

millivolts = 1024 * 1230 / ADCW

`uint16_t mV = (1259520uL) / ADCW;`

Note that the Compiler will use 32-bit division to get the result. And then convert the result to 16-bit.

I would guess that using a regular linear regulator would give you a reliable 5.0V

David.

p.s. I am impressed that you have done the paper calculation for the zener. I have not checked it. However you need to calculate the currents in the 10k and the zener.
If you allow for 10-50mA through the zener, you get the 'nominal' 5.1V
You get 0.5mA through the 10k
You need VIN of 565V to get 10mA through the 56k

If you calculate for a 0.5mA zener current:
The zener voltage is between 2.5V and 4V depending on device.
VIN is about 60V to achieve 1mA through the 56k

Using a regular 7805 chip is far easier!

It does the job,
have a look
http://youtu.be/lNYGRphX3d8

So the purpose was to monitor a lead-acid car battery !
You were just using a 0.1515 voltage divider to produce an in-range voltage for the ADC.

The divider will produce 0.1515 * 13.29V = 2.01V
This will be fine for the AVCC = 5.0V reference.

The zener diode is only there to limit the voltage.
A nominal 5.1V zener only takes < 1uA at 2V.

The voltage divider takes about 200uA for 13.29V

ADCW = 1024 * Vmeasure / Vref
Vmeasure = ADCW * Vref / 1024