www.mikrocontroller.net

Forum: Projekte & Code Ladungsmessgerät


Autor: Stefan Gemmel (steg13)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Zum Messen von Batterien und Akkus (laden oder entladen)

Anlass war, dass Aldi plötzlich neue Mignonzellen hat.
Ich wollte wissen ob die für das Geld die gleiche Kapazität bieten.
Ein Messgerät in der Art konnte ich nicht finden.
Da ich gerade angefangen habe AVR in C zu programmieren
(nachdem die Leuchtdiode geblinkt hat), bot sich das Projekt geradezu 
an.

Funktion:
Bildet ein Integral über den gemessenen Strom.
Zeigt das Ergebnis in mAh an.
Ausserdem die Zeit und den aktuellen Strom.
Anzeige über RS232 möglich
Man kann z.B. einen Akku über eine Last entladen
und den Strom mit diesem gerät messen.
Hinterher weiss man wieviel drin war.
Der Spannungsabfall ist so gering (z.B. 20mV bei 200mA),
dass vermutlich auch Akkuladegeräte fast korrekt funktionieren
und man somit auch Ladungen beim Aufladen messen kann

Hardware:
Das Gerät besitzt ledigleich 2 Anschlüssen.
Zwischen beiden ist ein Widerstand von 0,1 Ohm.
Der Spannungsabfall wird verstärkt und mit einem Mega8 gemessen.

Software:
Ich habe es mit dem AVR-Studio in GCC geschrieben (kein Makefile)

Ich verwende einen Takt von 9,6MHz (ist Zufall den hatte ich gerade da).
Achtung: Externer Takt -> Fuse Bits entsprechedn setzen
Das Hauptprogramm macht eigentlich nichts.
Timer 1 löst jede Sekunde einen Interrupt aus.
Der Strom wird gemessen bis (1024 mA)
Man könnte auch andere Faktoren einstellen ("strom = adWert * Faktor;")
Den Strom gebe ich zudem als ASCII auf die serielle Schnittstelle.
Dann wird der Wert je Sekunde aufaddiert und durch 3600 geteilt.
Das Ergebnis sind die mAh.
Der LCD Treiber stammt von Peter Fleury.

Probleme:
sehr kleine Ströme (unter 20mA) werden nicht korrekt gemessen,
da der OP in diesem Bereich nicht linear arbeitet.
Nachdem ich ihm eine negative Versorgungsspannung spendiert hatte,
wurde es kaum besser. Ein CA3140 brauchte auch kein besseres Ergebnis.
Ausserdem habe ich noch 2 Schaltungen getestet die im Forum empfohlen 
wurden.
Aber keine hatte eine 100% ige Linearität.
So dass ich beschlossen habe mit einer geringen Abweichung (bis zu 3mA) 
zu leben.

Die Platine habe ich mit Eagle gezeichnet. Leider musste ich 
nachträglich
am Layout einiges ändern, weil ich Fehler im Schaltplan hatte.
Die Fehler habe ich Layout nicht mehr korrigiert. Deshalb nur der 
Schaltplan.
************************
Das Hauptprogramm habe ich mal angehängt

Alle Files habe ich auch auf meine HP gestellt:
http://www.stegem.de/Ladungsmesser/

Ich wäre dankbar für viele Verbesserungsvorschläge, weil ich an dem 
Projekt ja lernen will.

Autor: Stefan Gemmel (steg13)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hier noch der Schaltplan

Autor: Stefan Gemmel (steg13)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hier das Hauptprogramm direkt als Text (lässt sicht nicht anhängen)
************
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h>
#include <stdint.h>
#include <Stdio.h>
#include "BinaryNr.h"

#define F_CPU            9600000     /* Quart 6,6 Mhz */
#define UART_BAUD_RATE      9600      /* 9600 baud */
#define UART_BAUD_SELECT (F_CPU/(UART_BAUD_RATE*16l)-1)
#define Prescaler 1024;
#define startwert  65537 - F_CPU/Prescaler; //Zähler Startwert, Überlauf bei 65536

#include "uart_routinen.c"
#include "lcd.h"
#include "lcd.c"
#include "sonst_routinen.c"

