Forum: Mikrocontroller und Digitale Elektronik Atmega328P Analog-Read (sei()) "friert Controller ein"


von Chris K. (lesso)


Lesenswert?

Hallo Forum,

ich habe ein Problem mit meiner Schaltung/meinem Code.
Eigentlich möchte ich von einem Potentiometer analog lesen um die 
Blinkgeschwindigkeit einer LED zu beeinflussen.
Das funktioniert nicht, da die Variable "speed" nie geändert wird.
Nach dem ersten Durchlauf der "while" in der Funktion "loop()" hängt 
sich mein Atmega auf. Wenn ich den Funktionsaufruf von "sei()" entferne, 
läuft der Loop erwartungsgemäß durch.

Der Poti funktioniert einwandfrei (getestet mit Multimeter).
Falls es hilft, kann ich auf ein Foto meiner Schaltung hochladen, aber 
ich vermute den Fehler eher im Code selbst.

Habt ihr eine Idee?

Grüße, Chris

Hier mein Code:
1
#ifndef F_CPU
2
#define F_CPU 16000000UL
3
#endif
4
5
6
#include <avr/io.h>
7
#include <avr/interrupt.h>
8
#include <util/delay.h>
9
10
void loop(void);
11
int read_analog();
12
13
int speed = 0;
14
15
int main(void) {
16
  DDRD = (1<<PORTD0) | (1<<PORTD1) | (1<<PORTD2);
17
  ADMUX = (1 << REFS0) | (1 << MUX2) | (1 << MUX0);
18
  ADCSRA = (1 << ADEN ) | (1 << ADIE) | (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2);
19
  DIDR0 = (1 << ADC5D);
20
  read_analog();
21
  sei(); // <--- problem
22
  loop();
23
  return 0;
24
}
25
26
void delay_ms_own(int ms) {
27
  while(ms > 0) {
28
    _delay_ms(1);
29
    ms--;
30
  }
31
}
32
33
void loop() {
34
  while (1) {
35
    PORTD |= (1<<PORTD2 );
36
    delay_ms_own(100);
37
    PORTD &= ~(1<<PORTD2);
38
    delay_ms_own(100);
39
    PORTD |= (1<<PORTD2 );
40
  }
41
}
42
43
int read_analog() {
44
  ADCSRA |= (1 << ADSC);
45
  return 0;
46
}
47
48
ISR(ADC_vect) {
49
  speed = ADC;
50
  read_analog();
51
}

von Thomas E. (thomase)


Lesenswert?

Er stürzt ab, wenn der Interrupt ausgeführt wird. Da die richtige ISR 
vorhanden ist, tippe ich mal auf einen falsch eingestellten Controller.

von S. Landolt (Gast)


Lesenswert?

> hängt sich mein Atmega auf
Wie äußert sich das?

> da die Variable "speed" nie geändert wird
Und wie das? Ich sehe nur eine Zuweisung an speed, aber sonst nichts, 
keine Abfrage darauf bzw. Weiterverarbeitung.

von Brummbär (Gast)


Lesenswert?

Ich werde trotzdem schonmal "volatile" ins Rennen.

von Hubert G. (hubertg)


Lesenswert?

Du vermischt hier Arduino Code mit normalen C-Code.
Im main wird alles nur einmal aufgerufen, da gibt es kein return, dann 
kommt eine for oder while Schleife, je nach belieben.

von Stefan F. (Gast)


Lesenswert?

Da wir das gerade erst hatten:

Hast du eventuell beim linken die MCU Angabe vergessen? In diesem Fall 
würde unter anderem die Sprungtabelle für die ISR Routinen fehlen.

von Stefan F. (Gast)


Lesenswert?

> Du vermischt hier Arduino Code mit normalen C-Code.

Der Arduino Style ist deutlich zu sehen, aber er hat keine Arduino 
Library eingebunden und keine Arduino Funktion verwendet.

Das "return 0" am Ende der main() Funktion ist fachlich korrekt, wenn 
auch beim avr-gcc ausnahmsweise unnötig.

>> hängt sich mein Atmega auf
> Wie äußert sich das?

Ist das nicht offensichtlich? Er wird wohl an PD2 eine LED angeschlossen 
haben, die aufhört, zu blinken.

von Chris K. (lesso)


Lesenswert?

Thomas E. schrieb:
> Er stürzt ab, wenn der Interrupt ausgeführt wird. Da die richtige ISR
> vorhanden ist, tippe ich mal auf einen falsch eingestellten Controller.

Das hilft mit leider nicht weiter. Was genau kann da falsch eingestellt 
sein?

S. Landolt schrieb:
> Wie äußert sich das?

