Forum: Compiler & IDEs AVR - Variable wird immer auf null zurückgesetz


von A.O. (Gast)


Lesenswert?

Hallo,

also erstmal möchte ich erwähnen, dass ich in diesem Bereich neu bin.

Ich versuche mit kleinen Testprogrammen einen einstieg in C bzw. uC zu 
bekommen, jedoch habe ich schon am Anfang Schwierigkeiten damit.

Darum wende ich mich an euch und hoffe auf Hilfe.

der code:
1
#define F_CPU 4000000UL 
2
#include <util/delay.h> 
3
#include <stdint.h>
4
#include <avr/io.h>
5
6
#define ZERO  0b00111111
7
#define ONE   0b00000110
8
#define TWO   0b01011011
9
#define THREE 0b01001111
10
#define FOUR  0b01100110
11
#define FIVE  0b01101101
12
#define SIX   0b01111101
13
#define SEVEN 0b00000111
14
#define EIGHT 0b01111111
15
#define NINE  0b01101111
16
17
int main(void)
18
{
19
    DDRD = 0xFF; //define as output
20
    DDRB = 0x00; //define as input
21
    PORTB = 0xFF;
22
    unsigned char nr = 0x00;
23
    while(1)
24
    {
25
    nr++;
26
    display(nr);
27
    _delay_ms(1000);
28
    }
29
}
30
31
void display(char key) {
32
  switch(key) {
33
      case 0x00:
34
        PORTD = ZERO;
35
        break;
36
      case 0x01:
37
        PORTD = ONE;
38
        break;
39
      case 0x02:
40
        PORTD = TWO;
41
        break;
42
      case 0x03:
43
        PORTD = THREE;
44
        break;
45
      case 0x04:
46
        PORTD = FOUR;
47
        break;
48
      case 0x05:
49
        PORTD = FIVE;
50
        break;
51
      case 0x06:
52
        PORTD = SIX;
53
        break;
54
      case 0x07:
55
        PORTD = SEVEN;
56
        break;
57
      case 0x08:
58
        PORTD = EIGHT;
59
        break;
60
      case 0x09:
61
        PORTD = NINE;
62
        break;
63
      default:
64
        PORTD = 0b10000000;
65
        break;
66
    }
67
}

Wenn ich den Code auf einen Atmega8 übertrage, zeigt mir die 
7-Segment-Anzeige immer eine "1" an. Sollte aber immer um "1" erhöht 
werden.

Daraus schließe ich, dass die Variable vor dem erhöhen immer den Wert 
"0" annimmt. Dies kann ich so genau sagen, weil ich in der Routine 
urspünglich zwei Taster hatte für "+1" & "-1". Aber schon dass einfache 
erhöhen in der Schleife funktioniert nicht!

Ich verstehe nicht warum.
Habe ich etwas falsch gemacht, vergessen oder ist der Compiler falsch 
konfiguriert (AVR Studio 5.0.1223).

Habe schon diverse Versuche und Nachforschungen betrieben  -> ohne 
Erfolg!!!

Schonmal Danke im voraus.

von hahaha (Gast)


Lesenswert?

vielleicht schlägt der Watchdog oder der Reset zu

von Bronco (Gast)


Lesenswert?

Laß mal eine LED in der Hauptschleife blinken.
Falls sie nicht mit genau 1Hz blinkt, ist es wahrscheinlich der 
Watchdog.

von Peter (Gast)


Lesenswert?

hallo,
auf jeden fall solltest du das Unterprogramm vor das Hauptprogramm 
setzen. Der Compiler sollte dir eigentlich eine Warnung ausgeben wenn du 
das so geschrieben hast, weil er zuerst den Aufruf zum Unterprogramm 
liest aber nicht weiß was damit gemeint ist.

von Christian F. (cmf) Benutzerseite


Lesenswert?

Dir ist bewusst, dass die 0 nie (selbst wenns funktioniert) angezeigt 
wird?
Eigentlich müsste es so sein:
1
int main(void)
2
{
3
    DDRD = 0xFF; //define as output
4
    DDRB = 0x00; //define as input
5
    PORTB = 0xFF;
6
    unsigned char nr = 0x00;
7
    while(1)
8
    {
9
    display(nr);
10
    _delay_ms(1000);
11
    nr++;
12
    }
13
}
Ist denn die Optimierung eingeschaltet (-Os)?
Ist am Reset Pin ein Widerstand 10k nach VCC?
Ist vielleicht der Watchdog dauerhaft aktiviert (Fuses)?
Hast du sonst was an den Fuses geändert?

von Oliver (Gast)


Lesenswert?

Hast du denn mal ausprobiert, ob dein Display überhaupt etwas anderes 
als "Eins" anzeigen kann? Denn prinzipiell ist das Programm schon soweit 
in Ordnung, daß  es von "Eins" an weiter zählen sollte.

Es ist auch immer empfehlenswert, allen Warnungen des Compilers 
nachzugehen, und diese zu beheben, auch wenn die mit dem aktuellen 
Problem nichts zu tun haben.

Oliver

von Michael M. (technikus)


Lesenswert?

Christian F. schrieb:
> Dir ist bewusst, dass die 0 nie (selbst wenns funktioniert) angezeigt
> wird?
Noch schlimmer: Wenn nr den Wert 9 überschritten hat, läuft display() 
für die Zahlen 10 bis 254 in den default-Zweig der switch-Anweisung. 
Nach den ersten 9 Sekunden heißt es also erstmal 4 Minuten ohne Anzeige 
warten. Denn ich gehe mal davon aus, daß Bit 7 von PORTD nicht genutzt 
ist. Dann läuft nr über und man hat wieder 9 s eine Anzeige.
Wollte ich nur mal so anmerken, auch wenn die Schleife nur ein Test ist. 
Vermeiden ließe sich das entweder durch
1
display(nr%10); // die Modulodivision /10 liefert immer nur 10 "Restklassen"
oder durch
1
if(nr >= 10) nr = 0;

Ansonsten: Wie Peter schon schrieb, sollte sich das Programm so gar 
nicht übersetzen lassen, weil display() in main() noch gar nicht bekannt 
ist. Main hinter allen anderen Funktionen im Code stehen zu haben ist 
etwas unschön, eine separate Deklaration void display(char key); vor 
main() finde ich besser.


Servus
Michael

von Peter II (Gast)


Lesenswert?

Michael M. schrieb:
> Ansonsten: Wie Peter schon schrieb, sollte sich das Programm so gar
> nicht übersetzen lassen, weil display() in main() noch gar nicht bekannt
> ist.

in C sollte das aber zulässig sein.

von Christian F. (cmf) Benutzerseite


Lesenswert?

Peter II schrieb:
> in C sollte das aber zulässig sein.

Wäre mir neu...

von Peter II (Gast)


Lesenswert?

Christian F. schrieb:
> Wäre mir neu...

ist aber so:
1
int main() {
2
   test1(1);
3
   test2("asdf");
4
}
5
6
void test1( int a ) {
7
   printf("%d\n", a );
8
}
9
10
void test2( char* s  ) {
11
   printf("%s\n", s );

gibt zwar warnungen, läuft aber (scheinbar) fehlerfrei.

von Stefan E. (sternst)


Lesenswert?

Christian F. schrieb:
> Wäre mir neu...

Es ist zulässig. Der Compiler versucht dann den Prototyp zu "erraten". 
Erst wenn das dann mit dem später bekannt gemachten echten Prototyp 
kollidiert, gibt es einen Fehler. Ansonsten höchstens eine Warnung.

von Michael M. (technikus)


Lesenswert?

Stefan Ernst schrieb:
> Es ist zulässig. Der Compiler versucht dann den Prototyp zu "erraten".
> Erst wenn das dann mit dem später bekannt gemachten echten Prototyp
> kollidiert, gibt es einen Fehler. Ansonsten höchstens eine Warnung.

Stimmt! Sorry, da war ich zu schnell, es sind nur Warnungen. Aber als 
Anfänger drei Warnungen wie
warning: implicit declaration of function 'display'
warning: conflicting types for 'display'
warning: previous implicit declaration of 'display' was here
zu ignorieren, wenn man ein Programm nicht zum Laufen bringt, ist schon 
etwas mutig.

Servus
Michael

von Coder (Gast)


Lesenswert?

Wie wäre es, falls vorhanden, einen Debugger zu verwenden? Gerade man 
mit uC anfängt ist sowas super. Ansonsten wie schon angeraten wurde, die 
Schaltung sowie deine Fuses prüfen.

von Karl H. (kbuchegg)


Lesenswert?

Eventuell ist es auch ein Problem in der Schaltung selbst, bzw. der 
Stromversorgung, die mit dem Einschalten der LED einbricht. Der µC 
resetet, das Programm beginnt wieder von vorne.

Um solche ungewollten Resets zu erkennen, ist es oft eine ganz gute 
Idee, am Programmanfang erst mal auf der Anzeige ein spezielles 'Muster' 
auszugeben. Benutzt werden kann alles was gefällt und was nicht mit 
einem Muster im regulären Betrieb verwechselt werden kann. Das hängt 
natürlich auch davon ab, welche Möglichkeiten man zur Verfügung hat. Hat 
man nur 7-Seg Anzeige würde sich zb anbieten erst mal an allen Stellen 
nur die Punkte einzuschalten und die für (hausnummer) 0.5 Sekunden 
leuchten zu lassen.
Genausogut funktioniert auch eine einzelne seperate LED, die im 
Normalbetrieb nicht verwendet wird oder zur Not auch einfach nur eine 
zeitlang nichts anzuzeigen, dann gibt einem das zeitweilige Ausbleiben 
einer Anzeige den entsprechenden Hinweis.

von Timm R. (Firma: privatfrickler.de) (treinisch)


Lesenswert?

Hallo,

Peter II schrieb:
>
> ist aber so:
> [….]
>
> gibt zwar warnungen, läuft aber (scheinbar) fehlerfrei.

Dass moderne Implementierungen dies ermöglichen, heisst ja noch nicht, 
dass es auch „erlaubt“ ist.

Als etwas skurrilen Vergleich könnte man einen Autofahrer wählen, der 
behauptet natürlich dürfe man in einer geschlossenen Ortschaft 100km/h 
fahren, er habe das bereits mehrfach ausprobiert und es habe immer 
funktioniert.

Im aktuellen Standard-Entwurf C11 vom 2010-12-02 ist noch immer der 
Funktionsaufruf ohne vorhergehende Deklaration explizit nicht 
ausdrücklich erlaubt (not specified).

Natürlich spricht nichts dagegen, wenn sprachmächtige Entwickler das 
dennoch tun, aber einem Anfänger (wie dem TO) sollte doch 
standardkonformes Entwickeln wärmstens ans Herz gelegt werden – denke 
ich.

Viele Grüße

 Timm

von Karl H. (kbuchegg)


Lesenswert?

Timm Reinisch schrieb:


> Im aktuellen Standard-Entwurf C11 vom 2010-12-02 ist noch immer der
> Funktionsaufruf ohne vorhergehende Deklaration explizit nicht
> ausdrücklich erlaubt (not specified).

Das Problem ist, dass das in C traditionell genau anders rum war.
In C war von Anfang an die implizite Funktionsdeklaration vorgesehen und 
es ist genau definiert, wie der Compiler in so einem Fall vorgeht. Das 
stammt alles noch aus der K&R Zeit.


Einer der wenigen Punkte, die C++ von Anfang an richtig gemacht hat: 
dort sind implicit declarations schon immer verboten. Daher ist das in 
C++ nicht nur eine Warning sondern ein echter Error.

Ändert natürlich nichts daran, dass implicit declarations keine gute 
Idee sind und man generell Warnings nicht ignoriert. Selbst wenn man ein 
Programm bekommt, welches scheinbar funktioniert.

von A.O. (Gast)


Lesenswert?

Hallo,
also erst mal Danke für all diese Beiträge.

Problem gelöst.
Es war der Watchdog-timer.
Den hatte ich nichtsahnend aktiviert.
Jetzt funktioniert es auch mit den Tastern, so wie ursprünglich gedacht 
war.

Zusätzliches:
Den 10k-Widerstand habe ich am Reset-Pin.

Die Optimierungen hatte ich auf 00, da ich meine gelesen zu haben, das 
es erst bei Ressourcen intensiven Programmen empfohlen wird. 
Funktioniert aber auch bei 01, hab's extra getestet.

Die vorgeschlagenen Tests mit den LEDs habe ich nicht durchgeführt, da 
es ja schon sofort ging, nachdem der Watchdog weg war.

Den Debug-mode hatte ich verwendet, ich glaube da funktionierte es. 
Komisch!

Das mit der Schleife und dem überschreiten der "9" wäre unkritisch 
gewesen, da es nur ein Test war(bei dem kompletten Code mit den Tastern 
habe ich entsprechende Vorkehrungen getroffen) und Pin7 wäre der Punkt.

Aber ich hätte noch 3 Fragen.

1. Kann mir einer sagen warum das mit dem aktivierten Watchdog nicht 
funktionierte, bzw. wofür der da ist? Oder besser gesagt, ich kann mir 
nicht vorstellen, dass seine Aufgabe das Resetten des IC ist.

2. Ich habe "void display(char key);" separat deklariert.
Weil ich finde es auch schicker, wenn main am beginn ist. Dann muss man 
nicht immer den Startpunkt suchen um ein Programm nachzuvollziehen.
Ich bin grundsätzlich für Formalitäten.
Vielleicht habe ich Karl Heinz Buchegger auch nur falsch verstanden aber 
es klingt für mich so als wäre das separate Deklarieren falsch bzw. 
nicht so gut?

3. Da das Thema sozusagen solved ist, sollte ich das irgendwie anmerken 
bzw. Diskussion schließen oder Ähnliches. Ist mein erster Beitrag.

Danke.

von Karl H. (kbuchegg)


Lesenswert?

A.O. schrieb:

> Aber ich hätte noch 3 Fragen.
>
> 1. Kann mir einer sagen warum das mit dem aktivierten Watchdog nicht
> funktionierte, bzw. wofür der da ist? Oder besser gesagt, ich kann mir
> nicht vorstellen, dass seine Aufgabe das Resetten des IC ist.

Doch.
Genau das ist seine Aufgabe.

Dein Programm muss den Watchdog regelmässig 'zurückpfeifen', sonst beißt 
er.

OK, das ist umgangssrachlich ausgedrückt.
Was ist die Idee: Die Idee dahinter ist es, dass dein Programm 
regelmässig, solange es noch korrekt läuft, den Watchdogtimer wieder auf 
0 zurücksetzt, so dass der nie seine Auslösezeit erreicht. Passiert in 
deinem Programm irgendwas, sei es eine unbeabsichtigte Endlosschleife 
oder ein Hardwraefehler, so dass dieses regelmässige Zurücksetzen 
unterbleibt, dann ist die Annahme die, dass mit dem µC irgendwas 
passiert ist und die beste Lösung darin besteht, das Programm wieder von 
vorne starten zu lassen, so dass dieser 'Hänger' von aussen abgewürgt 
wird und dein Programm die Chance bekommt wieder in geordneten Bahnen zu 
operieren.

> 2. Ich habe "void display(char key);" separat deklariert.
> Weil ich finde es auch schicker, wenn main am beginn ist. Dann muss man
> nicht immer den Startpunkt suchen um ein Programm nachzuvollziehen.
> Ich bin grundsätzlich für Formalitäten.
> Vielleicht habe ich Karl Heinz Buchegger auch nur falsch verstanden aber
> es klingt für mich so als wäre das separate Deklarieren falsch bzw.
> nicht so gut?

Im Grunde spielts keine allzugroße Rolle. Solange du nur einen 
Funktionsprototypen dafür hast. Halte dich einfach an die Regel: Wenn du 
das Programm von oben nach unten liest, dann darfst du Dinge erst dann 
verwenden, wenn du sie vorher zumindest deklariert hast.
1
void display(char key);
2
3
void main()
4
{
5
  ....
6
7
  display( 's' );
8
}
9
10
void display(char key)
11
{
12
   ....
13
}

ist perfekt in Ordnung. Dagegen ist nichts einzuwenden. Ohne den 
Prototypen am Anfang hingegen sieht die Sache gleich ganz anders aus.

So rum
1
void main()
2
{
3
  ....
4
5
  display( 's' );
6
}
7
8
void display(char key)
9
{
10
   ....
11
}

wärs schlecht.
Und da es auf Dauer lästig ist, immer einen Protoypen zu haben, bzw. es 
zusätzlichen Aufwand bedeutet, den Protoypen immer mit zu ändern, wenn 
sich an der Funktionssignatur etwas ändert, zieht man die Funktion dann 
eben vor
1
void display(char key)
2
{
3
   ....
4
}
5
6
void main()
7
{
8
  ....
9
10
  display( 's' );
11
}

So ist die Funktion selbst ihr eigener Protoyp und man muss nicht bei 
Änderungen an 2 Stellen ändern.

von Peter II (Gast)


Lesenswert?

A.O. schrieb:
> 1. Kann mir einer sagen warum das mit dem aktivierten Watchdog nicht
> funktionierte, bzw. wofür der da ist? Oder besser gesagt, ich kann mir
> nicht vorstellen, dass seine Aufgabe das Resetten des IC ist.

doch genau das ist seine aufgabe. Man muss ihn Regelmäßig zurücksetzen, 
wenn das ausbleibt dann resetet er den µC. Damit kann man verhinder das 
ein Programm in einer Entlosscheife festhängt und damit z.b. eine 
Raumsonde abstürzt.

> Ich habe "void display(char key);" separat deklariert.
> Weil ich finde es auch schicker, wenn main am beginn ist. Dann muss man
> nicht immer den Startpunkt suchen um ein Programm nachzuvollziehen.
wiederrum weiss jeder der regelmäßig C programmiert das main meist unten 
ist.

> Die Optimierungen hatte ich auf 00, da ich meine gelesen zu haben, das
> es erst bei Ressourcen intensiven Programmen empfohlen wird.
> Funktioniert aber auch bei 01, hab's extra getestet.
das delay funktioniert nur mit optimierung. Stelle sie auf S. Wenn dein 
Programm mit optimierung nicht funktioniert dann ist es zu 99.9% ein 
fehler im code.

von A.O. (Gast)


Lesenswert?

OK

der Watchdog ist mir nun klar.

Ich werde in Zukunft versuchen main ans Ende zu verfrachten.

Peter II schrieb:
> wiederrum weiss jeder der regelmäßig C programmiert das main meist unten
> ist.

Wie ich schon ganz Oben schrieb, ist C bzw. uC fast Neuland für mich.
Habe schon einige Codes gesehen und so aber bislang nur mit Basic und 
Stückchenweise mir Assembler gearbeitet. Dennoch Danke, war ja gut 
gemeint.

Würdet ihr mir empfehlen die Optimierungen einzuschalten und im 
Konfliktfall aus S zu stellen?

von Peter II (Gast)


Lesenswert?

A.O. schrieb:
> Würdet ihr mir empfehlen die Optimierungen einzuschalten und im
> Konfliktfall aus S zu stellen?
nicht im Konfliktfall sondern immer auf S

von A.O. (Gast)


Lesenswert?

OK

Danke

Damit wären alle meine Fragen geklärt, bis auf eine.

Muss ich noch irgendetwas bezüglich des Beitrags machen schließen, als 
gelöst hinterlegen oder einfach so lassen.

von Michael M. (technikus)


Lesenswert?

Peter II schrieb:
>> Ich habe "void display(char key);" separat deklariert.
>> Weil ich finde es auch schicker, wenn main am beginn ist. Dann muss man
>> nicht immer den Startpunkt suchen um ein Programm nachzuvollziehen.
> wiederrum weiss jeder der regelmäßig C programmiert das main meist unten
> ist.

Bei mir steht main() meist am Anfang. Die ganzen Prototypen sind für 
jede .c-Datei in einer separaten .h-Datei. Die kann man dann leicht mit 
#include "beispiel.h" auch in andere Quelltextdateien einbinden. 
Außerdem finde ich die Liste der Prototypen in der .h-Datei auch sehr 
praktisch, um sich schnell einen Überblick über die Funktionen einer 
Quelltextdatei zu verschaffen.
Gerade weil ich regelmäßig programmiere, kenne ich nicht jede Funktion 
auswendig, die ich je programmiert habe. Da ist die Liste der Prototypen 
bequemer, als nochmal den ganzen Quelltext durchsehen zu müssen.

Ansonsten: Auf die 10kOhm Pullup an Reset kann man in einer einigermaßen 
störungsfreien Umgebung problemlos verzichten.

Optimierung steht bei mir immer auf Os - kostet ja nichts ;-) Außer sie 
regt mich beim Debuggen auf C-Ebene auf.

Servus
Michael

von Peter II (Gast)


Lesenswert?

Michael M. schrieb:
> Bei mir steht main() meist am Anfang. Die ganzen Prototypen sind für
> jede .c-Datei in einer separaten .h-Datei. Die kann man dann leicht mit
> #include "beispiel.h" auch in andere Quelltextdateien einbinden.

da stellt sich aber schon die Frage warum man funktionen aus der 
hauptdatei in einen anderen dateien braucht. Das ist eigentlich nicht 
üblich.

von Christian F. (cmf) Benutzerseite


Lesenswert?

Michael M. schrieb:
> Ansonsten: Auf die 10kOhm Pullup an Reset kann man in einer einigermaßen
> störungsfreien Umgebung problemlos verzichten.

So habe ich das auch immer gehandhabt. Bis ich mal stundenlang nach dem 
Fehler bei einer RS485 Datenübertragung zwischen 2 µCs auf einem 
Breadbord gesucht habe. Mit Pullup lief es dann auf Anhieb.

> Muss ich noch irgendetwas bezüglich des Beitrags machen schließen, als
> gelöst hinterlegen oder einfach so lassen.

Einfach so lassen.

von Klaus (Gast)


Lesenswert?

A.O. schrieb:
> Die Optimierungen hatte ich auf 00, da ich meine gelesen zu haben, das
> es erst bei Ressourcen intensiven Programmen empfohlen wird.

NEIN! Das ist Unsinn :-) Erstens funktionieren z. B. die Delaysroutinen 
nicht, ohne optimierung. Und zweitens verdeckt man sich manche Fehler 
durch das abschalten der Optimierung (fehlende volatiles z. B.).

Der einzige Grund die Optimierung aus zu schalten ist, wenn du per 
JTAG-Debugger Breakpoints auf ganz bestimmte Codezeilen setzten 
möchtest, oder ähnliches.

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.