#define u8 unsigned char
#define u16 unsigned int

//Variablen
u8 y;
u8 sekundenH=0;
u8 minutenH=0;
u8 stunden=0;
u8 sekundenL=0;
u8 minutenL=0;

char s[4];  //hilfsvariablen
char z[4];


u16 adWert; // AD-Wert 
u16 strom;  // Strom 
u16 summe;  // Strom aussummieren
u16 mah;    // Umgerechnet 



/* Unterprogramme */
/* INIT */

void port_init(void){       // initialize ports 
  DDRC  = 0x20;             // PORTA als Eingang BIT5 (LED) Ausgang
}

void timer_init(void){      // timer 0 overflow 
  TCCR1B = B00000101;       // Prescaler auf 1024 stellen
  TIMSK  |= (1 << TOIE1);   // Timer 1 Overflow Interrupt enable
  TCNT1 = startwert;        // vorbesetzen (zählt bis FFFF)
}

void ad_init(void){         // initialize ad-wandler 
  ADMUX = 0x00;             // Kanal0
  ADMUX |= (1<<REFS1) | (1<<REFS0); //interne Referenzspannung 2,5V nutzen 
  ADCSR=B11000111;          // enable ADC
}

//lese ad0 (Strom)
int leseAD(void)
{ 
  uint16_t wert;
  uint8_t  highwert;
  ADCSR |= (1<<ADSC);              // eine Wandlung starten
  while ( ADCSR & (1<<ADSC) ) {;}  // auf Abschluss der Konvertierung warten
  wert     = ADCL;                 // Low  auslesen   
  highwert = ADCH;                 // High auslesen
  wert = wert + highwert*256;//   
  return wert;
}


//Interrupt wenn Zeichen per uart empfangen (wird hier nicht gebraucht)
ISR(SIG_UART_RECV)      
{
    y = UDR;                // lese byte aus UART
}

//Anzeige der Ladung auf LCD
void zeige_Ladung(u16 mah){
  itoa(mah,s,10);           // wert in ACSII  
  lcd_puts("Ladung: ");
  lcd_puts(s);                    
  lcd_puts(" mAh ");
}

//Anzeige der Zeit auf LCD
void zeige_Zeit(void){      //hh,mm,ss berechnen
  sekundenL++;              // Sekunden zählen
  if(sekundenL==10){
    sekundenH++;sekundenL=0;}
  if(sekundenH==6){
  minutenL++;sekundenH=0;}
  if(minutenL==10){
    minutenH++;minutenL=0;}
  if(minutenH==6){
    stunden++;minutenH=0;}
                // auf LCD ausgeben   
  itoa(stunden,s,10);lcd_puts(s);lcd_puts(":");
  itoa(minutenH,s,10);lcd_puts(s);
  itoa(minutenL,s,10);lcd_puts(s);lcd_puts(":");
  itoa(sekundenH,s,10);lcd_puts(s);
  itoa(sekundenL,s,10);lcd_puts(s);
}

//Anzeige aktueller Strom auf LCD
void zeige_Strom(u16 strom){
  itoa(strom,s,10);          
  lcd_puts(s);                    
  lcd_puts(" mA");  
}


//Timer Interrupt - genau jede Sekunde
SIGNAL (TIMER1_OVF_vect)      // Timer1 overflow 
{
  cli();
  TCNT1 = startwert;          // Vorbesetzen (1s)

  adWert=leseAD();            // lese Spannungswert    
  strom = adWert;             // 1024 = 1024mA (1 zu 1)

  itoa(strom,s,10);           // wert in ACSII  
  uart_puts(s);               // auf uart ausgeben  
  uart_puts(" ");             // auf uart ausgeben  

  summe = summe + strom;      // Strom aussummieren

  if (summe >= 3600){         // in mAh umrechnen   
    mah++;                    // wenn 1mAh voll +1   
  summe = summe % 3600;       // Rest weiterzählen
  }                          


  lcd_clrscr();             // 1.Zeile
  zeige_Ladung(mah);        // Ladung anzeigen  

  lcd_gotoxy(0,1);          // 2. Zeile
  zeige_Zeit();             // Zeit anzeigen

  lcd_gotoxy(10,1);         // 2. Zeile Stelle 10
  zeige_Strom(strom);       // aktuellen Strom anzeigen

  sei();                    // enable interrupts
}



