Forum: Mikrocontroller und Digitale Elektronik Benötige hilfe bei ATmega8 Timer


von Fabi (Gast)


Lesenswert?

Hallo zusammen,

ich bin totaler Neuling was Mikrocontroller angeht. Ich habe versucht 
eine LED mit meinem ATmega8-16PU im Sekundentakt blinken zu lassen, bin 
daran aber jämmerlich gescheitert.
Die LED macht einfach überhaupt nichts.
Kann mir bitte einer sagen was in meinem Programm nicht stimmt.
(F_CPU = 4MHz)

#include <avr/io.h>
#include <avr/interrupt.h>

//Variable für die Zeit
volatile unsigned int bruchteile;

int main(void)
{
  DDRC = (1<<PC5);
  // Timer 0 konfigurieren
  TCCR0 = (1<<CS00); // Prescaler 0

  // Overflow Interrupt erlauben
  TIMSK |= (1<<TOIE0);

  // Global Interrupts aktivieren
  sei();

  while(1)
  {
    if(bruchteile == 0) PORTC ^= (1<<PC5);
  }
  return 0;
}

/*
Der Overflow Interrupt Handler
wird aufgerufen, wenn TCNT0 von
255 auf 0 wechselt (256 Schritte),
*/
ISR(TIMER0_OVF_vect)
{
  /* Interrupt Aktion alle
  4000000/256 Hz = 15625 Hz
  bzw.
  1/15625 = 0,064 ms
  */
  bruchteile++;
  if(bruchteile == 15625) bruchteile=0;

}

von Stefan E. (sternst)


Lesenswert?

bruchteile ist 256 Takte lang Null. In dieser Zeit toggelst du laufend 
(also mehrfach) die LED. Und wenn diese Toggel-Anzahl ganzzahlig ist, 
hat sie hinterher den gleichen Zustand wie vorher, du siehst dann also 
maximal ein sehr kurzes Aufblitzen.

von Fabi (Gast)


Lesenswert?

Dann müsste ja, wenn ich es richtig verstanden habe, das Problem behoben 
sein wenn ich schreibe:

ISR(TIMER0_OVF_vect)
{
  /* Interrupt Aktion alle
  4000000/256 Hz = 15625 Hz
  bzw.
  1/15625 = 0,064 ms
  */
  bruchteile++;
  if(bruchteile == 15625)
  {
   PORTC ^= (1<<PC5);
   bruchteile=0;
  }

}
(Meine while-Schleife ist jetzt leer)
Allerdings tut sich immernoch nichts.

von 1.8T-Passat (Gast)


Lesenswert?

Setze in Deinem Timer-Interrupt ein flag. Zum Beispiel so:

if(bruchteile == 15625)
        {
        bruchteile=0;
        flag |= 1<<0
        }

In der while(1) Schleife das Bit abfragen, und nach einmaligem toggeln 
wieder zurücksetzen:

while(1)
  {
    if(bruchteile == 0) PORTC ^= (1<<PC5);
    flag &= ~(1<<0)
  }
  return 0;

von 1.8T-Passat (Gast)


Lesenswert?

sorry, Korrektur:

if((bruchteile == 15625) && (flag & 1<<0))
        {
        bruchteile=0;
        flag |= 1<<0
        }

von Joachim B. (jojo84)


Lesenswert?

Hi!

Aus gegebenem Anlass (kam hier im Forum auch schon öffers mal vor) frage 
ich:
Hast du die LED richtig herum in der Schaltung? Da hilft der beste Code 
nix, wenn die LED mit der Kathode an + hängt... ;)

von Magnetus (Gast)


Lesenswert?

Die Main bekommt es nicht mit, wenn sich die Variable "bruchteile" in 
der ISR ändert, solange diese nicht als volatile definiert ist!

Gruß,
Magnetus

von spess53 (Gast)


Lesenswert?

Hi

>Die Main bekommt es nicht mit, wenn sich die Variable "bruchteile" in
>der ISR ändert, solange diese nicht als volatile definiert ist!

Wer Lesen kann....

MfG Spess

von Fabi (Gast)


Lesenswert?

Joachim A. schrieb:
> Hast du die LED richtig herum in der Schaltung?

Ja, in anderen Schaltungen hat sie Funktioniert, es liegt wirklich nur 
am Programm.

Magnetus schrieb:
> Die Main bekommt es nicht mit, wenn sich die Variable "bruchteile" in
> der ISR ändert, solange diese nicht als volatile definiert ist!

Siehe oben, ist als volatile definiert.

1.8T-Passat schrieb:
> Setze in Deinem Timer-Interrupt ein flag.

Ich verstehe das nicht wirklich. Wie muss ich das flag definieren? Und 
wie frag ich es später ab? Wie einen Pin oder anders?

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Dein Code hat mehrere Probleme:

Wenn bruchteile gleich Null ist, ist das immer 256 Takte lang so. Das 
ist ein Logikproblem beim Programmentwurf.

