Moin moin
ich bin dabei ein Kapazitätsmessgerät zu bauen, und habe auch schon
einstellbare Konstatstromsenken gebaut, die mir immer exakt 1.3A
entladen, nun möchte ich gerne per AVR die Spannung kontrollieren und ab
2.9V abschalten.
Da ich mit einem AVR 3 überwachen möchte weiß ich nicht ganz wie ich das
am besten angehe. Das ganze soll so funktionieren, dass ich 3 Taster
habe, pro Zelle einen Taster, drücke ich Taster 1 soll es anfangen zu
entladen, und der Timer losstarten, alle Sekunden soll er mal die
Spannung messen, das selbe bei Zelle 2 und 3.
Wie mach ich das am besten mit dem Timer?
Ich habe an der Konstantstromsenke schon einen Eingang vorgestehen, wo
ich die Entladung abschalten kann, soll heißen, zb. solang am PD0 ne 1
anliegt, entlädt garnichts, ab Tasterdruck liegt ne 0 an und es entlädt,
sobald die Spannung der Zelle auf 2.9V gefallen ist, kommt wieder ne 1
damit sie nicht tiefentladen wird.
Nun weiß ich eben nicht wie ich das mit dem Timer mache, mach ich einen
Haupttimer, der aktiviert wird beim drücken des ersten Tasters, und bei
Taster 2 und 3 speichere ich den jeweiligen Timerwert in eine variable
und rechne danach zurück um auf den Wert zu kommen?
Das ergebnis würde ich dann gerne an einen PC senden, dasheißt sobald
bei einer Zelle die 2.9V erreicht sind, sendet er die so errechnete
Kapazität an den PC.
Danke
Schönen Gruß aus Österreich,
Znup
Naja ich würde jetzt einfach 3 globale Variablen anlegen wo der
Entladestatus für jeden Akku gespeichert ist.
Also wenn der Taster 1 gedrückt wird, setzt du die Variable "stat_akku1
= 1", für Taster 2 "stat_akku2 = 1" etc.
In der Timer ISR überprüfst du dann einfach welche Variable 1 ist und
liest dann den entsprechenden ADC-Kanal ein.
Klar alle Spannungen werden dann Synchron aktualisiert, aber das ist
doch nicht schlimm, oder?
Znup schrieb:> Nun weiß ich eben nicht wie ich das mit dem Timer mache, mach ich einen> Haupttimer, der aktiviert wird beim drücken des ersten Tasters,
Würd ich gar nicht machen.
Den Timer würde ich ständig mitlaufen lassen.
Der Timer liefert einen Basistakt von zb 10ms.
Dazu hast du noch 3 Zählvariablen, die jeweils in der Timer-ISR von 100
auf 0 runtergzählt werden. Ist einer der Zähler 0 geworden, sie ist für
den jeweiligen Zählkanal damit 1 Sekunde vergangen. das Ist dann in der
Hauptschleife das Signal, den jeweiligen ADC Kanal zu sampeln und die
Spannung der Zelle festzustellen. Nach dem Sampeln, wird dann der Zähler
(sofern nicht überhaupt abgeschaltet werden soll) wieder auf 100 gesetzt
und das Entladen geht weiter.
Die Zählvariable hat 3 'Zustände'
* eine Zahl größer 0 bedeutet, dass die jeweilige Zeit nach abgewartet
werden muss, damit 1 Sekunde um ist
* eine 0 bedeutet, dass die Sekunde abgelaufen ist
* eine -1 bedeutet, dass dieser Enladekanal überhaupt inaktiv ist
1
volatileint8_tChannel1Wait;
2
volatileint8_tChannel2Wait;
3
volatileint8_tChannel3Wait;
4
5
ISR(....)// wird alle 10ms aufgerufen
6
{
7
if(Channel1Wait>0)
8
Channel1Wait--;
9
10
if(Channel2Wait>0)
11
Channel2Wait--;
12
13
if(Channel3Wait>0)
14
Channel3Wait--;
15
}
16
17
intmain()
18
{
19
....
20
// Timer so einrichten dass alle 10ms die ISR aufgerufen wird
21
22
// Momentan ist keine Entladung aktiv
23
Channel1Wait=-1;
24
Channel2Wait=-1;
25
Channel3Wait=-1;
26
27
sei();// und los gehts
28
29
while(1){
30
31
if(Taste1gedrückt){
32
Kapazität1=0;
33
Channel1Wait=100;// 100 * 10 ms -> 1 Sekunde
34
Entladung_Kanal_1einschalten;
35
}
36
37
if(Channel1Wait==0){
38
Entladung_Kanal_1ausschalten;
39
ADCMessungKanal1vornehmen;
40
41
if(Messwert<Abschaltspannung)
42
Channel1Wait=-1;// Enladung ist fertig
43
44
else{
45
Kapazität+=Konstantefür1.3Aund1Sekunde;
46
Channel1Wait=100;// die nächste Sekunde beginnt
47
Entladung_Kanal_1einschalten;
48
}
49
}
50
51
if(Taste2gedrückt){
52
....
53
}
54
55
if(Channel2Wait==0){
56
....
57
}
58
59
if(Taste3gedrückt){
60
....
61
}
62
63
if(Channel3Wait==0){
64
....
65
}
66
}
67
}
Kanal 2 und Kanal 3 gehen völlig analog.
Wenn sich herausstellt, dass der Zeitverlust durch die ADC Messung nicht
akzeptabel ist, dann müsste man das ebenfalls entflechten und so ein
bauen, dass keine oder kaum Zeit verloren geht.
Ach ja:
Die ISR ist dann auch noch ein guter Platz um die Dannegger Entprellung
zur Tastenabfrage einzubauen. Den Teil mit dem Nachladen des Timers
kannst du dabei getrost aus der Entprellung rauslassen. Wenn die ISR so
alle 10 bis 15ms aufgerufen wird, ist das perfekt. Der Rest ist dann nur
Zahlenspielerei um wieder auf die 1 Sekunde zu kommen.
Znup schrieb:> Hallo!>> Wow das sieht verdammt gut aus, allerdings verstehe ich nicht, wieso ich> dazwischen die entladung ausschalten soll, um die Spannung zu messen?
Aus demselben Grund warum du auch beim Laden die Ladespannung abschalten
musst. Du misst sonst die Höhe der Ladespannung bzw. bei dir die 'Tiefe'
der Entladespannung und nicht die Zellenspannung selber.
Damit eine Zelle entlädt, musst du sie mit einem niedrigeren Potential
verbinden ... einer Senke in die der Strom reinläuft. Du misst sonst die
Höhe der Spannung dieser Senke, wenn du sie nicht abschaltest.
Wenn du natürlich die Abschaltspannung unter Last festlegen willst, dann
lässt die die Konstantstromsenke eingeschaltet beim messen. Die
simuliert dann deine Last.
Znup schrieb:> Aja !>> Wow danke, aber hab ich dann nicht ein paar ms pro Sekunden Fehler, weil> ich in der Zeit nicht entlade?
Ja. Allerdings bewegt sich das nicht im Millisekundenbereich sondern
darunter. Solange dauert eine ADC Messung dann auch wieder nicht (und
wie lange die dauert ist bekannt).
Die Frage ist wie gravierend das ist.
Kapazitätsangaben von Akkus sind sowieso eher mehr Schätzwerte als exakt
definiert.
Interessanter als ob der angegebene Wert jetzt 825 oder 829mAh ist, ist
ja die Fragestellung: Wie entwickelt sich der Akku über seine Lebenszeit
gesehen. Im Vergleich zum letzten mal: hat der Wert abgenommen und wenn
ja um wieviele Prozentpunkte.
naja 4mAh ist natürlich nicht schlimm, aber es sollte bei einem 1300mAh
Akku hald schon auf mehr als 100mAh genau gehen, aber ich denke der
Fehler wird eh weniger als 10mAh sein
Znup schrieb:> naja 4mAh ist natürlich nicht schlimm, aber es sollte bei einem 1300mAh> Akku hald schon auf mehr als 100mAh genau gehen, aber ich denke der> Fehler wird eh weniger als 10mAh sein
Weniger. Weit weniger.
Mann kann ja auch das zwischenzeitliche abschalten/einschalten in die
ISR verlagern. Dann stimmt die Zeitdauer vom jeweiligen Starten einer
Ladung bis zum jeweiligen Abschalten nach 1 Sekunde auf ein paar µs
genau, egal was in der Zwischenzeit im main passiert.
1
ISR(....)// wird alle 10ms aufgerufen
2
{
3
if(Channel1Wait>0){
4
5
if(ChannelWait1==100)
6
Kanaleinschalten;
7
8
Channel1Wait--;
9
10
if(Channel1Wert==0)
11
Kanalausschalten;
12
}
13
14
....
15
}
jetzt ist die Genauigkeit der Zeit nur noch durch die Genauigkeit
limitiert mit der der Timer tickt plus einem winzig kleinen Jitter in
der Gesamtzeit, der aus den unterschiedlichen Pfaden durch die if
resultiert.
So ich hab das ganze mal so programmiert, kannst du mal drüberschauen ob
das so past?
Interrupt löst leider alle 9,92ms aus.
Und Tastenentprellung hab ich leider noch nicht, da ich mich da noch
einlesen muss, hab ich bisher leider noch nie gebraucht.
Ebenso hab ich das ganze noch nicht im Interrupt stehen, sondern normal
im Hauptprogramm.
1
#include<avr/io.h>
2
#include<avr/interrupt.h>
3
4
5
volatileint8_tChannel1Wait;
6
volatileint8_tChannel2Wait;
7
volatileint8_tChannel3Wait;
8
9
ISR(TIMER2_COMP_vect)// wird alle 10ms aufgerufen
10
{
11
if(Channel1Wait>0)
12
Channel1Wait--;
13
14
if(Channel2Wait>0)
15
Channel2Wait--;
16
17
if(Channel3Wait>0)
18
Channel3Wait--;
19
}
20
21
/* ADC initialisieren */
22
voidADC_Init(void)
23
{
24
25
uint16_tresult;
26
27
ADMUX=(1<<REFS0);// AVCC Referenzspannung nutzen
28
ADCSRA=(1<<ADPS2);// Frequenzvorteiler
29
ADCSRA|=(1<<ADEN);// ADC aktivieren
30
31
/* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
32
also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
33
34
ADCSRA|=(1<<ADSC);// eine ADC-Wandlung
35
while(ADCSRA&(1<<ADSC));// auf Abschluss der Konvertierung warten
36
/* ADCW muss einmal gelesen werden, sonst wird Ergebnis der nächsten
37
Wandlung nicht übernommen. */
38
result=ADCW;
39
}
40
41
/* ADC Einzelmessung */
42
uint16_tADC_Read(uint8_tchannel)
43
{
44
// Kanal waehlen, ohne andere Bits zu beeinflußen
45
ADMUX=(ADMUX&~(0x1F))|(channel&0x1F);
46
ADCSRA|=(1<<ADSC);// eine Wandlung "single conversion"
47
while(ADCSRA&(1<<ADSC))// auf Abschluss der Konvertierung warten
48
;
49
returnADCW;// ADC auslesen und zurückgeben
50
}
51
52
53
intmain()
54
{ADC_Init();
55
56
DDRB=(1<<DDB3)|(1<<DDB4)|(1<<DDB5);
57
DDRB&=~((1<<DDB0)|(1<<DDB1)|(1<<DDB2));
58
59
PORTB=0xFF;
60
61
// Timer 0 konfigurieren
62
TCCR2=(1<<WGM21);// CTC Modus
63
TCCR2=(1<<CS00)|(1<<CS01);// Prescaler 64
64
// ((1000000/64)/100) = 156
65
OCR2=156-1;
66
67
// Compare Interrupt erlauben
68
TIMSK|=(1<<OCIE2);
69
70
// Momentan ist keine Entladung aktiv
71
72
uint8_tKapazitat1;
73
74
Channel1Wait=-1;
75
Channel2Wait=-1;
76
Channel3Wait=-1;
77
78
sei();// und los gehts
79
80
while(1)
81
{
82
83
if(!(PINB&(1<<PINB0)))// Taste1 gedrückt
84
{
85
Kapazitat1=0;
86
Channel1Wait=100;// 100 * 10 ms -> 1 Sekunde
87
PORTB&=~(1<<PB3);//Entladung_Kanal_1 einschalten;
88
}
89
90
if(Channel1Wait==0)
91
{
92
PORTB|=(1<<PB3);//Entladung_Kanal_1 ausschalten;
93
94
95
if(ADC_Read(0)<593)//ADC Messung Kanal 1 vornehmen und kleiner 2.9V;
Mit deiner Methode die Entladung auszuschalten, brauch ich auch keine
Angst haben, dass während der Messung auf den Leitungen soviel Spannung
abfällt, das mir die Messung verfälscht wird richtig?
MfG
Znup
Znup schrieb:> Mit deiner Methode die Entladung auszuschalten, brauch ich auch keine> Angst haben, dass während der Messung auf den Leitungen soviel Spannung> abfällt, das mir die Messung verfälscht wird richtig?
Das würde ich sagen, ist sicherlich kein großes Problem. Den
Spannungsabfall kannst du vernachlässigen.
Ich würd das ein/AUsschalten trotzdem in die ISR verlagern und so dem
Akku noch ein wenig Zeit geben (wenn auch nicht viel) um sich nach dem
Entladepuls ein wenig zu erholen, ehe dann gemessen wird.
/* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
41
also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
42
43
ADCSRA|=(1<<ADSC);// eine ADC-Wandlung
44
while(ADCSRA&(1<<ADSC));// auf Abschluss der Konvertierung warten
45
/* ADCW muss einmal gelesen werden, sonst wird Ergebnis der nächsten
46
Wandlung nicht übernommen. */
47
result=ADCW;
48
}
49
50
/* ADC Einzelmessung */
51
uint16_tADC_Read(uint8_tchannel)
52
{
53
// Kanal waehlen, ohne andere Bits zu beeinflußen
54
ADMUX=(ADMUX&~(0x1F))|(channel&0x1F);
55
ADCSRA|=(1<<ADSC);// eine Wandlung "single conversion"
56
while(ADCSRA&(1<<ADSC))// auf Abschluss der Konvertierung warten
57
;
58
returnADCW;// ADC auslesen und zurückgeben
59
}
60
61
62
intmain()
63
{ADC_Init();
64
65
DDRB=(1<<DDB3)|(1<<DDB4)|(1<<DDB5);
66
DDRB&=~((1<<DDB0)|(1<<DDB1)|(1<<DDB2));
67
68
PORTB=0xFF;
69
70
// Timer 0 konfigurieren
71
TCCR2=(1<<WGM21);// CTC Modus
72
TCCR2=(1<<CS00)|(1<<CS01);// Prescaler 64
73
// ((1000000/64)/100) = 156
74
OCR2=156-1;
75
76
// Compare Interrupt erlauben
77
TIMSK|=(1<<OCIE2);
78
79
// Momentan ist keine Entladung aktiv
80
81
uint8_tKapazitat1;
82
83
Channel1Wait=-1;
84
Channel2Wait=-1;
85
Channel3Wait=-1;
86
87
sei();// und los gehts
88
89
while(1)
90
{
91
92
if(!(PINB&(1<<PINB0)))// Taste1 gedrückt
93
{
94
Kapazitat1=0;
95
Channel1Wait=100;// 100 * 10 ms -> 1 Sekunde
96
}
97
98
if(Channel1Wait==0)
99
{
100
101
if(ADC_Read(0)<593)//ADC Messung Kanal 1 vornehmen und kleiner 2.9V;
102
Channel1Wait=-1;//Entladung ist fertig
103
104
else
105
{
106
//Kapazitat1 += Konstante für 1.3A und 1 Sekunde;
107
Channel1Wait=100;// die nächste Sekunde beginnt
108
}
109
}
110
/*
111
if( Taste2 gedrückt ) {
112
....
113
}
114
115
if( Channel2Wait == 0 ) {
116
....
117
}
118
119
if( Taste3 gedrückt ) {
120
....
121
}
122
123
if( Channel3Wait == 0 ) {
124
....
125
}*/
126
}
127
}
Magst du mal drüberschauen ob das so past?
Eine Frage noch undzwar, wenn er auf 2.9V geht, schaltet er aus, aber es
ist doch ziemlich wahrscheinlich, dass die Spannung am Akku wieder
leicht steigt, er dadurch also wieder einschaltet etc. etc. etc. ist das
nicht doof?
Znup schrieb:> Magst du mal drüberschauen ob das so past?
Sieht für mich gut aus.
Ab in die Hrdware damit und Tests fahren.
> Eine Frage noch undzwar, wenn er auf 2.9V geht, schaltet er aus, aber es> ist doch ziemlich wahrscheinlich, dass die Spannung am Akku wieder> leicht steigt
anzunehmen
> er dadurch also wieder einschaltet etc. etc. etc. ist das> nicht doof?
Tut er nicht.
Wenn die Messung einmal abgeschaltet hat, also Channel1Wait auf -1
gegangen ist, kommt der Kanal da nur noch mit einem Tastendruck davon
wieder weg.
1
....
2
if(Channel1Wait==0)
3
{
4
if(ADC_Read(0)<593)//ADC Messung Kanal 1 vornehmen und kleiner 2.9V;
5
...
mit einem Channel1Wait von -1 schaltet weder die ISR den Kanal wieder
auf Entladung noch erfolgen weitere Messungen.
Achja, echt super :)
Jetzt muss ich noch schauen, dass ich die Tastenentprellung irgendwie
hinbekomm und schon kann ich mal tests machen :) sieht ja schon gut aus.
Vielen vielen vielen dank schonmal !!!
MfG
Znup
Znup schrieb:> Achja, echt super :)>> Jetzt muss ich noch schauen, dass ich die Tastenentprellung irgendwie> hinbekomm und schon kann ich mal tests machen :) sieht ja schon gut aus.
Nimm die hier
http://www.mikrocontroller.net/articles/Entprellung#Komfortroutine_.28C_f.C3.BCr_AVR.29
Deinen Timer kannst du so wie er ist schon benutzen.
Den ISR-Teil wandelst du etwas ab, so dass der Timer nicht nachgeladen
wird
1
ISR(TIMER2_COMP_vect)// wird alle 10ms aufgerufen
2
{
3
staticuint8_tct0,ct1,rpt;
4
uint8_ti;
5
6
i=key_state^~KEY_PIN;// key changed ?
7
ct0=~(ct0&i);// reset or count ct0
8
ct1=ct0^(ct1&i);// reset or count ct1
9
i&=ct0&ct1;// count until roll over ?
10
key_state^=i;// then toggle debounced state
11
key_press|=key_state&i;// 0->1: key press detect
12
13
if((key_state&REPEAT_MASK)==0)// check repeat function
14
rpt=REPEAT_START;// start delay
15
if(--rpt==0){
16
rpt=REPEAT_NEXT;// repeat delay
17
key_rpt|=key_state&REPEAT_MASK;
18
}
19
20
if(Channel....
21
...
und der Rest (die get_..Funktionen) bleibt so wie er dort angegeben ist.
Noch in den KEY_xxx Makros deine Ports bzw. Pins eintragen und fertig.
Kümmere dich nicht darum, was da genau in der ISR passiert. Das ist
ziemlich trickreich. Funktioniert aber erste Sahne. Für dich sind nur
die get_... Funktionen interessant, im Speziellen die get_key_press.
Ich hab das Programm gerade mal ans laufen gebracht, funktionierst
soweit, Entprellung hab ich noch nicht eingebaut, was allerdings
auffällt, pro Sekunde ist er 8.24ms nicht am entladen. Das sollte wohl
so nicht sein, sind immerhin fast 1% Fehler oder ?
Ich hab gerade herausgefunden, dass irgendwas mit dem Timer nicht past,
er macht alle 8,24ms einen Interrupt.
Ich benutze den internen RC Oszillator auf 1MHz aber so ungenau kann der
doch garnicht sein oder ? Hab es auch schon mit einem anderen AtMega8
ausprobiert, auch 8.24ms.
Berechnet habe ich das einfach mit (1000000Mhz/64/100)-1 also
Taktfrequenz durch Prescaler durch gewünschte Interrupt pro Sekunde
minus 1
Past das so nicht ?
heißen, hab das Oder vergessen.
Aber trotzdem, 5ms von 1s wird nicht entladen, macht 0.5% der Zeit, das
ist doch eine Verschlechterung zu der Version ohne das ganze im
Interrupt geschehen zu lassen, oder nicht ?
War noch ein Fehler, hab den falschen Prescaler eingestellt gehabt ;)
Jetzt hab ich außerdem noch den 16 Bit Timer verwedendet um exakte 10ms
zu haben.
nun schaltet er aber immer noch 1% auf nicht entladen.
Das sollte doch kürzer gehen oder ? Wenn ich es aus dem Interrupt
herauslasse.
Kann ich das entladung ein und ausschalten wieder in das Hauptprogramm
werfen, um diese Zeit wo es nicht entlädt so kurz als möglich zu
bekommen?
Oder gibt es hierbei dann Fehler?