/*********************/
int main()
{
  port_init();          // alles initialisieren
  uart_init();
  ad_init();
  timer_init();
  sei();                // enable interrupts  
  lcd_init(LCD_DISP_ON);// initialisiere display, cursor off 
  for(;;){}             // tut nix, Endlosschleife
}

Autor: Stefan Gemmel (steg13)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nachdem das Gerät nun 2 Tage gelaufen ist, erste Ergebnisse.

Alle Alkaline Batterien haben ähnliche Werte (Mignon um die 2Ah), also 
kann man auch billige kaufen (Aldi und Lidl sind gleich)
Die Anschaffung des Gerätes zu diesem Zweck ist uninteressant.

Ich habe eine Mignonzelle mit meinem Reflexlader aufgeladen.
Der hier: 
http://www.klaus-leidinger.de/mp/RC-Elektronik/Ref...

Durch den Messtakt von 1s ergaben sich anfangs große Abweichungen, da 
der Reflexlader wohl nur Ladeimpulse schickt und dann wieder misst.
Bei längerer Laufzeit, hat man aber korrekte Werte, der Fehler gleicht 
sich aus.
Am Ende ergab sich eine Abweichung der Kapazität von 10%. Ich vermute 
aber eher dass der Reflexlader nicht richtig abgeglichen ist. Muss mal 
reingucken.
Gestört hat der Spannungsabfall den Reflexlader nicht.

Autor: Dirk (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo, vielleicht kommt deine Abweichung hier her.

//lese ad0 (Strom)
int leseAD(void)
{ 
  uint16_t wert;
  uint8_t  highwert;
  ADCSR |= (1<<ADSC);              // eine Wandlung starten
  while ( ADCSR & (1<<ADSC) ) {;}  // auf Abschluss der Konvertierung warten
  wert     = ADCL;                 // Low  auslesen   
  highwert = ADCH;                 // High auslesen
  wert = wert + highwert*256;//   
  return wert;
}

Atmel schlaegt zwei Messungen beim ersten mal vor um ein richtigen Wert 
zuerhalten.

Autor: Stefan Gemmel (steg13)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für den Hinweis
ich werde auf jeden Fall mal 2 Messungen machen und den Durchschnitt 
bilden.

Inzwischen habe ich mal versucht den Reflexlader abzugleichen, was nicht 
ganz einfach ist, da er ja ständig kurz entlädt um zu messen.
Ich denke meine Schaltung ist genau. Wenn ich 1Stunde genau 1A fliessen 
lasse stimmt die Anzeige.

Autor: Pieter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
moin moin,


der LM358 ist für Messaufgaben wegen seines Offsets ( ca. 2mV )schlecht 
geeignet. Entweder es kommt noch ein Offsetabgleich hinzu oder gleich 
einen besseren OPV. Bei so etwas verwende ich den LTC1049, Offset <10µV.

mfg
Pieter

Autor: Stefan Gemmel (steg13)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
tolles Teil der LTC1049 :-)
steht auf meiner Bestellliste drauf. Ich werde ihn aber für genauere 
Messungen aufheben. Beim Ladungsmessgerät kommt es mir nicht auf 
Genauigkeit an.

Autor: Sergej Schikowski (sergo)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo, ich habe da einen Fehler gefunden:

  summe = summe + strom;      // Strom aussummieren

  if (summe >= 3600){         // in mAh umrechnen
    mah++;                    // wenn 1mAh voll +1
  summe = summe % 3600;       // Rest weiterzählen
  }

Es muss doch "summe = summe - 3600" sein. Ansonsten egibt es für mich 
keinen Sinn. Es mag bei dir zwar so funktioniert haben, aber mit einem 
kleinen Fehler.

Ich hoffe ich habe da kein Misst geschrieben.
Bin gerade am Überlegen mir einen Akku Tester zu bauen. Da habe ich mir 
deinen Code angeschaut.

MfG

