Forum: Mikrocontroller und Digitale Elektronik ATTiny2313 - Timerproblem


von Gast (Gast)


Lesenswert?

Hallo liebes Forum.

Ich habe derzeit ein Problem mit einem ATTiny 2313.
Ich habe an den ATTiny extern an XTAL1 und XTAL2 einen Chrystal 
Oszillator 4,096 MHz gehängt.

Die Fusebits für den Chip habe ich so gesetzt :
CKSEL0 = 1
CKSEL1 = 0
CKSEL2 = 1
CKSEL3 = 1
SUT0 = 1
SUT1 = 1
CKOUT = 1
CKDIV = 1

Meine Initialisierung für den Timer0 sieht folgendermaßen aus:
  TIMSK |= (1<<TOIE0);// Timer/Counter0  Overflow Interrupt Enable
  TCCR0B |= (1<<CS02)|(1<<CS00);// Prescaler 1024
  sei();  // Global Interrupt Enable

wenn der Timer0 überläuft soll jeweils dies ausgeführt werden:
ISR (TIMER0_OVF_vect) {
  if (counter == 40) //entspricht 100HZ
  {
    if (counter2 == 100) //entspricht 1HZ
    {
      counter2 = 0;
      if(Zustand == 0)
      {
        PORTB &= ~(1<<Hour);
        Zustand = 1;
      }
      else
      {
        PORTB |= (1<<Hour);
        Zustand = 0;
      }
    }
    else counter2++;
  }
  else counter++;
}

jedoch entspricht die IF Abfrage von Counter2 nicht annähernd 1HZ.

Ist dort irgendetwas Falsch ?

oder an meiner Berechnung für 1 HZ:
4096000HZ / 1024 = 4000
erste If Schleife:  4000/40 = 100
zweite IF Schleife:  100/100 = 1

Ich sehe nirgends einen Fehler. Wäre nett, falls einer den Fehler 
findet.

MFG

von Koko Lores (Gast)


Lesenswert?

du hast den overflow übersehen!?

timer-takt ist clck/1024. timer 0 ist 10 bit? dann macht er mit dieser 
frequenz 1024 schritte -> overflow alle 3.9s

von Hannes L. (hannes)


Lesenswert?

Koko Lores wrote:
> du hast den overflow übersehen!?
>
> timer-takt ist clck/1024. timer 0 ist 10 bit? dann macht er mit dieser
> frequenz 1024 schritte -> overflow alle 3.9s

Ich kann zwar verstehen, dass Du auch mal helfen möchtest, aber Du 
solltest da doch vorher mal einen Blick ins Datenblatt des Tiny2313 
werfen. Timer0 und 10 Bit passt irgendwie nicht so recht zusammen...

-------

> Ich habe an den ATTiny extern an XTAL1 und XTAL2 einen Chrystal
> Oszillator 4,096 MHz gehängt.

Diese Info halte ich für falsch, denn ein Quarzoszillator 
(Chrystal-oscillator) braucht nur einen XTAL-Anschluss. Er wird 
Fusebit-mäßig als external clock angesehen. Wenn Du beide XTAL-Pins 
benutzt, dann hast Du vermutlich einen einfachen Quarz, der vom im 
Tiny2313 integrierten Oszillator zum Schwingen gebracht wird.

Aus dem Rest halte ich mich mangels C-Wissen 'raus, ich programmiere 
AVRs nur in ASM.

...

von Gast (Gast)


Lesenswert?

Ich habe den Quarz wie in Figure12 auf Seite 26 des Datenblattes des 
ATTiny2313 angehängt.

von johnny.m (Gast)


Lesenswert?

Bis auf die falsche Annahme über die Beschaffenheit des Timers hat Koko 
Recht:
Allerdings hat der Timer 8 Bit und dementsprechend bekommst Du bei einem 
Prescaler von 1024 und f_CPU=4,096 MHz eine Overflow-Frequenz von etwas 
mehr als 15 Hz (MAX ist beim 8-Bit-Timer 255). Ein Overflow tritt also 
alle 64 ms auf. Das ist, um genaue Sekunden zu zählen, ein ziemlich 
ungünstiger Wert. Stelle besser den Prescaler auf 8. Dann bekommst Du 
alle 500 µs einen Overflow (also mit einer Frequenz von 2 kHz). Da lässt 
es sich wesentlich leichter zählen. Noch besser gehts i.d.R. mit dem 
CTC-Modus...

> Ich habe den Quarz wie in Figure12 auf Seite 26 des Datenblattes des
> ATTiny2313 angehängt.
Hannes' Beitrag bezog sich darauf, dass Du einen QuarzOSZILLATOR erwähnt 
hast. Und das ist was völlig anderes...

von johnny.m (Gast)


