Forum: Mikrocontroller und Digitale Elektronik Anfängerfehler mit ATmega8?


von Gunter N. (tusor)


Angehängte Dateien:

Lesenswert?

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_t myTeiler;
6
uint8_t Wert1;
7
char richtung;
8
 
9
int main (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
  return 0;
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

von Falk B. (falk)


Lesenswert?

@  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

von Falk B. (falk)


Lesenswert?

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

von Michael U. (amiga)


Lesenswert?

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

von Erich R. (riedi)


Lesenswert?

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...

von Gast (Gast)


Lesenswert?

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.

von Gunter N. (tusor)


Lesenswert?

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.

von Gunter N. (tusor)


Lesenswert?

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)

von Johannes M. (johnny-m)


Lesenswert?

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.

von Gunter N. (tusor)


Lesenswert?

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.

von Gunter N. (tusor)


Lesenswert?

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.

von Gunter N. (tusor)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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

von uwe (Gast)


Lesenswert?

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...

von (prx) A. K. (prx)


Lesenswert?

@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.

von Gunter N. (tusor)


Lesenswert?

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.

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.