Und es wird in main() auf eine im Interrupt ggf. veränderte Variable 
zugegriffen, die größer 1 Byte ist, d.h. der Zugriff ist nicht atomar. 
Dieses Thema wird im AVR-GCC-Tutorial behandelt.

Eine mögliche Lösung für beide Probleme mit einem Kommunikations-Flag 
zwischen ISR und main() sähe so aus:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
//Variable für die Zeit
5
volatile uint16_t bruchteile;
6
volatile uint8_t aktion_ausfuehren;
7
8
int main(void)
9
{
10
  DDRC = (1<<PC5);
11
  // Timer 0 konfigurieren
12
  TCCR0 = (1<<CS00); // No Prescaler
13
14
  // Overflow Interrupt erlauben
15
  TIMSK |= (1<<TOIE0);
16
17
  // Global Interrupts aktivieren
18
  sei();
19
20
  while(1)
21
  {
22
    if( aktion_ausfuehren )
23
    { 
24
      aktion_ausfuehren = 0;
25
      PORTC ^= (1<<PC5);
26
    }
27
  }
28
  return 0;
29
}
30
31
/*
32
Der Overflow Interrupt Handler
33
wird aufgerufen, wenn TCNT0 von
34
255 auf 0 wechselt (256 Schritte),
35
*/
36
ISR(TIMER0_OVF_vect)
37
{
38
  /* Interrupt Aktion alle
39
  4000000/256 Hz = 15625 Hz
40
  bzw.
41
  1/15625 = 0,064 ms
42
  */
43
  bruchteile++;
44
  if( bruchteile == F_CPU/256 )
45
  { 
46
    bruchteile = 0;
47
    aktion_ausfuehren = 1;
48
  }
49
}

bruchteile bräuchte dafür nicht volatile zu sein, da es ausserhalb der 
ISR nicht abgefragt oder geändert wird.

von Fabi (Gast)


Lesenswert?

@ Stefan

Danke für deine Mühe, ich habe das Programm so eingefügt, aber es hat 
auch nicht geklappt. Habe es bereits auch mit einem neuen ATmega8 
versucht, aber auch keine blinkende LED.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Dann ist was oberfaul.

Ich habe das Programm auf dem Pollin Funk-AVR-Evaluationsboard am 
laufen, allerdings weil ich die Onboard-LED nutzen will habe ich 
DDRC/PORTC/PC5 auf DDRD/PORTD/PD5 angepasst. Es blinkt fröhlich im 1s 
Hell/Dunkel Wechsel.

Dir ist es SICHER bereits gelungen, genau diese LED in genau dieser 
Schaltung an PC5 ein- und auszuschalten?

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Stefan B. schrieb:

> Dir ist es SICHER bereits gelungen, genau diese LED in genau dieser
> Schaltung an PC5 ein- und auszuschalten?

Wenn das bereits funktioniert hat: Schalte die LED direkt nach der 
DDR... Anweisung EIN. Die sollte dann 1s lang leuchten und dann im 
Wechsel aus/an gehen.

Wenn - Spekulation - der AVR vor dem ersten Blinken ein RESET macht, 
dann siehst du wenigstens, ob er mal angelaufen ist (LED kurz an). Beim 
jetzigen Code siehst du in diesem Fall nix, Dunkel bleibt Dunkel. Sollte 
ein RESET passieren, dann müssen wir den Watchdog kontrollieren und uns 
näher mit der Schaltung befassen und besonders mit der 
Spannungsversorgung.

von Fabi (Gast)


Lesenswert?

Stefan B. schrieb:
> Dir ist es SICHER bereits gelungen, genau diese LED in genau dieser
> Schaltung an PC5 ein- und auszuschalten?

Ja, soweit war ich schon. Hab es gerade eben auch nochmal mit einem 
anderem Programm getestet. Also an der Schaltung liegt es sicher nicht.

Kann es sein das ich einen Fehler gemacht habe als ich die Frequenz 
eingestellt habe? Ich habe dazu bei den Fuse-bits "Int. RC Osc. 4MHz; 
Start-up time; 6 CK + 0ms" ausgewählt.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Denke das ist OK. Jedenfalls müsste in endlicher Zeit irgendwas 
passieren. Deine Programm mit dem einfachen LED einschalten funktioniert 
doch auch mit dieser Fuseeinstellung, oder?

Mit was arbeitest du -  AVR Studio+WinAVR, WinAVR allein, AVR-GCC? Hast 
du daran gedacht F_CPU in den Compileroptionen zu setzen? Stimmt der 
Wert, besonders die Anzahl der Nullen :)

Was macht dieses Programm (mit -Os Optimierung übersetzen):
1
#include <avr/io.h>
2
#include <util/delay.h>
3
4
int main(void)
5
{
6
  DDRC = 1<<PC5;
7
  while(1)
8
  {
9
    PORTC ^= 1<<PC5;
10
    _delay_ms(1000);
11
  }
12
}

Wenn es blinken sollte, dann bitte Zählen wie oft in einer Minute.

