Hallo,
ich habe mich dieses Wochenende mal an das Projekt "ATmega8" gewagt. Ich
habe mir hier das Tutorial angeschaut, Datenblatt gelesen (nagut ich
gebe zu: überflogen) usw. Als ich dachte, dass ich soweit bin, habe ich
mir die Schaltung aus dem Anhang aufgebaut und mir ein kleines Programm
gebaut, welches ein Lauflicht erzeugt (soll hin und her laufen; Hello
World eines µC):
1
#include<avr/io.h>
2
#include<avr/interrupt.h>
3
#include<stdint.h>
4
5
uint8_tmyTeiler;
6
uint8_tWert1;
7
charrichtung;
8
9
intmain(void){
10
11
DDRD=0xff;//Port D als Ausgang setzen
12
PORTD=0b00000001;//Wert auf Port D ausgeben
13
14
TCCR0|=(1<<CS02)|(0<<CS00);//Timer0 starten
15
16
TCNT0=0;//Startwert für den Timer
17
18
TIMSK|=(1<<TOIE0);//Interrupt für Überlauf bei Timer 0 anschalten
19
20
myTeiler=0;
21
Wert1=1;
22
richtung='u';
23
24
sei();//Interrupts generell anschalten
25
26
while(1){
27
if(myTeiler==1){
28
myTeiler=0;
29
30
// Steuerung des Lauflichtmusters
31
if(richtung=='u')Wert1=Wert1<<1;
32
if(richtung=='d')Wert1=Wert1>>1;
33
if(Wert1==128)richtung='d';
34
if(Wert1==1)richtung='u';
35
36
PORTD=Wert1;
37
}
38
}
39
40
/* wird nie erreicht */
41
return0;
42
}
43
44
45
//Interruptserviceroutine für den Timer 0
46
ISR(TIMER0_OVF_vect){
47
48
cli();
49
50
myTeiler++;
51
52
TCNT0=0;
53
54
sei();
55
}
Ich starte den Controller darüber, dass ich die Batterie (9V-Block)
anklemme (vielleicht liegt da schon die Ursache).
Aber nun zum Problem: Manchmal läuft das Lauflicht super mehrere Minuten
durch, ohne Probleme. Manchmal stopt es aber nach wenigen Sekunden, oder
es produziert komische Muster, oder es gehen alle LEDs an. Nach einem
Reset (per Büroklammer, da mir der Taster dafür zu schade war ;-) )läuft
es dann meistens stabiler, aber auch nicht immer. Jetzt ist aber die
große Frage wieso das passiert. Ich hatte auch andere Programme, wo eine
LED einfach nur blinken sollte. Die hat zwischendurch einfach ihren
Rhythmus geändert. Ich vermute, dass das mit dem anderen Problem
zusammenhängt, aber mir fehlt die Erfahrung darauf zu kommen, was ich
falsch gemacht habe. Vielleicht kann mir hier ja jemand helfen, denn mit
diesem Zustand kann ich das nächste Projekt "Display" wohl vergessen.
Vielen Dank schon mal im Voraus.
mfg
Tusor
@ Gunter N. (tusor)
> while(1) {> if (myTeiler == 1){> myTeiler = 0;
Das wird aber ein ziemlich schnelles Lauflicht.
>//Interruptserviceroutine für den Timer 0>ISR(TIMER0_OVF_vect){> cli();
Böse. Das hat in einer ISR nichts zu suchen!
> myTeiler++;> TCNT0 = 0;
Sinnlos, der Zähler ist schon auf 0 nach dem Overflow.
> sei();
Ganz böse!
MFG
Falk
Ach ja, das fehlt eine volatile, siehe Interrupt.
volatile uint8_t myTeiler;
Und ganz grosser Fehler! Es fehlt der 100nF Kondensator zwischen Vcc und
Gnd NAH am AVR!
MFG
Falk
Hallo,
die 100n an 7805 Ein- und Ausgang fehlen, die 100n an AVCC-GND und
VCC-GND nahe am AVR auch.
Ein 9V-Block ist schneller leer als man meint, wenn man
rumexperimentiert, der Innenwiderstand steigt und die
Spannungsschwankungen erzeugen lustige Effekte und lange Fehlersuche.
Als Notbehelf gegen die dynamischen Effekte also am Eingang des 7805
noch einen 100µF-Elko parallel.
Ansonsten über eine bessere Spannungsquelle nachdanken.
Über die Software habe ich jetzt nicht drüber geschaut, überlasse ich
den C-Freaks. ;)
Gruß aus Berlin
Michael
Gebe Micheal U. vollkommen recht. Die 100nF-Stütz-Cs sind durchaus nicht
umsonst in nahezu allen Schaltungen möglichst nahe an ICs.
Hier fliest z.B. beim Schalten der LEDs ein relativ hoher Strom, welcher
im Schaltmoment die Versorgungsspannung einbrechen lässt. Dadurch wird
bei einem uC u.U. der Brownout-Reset ausgelöst, oder der Controller geht
in einen unvorhergesehenen Zustand.
Um das Thema noch ein wenig zu Ergänzen: zwischenzeitlich ist man bei
neuen Designs dazu übergegangen die 100nF Kondensatoren durch 10nF Typen
zu ersetzen, da diese genügend Energie zur Pufferung bereitstellen aber
deutlich bessere HF-Eigenschaften besitzen (Thema EMV).
Beste Grüße an alle...
Warum fängst du als Anfänger gleich mit Interrupts und Timer an? Ein
kleines Lauflicht kannst du auch einfach per Endlosschleife und Pausen
erreichen. Würde deine Fehlerquellen doch um einiges Einschränken und
wäre erstmal einfacher zum lernen.
ui ui ui.....so viele Fehler hätte ich doch nicht erwartet.
Also die Kondensatoren hatte ich eigentlich bewusst weggelassen, da ich
in irgendwelchen Beschreibungen gefunden hatte, dass der am Eingang des
7805 Schwankungen der Spannungsversorgung glätten soll (ich hatte da an
Rippel aus einem Gleichrichter gedacht). Außerdem hatte ich auch
gelesen, dass man dem am Ausgang des 7805 nicht unbedingt braucht. Da
sieht man mal wieder, dass man sich auf das, was im Internet steht nicht
immer verlassen sollte ;-) Ich werde mir jetzt mal noch ein paar
Kondensatoren bestellen und die noch mit reinbasteln (hoffentlich passen
die noch hin).
@ Falk: Das cli() und sei() stehen da noch drin, bevor ich die
Simulation gemacht habe. Ich konnte mich nämlich noch erinnern, dass man
während einer Abarbeitung einer ISR erstmal andere Interrupts abschalten
sollte. Aber vielleicht erinnere ich mich da auch falsch. Das Rücksetzen
auf 0 steht da noch drin, da ich auch mit anderen Zahlen experimentiert
habe und die Zeile nicht jedesmal neu tippen wollte.
zur Geschwindigkeit des Lauflichts: offenbar lasse ich mich da durch die
Effekte mit dem 9V-Block irritieren. Denn wenn es läuft, dann läuft es
nämlich eigentlich langsam (ca. 1s von rechts wieder nach rechts).
Manchmal läuft es aber auch so schnell, dass man es wirklich kaum als
Lauflicht erkennt. Vermutlich ist das die richtige Frequenz und ich
lasse mich durch etwas anderes beirren.
Ich werde mir jetzt mal ein paar Kondensatoren bestellen und evtl. eine
andere Spannungsversorgung basteln. Dann wird der Code nochmal
überarbeitet und dann bin ich gespannt, wie es dann läuft.
Vielen Dank nochmal.
Gast wrote:
> Warum fängst du als Anfänger gleich mit Interrupts und Timer an? Ein> kleines Lauflicht kannst du auch einfach per Endlosschleife und Pausen> erreichen. Würde deine Fehlerquellen doch um einiges Einschränken und> wäre erstmal einfacher zum lernen.
Naja hundertprozentiger Anfänger bin ich nicht. Wir haben irgendwann in
der Uni mal 8051 programmiert und das hat schon viel Spaß gemacht (war
damals auch noch Assembler). Aber es ist etwas anderes, wenn man als
Student vor ein fertig eingerichtetes Board und Programm gesetzt wird,
als wenn man es selbst programmiert. Aber die Interrupts sind ja
eigentlich nicht so schwierig (dachte ich)
Gunter N. wrote:
> @ Falk: Das cli() und sei() stehen da noch drin, bevor ich die> Simulation gemacht habe. Ich konnte mich nämlich noch erinnern, dass man> während einer Abarbeitung einer ISR erstmal andere Interrupts abschalten> sollte. Aber vielleicht erinnere ich mich da auch falsch.
Das Abschalten der Interrupt-Freigabe erledigt Dein Mikrocontroller vom
Typ AVR ganz automatisch für Dich, sogar ohne Dich um Erlaubnis zu
bitten. Das ist in der Hardware so verdrahtet, dass das I-Bit im
Statusregister beim Sprung in den Interrupt-Vektor gelöscht und beim
Befehl reti (Return from Interrupt, der quasi in der abschließenden
Klammer } drinsteckt) wieder gesetzt wird. Das cli() ist nicht tragisch,
sondern nur überflüssig. Das sei() am Ende kann jedoch unter ganz
bestimmten Umständen zu Problemen führen, weil damit die Abarbeitung
anderer Interrupts noch vor dem Verlassen des Interrupt Handlers (und
vor dem Zurückschreiben der gesicherten Register) wieder freigegeben
wird. Also generell weglassen.
Johannes M. wrote:
> Gunter N. wrote:>> @ Falk: Das cli() und sei() stehen da noch drin, bevor ich die>> Simulation gemacht habe. Ich konnte mich nämlich noch erinnern, dass man>> während einer Abarbeitung einer ISR erstmal andere Interrupts abschalten>> sollte. Aber vielleicht erinnere ich mich da auch falsch.> Das Abschalten der Interrupt-Freigabe erledigt Dein Mikrocontroller vom> Typ AVR ganz automatisch für Dich, sogar ohne Dich um Erlaubnis zu> bitten. Das ist in der Hardware so verdrahtet, dass das I-Bit im> Statusregister beim Sprung in den Interrupt-Vektor gelöscht und beim> Befehl reti (Return from Interrupt, der quasi in der abschließenden> Klammer } drinsteckt) wieder gesetzt wird. Das cli() ist nicht tragisch,> sondern nur überflüssig. Das sei() am Ende kann jedoch unter ganz> bestimmten Umständen zu Problemen führen, weil damit die Abarbeitung> anderer Interrupts noch vor dem Verlassen des Interrupt Handlers (und> vor dem Zurückschreiben der gesicherten Register) wieder freigegeben> wird. Also generell weglassen.
Danke für die Erklärung. Freut mich, dass ich mit meiner Erinnerung
nicht ganz falsch lag.
Die Fehler im Code habe ich bereits behoben, da ja nicht weiter schwer.
Zum Geschwindigkeitsproblem: Das liegt definitiv an dem 9V-Block. Ich
habe kurzerhand ein altes PC-Netzteil als Spannungsversorgung
hergenommen und da lieft das Lauflicht deutlich schneller. Aber es
bleibt leider immer noch manchmal stehen. Ich hoffe, dass ich das dann
mit den Kondensatoren beheben kann.
So, heute sind endlich meine Kondensatoren angekommen. Ich bin sehr
erstaunt was so poplige Dinger an den richtigen Stellen ausmachen können
;-). Ich habe jetzt je 100nF an Eingang und Ausgang des 7805 und fast
direkt am Controller zwischen VCC und GND. Es scheint so zu laufen.
Allerdings bin ich über die Geschwindigkeit erstaunt. Denn als ich es
ohne die Kondensatoren mit 12V aus dem PC-Netzteil probiert habe (am
Eingang des 7805; bevor hier jemand schreit, dass ich den Controller
röste) lief es so schnell, dass man es nicht als Lauflicht erkennen
konnte. Und nun ist es so langsam, dass die LEDs mit ca. 4Hz
wechseln...wenn nicht sogar noch weniger. Wenn ich das richtig zurück
gerechnet habe, müsste der Controller da mit ca. 1MHz laufen (im
Gegensatz zu obigem Quelltext arbeite ich jetzt mit 1024er Prescaler).
Kann das stimmen? Ich bilde mir ein, gelesen zu haben, dass der interne
Oszillator 4MHz bzw. bei manchem Typen 8MHz macht (im Handbuch habe ich
leider nichts dazu gefunden, oder war ich nur zu blöd zum suchen?) Ich
muss mal schauen, wo ich auf meiner Platine noch einen externen
Oszillator unterbekomme, dann weiß ich zumindest sicher, mit welcher
Taktfrequenz der Controller läuft.
Vielen Dank auf jeden Fall noch mal für den Tip mit den Kondensatoren.
Jetzt kann ich mich an die nächsten Aufgaben wagen.
Gut, die Frage nach der Taktfrequenz hat sich gerade geklärt. Man sollte
doch nicht nur per Hand in einem PDF suchen, sondern auch mal die Suche
vom Acrobat bemühen. Und sie da, man kann die interne Frequenz mit den
Fusebits einstellen und standardmäßig steht die tatsächlich auch 1MHz.
Gunter N. wrote:
> @ Falk: Das cli() und sei() stehen da noch drin, bevor ich die> Simulation gemacht habe. Ich konnte mich nämlich noch erinnern, dass man> während einer Abarbeitung einer ISR erstmal andere Interrupts abschalten> sollte. Aber vielleicht erinnere ich mich da auch falsch.
Ich will nicht 100% ausschließen, daß es solche CPUs gibt, wo man das
manuell machen muß.
Aber beim 8051 oder AVR geht das automatisch, da hat eine Manipulation
der Interruptlogik im Interrupthandler nichts verloren.
Peter
Hm.
In der ISR mit cli() und sei() zu hantieren hat noch einen weiteren,
handfesten Nachteil:
Wenn Du in der ISR bist, und andere Interrupts auftreten, bspw.
CounterOverflows oder oder, dann gehen diese einfach verloren, wenn Du
die Interrupts bei Eintritt in die ISR abschaltest.....
Wenn ich mich richtig erinnere, werden ISRs auch gequeued, wenn während
des Ausführens einer ISR eine andere (bzw. ein andere Interrupt)
eintritt ....
Hoffe, das richtig dargestellt zu haben...
@uwe:
Ein cli() am Anfang der ISR ist schlicht wirkungslos, weil ohnehin schon
abgeschaltet.
Ein sei() am Ende der ISR hingegen kann zur Folge haben, dass ein
anhängiger Interrupt die ISR dort unterbricht und damit doppelt Stack
verbraucht wird. Man riskiert einen möglicherweise selten auftretenden
und daher schwer zu findenden Absturz aufgrund von Stacküberlauf.
Gequeued wird das nicht. Es geht aber nicht verloren, weil der Interrupt
nach Ende der ISR nach wie vor ansteht und dann verarbeitet wird. Eine
anstehende Interruptanforderung ist ein Zustand, kein Ereignis, auch
wenn sie von einem flankengetriggerten Input stammt. Folglich gehen
Interrupts erst dann verloren, wenn während der Abarbeitung von anderen
Interrupts mehrere Interrupts aus der gleiche Quelle auftreten.
Peter Dannegger wrote:
> Gunter N. wrote:>> @ Falk: Das cli() und sei() stehen da noch drin, bevor ich die>> Simulation gemacht habe. Ich konnte mich nämlich noch erinnern, dass man>> während einer Abarbeitung einer ISR erstmal andere Interrupts abschalten>> sollte. Aber vielleicht erinnere ich mich da auch falsch.>> Ich will nicht 100% ausschließen, daß es solche CPUs gibt, wo man das> manuell machen muß.>> Aber beim 8051 oder AVR geht das automatisch, da hat eine Manipulation> der Interruptlogik im Interrupthandler nichts verloren.>>> Peter
Das mir das jemand gesagt hat, ist nun leider schon eine Weile her (ca.
3 Jahre). Wahrscheinlich habe ich da die Hälfte schon wieder vergessen
und die eigentliche Aussage lautete: "Man muss die Interrupts in einer
ISR abschalten und hinterher wieder an, aber das macht der Controller
für uns." Aber das ist ja nun auch egal. Ab sofort lasse ich es weg und
gut ist.