Lesenswert?

> erste If Schleife: ...
Es gibt keine "if-Schleifen"! Eine "if"-Anweisung stellt eine 
Entscheidung oder Verzweigung dar, aber keine Schleife...

von Koko Lores (Gast)


Lesenswert?

> ... auch mal helfen ...
Das war kein Schuss ins Blaue.. ;-)

Den Fehler sieht man in dieser Annahme:

>4096000HZ / 1024 = 4000
>erste If Schleife:  _4000_/40 = 100

von Gast (Gast)


Lesenswert?

sehe ich das Falsch:

4,096 MHz mit einem Prescaler von 8 sind doch 512kHz oder ?

von Koko Lores (Gast)


Lesenswert?

korrekt

von Joerg X. (Gast)


Lesenswert?

@ Koko Lores (Gast):
"counter" scheint eine globale Variable zu sein, deren Typ allerdings 
nicht angegeben ist, wenn das mindestens ein "int" ist sollte dieser 
Teil funktionieren. Man sieht aber, dass es besser ist die Formel in den 
Code zu schreiben statt der "magic numbers".

von johnny.m (Gast)


Lesenswert?

@Gast:
Ich glaube, Dir fehlt ein wenig Grundverständnis zum Thema Timer. Der 
Timer besteht im Prinzip aus einem 8-Bit-Register und ein wenig Hardware 
rundrum. Wenn der Timer eine Taktflanke bekommt, dann wird der 
(Binär-)Wert des Registers (TCNT0 ist das beim AVR) um eins erhöht. Das 
geschieht nun bei jeder entsprechenden Taktflanke. Irgendwann erreicht 
der Wert im Register "11111111" oder in Dezimalschreibweise 255. Das ist 
der höchste mit 8 Bit darstellbare Wert. Wenn jetzt noch eine Taktflanke 
kommt, dann wird der Wert noch mal um eins erhöht. Da 256 aber in 8 Bit 
nicht mehr darstellbar ist, "springt" das Zählregister TCNT0 auf "0", 
d.h. der Timer fängt wieder von vorne an zu zählen. Das passiert bei 
jedem 256. Takt (beim 8-Bit-Timer). Dieses Umspringen vom Maximalwert 
auf 0 nennt man Overflow. Tritt ein Overflow auf, dann wird das 
dazugehörige Interrupt-Flag gesetzt und (wenn doe Interrupt-Bearbeitung 
freigegeben ist) der dazugehörige Interrupt Handler ausgeführt.

Der Takt, mit dem der Timer zählt, wird über einen Vorteiler (den 
Prescaler) aus dem Systemtakt erzeugt. Wenn als Taktquelle für den Timer 
der Prescaler-Ausgang "f_CPU/1024" ausgewählt ist, dann heißt das, dass 
bei jeder 1024. Taktflanke des Prozessortaktes (der bei Dir 4096000 Hz 
beträgt) das Timerregister TCNT0 um eins erhöht wird. Das bedeutet, der 
Timer selbst wird mit 4096000/1024 Hz = 4000 Hz getaktet.

Ein Overflow tritt nun bei jedem 256. Timertakt auf, also mit einer 
"Frequenz" von 4000 Hz/256 = 15,625 Hz. Das macht einen Overflow alle 64 
ms.

Jetzt klar?

von Gast (Gast)


Lesenswert?

counter und counter2 sind vom Typ uint8_t die in der Library inttypes.h 
deffiniert sind.

wieso kommt johnny m. denn mit einem Prescaler von 8 auf 500µS ?

wenn ich also einen Prescaler von 1024 wählen würde, müsste ich dann den 
16 Bit Timer nehmen ?

von johnny.m (Gast)


Lesenswert?

und

von Koko Lores (Gast)


Lesenswert?

Joerg X, ich verstehe nicht.. Meinst Du mich?

von Gast (Gast)


Lesenswert?

sehe ich das jetzt so richtig :

Der Vorteiler teilt mir meine FCPU = 4,096 MHz auf 512 kHz. Mit dieser 
Taktrate wird dann das Register des Timer inkrementiert. Sobald das 
Register denn Wert 255 erreicht hat, wird beim nächsten Takt das 
Register wieder auf 0 gesetzt und ein Interrupt ausgelöst ?

und daher komme ich dann auch auf die 2kHz :)

von Joerg X. (Gast)


Lesenswert?

@Koko Lores:
 Sry, Hast recht!
 jetzt seh ich auch, dass da das eigentliche Zählen fehlt (noch mal 
durch TOP +1 teilen)

@Gast
 genau! (und 4000 passen nicht in ein "uint8_t"...)

von Koko Lores (Gast)


Lesenswert?

> (und 4000 passen nicht in ein "uint8_t"...)
Das war wiederum nicht das Problem..

