Forum: Projekte & Code Ladungsmessgerät


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Stefan G. (steg13)


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.

von Stefan G. (steg13)


Angehängte Dateien:

Lesenswert?

Hier noch der Schaltplan

von Stefan G. (steg13)


Lesenswert?

Hier das Hauptprogramm direkt als Text (lässt sicht nicht anhängen)
************
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <stdlib.h>
4
#include <stdint.h>
5
#include <Stdio.h>
6
#include "BinaryNr.h"
7
8
#define F_CPU            9600000     /* Quart 6,6 Mhz */
9
#define UART_BAUD_RATE      9600      /* 9600 baud */
10
#define UART_BAUD_SELECT (F_CPU/(UART_BAUD_RATE*16l)-1)
11
#define Prescaler 1024;
12
#define startwert  65537 - F_CPU/Prescaler; //Zähler Startwert, Überlauf bei 65536
13
14
#include "uart_routinen.c"
15
#include "lcd.h"
16
#include "lcd.c"
17
#include "sonst_routinen.c"
18
19
#define u8 unsigned char
20
#define u16 unsigned int
21
22
//Variablen
23
u8 y;
24
u8 sekundenH=0;
25
u8 minutenH=0;
26
u8 stunden=0;
27
u8 sekundenL=0;
28
u8 minutenL=0;
29
30
char s[4];  //hilfsvariablen
31
char z[4];
32
33
34
u16 adWert; // AD-Wert 
35
u16 strom;  // Strom 
36
u16 summe;  // Strom aussummieren
37
u16 mah;    // Umgerechnet 
38
39
40
41
/* Unterprogramme */
42
/* INIT */
43
44
void port_init(void){       // initialize ports 
45
  DDRC  = 0x20;             // PORTA als Eingang BIT5 (LED) Ausgang
46
}
47
48
void timer_init(void){      // timer 0 overflow 
49
  TCCR1B = B00000101;       // Prescaler auf 1024 stellen
50
  TIMSK  |= (1 << TOIE1);   // Timer 1 Overflow Interrupt enable
51
  TCNT1 = startwert;        // vorbesetzen (zählt bis FFFF)
52
}
53
54
void ad_init(void){         // initialize ad-wandler 
55
  ADMUX = 0x00;             // Kanal0
56
  ADMUX |= (1<<REFS1) | (1<<REFS0); //interne Referenzspannung 2,5V nutzen 
57
  ADCSR=B11000111;          // enable ADC
58
}
59
60
//lese ad0 (Strom)
61
int leseAD(void)
62
{ 
63
  uint16_t wert;
64
  uint8_t  highwert;
65
  ADCSR |= (1<<ADSC);              // eine Wandlung starten
66
  while ( ADCSR & (1<<ADSC) ) {;}  // auf Abschluss der Konvertierung warten
67
  wert     = ADCL;                 // Low  auslesen   
68
  highwert = ADCH;                 // High auslesen
69
  wert = wert + highwert*256;//   
70
  return wert;
71
}
72
73
74
//Interrupt wenn Zeichen per uart empfangen (wird hier nicht gebraucht)
75
ISR(SIG_UART_RECV)      
76
{
77
    y = UDR;                // lese byte aus UART
78
}
79
80
//Anzeige der Ladung auf LCD
81
void zeige_Ladung(u16 mah){
82
  itoa(mah,s,10);           // wert in ACSII  
83
  lcd_puts("Ladung: ");
84
  lcd_puts(s);                    
85
  lcd_puts(" mAh ");
86
}
87
88
//Anzeige der Zeit auf LCD
89
void zeige_Zeit(void){      //hh,mm,ss berechnen
90
  sekundenL++;              // Sekunden zählen
91
  if(sekundenL==10){
92
    sekundenH++;sekundenL=0;}
93
  if(sekundenH==6){
94
  minutenL++;sekundenH=0;}
95
  if(minutenL==10){
96
    minutenH++;minutenL=0;}
97
  if(minutenH==6){
98
    stunden++;minutenH=0;}
99
                // auf LCD ausgeben   
100
  itoa(stunden,s,10);lcd_puts(s);lcd_puts(":");
101
  itoa(minutenH,s,10);lcd_puts(s);
102
  itoa(minutenL,s,10);lcd_puts(s);lcd_puts(":");
103
  itoa(sekundenH,s,10);lcd_puts(s);
104
  itoa(sekundenL,s,10);lcd_puts(s);
105
}
106
107
//Anzeige aktueller Strom auf LCD
108
void zeige_Strom(u16 strom){
109
  itoa(strom,s,10);          
110
  lcd_puts(s);                    
111
  lcd_puts(" mA");  
112
}
113
114
115
//Timer Interrupt - genau jede Sekunde
116
SIGNAL (TIMER1_OVF_vect)      // Timer1 overflow 
117
{
118
  cli();
119
  TCNT1 = startwert;          // Vorbesetzen (1s)
120
121
  adWert=leseAD();            // lese Spannungswert    
122
  strom = adWert;             // 1024 = 1024mA (1 zu 1)
123
124
  itoa(strom,s,10);           // wert in ACSII  
125
  uart_puts(s);               // auf uart ausgeben  
126
  uart_puts(" ");             // auf uart ausgeben  
127
128
  summe = summe + strom;      // Strom aussummieren
129
130
  if (summe >= 3600){         // in mAh umrechnen   
131
    mah++;                    // wenn 1mAh voll +1   
132
  summe = summe % 3600;       // Rest weiterzählen
133
  }                          
134
135
136
  lcd_clrscr();             // 1.Zeile
137
  zeige_Ladung(mah);        // Ladung anzeigen  
138
139
  lcd_gotoxy(0,1);          // 2. Zeile
140
  zeige_Zeit();             // Zeit anzeigen
141
142
  lcd_gotoxy(10,1);         // 2. Zeile Stelle 10
143
  zeige_Strom(strom);       // aktuellen Strom anzeigen
144
145
  sei();                    // enable interrupts
146
}
147
148
149
150
/*********************/
151
int main()
152
{
153
  port_init();          // alles initialisieren
154
  uart_init();
155
  ad_init();
156
  timer_init();
157
  sei();                // enable interrupts  
158
  lcd_init(LCD_DISP_ON);// initialisiere display, cursor off 
159
  for(;;){}             // tut nix, Endlosschleife
160
}