Der Inhalt der "while"-Schleife in der Funktion "loop()" wird nur einmal 
ausgeführt und nicht wie durch die "while(1)" zu erwarten unendlich oft.
Sprich: die LED schaltet sich an, nach 100 Millisekunden aus, nach 
weiteren 100ms wieder an und danach bleibt sie angeschalten.

S. Landolt schrieb:
> Und wie das? Ich sehe nur eine Zuweisung an speed, aber sonst nichts,
> keine Abfrage darauf bzw. Weiterverarbeitung.

Ich würde die Variable "speed" gerne verwenden. Den lesenden Zugriff 
baue ich ein sobald es funktioniert. Man könnte zB. sowas machen:
1
while (1) {
2
  PORTD |= (1<<PORTD2 );
3
  delay_ms_own(speed);
4
  PORTD &= ~(1<<PORTD2);
5
  delay_ms_own(speed);
6
}
Das bringt mir allerdings aktuell nichts, weil sich der Controller wie 
gesagt aufhängt. Dass die Variable derzeit nur geschrieben, aber nicht 
gelesen wird, ist mir klar.

Stefan U. schrieb:
> Da wir das gerade erst hatten:
>
> Hast du eventuell beim linken die MCU Angabe vergessen? In diesem Fall
> würde unter anderem die Sprungtabelle für die ISR Routinen fehlen.

Ich bin ziemlich unerfahren. Kannst du mir sagen wie ich das in Atmel 
Studio 7 erreichen kann?

Stefan U. schrieb:
> Ist das nicht offensichtlich? Er wird wohl an PD2 eine LED angeschlossen
> haben...

Korrekt. An PD2 ist eine LED.

: Bearbeitet durch User
von S. Landolt (Gast)


Lesenswert?

> LED ... an, nach 100 Millisekunden aus, nach
> weiteren 100ms wieder an
Das ist doch aussagekräftiger als "friert ein" und "hängt sich auf".

Auch wenn ich mir darauf keinen Reim machen kann; sollte die ADC-ISR 
wirklich, wie von zweien vermutet, in einen Reset münden, so kann doch 
der ADC nicht so lange benötigen.

von Stefan F. (Gast)


Lesenswert?

>> Hast du eventuell beim linken die MCU Angabe vergessen?

> Ich bin ziemlich unerfahren. Kannst du mir sagen wie ich das in
> Atmel Studio 7 erreichen kann?

Gar nicht. Das kann Dir nur passieren, wenn du "manuell" auf der 
Kommandozeile compilierst oder ein fehlerhaftes Makefile verwendest. 
Atmel Studio benutzt normalerweise kein Makefile.

Und du hast ganz sicher den richtigen Mikrocontroller-Typ für den 
Compiler eingestellt? Gibt der Compiler irgendwelche Hinweise dazu aus, 
oder gar Warnmeldungen?

Hast du AVCC angeschlossen? Hast du vielleicht Abblock-Kondensatoren 
(nahe zum Mikrocontroller) vergessen?

von Thomas E. (thomase)


Lesenswert?

Chris K. schrieb:
> Das hilft mit leider nicht weiter. Was genau kann da falsch eingestellt
> sein?

Hast du das schon kontrolliert?

Wenn du für einen anderen Controller kompilierst, und da wärest du 
wahrlich nicht der erste, wird eine für deinen Controller nicht passende 
Interrupt-Vektor-Tabelle angelegt. Dann springt das Programm sonstwo 
hin, nur nicht in die ADC-ISR.

: Bearbeitet durch User
von Chris K. (lesso)


Lesenswert?

Ich habe einen Teil des Codes falsch kopiert:
Der Inhalt der "while" sieht eigentlich so aus:
1
if(speed) {
2
  PORTD |= (1<<PORTD2 );
3
  delay_ms_own(speed);
4
  PORTD &= ~(1<<PORTD2);
5
  delay_ms_own(speed);
6
}
Ich habe das "if" absichtlich nicht mit kopiert, weil ich es für 
bedeutungslos hielt. Der Ausdruck wird sowieso immer "true" sein, war 
meine Annahme.

Nach wiederholtem Programmieren des Controllers blinkt jetzt gar nichts 
mehr.
Wenn ich allerdings die "if" zu
1
if(1) //always true
ändere und alles andere gleich lasse, funktioniert alles wie erwartet.
Die LED blinkt und durch das Verstellen des Potentiometer schneller oder 
langsamer.

Wieso löst die "if" nicht auf "true" auf wenn ich nur "speed" als 
Condition angebe?

: Bearbeitet durch User
von S. Landolt (Gast)


Lesenswert?

Kommt jetzt Brummbär zum Zuge mit dem 'volatile'?

von Chris K. (lesso)


Lesenswert?

S. Landolt schrieb:
> Kommt jetzt Brummbär zum Zuge mit dem 'volatile'?

Das war's.
1
volatile int speed = 0;
löst das Problem.