Dann hat Gast ja jetzt bald sein Sekunden-Blinken - Du kannst übrigens 
auch mit einer exklusiv-Oder Verknüpfung den Ausgangspin direkt 
Umschalten. Guck mal in der Artikelsammlung (links 'alle artikel') nach 
bitmanipulation.

von Gast (Gast)


Lesenswert?

ich bins mal wieder :(

habe das Programm soweit jetzt geändert :
1
/* Library´s */
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#include <inttypes.h>
5
6
/* Unterprogramme */
7
void PORT_INIT (void);
8
void TIMER_INIT (void);
9
10
/* Define´s */
11
#define Hour PB2
12
#define Min   PB1
13
#define Sec  PB0
14
15
/* Variabeln */
16
uint8_t counter = 0;        // counter
17
uint8_t counter2 = 0;
18
uint8_t sec_count = 30;
19
uint8_t min_count = 30;
20
uint8_t hour_count = 30;
21
uint8_t Zustand = 0;
22
23
int main (void) {
24
  // Hauptprogramm
25
  PORT_INIT();
26
  TIMER_INIT();
27
  while(1);
28
  return 1;
29
}
30
31
void PORT_INIT (void) { 
32
  DDRB = (1<<Sec)|(1<<Min)|(1<<Hour);
33
  DDRD = 0x3F;
34
  PORTD = 0x3F;
35
  PORTB = 0xFF;
36
}
37
38
void TIMER_INIT (void) {
39
  TIMSK |= (1<<TOIE0);      // Timer/Counter0 Overflow Interrupt Enable
40
  TCCR0B |= (1<<CS01);      // Prescaler 8
41
  sei();              // Global Interrupt Enable
42
}
43
44
ISR (TIMER0_OVF_vect) {
45
  if (counter == 20) // entspricht 100 Hz
46
  {
47
    if (counter2 == 100)
48
    {
49
      counter2 = 0;
50
      if(Zustand == 0) 
51
      {
52
        PORTB &= ~(1<<Hour);
53
        Zustand = 1;
54
      }
55
      else 
56
      {
57
        PORTB |= (1<<Hour);
58
        Zustand = 0;
59
      }
60
    }
61
    else counter2++;
62
  }
63
  else counter++;
64
}

jedoch wird kein Sekunden Takt erzeugt, ist im Programm ein Fehler oder 
bei den (im ersten Beitrag) Einstellung der Fusebits ?

von Gast (Gast)


Lesenswert?

Ich würde mich echt freuen, wenn einer mir hierbei weiterhelfen kann.

von johnny.m (Gast)


Lesenswert?

Ich denke, Du müsstest, wenn counter 20 ist, diese Variable auch wieder 
auf Null setzen, wie Du es ja auch mit counter2 machst.

von Gast (Gast)


Lesenswert?

autsch *Kopf => Wand ;)*
danke Johnny (mal wieder :) ).

nochma eine kleine Frage zur Logik, um einen genauen Sekunden Takt zu 
erzeugen, müsste ich doch eigentlich bei den beiden IF-Anweisungen die 
Werte die ich abfrage um 1 verringern. Also bei if(counter==19) und 
if(counter2==99) um einen genauen Sekunde Takt zu erzeugen ?

Da (so wie ich es denke) der Interrupt 20 mal aufgerufen werden muss um 
den Counter auf 20 hochzuzählen. Beim 21.ten Aufruf würde dann erst der 
nächste Zähler hochgezählt wird. Stimmt meine Logik?

von johnny.m (Gast)


Lesenswert?

In Deinem Beispiel müsstest Du tatsächlich genaugenommen die Werte um 1 
reduzieren, da Du erst nach der Abfrage die Zähler inkrementierst und 
die Abfrage des inkrementierten Zählers erst im nächsten Zyklus 
geschieht. Ich persönlich bevorzuge die Pre-Increment-Methode, bei der 
der betreffende Zähler erst inkrementiert und dann abgefragt wird.
1
ISR (TIMER0_OVF_vect) 
2
{
3
  if (++counter == 20) // entspricht 100 Hz
4
  {
5
  counter = 0;
6
    if (++counter2 == 100)
7
    {
8
       counter2 = 0;
9
       //Code
10
    }
11
  }
12
}
So ginge es mit 20 und 100 und erspart die else-Anweisungen...

Wenn Du es so lässt wie oben, dann musst Du 19 und 99 nehmen, damit es 
genau stimmt.

von Gast (Gast)


Lesenswert?

Danke Johnny :)
Die Schreibweise in einer If-Anweisung war mir bisher nicht bekannt.
Aber ma lernt nie aus (wobei ich noch ne "Menge" zu lernen habe).

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.