von Stefan G. (steg13)


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/Reflexlader/Reflexlader_m.html

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.

von Dirk (Gast)


Lesenswert?

Hallo, vielleicht kommt deine Abweichung hier her.

1
//lese ad0 (Strom)
2
int leseAD(void)
3
{ 
4
  uint16_t wert;
5
  uint8_t  highwert;
6
  ADCSR |= (1<<ADSC);              // eine Wandlung starten
7
  while ( ADCSR & (1<<ADSC) ) {;}  // auf Abschluss der Konvertierung warten
8
  wert     = ADCL;                 // Low  auslesen   
9
  highwert = ADCH;                 // High auslesen
10
  wert = wert + highwert*256;//   
11
  return wert;
12
}

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

von Stefan G. (steg13)


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.

von Pieter (Gast)


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

von Stefan G. (steg13)


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.

von Sergej S. (sergo)


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

von Stefan G. (steg13)


Lesenswert?

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

von Frank N. (betafrank)


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

von Stefan G. (steg13)


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"

von Frank N. (betafrank)


Lesenswert?

Ich meine AVCC, nicht AREF ;-)

von Stefan G. (steg13)


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?

von fieser Rahul (auch Oskar genannt) (Gast)


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.

von Stefan G. (steg13)


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

von Frank N. (betafrank)


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

von Stefan G. (steg13)


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

von Bernd B. (behbeh)


Lesenswert?

Wo ist denn die Datei "BinaryNr.h" zu finden?
bb

von Stefan G. (steg13)


Lesenswert?


von Klaus2 (Gast)


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.

von cdg (Gast)


Lesenswert?

Hi Stefan,

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

cdg

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.