Benötige hilfe bei ATmega8 Timer

Gast #1614379
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;

}
Gast #1614398
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.
Gast #1614481
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;
Gast #1614578
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?
Persönliche Seite #1614657
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.
Persönliche Seite #1614851
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.
Gast #1614852
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.
Persönliche Seite #1614856
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.
Gast #1614875
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.
Persönliche Seite #1614900
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 :)
Gast #1614944
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
Persönliche Seite #1615877
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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren