Forum: Mikrocontroller und Digitale Elektronik ATmega88 mit CTC Modus


von Jonas (Gast)


Lesenswert?

Hallo,
ich bin gerade dabei eine Uhr selbst zu bauen.
Das Programm ist schon fertig geschrieben. Die Minuten/Stunden werden 
richtig gezählt und an mehreren 7-Segment Anzeigen ausgegeben, nur der 
8bit-Timer im CTC Modus scheint nicht richtig zu funktionieren.
Die Suchfunktion im Forum und in Google, sowie das Datenblatt habe ich 
mehrfach durchsucht, allerdings kann ich meinen Fehler nicht finden.

Ich verwende einen ATmega88 mit externem 7.2728Mhz Quarz. Der Timer0 
(8Bit, im CTC Modus, Prescaler 1024) soll die ISR alle hundertstel 
Sekunden aufrufen und diese zählen. Nach einer Minute werden die Minuten 
hochgezählt und der Sekundenzähler zurückgesetzt.
Ich hoffe ihr könnt mir helfen.
1
#include <avr/io.h>
2
#ifndef F_CPU                   //Vordefinieren fuer delay.h
3
#define F_CPU 7372800UL         //Takt ueber externen 7.3728Mhz Quarz
4
#endif      
5
#include <util/delay.h>
6
#include <avr/interrupt.h>
7
8
[]
9
10
unsigned int sekunden=0;         //Zaehler fuer Hundertstel-Sekunden
11
unsigned int minuten=0;          //Zaehler fuer Minuten
12
unsigned int stunden=0;          //Zaehler fuer Stunden
13
14
[]
15
16
ISR(TIMER0_COMPA_vect)            //Interrupt Routine
17
{
18
  sekunden++;                    //Sekunden werden um eins addiert
19
  if (sekunden==6000)            //Wenn eine Minute vorbei...
20
  {
21
    sekunden=0;
22
    minuten++;
23
  }
24
}
25
26
[]
27
28
int main(void)
29
{
30
  TCCR0A |= (1<<WGM01);                // CTC Modus
31
  TCCR0B |= (1<<CS02) | (1<<CS00);    // Prescaler 1024
32
  OCR0A = 72-1;
33
// 7372800/(1024*100)=72 --> 1/100 s
34
  TIMSK0 |= (1<<OCIE0A);              // Output Compare A enable
35
  sei();                              // interrupt an
36
                                      
37
  DDRD = 0b11111111;                     //Alles Ausgaenge
38
  DDRC |= (1 << DDC0) | (1 << DDC1) | (1 << DDC2) | (1 << DDC3) | (1 << DDC4); 
39
//PORTC 0,1,2,3,4 Ausgaenge
40
    
41
  while(1)
42
      {
43
    
44
    // eigentliches Programm
45
              
46
      }
47
}

von Karl H. (kbuchegg)


Lesenswert?

Jonas schrieb:
> Hallo,
> ich bin gerade dabei eine Uhr selbst zu bauen.
> Das Programm ist schon fertig geschrieben. Die Minuten/Stunden werden
> richtig gezählt und an mehreren 7-Segment Anzeigen ausgegeben, nur der
> 8bit-Timer im CTC Modus scheint nicht richtig zu funktionieren.

Was heißt das?
Wird die ISR nicht aufgerufen?
Wird sie zu oft aufgerufen?

'funktioniert nicht richtig' ist die nichtssagenste Fehlerbeschreibung 
die du geben kannst

> unsigned int sekunden=0;         //Zaehler fuer Hundertstel-Sekunden
> unsigned int minuten=0;          //Zaehler fuer Minuten
> unsigned int stunden=0;          //Zaehler fuer Stunden

die müssen volatile sein.
Und eigentlich musst du beim Auslesen auch auf atomaren Zugriff achten.
Daher ist es nicht besonders schlau dafür unsigned int zu nehmen. 
uint8_t wären besser, weil du dadurch um das Problem des atomaren 
Zugriffs rumkommst.
Aber volatile muss auf jeden Fall sein

FAQ: Was hat es mit volatile auf sich

von Jonas (Gast)


Lesenswert?

Verzeihung, meine Beschreibung war tatsächlich nichtssagend.
"funktioniert nicht richtig" bedeuten, die ISR wird aufgerufen, 
allerdings viel zu selten.
Wenn ich den Wert in der ISR-IF Abfrage von 6000 auf 60 setze, erfolgt 
ca. alle fünf Sekunden ein Wechsel an der Anzeige. Dementsprechend lange 
dauert der Wechsel beim Wert 6000.

von Jonas (Gast)


Lesenswert?

Ich habe zum Test aus dem Programm alle "überflüssigen" Angaben gelöscht 
und ein Segment im Sekundentakt blinken lassen:

#include <avr/io.h>
#ifndef F_CPU                   //Vordefinieren fuer delay.h
#define F_CPU 7372800UL         //Takt ueber externen 7.3728Mhz Quarz
#endif
#include <util/delay.h>
#include <avr/interrupt.h>

int x;
int y;

int main(void)
{
  DDRD = 0b11111111;                     //Alles Ausgaenge
  DDRC |= (1 << DDC0) | (1 << DDC1) | (1 << DDC2) | (1 << DDC3) | (1 << 
DDC4);
//PORTC 0,1,2,3,4 Ausgaenge

  while(1)
      {

    PORTD = 0b10000000;
    // mit ULN2803 verbunden, schaltet gemeinsame Kathode der
       Segmentanzeige auf Ground

    PORTC = 0b00000001;  // schaltet ein Segment ein
    for (x=100;x>0;x--)
    _delay_ms(10);
    PORTC = 0b00000000;  // schaltet ein Segment ein
    for (y=100;y>0;y--)
    _delay_ms(10);

      }
}

Die Anzeige wechselt allerdings nur alle 8 Sekunden von an zu aus, bzw 
von aus zu an.
Daraufhin habe ich den Quarz gewechselt (6.5536Mhz) und die Angabe im 
Programmcode geändert (#define F_CPU 6553600UL). Auch hier wechselt das 
Segment nur alle 8 Sekunden den Zustand.
An der Definition des Timers für die ISR und am Quarz kann es also nicht 
liegen. Wo ist der Fehler?

von abc123 (Gast)


Lesenswert?

Jonas schrieb:
> Die Anzeige wechselt allerdings nur alle 8 Sekunden von an zu aus, bzw
> von aus zu an.
Bei Faktor 8 denke ich an Clkdiv-Fuse, hat der 88 sowas?

von Karl H. (kbuchegg)


Lesenswert?

Jonas schrieb:


> Die Anzeige wechselt allerdings nur alle 8 Sekunden von an zu aus, bzw
> von aus zu an.
> Daraufhin habe ich den Quarz gewechselt (6.5536Mhz) und die Angabe im
> Programmcode geändert (#define F_CPU 6553600UL). Auch hier wechselt das
> Segment nur alle 8 Sekunden den Zustand.

Damit der Quarz auch benutzt wird, musst du ihn per Fuse auch 
aktivieren. Einfach anschliessen ist zu wenig. Hast du das getan?

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.