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
intmain(void)
18
{
19
DDRD=0xFF;//define as output
20
DDRB=0x00;//define as input
21
PORTB=0xFF;
22
unsignedcharnr=0x00;
23
while(1)
24
{
25
nr++;
26
display(nr);
27
_delay_ms(1000);
28
}
29
}
30
31
voiddisplay(charkey){
32
switch(key){
33
case0x00:
34
PORTD=ZERO;
35
break;
36
case0x01:
37
PORTD=ONE;
38
break;
39
case0x02:
40
PORTD=TWO;
41
break;
42
case0x03:
43
PORTD=THREE;
44
break;
45
case0x04:
46
PORTD=FOUR;
47
break;
48
case0x05:
49
PORTD=FIVE;
50
break;
51
case0x06:
52
PORTD=SIX;
53
break;
54
case0x07:
55
PORTD=SEVEN;
56
break;
57
case0x08:
58
PORTD=EIGHT;
59
break;
60
case0x09:
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.
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.
Dir ist bewusst, dass die 0 nie (selbst wenns funktioniert) angezeigt
wird?
Eigentlich müsste es so sein:
1
intmain(void)
2
{
3
DDRD=0xFF;//define as output
4
DDRB=0x00;//define as input
5
PORTB=0xFF;
6
unsignedcharnr=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?
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
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
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.
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.
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
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.
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.
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
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.
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.
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
voiddisplay(charkey);
2
3
voidmain()
4
{
5
....
6
7
display('s');
8
}
9
10
voiddisplay(charkey)
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
voidmain()
2
{
3
....
4
5
display('s');
6
}
7
8
voiddisplay(charkey)
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
voiddisplay(charkey)
2
{
3
....
4
}
5
6
voidmain()
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.
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.
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?
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
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.
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
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.
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.
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.