von Fabi (Gast)


Lesenswert?

Stefan B. schrieb:
> Mit was arbeitest du -  AVR Studio+WinAVR, WinAVR allein, AVR-GCC? Hast
> du daran gedacht F_CPU in den Compileroptionen zu setzen? Stimmt der
> Wert, besonders die Anzahl der Nullen :)

Ich arbeite mit AVR Studio+WinAVR.
ÖÖÖÖHM, F_CPU setzen? Nein, habe ich nicht. Wär dir sehr dankbar wenn du 
mir sagen könntest wie ich das mache.

Stefan B. schrieb:
> Was macht dieses Programm (mit -Os Optimierung übersetzen)

Die LED blinkt fröhlich vor sich hin.

Stefan B. schrieb:
> Wenn es blinken sollte, dann bitte Zählen wie oft in einer Minute.

Ich habe 120 mal gezählt. Und wie gesagt, habe 4MHz eingestellt.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Fabi schrieb:

> ÖÖÖÖHM, F_CPU setzen? Nein, habe ich nicht.

Dann müsste in deinem Build Fenster sowas stehen:
1
d:/winavr/bin/../avr/include/util/delay.h:85:3: warning: #warning "F_CPU not defined for <util/delay.h>"

Die util/delay.h setzt dann einen Defaultwert von 1000000UL. Und damit 
werden die _delay_ms(1000) berechnet. In 1 Min. hättest du 30 
Hellphasen.

> Ich habe 120 mal gezählt. Und wie gesagt, habe 4MHz eingestellt.
Du beobachtest 120, d.h. 4x30. Dein AVR läuft tatsächlich mit 4 MHz!

> Wär dir sehr dankbar wenn du
> mir sagen könntest wie ich das mache.

In AVR Studio im Menü Projekt unter Configuration Options bei 
Frequency 4000000 eintragen. Das neuübersetzte Einfachprogramm sollte 
dann ein Ruhepuls von 30 haben...

Warum das Timer-Programm nicht läuft ist mir ein Rätsel. Hänge bitte die 
HEX-Datei an, ich disassembliere das dann. Hat aber Zeit bis morgen, 
weil ich gleich auf die Piste muss :)

von Fabi (Gast)


Lesenswert?

Stefan B. schrieb:
> d:/winavr/bin/../avr/include/util/delay.h:85:3: warning: #warning "F_CPU not 
defined for <util/delay.h>"

Stimmt! Ich hab nicht gewusst das man zusätzlich noch den MC auswählen 
muss.
Jetzt funktioniert alles super! Also auch das Programm, dass du mir 
verbessert hast.

Vielen Dank für diese super Hilfe.

Gruß Fabi

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Hi Fabi

ich schreibe hier statt auf deine Email zu antworten, weil dann auch 
andere ihren Senf^H^H^H Wissen dazugeben können und weil ich in der 
Sache nicht 100% sicher bin.

Also in der Email schreibst du, dass du statt 30 Hellphasen in einer 
Minute ca. 31-32 beobachtest und fragst, woher das kommen kann und wie 
man das beheben kann.

Ich reime mir das mit der (Un)- Genauigkeit des verwendeten internen 
RC-Oszillators zusammen.

Die Genauigkeit des internen RC-Oszillators ist von der 
Versorgungsspannung und von der Temperatur und von Fertigungstoleranzen 
abhängig. Atmel kalibriert die Chips lt. Datenblatt so, dass bei 5V 25°C 
+- 3% erreicht werden.

Die 31 sind (31/30 - 1)*100 % = +3.3% (32 sind +6,7%) in der Taktrate. 
Also in der Region, wo das die Kalibrierung in Betracht kommen kann.

Ein zweiter Effekt ist die Auflösung deiner Messung. Ich weiss nicht 
wie du die 1 Minute misst, vielleiht mit dem Sekundenzeiger einer Uhr, 
also das wäre eine Hell- oder Dunkelphase mehr oder weniger abhängig 
davon wann du die Messung genau startest oder beendest. Und du zählst. 
Der kleinste Zählschritt ist eine Hell- oder Dunkelphase. Schwankungen 
in der Messung von +-1 Schritt wären also quasi vorprogrammiert. Bei 
längeren Messungen sollte das besser werden.

Wie kann man die Genauigkeit erhöhen?

Atmel hat eine Appnote zum Rekalibrieren des internen RC-Oszillators 
mithilfe des OSCCAL Registers. Laut Atmel sollen dabei +-1% machbar 
sein. Ich selbst habe dazu aber keine praktische Erfahrung.

Oder du verwendest eine von Haus aus genauere Taktquelle wie z.B. einen 
externen Ouarz mit wenigen hundert ppm (100 ppm = 0,1 o/oo = 0,01 %) 
Fehler.

Wenn dich das Thema Genauigkeit und Auflösung weiter interessiert: Da 
gibt es den schönen Artikel Auflösung und Genauigkeit

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.