Guten Tag,
Ich habe einen Code geschaffen, der bei Sekundenänderung das Ergebnis
des ADC mit einem vorherigen ADC (Referenz) vergleicht und bei gleichem
Ergebnis einen Zähler hochzählen soll, der dann bei erreichtem Wert
einen Ausgang schalten soll.
Der Code an sich funktioniert, wenn es um den Zähler geht: nach 59
Zählungen schlatet nämlich der Ausgang. Wenn ich aber die Spannung am
ADC verändere, so passiert nichts, der Ausgang bleibt geschaltet, obwohl
der Wert nicht mehr im Rahmen liegt.
Ja, ich habe alles mögliche am Code probiert, allerdings scheint es
nicht zu funktionieren, deshalb frage ich nun hier:
Ja schrieb:
Das hier
> if ( Zaehler > 59)> { PINB |= (1<<PINB0)|(1<<PINB1)|(1<<PINB2);> }
ist wohl das einschalten.
Aber wo ist das Ausschalten?
So ein Ausgang schaltet sich ja nicht von selbst aus. Er behält immer
den letzten Zustand bei. Wenn du ihn also abgeschaltet haben willst,
dann musst du das auch programmieren.
Ganz angesehen davon, denke ich nicht, dass du hier das PIN Register
benutzen willst. Je nach verwendetem Prozessor ist das maximal ein
Toggeln des Ausgangs.
Karl Heinz schrieb:> Ganz abgesehen davon.> Wo innerhalb der Schleife liest du den den ADC erneut aus?
Habs gerade gesehen. Der ADC ist auf Interrupt geschaltet.
Noch was komplizierteres ist dir nicht eingefallen?
Oh Mann. Du hast schon eine gewisse Tendenz, dir selbst mit möglichst
komplizierten Programmstrukturen selbst ein Bein zu stellen anstatt dir
erst mal mit einem ganz simplen Programm eine funktionierende Basis zu
schaffen.
Bernhard Spitzer schrieb:> Davon Abgesehen, die gegebenen Hinweise zu && und || beachten.
besonders hier:
Ja schrieb:> if ((sekundeCOM2 != sekundeCOM) & (analogResult < (analogReferenz +10))> & (analogResult > (analogReferenz -10)) )
Ralph S. schrieb:> Bernhard Spitzer schrieb:>> Davon Abgesehen, die gegebenen Hinweise zu && und || beachten.>> besonders hier:>> Ja schrieb:>> if ((sekundeCOM2 != sekundeCOM) & (analogResult < (analogReferenz +10))>> & (analogResult > (analogReferenz -10)) )
Wobei das an der Stelle vermutlich gar keinen Unterschied macht
(korrigiert gehört es natürlich trotzdem)
Weil Zuweisungen immer von rechts nach links gehen.
Du schreibst ja auch OCR0A = 125-1; weil du die Zahl ins Register
schreiben willst und nicht umgekehrt.
?
Und warum soll hier
Ja schrieb:> ADCH = analogresult;
nur das HI-Byte ausgelesen werden, nicht aber nicht das LOW-Byte?
(Abgesehen davon, dass es eigentlich anders herum stehen müsste)
Hi
>Und warum eigentlich>analogResult = (ADCH<<8)|ADCL;>und nicht gleich>analogResult = ADC;
Weil ADLAR gesetzt ist und damit das Ergebnis 8-Bittig in ADCH steht.
MfG Spess
ich glaube kaum, dass du den Zähler auch dann immer wieder zurück setzen
willst, wenn die 1 Sekunde noch nicht um ist.
Tip: Trenn doch erst mal die Dinge.
Das eine ist, dass du jede 1 Sekunde eine Messung machen und auswerten
willst.
Und das andere ist die Auswertung der Messung. Gerade am Anfang ist es
des öfteren keine so gute Idee, wenn man das alles in ein einziges if
quetschen will. Man verheddert sich dann leicht in den Bedingungen.
(Und es ist auch eine gute Idee, sich für Variablen einen vernünftigen
Namen einfallen zu lassen, der ihrer Funktion entspricht. FLAG ist ein
recht nichtssagender Name. 'MessungMachen' oder 'SekundeVorbei' wären
jetzt mal angesehen vom Läge der Namen, wesentlich besser. Denn genau
das wird damit angezeigt: 1 Sekunde ist vorbei. Es soll eine neue
Messung gemacht werden.
1
....
2
3
while(1){
4
5
if(FLAG==1){// 1 Sekunde ist vorbei. Messung machen und auswerten
[Erbsenzähler]
> Ungetestet!
Stimmt, doMeasurement ist nicht deklariert :-)
Außerdem: gibt es auf dem AVR wirklich keine Möglichkeit, einzelne Bits
als Flags zu nutzen?
Und ich finde
1
for(;;)
irgendwie verwirrender als
1
while(1)
auch wenn das 1 Tastendruck mehr ist. Dafür sind
1
return0;
nach einer ENDLOSSCHLEIFE 9 unnötige Tastendrücke.
[/Erbsenzähler]
;-)
spess53 schrieb:> Hi>>>Und warum eigentlich>>>analogResult = (ADCH<<8)|ADCL;>>>>und nicht gleich>>>analogResult = ADC;>> Weil ADLAR gesetzt ist und damit das Ergebnis 8-Bittig in ADCH steht.>> MfG Spess
Ja und? ADC ist (ADCH << 8) | ADCL, dabei ist es völlig egal ob ADLAR
gesetzt ist oder nicht. Hätte da jetzt nur analogResult = ADCH gestanden
wäre es noch logisch gewesen aber so? Das macht hier keinen Sinn ADC
nicht zu benutzen.
Bernhard Spitzer schrieb:>> Ungetestet!> Stimmt, doMeasurement ist nicht deklariert :-)
Mea culpa, mea maxima culpa!
1
unsignedintdoMeasurement;
Bitte schön!
> Außerdem: gibt es auf dem AVR wirklich keine Möglichkeit, einzelne Bits> als Flags zu nutzen?
Ich glaub nicht. Wenigstens nicht in dem Sinne wie bei den ARM Cortex
Bitfields...
> Und ich finde>
1
for(;;)
> irgendwie verwirrender als>
1
while(1)
> auch wenn das 1 Tastendruck mehr ist.
Geschmackssache.
> Dafür sind>
1
return0;
> nach einer ENDLOSSCHLEIFE 9 unnötige Tastendrücke.
Nicht für den OT. Der muss das ganze nur Copy/Pasten auf gut Neu-deutsch
;-)
Und ich mag den Sonderfall für main() nicht. main() ist als int
definiert und soll deswegen irgendeine integer zurückliefern, auch wenn
er davor ein endlos schleift.
Edit: mich würde aber interessieren ob meine Version für den OT
funktioniert. ich habe kein AVR hier zum ausprobieren.
> Bitte schön!>
Du bist auf ARM zu Hause?
Dann sei dir verziehen.
Auf einem AVR nimmst du nicht ungestraft und gedankenlos einfach einen
int her. Ein int ist 16 Bit gross! Damit halst du dem armen 8-Bitter
unnötige Mehrarbeit auf.
ein
1
uint8_tdoMeasurement;
tuts völlig.
>> Außerdem: gibt es auf dem AVR wirklich keine Möglichkeit, einzelne Bits>> als Flags zu nutzen?>> Ich glaub nicht. Wenigstens nicht in dem Sinne wie bei den ARM Cortex> Bitfields...
Bitfields gibt es als C Ding selbstverständlich.
Eric B. schrieb:> mich würde aber interessieren ob meine Version für den OT> funktioniert. ich habe kein AVR hier zum ausprobieren.
Hallo, vielen Dank für deine Mühe. Leider funktioniert dein Code nicht.
Ich habe den ADC sogar an Masse gelegt, um Fehlerquellen auszuschließen,
leider wird PB0_TO_3 anscheinend nie aktiviert...
Ja schrieb:> Karl Heinz schrieb:>> volatile uint8_t doMeasurement;>> Ja und ADCReference darf auch keine constante sein oder?
Moment. Muss den Code noch genauer studieren.
Ja schrieb:> Karl Heinz schrieb:>> volatile uint8_t doMeasurement;>> Ja und ADCReference darf auch keine constante sein oder?
So wie das vom TO angedacht war. Korrekt: darf keine Konstante sein.
Da ist auch noch ein Mischmasch
1
if(((adcRefererence-adcRefMargin)<=adcResult)
2
// **************
3
&&(adcResult<(adcRefererence+adcRefMargin)))
4
// **************
5
{
6
....
7
}
8
else
9
{
10
....
11
analogreferenz=adcResult;
12
// **************
13
...
die Idee ist es ja, dass sich die Referenz dauernd ständig anpasst. Das
muss also dieselbe Variable sein.
Das bringt uns zur Frage:
> Leider funktioniert dein Code nicht.
was hast du noch verändert? So wie gepostet compiliert der Code ganz
sicher nicht. Du musst ihn also verändert haben.
// One second has passed - do the next AD conversion
64
65
ADCSRA|=(1<<ADSC);
66
while(ADCSRA&(1<<ADSC))
67
{
68
//do nothing - just wait till conversion complete
69
}
70
adcResult=ADCH;
71
72
// Check result range
73
if(((adcReference-adcRefMargin)<=adcResult)
74
&&(adcResult<(adcReference+adcRefMargin)))
75
{
76
// Show that count limit is reached
77
if(inRangeCounter>inRangeCountLimit)
78
{
79
PORTB|=(1<<PB3);
80
}
81
else
82
{
83
inRangeCounter++;
84
}
85
}
86
else
87
{
88
// Reset everything
89
inRangeCounter=0;
90
adcReference=adcResult;
91
PORTB&=~(1<<PB3);
92
}
93
94
// Ready for next measurement
95
doMeasurement=0;
96
}
97
}
98
return0;
99
}
100
101
ISR(TIMER0_COMPA_vect)
102
{
103
milliSeconds++;
104
if(milliSeconds>=1000)
105
{
106
milliSeconds=0;
107
doMeasurement=1;
108
}
109
}
einige Variablen waren Quark, so compililiert er mir das. Allerdings
schaltet der Ausgang niemals. Selbst dann nicht, wenn ADC an Masse liegt
(Veränderung = 0).
Nur zur Sicherheit:
Deine Eingangsspannung liegt aber schon am ADC2 Pin an?
(Persönlich hasse ich den Sch..., die ADC Routinen jedesmal neu zu
schreiben. Im Tut gibt es so schöne Routinen dafür)
Ja schrieb:> initialize();> for(;;) und return 0; können doch auch einfach mit while (1) dargestellt> werden oder?
Ja, aber das ist alles nicht das Problem.
Karl Heinz schrieb:> Eingangsspannung
Die auszuwertende, analoge Spannung liegt an ADC2 an, ja.
Ist ein Attiny85...
Karl Heinz schrieb:> Ja, aber das ist alles nicht das Problem.
okay...
Da sich das jetzt schon in den 5. Tag reinzieht:
Können wir das ganze systematisch von mehr oder weniger bei 0 beginnend,
in Schritten noch mal neu aufsetzen?
(Problem bei mir ist nämlich, dass ich auch zur Zeit auf keinen AVR
zugreifen kann.)
Ja schrieb:> Die auszuwertende, analoge Spannung liegt an ADC2 an, ja.> Ist ein Attiny85...
Das ist schon mal gut zu wissen.
Denn der hat 3 Bits für die Referenzspannung.
Und die Kombination
Karl Heinz schrieb:> Da sich das jetzt schon in den 5. Tag reinzieht:> Können wir das ganze systematisch von mehr oder weniger bei 0 beginnend,> in Schritten noch mal neu aufsetzen
Klar, also:
Meine Intention:
Ich habe einen Attiny85 und eine analoge Spannung, die ausgewertet
werden soll.
Auswertung: es soll ausgewertet werden, ob sich die Spannung verändert
(in Betrachtung der Toleranz). Am besten soll dieser Check sekündlich
stattfinden.
Wenn sich die Spannung nämlich nicht sonderlich verändert hat über eine
Minute (nicht sonderlich = incl. Toleranz), dann soll/en Port/s als
Ausgang geschaltet werden.
Das ist so die prinzipielle Idee.
Ja schrieb:> Karl Heinz schrieb:>> Da sich das jetzt schon in den 5. Tag reinzieht:>> Können wir das ganze systematisch von mehr oder weniger bei 0 beginnend,>> in Schritten noch mal neu aufsetzen>> Klar, also:> Meine Intention:> Ich habe einen Attiny85 und eine analoge Spannung, die ausgewertet> werden soll.> Auswertung: es soll ausgewertet werden, ob sich die Spannung verändert> (in Betrachtung der Toleranz). Am besten soll dieser Check sekündlich> stattfinden.> Wenn sich die Spannung nämlich nicht sonderlich verändert hat über eine> Minute (nicht sonderlich = incl. Toleranz), dann soll/en Port/s als> Ausgang geschaltet werden.> Das ist so die prinzipielle Idee.
Alles schön und gut.
Nur fangen wir viel simpler an.
Du hast 4 LED und an diese 4 LED wird der Messwert ausgegeben (geeignet
runtergeteilt). Ein Poti hast du am ADC EIngang oder sonst irgendeine
Möglichkeit, dort eine veränderbare Spannung anzulegen?
Wie gross ist die zu messende Spannung maximal und was wollen wir als
Referenzspannung nehmen.
Ehe das nicht klappt, dass du beim Drehen am Poti die LED sich nicht
verändern siehst, brauchen wir mit komplexeren Dingen gar nicht erst
anfangen. erst mal muss das einfache funktionieren.
Ergibt durchaus Sinn; ja, Potis hab ich hier ein paar liegen.
Ich leg Vcc einfach an ADC an mit zwischengeschaltetem Poti, oder?
Zum Bild: was bedeutet das "X"?
// die Versorgungsspannung AVcc als Refernz wählen:
10
ADMUX=0;
11
12
ADCSRA=(1<<ADPS1)|(1<<ADPS0);// Frequenzvorteiler
13
ADCSRA|=(1<<ADEN);// ADC aktivieren
14
15
ADCSRA|=(1<<ADSC);// eine ADC-Wandlung
16
while(ADCSRA&(1<<ADSC)){// auf Abschluss der Konvertierung warten
17
}
18
(void)ADCW;
19
}
20
21
uint16_tADC_Read(uint8_tchannel)
22
{
23
// Kanal waehlen, ohne andere Bits zu beeinflußen
24
ADMUX=(ADMUX&~(0x0F))|(channel&0x0F);
25
26
ADCSRA|=(1<<ADSC);// eine Wandlung "single conversion"
27
while(ADCSRA&(1<<ADSC)){// auf Abschluss der Konvertierung warten
28
}
29
returnADCW;// ADC auslesen und zurückgeben
30
}
31
32
intmain(void)
33
{
34
uint16_tadcValue;
35
36
DDRB|=(1<<DDB0)|(1<<DDB1)|(1<<DDB2)|(1<<DDB3);
37
38
ADC_Init();
39
40
while(1)
41
{
42
adcValue=ADC_Read(2);
43
PORTB=adcValue/64;
44
}
45
}
das erste Testprogramm.
Bei Drehen am Poti müss sich an den LED was tun.
by the Way: wie rum sind deine LED angeschlossen? Leuchten die bei einer
0 am Portpin oder bei einer 1?
Wenn: bei einer 0, dann eben
Karl Heinz schrieb:> Klappts nicht?
Vielen vielen Dank erstmal: also, da scheint sich nichts zu tun...
Ich habe es mit Masse, Widerstand und VCC probiert, da leuchtet nichts.
Kathode der LED liegt an Masse an, also passt das theoretisch. Ich weiß
echt nicht, woran das liegen könnte...
Hmmmmm.
Sollte aber eigentlich. Ausser der Einstellung für die Referenzspannung
mit allen MUX Bits auf 0, ist mir nichts grossartiges mehr im Datenblatt
aufgefallen.
OK, den Digitalportpin sollte man noch abschalten und da bin ich
überfragt, ob das für den ADC ein Problem darstellt, wenn man es nicht
macht. Sicherheitshalber
1
intmain(void)
2
{
3
uint16_tadcValue;
4
5
DDRB|=(1<<DDB0)|(1<<DDB1)|(1<<DDB2)|(1<<DDB3);
6
DIDR0|=(1<<ADC2D);
7
8
ADC_Init();
9
10
while.....
11
....
Deine LED funktionieren aber schon?
Wieder: sicherheitshalber
1
intmain(void)
2
{
3
uint16_tadcValue;
4
5
DDRB|=(1<<DDB0)|(1<<DDB1)|(1<<DDB2)|(1<<DDB3);
6
DIDR0|=(1<<ADC2D);
7
8
ADC_Init();
9
10
PORTB=0x0F;
11
_delay_ms(200);
12
PORTB=0x00;
13
_delay_ms(200);
14
PORTB=0x0F;
15
_delay_ms(200);
16
17
while.....
18
....
Das gibt mal Lichtspiele, wenn das Programm startet. Kann nie schaden,
wenn man da eine Bestätigung hat.
Karl Heinz schrieb:> Das gibt mal Lichtspiele, wenn das Programm startet. Kann nie schaden,> wenn man da eine Bestätigung hat.
Der erste Code funktionert, also der mit dem ADC-Code. Also der ADC
funktioniert!
Ja schrieb:> Karl Heinz schrieb:>> Das gibt mal Lichtspiele, wenn das Programm startet. Kann nie schaden,>> wenn man da eine Bestätigung hat.>> Der erste Code funktionert, also der mit dem ADC-Code. Also der ADC> funktioniert!
Sag das doch gleich.
Gut. Dann gehts weiter.
Wir bringen jetzt mal die 1 Sekunde ins Spiel.
Das mach ich erst mal pragmatisch, indem ich mit einem _delay_ms() die
Durchlaufzeit durch die Schleife auf ca 1 Sekunde anhebe. Um den delay
kümmern wir uns später und ersetzen den durch einen Timer. Das ist im
Moment nicht so wichtig.
Weiters bauen wir den Referenzwert ein, wobei uns diese zeitliche
Mehfachreferenz noch wurscht ist. Wir gewinnen einfach jede 1 Sekunde
einen neuen Wert und vergleichen ihn mit dem 'Referenzwert' aus der
vorhergehenden Runde. Sind die beide (mit einer erlaubten Abweichung)
gleich, dann: LED an (oder aus), wenn nicht dann eben LED aus (oder an).
Da die Differenz der beiden Werte negativ sein kann, hab ich den
Datentyp für die Werte von einem uint16_t auf einen int16_t verändert.
Dann gibt es bei der Subtraktion kein Problem mit negativen Werten.
1
intmain(void)
2
{
3
int16_tadcReference;
4
int16_tadcValue;
5
6
DDRB|=(1<<DDB0)|(1<<DDB1)|(1<<DDB2)|(1<<DDB3);
7
8
ADC_Init();
9
10
adcReference=ADC_Read(2);
11
12
while(1)
13
{
14
adcValue=ADC_Read(2);
15
16
if(abs(adcReference-adcValue)<20)
17
PORTB|=0xF0;
18
else
19
PORTB&=~0xF0;
20
21
adcReference=adcValue;
22
_delay_ms(1000);
23
}
24
}
Erwartetes Verhalten:
nach einer Änderung an der Eingangsspannung (am Poti drehen), sollten
die LED erst mal abschalten um dann nach einer Zeit nicht länger als 1
Sekunde wieder den ursprünglichen Zustand einzunehmen.
(Ich weiss nicht, ob Pin auf 1 bei dir LED leuchtet bedeutet oder nicht.
Deine Aussage diesbezüglich war für mich nicht klar)
Karl Heinz schrieb:> die LED erst mal abschalten um dann nach einer Zeit nicht länger als 1> Sekunde wieder den ursprünglichen Zustand einzunehmen.
die Zeit kann ich leider nicht genauer festmachen, weil wir ja nicht
wissen, wann genau du am Poti drehst, während der µC im delay hängt. Das
kann sein, dass der gerade erst seine 1 Sekunde Wartezeit begonnen hat,
es kann aber auch sein, dass sich die Wartezeit schon dem Ende zuneigt.
Sehen sollte man allerdings was.
lauten!
(Was lernen wir daraus: wenn ich nicht zu faul gewesen wäre und mir
statt dessen Makros für die LED definiert hätte, dann wäre der Fehler
nicht passiert)
Ja schrieb:> Karl Heinz schrieb:>> das muss natürlich PORTB |= 0x0F;>> Nachdem ich das geändert habe, funktioniert es jetzt!
Gut.
Dann kommt als nächstes ein Zähler dazu, der mitzählt, wie oft auf
'gleich' detektiert wurde.
Meld mich gleich wieder.
OK. Nächste Version
Dazugekommen ist ein Zähler, der bei Übereinstimmung um 1 hochgezählt
wird. Sind die Werte zu verschieden, dann wird der Zähler auf 0 gesetzt
und die dann anliegende SPannung als neue Referenz genommen.
Ich hab die Anzahl erforderlicher Übereinstimmungen mal auf 5 gesetzt.
Auch hab ich eine kleine Modifikation gemacht. Solange die erforderliche
Anzahl nicht beisammen ist, schalte ich die LED nicht einfach ab,
sondern lass mir dort diesen Zähler ausgeben. D.h. an den LED müsste man
sehen können, dass der Zähler sukzessive hochzählt.
Erst wenn die erforderliche Anzahl beisammen ist, dann gibt es an den 4
LED 'die volle Dröhnung'
(OK, ich geb zu, dass ist auch eine kleine Spielerei. Aber ich seh sonst
keine Möglichkeit festzustellen, ob der Zähler tatsächlich hochgezählt
wird, oder ob es da noch einen nicht bedachten Effekt gibt)
1
#define F_CPU 1000000UL // 1 MHZ
2
3
#include<avr/io.h>
4
#include<util/delay.h>
5
6
/* ADC initialisieren */
7
voidADC_Init(void)
8
{
9
// die Versorgungsspannung AVcc als Refernz wählen:
10
ADMUX=0;
11
12
ADCSRA=(1<<ADPS1)|(1<<ADPS0);// Frequenzvorteiler
13
ADCSRA|=(1<<ADEN);// ADC aktivieren
14
15
ADCSRA|=(1<<ADSC);// eine ADC-Wandlung
16
while(ADCSRA&(1<<ADSC)){// auf Abschluss der Konvertierung warten
17
}
18
(void)ADCW;
19
}
20
21
uint16_tADC_Read(uint8_tchannel)
22
{
23
// Kanal waehlen, ohne andere Bits zu beeinflußen
24
ADMUX=(ADMUX&~(0x0F))|(channel&0x0F);
25
26
ADCSRA|=(1<<ADSC);// eine Wandlung "single conversion"
27
while(ADCSRA&(1<<ADSC)){// auf Abschluss der Konvertierung warten
28
}
29
returnADCW;// ADC auslesen und zurückgeben
30
}
31
32
#define REQUIRED 5
33
34
intmain(void)
35
{
36
int16_tadcReference;
37
int16_tadcValue;
38
uint8_tcountEqual;
39
40
DDRB|=(1<<DDB0)|(1<<DDB1)|(1<<DDB2)|(1<<DDB3);
41
42
ADC_Init();
43
44
adcReference=ADC_Read(2);
45
countEqual=0;
46
47
while(1)
48
{
49
adcValue=ADC_Read(2);
50
51
if(abs(adcReference-adcValue)<20)
52
{
53
if(count<REQUIRED)
54
countEqual++;
55
}
56
else{
57
countEqual=0;
58
adcReference=adcValue;
59
}
60
61
if(count==REQUIRED)
62
PORTB|=0x0F;
63
else
64
{
65
// PORTB &= ~0xF0; // so wirds dann im Endeffekt werden, ....
66
PORTB=(count&0x0F);// aber z uDebugzwecken lassen wir uns den count ausgeben.
Karl Heinz schrieb:> OK. Nächste Version
Ach ja.
Was ist das erwartete Verhalten?
Wird am Poti gedreht, dann sollten alle LED erst mal ausgehen Nach
Beendigung des Drehens sollten die LED sukzessive binär von 0 bis 4
hochzählen
1
. . . .
2
. . . X
3
. . X .
4
. . X X
5
. X . .
und beim 5.ten 'Takt', dann das Feuerwerk einschalten
1
X X X X
Soweit zur Erwartungshaltung, was passieren sollte :-)
Mal sehen, was wirklich passiert.
Ja schrieb:> Karl Heinz schrieb:>> count>> ist nicht definiert oder?
Danke. Muss natürlich countEqual heissen.
Ich hätte noch mal drüber compilieren sollen. Ich änder das in meinem
Studio gleich
Ja schrieb:> Funktioniert ;)
zu spät gesehen.
Gratulation.
Aber noch nicht ganz.
WEnn die Schaltung etwas über 18 Stunden steht, ohne dass sich die
Spannung am Eingang ändert, dann läuft dir dein Zaehler über. Aus 65535
wird 0 und dann ... meldet deine Auswertung das falsche. D.h du musst
diesen Überlauf verhindern.
Du kannst also hier
1
if(abs(adcReference-adcValue)<20)
2
Zaehler++;
nicht einfach nur den Zaehler in jedem Fall erhöhen.
Auf der sicheren Seite bist du, wenn du den Zaehler nur dann erhöhst,
wenn er kleiner als die von dir geforderte Anzahl bist. Da kann nichts
passieren. Sieh dir an, wie ich das gemacht habe. Und sieh dir auch an,
wie ich es gemacht habe, dass die beiden Zahlenwerte bei dieser
Sicherung und bei der Auswertung auch definitiv immer übereinstimmen.
Ja schrieb:> Super, funktioniert!> Der nächste Schritt wäre vermutlich der Timer oder?
Jep.
D.h. falls du den überhaupt noch brauchst.
Wie wichtig ist die genaue Zeit? Gibt es noch etwas, was der µC tun
müsste?
Wenn du sagst "Soooo wichtig ist die Zeit nicht, denn ich hab ja sowieso
eine Granulierung in der ersten Sekunde nachdem sich die Spannung
stabilisiert hat, also kommt es auf ein paar Zehntelmillisekunden pro
Schleifendurchlauf auch nicht an"
bzw. wenn du sagst "Nö, das ist die einzige Aufgabe, die der µC hat"
dann brauchen wir nicht päpstlicher als der Papst sein und können das so
lassen.
Wenn du aber sagst, nope geht so nicht, dann bauen wir den Timer rein.
Karl Heinz schrieb:> Auf der sicheren Seite bist du, wenn du den Zaehler nur dann erhöhst,> wenn er kleiner als die von dir geforderte Anzahl bist. Da kann nichts> passieren. Sieh dir an, wie ich das gemacht habe. Und sieh dir auch an,> wie ich es gemacht habe, dass die beiden Zahlenwerte bei dieser> Sicherung und bei der Auswertung auch definitiv immer übereinstimmen
Danke für den Tipp!
Ergibt durchaus Sinn.
stimmt in deinem Programm auch noch nicht. Du willst nicht auf jeden
Fall immer den aktuellen Spannungswert als neue Referenz nehmen. Denn
wenn du nur langsam genug am Poti drehst, dann ändert sich der auch pro
Durchlauf nur wenig. D.h. Obwohl sich deine Spannung laufend verändert,
ist von einem Durchgang zum nächsten die Differenz immer kleiner als die
vorgegebenen 20 und du zählst munter den Zaehler hoch, obwohl du bei
entsprechend langsamen Drehen das Poti von einem Ende zum anderen
durchdrehen kannst!
Und rück deinen Code ein!
Karl Heinz schrieb:> Wenn du aber sagst, nope geht so nicht, dann bauen wir den Timer rein
Timer wäre schon nötig, da das ganze auch erst nach einer bestimmten
Zeit losgehen soll..
OK.
Das ist jetzt leicht, denn der Timer hat ja (denk ich) von Anfang an
funktioniert.
Den Teil übernehmen wir einfach mal im wesentlichen.
OK. Hier mein Vorschlag.
Die Sache mit den Millisekunden ist gleich geblieben.
Die Auswertung (Messung plus vergleich) findet als EInheit nur dann
statt,
wenn der Timer sein 'GO' gegeben hat.
Die Technik, wie man sowas macht, kennst du ja schon: eine (volatile)
uint8_t Variable, die von der Timer-ISR auf 1 gesetzt wird und in der
Hauptschleife wird geprüft, ob sie 1 ist und wenn ja, wird der
Mechanismus angeworfen.
1
#define F_CPU 1000000UL // 1 MHZ
2
3
#include<avr/io.h>
4
#include<util/delay.h>
5
#include<avr/interrupt.h>
6
7
#define REQUIRED 9 // wieviele 'gleiche' Messwerte sind erforderlich?
8
#define EPSILON 20 // in welchem +-Bereich sind die Messwerte noch als gleich anzusehen (ADC-EInheiten)
9
10
volatileuint16_tmilliSekunden;
11
volatileuint8_tdoMeasure;
12
13
/* ADC initialisieren */
14
voidADC_Init(void)
15
{
16
// die Versorgungsspannung AVcc als Refernz wählen:
17
ADMUX=0;
18
19
ADCSRA=(1<<ADPS1)|(1<<ADPS0);// Frequenzvorteiler
20
ADCSRA|=(1<<ADEN);// ADC aktivieren
21
22
ADCSRA|=(1<<ADSC);// eine ADC-Wandlung
23
while(ADCSRA&(1<<ADSC)){// auf Abschluss der Konvertierung warten
24
}
25
(void)ADCW;
26
}
27
28
uint16_tADC_Read(uint8_tchannel)
29
{
30
// Kanal waehlen, ohne andere Bits zu beeinflußen
31
ADMUX=(ADMUX&~(0x0F))|(channel&0x0F);
32
33
ADCSRA|=(1<<ADSC);// eine Wandlung "single conversion"
34
while(ADCSRA&(1<<ADSC)){// auf Abschluss der Konvertierung warten
35
}
36
returnADCW;// ADC auslesen und zurückgeben
37
}
38
39
intmain(void)
40
{
41
int16_tadcReference;
42
int16_tadcValue;
43
uint8_tcountEqual;
44
45
DDRB|=(1<<DDB0)|(1<<DDB1)|(1<<DDB2)|(1<<DDB3);
46
47
TCCR0A=(1<<WGM01);// CTC Modus
48
TCCR0B|=(1<<CS01);// Prescaler 8
49
OCR0A=((F_CPU/8)/1000)-1;// auf 1 Millisekunde einstellen
50
51
// Compare Interrupt erlauben
52
TIMSK|=(1<<OCIE0A);
53
ADC_Init();
54
55
adcReference=ADC_Read(2);
56
countEqual=0;
57
58
sei();// alles fertig eingestellt: und los gehts!
59
60
while(1)
61
{
62
if(doMeasure)
63
{
64
doMeasure=0;
65
66
adcValue=ADC_Read(2);
67
68
if(abs(adcReference-adcValue)<EPSILON)
69
{
70
if(countEqual<REQUIRED)
71
countEqual++;
72
}
73
else{
74
countEqual=0;
75
adcReference=adcValue;
76
}
77
78
if(countEqual==REQUIRED)
79
PORTB|=0x0F;
80
else
81
PORTB&=~0xF0;
82
}
83
}
84
}
85
86
ISR(TIMER0_COMPA_vect)// wird im Millisekundentakt aufgerufen
Ein bischen übersichtlicher wird das Ganze, wenn wir den ganzen Teil mit
der Messung und Auswertung aus main() rausnehmen und in eine eigene
Funktion stecken.
1
#define F_CPU 1000000UL // 1 MHZ
2
3
#include<avr/io.h>
4
#include<util/delay.h>
5
#include<avr/interrupt.h>
6
7
#define REQUIRED 9 // wieviele 'gleiche' Messwerte sind erforderlich?
8
#define EPSILON 20 // in welchem +-Bereich sind die Messwerte noch als gleich anzusehen (ADC-EInheiten)
9
10
volatileuint16_tmilliSekunden;
11
volatileuint8_tdoMeasure;
12
13
/* ADC initialisieren */
14
voidADC_Init(void)
15
{
16
// die Versorgungsspannung AVcc als Refernz wählen:
17
ADMUX=0;
18
19
ADCSRA=(1<<ADPS1)|(1<<ADPS0);// Frequenzvorteiler
20
ADCSRA|=(1<<ADEN);// ADC aktivieren
21
22
ADCSRA|=(1<<ADSC);// eine ADC-Wandlung
23
while(ADCSRA&(1<<ADSC)){// auf Abschluss der Konvertierung warten
24
}
25
(void)ADCW;
26
}
27
28
uint16_tADC_Read(uint8_tchannel)
29
{
30
// Kanal waehlen, ohne andere Bits zu beeinflußen
31
ADMUX=(ADMUX&~(0x0F))|(channel&0x0F);
32
33
ADCSRA|=(1<<ADSC);// eine Wandlung "single conversion"
34
while(ADCSRA&(1<<ADSC)){// auf Abschluss der Konvertierung warten
35
}
36
returnADCW;// ADC auslesen und zurückgeben
37
}
38
39
int16_tadcReference;
40
int16_tadcValue;
41
uint8_tcountEqual;
42
43
voidperformCheck()
44
{
45
adcValue=ADC_Read(2);
46
47
if(abs(adcReference-adcValue)<EPSILON)
48
{
49
if(countEqual<REQUIRED)
50
countEqual++;
51
}
52
else{
53
countEqual=0;
54
adcReference=adcValue;
55
}
56
57
if(countEqual==REQUIRED)
58
PORTB|=0x0F;
59
else
60
PORTB&=~0xF0;
61
}
62
63
intmain(void)
64
{
65
DDRB|=(1<<DDB0)|(1<<DDB1)|(1<<DDB2)|(1<<DDB3);
66
67
TCCR0A=(1<<WGM01);// CTC Modus
68
TCCR0B|=(1<<CS01);// Prescaler 8
69
OCR0A=((F_CPU/8)/1000)-1;// auf 1 Millisekunde einstellen
70
71
// Compare Interrupt erlauben
72
TIMSK|=(1<<OCIE0A);
73
ADC_Init();
74
75
adcReference=ADC_Read(2);
76
countEqual=0;
77
78
sei();// alles fertig eingestellt: und los gehts!
79
80
while(1)
81
{
82
if(doMeasure)
83
{
84
doMeasure=0;
85
performCheck();
86
}
87
}
88
}
89
90
ISR(TIMER0_COMPA_vect)// wird im Millisekundentakt aufgerufen
91
{
92
milliSekunden++;
93
if(milliSekunden==1000)
94
{
95
milliSekunden=0;
96
97
doMeasure=1;
98
}
99
}
Dann ist der ganze Mess-Auswertevorgang eine abgeschlossene Einheit und
hier in der Hauptschleife
1
...
2
while(1)
3
{
4
if(doMeasure)
5
{
6
doMeasure=0;
7
performCheck();
8
}
9
}
ist es auch sehr leicht zu verstehen, wie die Logik läuft. Wenn der
Timer sagt, dass der ADC zu überprüfen ist, dann macht das Programm
genau das: den ADC überprüfen.
Okay, es funktioniert perfekt, viel viel viel Dank, echt, ich habe durch
diesen Prozess hier gerade sehr viel gelernt, und vermutlich auch viele
andere, die das hier noch lesen werden!
Eine Frage hätte ich noch:
Ich habe stunden, minuten, sekunden auch noch in den ISR
(TIMER0_COMPA_vect) hinzugefügt nach bekanntem Muster und auch als
variable deklariert, meine Frage:
wie ist es zu lösen, dass die ganze Abfragerei erst nach minute > 0
beginnt?
1
while(minute>0)
funktioniert nicht [es passiert nichts], ebenso wie
1
if(countEqual==REQUIRED&&(minute>0)
2
PORTB|=0x0F;
[hier leuchten von Anfang an 2 LEDS, nach 1 minute leuchten dann die
anderen]?
Ja schrieb:> Okay, es funktioniert perfekt, viel viel viel Dank, echt, ich habe durch> diesen Prozess hier gerade sehr viel gelernt,
genau das war der Sinn der ganzen Sache.
> Eine Frage hätte ich noch:> Ich habe stunden, minuten, sekunden auch noch in den ISR> (TIMER0_COMPA_vect) hinzugefügt nach bekanntem Muster und auch als> variable deklariert, meine Frage:> wie ist es zu lösen, dass die ganze Abfragerei erst nach minute > 0> beginnt?
Da gibts mehere möglichkeiten.
Eine wäre zb, dass in der ISR die Variable doMeasure erst dann auf 1
gesetzt wird, wenn die Stunden bzw. Minuten richtig stehen :-)
>>
1
>while(minute>0)
2
>
> funktioniert nicht [es passiert nichts],
Du musst weg von deinem Schleifendenken!
Du musst hin zu einem denken von:
Ich wach jetzt gerade auf. Ich seh mich um, wie ist meine Situation, was
spielt sich in meiner Umgebung ab? Muss ich auf ein Ereignis reagieren?
WEnn ja, dann mach ich das und dann leg ich mich wieder schlafen.
Auf etwas warten bedeutet nicht notwendigerweise, dass man aktiv daneben
stehen bleibt und zusieht. Auf etwas warten kann auch bedeuten, dass man
sich sozusagen 'den Weckere stellt' und wenn der klingelt man sich
umsieht, ob es schon etwas zu tun gibt. Wenn ja, dann macht man das.
Wenn nein, dann stellt man sich den Wecker für die nächste Periode und
macht was anderes oder schläft.
Genau das ist die Art und Weise, wie man µC sinnvoll programmiert. Nicht
in Warteschleifen denken, sondern in FOrm von Ereignissen die eintreten.
Bei dir ist das leicht, denn das einzige Ereignis, das 'eintritt', das
ist, dass wieder eine Millisekunde um ist.
[hier leuchten von Anfang an 2 LEDS, nach 1
> minute leuchten dann die anderen]?
Du musst hier ein bisschen vorsichtig sein!
Denn dieses if hat ein else, das immer dann zum Zug kommt, wenn die
Bedingung IN IHRERE GESAMTHEIT nicht zutrifft. IN diesem else passieren
aber Dinge. Und die passieren dann auch dann, wenn die minute eben noch
nicht größer als 0 ist.
Wenn du also zu einer Bedingung weitere Ausdrücke hinzufügst (die in
deinem Beispiel mit einem && verknüpft werden), dann musst du dich auch
immer fragen: was bedeutet das jetzt wenn genau dieser Teil der Bedingun
eben NICHT zutrifft. Was geht dann in meinem else ab?
1
if(minuten>0)
2
{
3
if(countEqual==REQUIRED)
4
....A
5
else
6
....B
7
}
ist eben nicht dasselbe wie
1
if(countEqual==REQUIRED&&minuten>0)
2
....A
3
else
4
....B
Für den Codeteil A ändert sich nichts. Der wird auch dann unter den
gleichen Bedingungen ausgeführt. Aber für den Codeteil B ist alles ganz
anders!
Karl Heinz schrieb:> ine wäre zb, dass in der ISR die Variable doMeasure erst dann auf 1> gesetzt wird, wenn die Stunden bzw. Minuten richtig stehen
Aber damit würde die Messung ja nur noch minütlich stattfinden, oder
nicht?
>> ADC funktioniert nicht
So wie es der Thread-Titel ausdrückt wäre der ausgerufene Sachverhalt
ja gleichzusetzen einem Defekt im/am ADC oder ein grösserer Defekt des
gesamten Prozessors.
.... also ein bischen voreilig ..... bzw Themaverfehlung ....
Oder frage ich mal in die Runde:
Ist es einfach "wurscht" was man im Titel schreibt?
Ja schrieb:> Karl Heinz schrieb:>> ine wäre zb, dass in der ISR die Variable doMeasure erst dann auf 1>> gesetzt wird, wenn die Stunden bzw. Minuten richtig stehen>> Aber damit würde die Messung ja nur noch minütlich stattfinden, oder> nicht?
Wieso?
1
ISR(TIMER0_COMPA_vect)// wird im Millisekundentakt aufgerufen
2
{
3
milliSekunden++;
4
if(milliSekunden==1000)
5
{
6
milliSekunden=0;
7
Sekunden++;
8
9
if(Sekunden==60)
10
{
11
Sekunden=0;
12
Minuten++;
13
}
14
15
// wir sind auf jeden Fall schon mal auf einer ganzen Sekunde
16
// sind seit dem Start mehr als 5 Minuten vergangen, dann darf
17
// daher auch der Auftrag zum Messen rausgegeben werden
Und wegen der Stunden.
Es ist einfacher, wenn du die Zeit komplett in Form von Minuten
ausdrückst. 2 Stunden und 23 Minuten sind 143 Minuten.
Wenn du da jetzt auch noch Stunden als eigene EInheit mit ins Boot
holst, dann kann man das natürlich machen. Aber der Aufwand!
1
ISR(TIMER0_COMPA_vect)// wird im Millisekundentakt aufgerufen
2
{
3
milliSekunden++;
4
if(milliSekunden==1000)
5
{
6
milliSekunden=0;
7
Sekunden++;
8
9
if(Sekunden==60)
10
{
11
Sekunden=0;
12
Minuten++;
13
14
if(Minuten==60)
15
{
16
Minuten=0;
17
Stunden++;
18
}
19
}
20
21
// wir sind auf jeden Fall schon mal auf einer ganzen Sekunde
22
// sind seit dem Start mehr als 2 Stunden und 23 Minuten vergangen,
23
// dann darf
24
// daher auch der Auftrag zum Messen rausgegeben werden
25
if((Stunden>2)||
26
(Stunden==2&&Minuten>5))
27
doMeasure=1;
28
}
29
}
und seien wir uns mal ehrlich. Mit einem uint16_t für die Minuten kann
man bis zu 65535 abzählen. Das sind 1092 Stunden. Oder 45.5 Tage.
Das sollte wohl reichen. :-)
Es tut mir fast ein wenig Leid, euch noch einmal belästigen zu müssen
aber:
ich habe versucht, 2 Modi einzufügen.
Der erste soll aktiviert werden, wenn ein Schalter auf Ground geht.
Der zweite, wenn der Schalter gegen nichts geht und der interne Pull-up
resistor greift und so an VCC geht.
der erste Modus: ganz normale Funktion, wie oben auch beschrieben
der zweite Modus: nach Wiederholungen des Watchdogs soll der gleiche
Code wie bei 1 ausgeführt werden; Watchdog wegen des Stromsparens.
Problem:
Bei dem ersten Modus schalten sich die Pins 10 Sekunden nach Aktivierung
wieder ab.
Der zweite scheint gar nicht erst zu funktionieren.
Könnte einer mal den Code überfliegen und Tipps geben?
Danke.
1
#define F_CPU 1000000UL // 1 MHZ
2
3
#include<avr/io.h>
4
#include<util/delay.h>
5
#include<avr/interrupt.h>
6
#include<avr/wdt.h> // Supplied Watch Dog Timer Macros
Ich habe die while-Schleife grundlegend verändert, den Watchdog und
seinen Interrupt definiert und einen neuen Modus hinzugefügt. In meinen
Augen ist da alles korrekt. Compilen geht auch. Leider will der
übermenschliche Code-Gott nicht so, wie ich das will.
Ja schrieb:> okay habs geschafft.
Glückwunsch dazu.
Schick doch den nun funktionierenden Quelltext hinterher, damit der
Nächste, dem das Gleiche passiert, nicht in der Sackgasse endet.
MfG Paul