Kann mir das jemand genauer erläutern?
Ich verstehe nicht wieso hier ein Unterschied entsteht.

von S. Landolt (Gast)


Lesenswert?

Sorry, ich kenne es nur vom Hörensagen, kann kein C.

von Sascha W. (sascha-w)


Lesenswert?

Chris K. schrieb:
> S. Landolt schrieb:
>> Kommt jetzt Brummbär zum Zuge mit dem 'volatile'?
>
> Das war's.
>
1
> volatile int speed = 0;
2
>
> löst das Problem.
>
> Kann mir das jemand genauer erläutern?
> Ich verstehe nicht wieso hier ein Unterschied entsteht.
Ohne volatile bekommt der Code in loop nichts davon mit das der Wert 
in einer ISR geändert wurde. Mit volatile wird der Wert jedesmal von 
der entsprechenden Speicherstelle neu geholt.

Sascha

von Thomas E. (thomase)


Lesenswert?

Chris K. schrieb:
> Ich habe einen Teil des Codes falsch kopiert:
> Der Inhalt der "while" sieht eigentlich so aus:

Also haben wir hier die ganze Zeit auf falschem Code rumgeraten?

Kopf - Tisch. Dreimal.

von S. Landolt (Gast)


Lesenswert?

> Kopf - Tisch. Dreimal.
Soviel zum Thema "offensichtlich".

von Np R. (samweis)


Lesenswert?

Der Compiler weiß nicht, dass speed in der ISR geändert wird, denn er 
kann nicht erkennen, wann oder ob die ISR überhaupt ausgeführt wird.

Für den Compiler steht da also nur:
1
int speed = 0;
2
...
3
4
if(speed) {
5
...

Das kann er auch gleich wegoptimieren.

Das "volatile" sagt ihm, dass speed auch außerhalb von main geändert 
werden kann.

von S. Landolt (Gast)


Lesenswert?

Schon, aber wie passt das zu:

Chris K. schrieb:
> Wenn ich allerdings die "if" zu
> if(1) //always true
> ändere und alles andere gleich lasse, funktioniert alles wie erwartet.
> Die LED blinkt und durch das Verstellen des Potentiometer schneller oder
> langsamer.

Hier kommt doch speed angeblich irgendwie rüber, ganz ohne volatile.

von Chris K. (lesso)


Lesenswert?

Thomas E. schrieb:
> Chris K. schrieb:
>> Ich habe einen Teil des Codes falsch kopiert:
>> Der Inhalt der "while" sieht eigentlich so aus:
>
> Also haben wir hier die ganze Zeit auf falschem Code rumgeraten?
>
> Kopf - Tisch. Dreimal.

S. Landolt schrieb:
>> Kopf - Tisch. Dreimal.
> Soviel zum Thema "offensichtlich".

Entschuldigt. Das war klar fehlendes Wissen meinerseits.
Aber dafür fragt man schließlich in einem Forum.
Auch wenn mein initialer Post das Problem nicht dargestellt hat.

np r. schrieb:
> Der Compiler weiß nicht, dass speed in der ISR geändert wird, denn
> er
> kann nicht erkennen, wann oder ob die ISR überhaupt ausgeführt wird.
>
> Für den Compiler steht da also nur:int speed = 0;
> ...
>
> if(speed) {
> ...
>
> Das kann er auch gleich wegoptimieren.
>
> Das "volatile" sagt ihm, dass speed auch außerhalb von main geändert
> werden kann.


Danke für die Klärung. Das macht natürlich Sinn.

von Np R. (samweis)


Lesenswert?

S. Landolt schrieb:
> Schon, aber wie passt das zu:
...

Bei if (speed) kann der Compiler, wenn seiner Ansicht nach speed = 0 
ist, das gesamte if-Statement wegwerfen - es wird ja sowieso nie 
ausgeführt.

Bei if(1) muss er das If-Statement und den Code darin behalten.
Darin wird speed verwendet. Und in der ISR geändert. In diesem Falle: 
Glück gehabt.

von Np R. (samweis)


Lesenswert?

Chris K. könnte ja mal die Größe des kompilierten Programms vergleichen, 
eimmal mit if(speed) (ohne volatile), einmal mit if(1) und einmal mit 
if(speed) und volatile.

von Thomas E. (thomase)


Lesenswert?

Chris K. schrieb:
> Aber dafür fragt man schließlich in einem Forum.

Ja, aber mit dem kompletten Code.

Zu den typischen Anfängerfehlern gehört das Weglassen/Vergessen von 
"volatile" und, das allerdings nicht nur bei Anfängern, das generelle 
Ausschließen von Fehlern: "Nein, daran kann es gar nicht liegen." Oder: 
"Daß da ein Fehler liegt, hätte ich niemals gedacht."
Fatal ist es, wenn beides auch noch zusammenkommt.

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.