Forum: Compiler & IDEs Komisches Verhalten des ADC bei Funktionsaufruf / µC friert ein


von CDA_verwirrt (Gast)


Lesenswert?

Guten Abend,
ATMega8, 8MHz
Ich habe ein für mich unverständliches und auch komisches Problem. Ich 
initialisiere den ADC, löse eine einmalkonvertierung in einem Interrupt 
aus, speichere den ADC-Wert in einer globalen Variable und greife in 
einer Unterfunktion in einer if-Abfrage auf diese Variable zu.
1
volatile uint16_t g_dimmdauer = 10;
2
3
void main (void) {
4
//ADC zur Dimmgeschwindigkeitseinstellung per Poti an PC2 / ADC2
5
ADMUX |= (1 << REFS0) | (1 << MUX1);
6
//ADC Enable und Vorteiler 64
7
ADCSRA |= (1 << ADEN) | (1 << ADPS1) | (1 << ADPS2);
8
ADCSRA |= (1 << ADSC);
9
10
  while (1) {
11
    buntflackern();
12
  }
13
}
14
15
ISR (TIMER2_OVF_vect) {
16
static uint16_t ADC_neu = 0;
17
18
//ADC Auswerten
19
while (!(ADCSRA & (1 << ADIF)));
20
ADC_neu = ADC/10;
21
//Zittern beseitigen
22
if ((ADC_neu < g_dimmdauer - 5) || (ADC_neu > g_dimmdauer +5))
23
  g_dimmdauer = ADC_neu;
24
25
//Hier kommen noch Tasterabfragen, die allerdings funktionieren, wenn ich den ADC auskommentiere oder der µC nicht einfriert.
26
}
27
28
void buntflackern(void) {
29
static uint16_t  i =0;
30
i++;
31
  if(i >= g_dimmdauer) {
32
    i=0;
33
    g_farbwahl++;
34
    g_menue_gesetzt=0;
35
  }
36
  if (g_farbwahl > 6)
37
    g_farbwahl =0;
38
  
39
  standfarbe();
40
}

Ok, soweit so klar. Was passiert? Ich stelle den Poti so, dass der ADC 
mir Werte nahe 0 ausgibt. Drehe ich am Poti, erhöhe ich den Wert, das 
buntflackern wird langsamer, da g_dimmdauer größer wird. Drehe ich jetzt 
allerdings in die andere Richtung, also verringere ich den ADC und damit 
g_dimmdauer, friert der µC ein. Er reagiert nicht mehr auf das Poti und 
auf Tastereingaben.

Ideen, wodran es liegt?

Grüße

von Karl H. (kbuchegg)


Lesenswert?

CDA_verwirrt schrieb:

> Ok, soweit so klar. Was passiert? Ich stelle den Poti so, dass der ADC
> mir Werte nahe 0 ausgibt. Drehe ich am Poti, erhöhe ich den Wert, das
> buntflackern wird langsamer, da g_dimmdauer größer wird.

Das kann ich so nicht nachvollziehen, da ich nirgends sehe, wo der ADC 
jemals neu gestartet werden würde. Und auf Autorun steht er auch nicht.

> while (!(ADCSRA & (1 << ADIF)));

Nimm halt nicht das Interrupt Flag her.

Du setzt das ADSC Bit um den ADC zu starten und wenn der ADC fertig ist, 
nimmt er es wieder auf 0 zurück. Einfach, simpel, geschmacklos und 
funktioniert perfekt.

Ein Interrupt Flag (ADIF) hingegen musst du explizit löschen, wenn du 
keine ISR dafür hast. Und auch das kann ich in deinem Code nicht 
feststellen.


Edit: Im Grunde kannst du dir das Abfragen auch sparen. Wenn du den ADC 
zum Ende der ISR neu startest, dann ist der mit Sicherheit beim nächsten 
Timeroverflow, mindestens 256 Takte später, schon längst mit der 
Wandlung fertig.

von CDA_verwirrt (Gast)


Lesenswert?

Ok blöd. Hab das mal geändert, aber kein Erfolg. Der Fehler ist nach wie 
vor da, der Atmega friert ein.

Ich habe den Code jetzt mal auf folgendes geändert:
1
volatile uint16_t g_dimmdauer = 10;
2
3
void main (void) {
4
//ADC zur Dimmgeschwindigkeitseinstellung per Poti an PC2 / ADC2
5
ADMUX |= (1 << REFS0) | (1 << MUX1);
6
//ADC Enable und Vorteiler 64
7
ADCSRA |= (1 << ADEN) | (1 << ADPS1) | (1 << ADPS2);
8
ADCSRA |= (1 << ADSC);
9
10
  while (1) {
11
    buntflackern();
12
  }
13
}
14
15
ISR (TIMER2_OVF_vect) {
16
static uint16_t ADC_neu = 0;
17
18
//ADC Auswerten
19
ADC_neu = ADC/10;
20
  g_dimmdauer = ADC_neu;
21
22
ADSCRA |= (1 << ADSC);
23
24
//Hier kommen noch Tasterabfragen, die allerdings funktionieren, wenn ich den ADC auskommentiere oder der µC nicht einfriert.
25
}
26
27
void buntflackern(void) {
28
static uint16_t  i =0;
29
i++;
30
  if(i >= g_dimmdauer) {
31
    i=0;
32
    g_farbwahl++;
33
    g_menue_gesetzt=0;
34
  }
35
  if (g_farbwahl > 6)
36
    g_farbwahl =0;
37
  
38
  standfarbe();
39
}