Autor: Stefan Gemmel (steg13)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
% ist Modulo Division. Das heißt die 3600 fallen weg, der Rest bleibt.
Ginge wohl auch mit minus.

Autor: Frank N. (betafrank)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Moin,

schönes Projekt und als Einstieg gut gelungen!

Modulo funktioniert natürlich, aber wenn man mit Resourcen (Flash, Zeit) 
geizen müsste, würde man Modulo (bzw. allgemein Division) möglichst 
durch Subtraktion oder Zuweisung "summe=0" ersetzen.

Laut Schaltplan kriegt AVCC keine Spannung!?!

Gruß Frank

Autor: Stefan Gemmel (steg13)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Laut Schaltplan kriegt AVCC keine Spannung!?!

scheint aber zu funktionieren
Ich habe REFS0 und REFS1 auf 1 gesetzt.
Im Datenblatt steht:
"Internal Voltage Reference (2.56V) with external Capacitor at AREF pin"

Autor: Frank N. (betafrank)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich meine AVCC, nicht AREF ;-)

Autor: Stefan Gemmel (steg13)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Frank N. wrote:
> Ich meine AVCC, nicht AREF ;-)

Sorry
Ich habe gerade nachgemessen AVCC liegt auf +5V. Ich frage mich nur 
warum. Ich habe keine Verbindung. Muss irgendwie intern geschaltet sein?

Autor: fieser Rahul (auch Oskar genannt) (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Atmel schlaegt zwei Messungen beim ersten mal vor um ein richtigen Wert
>zuerhalten.
Wo steht das?
Das betrifft nur das Umschalten der Referenzen, nicht die AD-Wandlung 
eines einzelnen Kanals (der ja offensichtlich nicht gewechstelt wird).

>Ich habe gerade nachgemessen AVCC liegt auf +5V. Ich frage mich nur
>warum. Ich habe keine Verbindung. Muss irgendwie intern geschaltet sein?
Interessant...
Übrigens solltest du laut Datenblatt bei ADC-Benutzung ein LC-Glied an 
dem Pin haben.

Autor: Stefan Gemmel (steg13)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>>Ich habe gerade nachgemessen AVCC liegt auf +5V. Ich frage mich nur
>>warum. Ich habe keine Verbindung. Muss irgendwie intern geschaltet sein?
> Interessant...
> Übrigens solltest du laut Datenblatt bei ADC-Benutzung ein LC-Glied an
> dem Pin haben.

ja weiss ich. Aber wieso geht es? ich habe jetzt sogar auf der Platine 
nachmessen keine Verbindung zwischen + und AVCC

Autor: Frank N. (betafrank)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hab hier grad nen ATMega8: VCC und AVCC sind niederohmig (~5ohm) 
miteinander verbunden und "Diodenprüfung" ergibt Spannungsdifferenz von 
0,08V in beide Richtungen. Also nix (parasitäre) Diode oder so. Viel 
nutzen dürfte das LC-Glied also nicht...

Autor: Stefan Gemmel (steg13)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ich habe mir jetzt mal die Werte anzeigen lassen.
Ohne AVCC sind die Messungen falsch.
Mit Referenz AVCC nimmt er 3,4V statt 5V
Mit Int. Ref 2V statt 2,5V

Bei meiner Schaltung ist das egal, weil ich sie entsprechend abgeglichen 
habe.

Hab gerade gemerkt, dass in der Schaltung ein Mega8 drin ist. Obige 
Messung war mit Tiny26

Autor: Bernd Bömer (behbeh)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wo ist denn die Datei "BinaryNr.h" zu finden?
bb

Autor: Stefan Gemmel (steg13)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

Autor: Klaus2 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
...Hi, nun werd ich mein KapMessgerät auch mal fertig stellen, scheint 
ja gerade in Mode zu sein.

Ist die ISR nicht etwas "voll"?! Sollte man doch für gewöhnlich in eine 
Unterfkt auslagern, oder?

Wofür ist die Diode am Ausgangsspgsteiler des OP?

Gruß, Klaus.

Autor: cdg (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi Stefan,

könntest Du die Ergebnisse Deiner Vergleichsmessung
verschiedener Discounter-Batterien mal hier einstellen.

cdg

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.