von CDA_verwirrt (Gast)


Lesenswert?

Bah... Ich kann nicht editieren. Der Schreibfehler beim ADCSRA |= (1 << 
ADSC); ist natürlich nicht im Originalcode mit drin.

von Karl H. (kbuchegg)


Lesenswert?

Dann würde ich mal auf den Timer konzentrieren

von CDA_verwirrt (Gast)


Lesenswert?

Ich hab mir das jetzt nochmal genau angeguckt. Sobald ich die Zeile
1
g_dimmdauer = ADC_neu;

auskommentiere, macht der µC alles, was ich möchte. Nur, dass sich die 
g_dimmdauer eben nicht per Poti verändern lässt. Heißt also, dass der 
Timer das ja auch alles schafft. Ich würd deswegen jetzt auf ein Problem 
mit

volatile uint16_t g_dimmdauer tippen. Hab ich das was nicht beachtet, 
was man nicht mit globalen Variablen tun darf? Oder sollte ich 
vielleicht den Poti seltener Abfragen und Verändern?

von Karl H. (kbuchegg)


Lesenswert?

volatile ist nur die halbe Miete. Du musst auch die Variable atomar 
auslesen (d.h. unter Interruptschutz), weil es eine 16 Bit Variable ist.

Ich würde das ganze ehrlich gesagt auch anders machen.
Ich würde mir in der ISR nur ein Flag setzen, dass es wieder mal was am 
ADC zu tun gibt. Die komplette Logik, die den ADC ausliest und 
auswertet, würde ich dann in die Hauptschleife verlagern, getriggert von 
diesem Flag.

von CDA_verwirrt (Gast)


Lesenswert?

Mensch... das heißt, wenn ich den ADC nur als 8-Bitter nehme, sollte es 
da keine Probleme geben? Ich teile ja sowieso den ADC-Wert durch 10.

Idee: Ich setze das ADLAR-Bit und damit wird der ADC-Wert von Links 
ausgelesen. Und dann lese ich nur ADCH aus. Netter Nebeneffekt: Ich 
brauche keine "Zitterabfrage" mehr, weil die unteren 2 Bits eh nichts 
zählen. Dann setze ich g_dimmdauer noch als 8-Bit Variable, weil der eh 
nur Werte bis Maximal 255 durch den ADC kriegen kann.

Stimmt der Gedankengang so? Testen und programmieren kann ich leider 
erst heute Abend zu Hause...

Grüße

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

CDA_verwirrt schrieb:
> Stimmt der Gedankengang so?

Ja.

von Karl H. (kbuchegg)


Lesenswert?

CDA_verwirrt schrieb:
> Mensch... das heißt, wenn ich den ADC nur als 8-Bitter nehme, sollte es
> da keine Probleme geben?

Das weiß ich nicht.

Denn: Das schlimmste was dir passieren kann, ist das du mit falschen 
Werten rechnest. Aber hängen sollte das Programm deswegen ja nicht.

Auf der anderen Seite werde ich das Gefühl nicht los, dass du nicht 
deinen kompletten Code herzeigst. Da sind ein paar Kommentare in deinem 
Code, die mich das glauben lassen bzw. es fehlen ganz offensichtlich 
Teile. Ob und wie diese Dinge da jetzt noch zusätzlich mit reinspielen 
kann kein Mensch sagen, solange er nicht den kompletten Code sieht.

von CDA_verwirrt (Gast)


Lesenswert?

Guten morgen :-)

Ich hab gestern noch das Problem gelöst gekriegt. Als ich erstmal alle 
variablen auf 8 Bit umgestellt hatte, lief der ATMega8 und machte genau 
das, was er sollte. Ich hatte in einem anderen Programmteil noch eine 
kleine Plausibilitätsabfrage, über die er mit falschen 16 Bit Werten 
vielleicht nicht kam. Glaube ich. Sicher bin ich mir an der Stelle nicht 
und will das auch nicht weiter ausprobieren. Jetzt tut er ja wieder das, 
was ich von ihm will und der 8 Bit ADC ist immernoch mehr als 
ausreichend.

Den kompletten Code hab ich tatsächlich nicht hergezeigt. Sind immerhin 
mittlerweile über 700 Zeilen Code... Also hab ich das aufs Wesentliche 
begrenzt. Man verzeihe mir.

Danke für die Lösungshilfe!

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.