Forum: Mikrocontroller und Digitale Elektronik Probleme mit Timer bei Atmega32


von Jakob (Gast)


Lesenswert?

Guten Tag, ich bin zur Zeit an einer Atmega32 Test Platine am testen.
Ganz Normaler Aufbau 8 LED's und 8 Taster alles Funktioniert Super.

Ich befasse mich zur Zeit mit dem Timer Interrupt. Verstehen tue ich das 
eigentlich alles soweit, Precascler etc. Aber irgendwie zählt er nicht 
richtig. Eingestellt habe ich den Prescaler auf 1ms.

Bei 16Mhz und 16Bit braucht der Timer 4,1mS um durch zulaufen bzw. 
Überzulaufen. Und für 1mS startet der Zähler bei 49550.

Eingestellt habe  ich den Timer so :

Der Prescaler
1
void start_timer1(int prescale,int compare)
2
{  TCCR1B = 0;
3
  //16Bit Timer Initialisierung:
4
  if (prescale == 0)
5
  {
6
    TCCR1B = (1<<CS10) | (1<<WGM12);        //Prescaler = 1, CTC    //
7
  }
8
  if (prescale == 1)
9
  {
10
    TCCR1B = (1<<CS11) | (1<<WGM12);        //Prescaler = 8, CTC
11
  }
12
  if (prescale == 2)
13
  {
14
    TCCR1B = (1<<CS11) | (1<<CS10) | (1<<WGM12);  //Prescaler = 64, CTC
15
  }
16
  if (prescale == 3)
17
  {
18
    TCCR1B = (1<<CS12) | (1<<WGM12);        //Prescaler = 256, CTC
19
  }
20
  if (prescale == 4)
21
  {
22
    TCCR1B = (1<<CS12) | (1<<CS10) | (1<<WGM12);  //Prescaler = 1024, CTC
23
  }
24
25
  TIMSK = (1<<OCIE1A);        //Timerinterrupt für Vergleich aktivieren
26
  OCR1A = compare;          //Vergleichswert bis wohin er durchzählt  zählt!(länge des Timers abhängig vom Prescaler)
27
  sei();                //Interrupts zulassen
28
}
29
30
void start_clock(void)
31
{
32
  start_timer1(0,49550);
33
}

start_clock wird im Hauptprogramm einfach aufgerufen.


Was soll der Timer machen ?

Er soll jegliche ne kleine Zeitsteuerung machen.
1
ISR(TIMER1_COMPA_vect){
2
  
3
  scan_key();
4
  
5
  MilSek+=1;
6
  if (MilSek==1000)
7
  {
8
    Sek+=1;
9
    MilSek=0;
10
    //PORTB = 0xFF;
11
  }
12
  
13
  if (Sek==59)
14
  {
15
    //PORTB = 0x00;
16
    Sek = 0;
17
  }
18
    
19
  
20
21
  
22
}
Mit und ohne Scan_key funktioniert es nicht.

Das Hauptprogramm :
1
int main(void)
2
{
3
  DDRB = 0xff;
4
  DDRD = 0x00;
5
  
6
  sei();
7
  MCUCR = (1<<ISC11)| (1<<ISC10) | (1<<ISC01) | (1<<ISC00); //Steigende Flanke löst aus // MCUCR = 0b00001111
8
  GICR = /*/(1<<INT1) | */(1<<INT0);
9
// INT0enable im General Interrupt Control Register 
10
    
11
  start_clock();
12
  PORTB = 0x81;
13
  
14
    while(1)
15
    {
16
    taster = readkey();  
17
    handle_event();
18
    
19
    
20
    if (Sek==10)
21
    {
22
      AmpelEin();
23
      
24
    }
25
    
26
    if (Sek==20)
27
    {
28
      AmpelAus();
29
      Sek=0;
30
    }
31
  }
Ich hoffe ihr könnt mir helfen, evtl. ist es auc nur ein Denkfehler. Im 
Tutorial wurde ich nicht schlauer oder hab die passende stelle nicht 
gefunden.

von Vabi (Gast)


Lesenswert?

Was genau passiert denn, wenn du dein Programm laufen lässt?

Dein Timer zählt von 0 auf 49550 hoch, das sind 3,1 ms. So stehts auch 
in den Kommentaren im Code; du scheinst aber zu glauben, dass er von 
49550 auf 65536 hochzählt. Versuchs mal mit compare = 16000, das sollte 
grob 1 ms sein.

von Jakob (Gast)


Lesenswert?

Also die 8 LED's sind wie 2 Ampeln angeordnet. Gesteckt sind aber nur 
6...

Über den scan_key Befehl im Timer lese ich die Taster aus und es 
passiert auch das was es soll. Testweise LED's an aus. Die Zeit des 
Timers reicht also aus die Tasten zu lesen.

Der Timer braucht aber viel zu lange um die Ampel zu wechseln hier auf 
10 und 20 Sek eingestellt. Braucht aber das Vielfache.

von Karl H. (kbuchegg)


Lesenswert?

>   start_timer1(0,49550);

>  void start_timer1(int prescale,int compare)


49550 passt nicht in einen int.


Gewöhn dir die Datentypen int, long, etc. ab.

verwende die expliziten Dtaentypen
int16_t, uint16_t, int8_t, uint8_t, int32_t, uint32_t
und du wirst weniger oft von seltsamen Problemen gebissen.

(allerdings hast du insofern Glück, als sich im Endeffekt alles wieder 
richtig zurechtrückt, weil mit int nur temporär eine andere 
Interpretation der Bits passiert und sich nichts verändert. Trotzdem: 
streiche int bei der AVR Programmierung zunächst mal aus deinem 
Wortschatz)

von Karl H. (kbuchegg)


Lesenswert?

Jakob schrieb:

> Der Timer braucht aber viel zu lange um die Ampel zu wechseln hier auf
> 10 und 20 Sek eingestellt. Braucht aber das Vielfache.

Das 'Vielfache' ist nicht zufällig ziemlich genau ein Faktor 16?

16Mhz versus 1Mhz Default Einstellung in den Fuses

von Uwe (de0508)


Lesenswert?

Hallo Jakob,

ich lasse mir immer den passenden Prescaler und TOP-Counterwert für den 
CTC-Mode vom Compiler berechnen.

Für
1
#define FREQUENZ 1000 // 1ms

Es gilt zu unterscheiden, wann die Bedingung erfüllt ist:

(uint16_t)(1.0* F_CPU /<prescale> /FREQUENZ) <= 65536

mit prescale E {1,8,64,256,1024}

Dann ist
OCR1A = ((uint16_t)(1.0*F_CPU /PRESCALER /FREQUENZ) -1);

Das "unterscheiden" macht der Preprozessor für uns.
1
#if ((uint16_t)(1.0* F_CPU /1 /FREQUENZ) <= 65536)
2
#else
3
#endif

: Bearbeitet durch User
von Jakob (Gast)


Lesenswert?

Stelle ich nun die Ampel so ein :
1
if (Sek==1)
2
    {  
3
      
4
      AmpelEin();
5
      
6
    }
7
    
8
    if (Sek==2)
9
    {
10
      AmpelAus();
11
      Sek=0;
12
    }

Ist er bei ca. 13-16 Sek also kommt der Faktor 16 hin.

Der Timer bleibt wie vorher auch.

Geändert habe ich auf void start_timer1(uint16_t prescale,uint16_t 
compare)

von Jakob (Gast)


Lesenswert?

@UWE Ich werde dies mal versuche aber erst mal möchte ich das mit mit 
meiner Berechnung hinbekommen.
1
void start_clock(void)
2
{
3
  start_timer1(0,16000);
4
  
5
}

von Uwe (de0508)


Lesenswert?

Hallo Jakob,

Die Obergrenze von 16000, ist so weit richtig, aber das ORC1A Register 
wird nicht mit dem richtigen Wert geladen !

TIPP
Ist ORC1A = 0, dann erfolgt ein Interrupt alle 65536 Ticks.

von spess53 (Gast)


Lesenswert?

Hi

>TIPP
>Ist ORC1A = 0, dann erfolgt ein Interrupt alle 65536 Ticks.

Nein, der Interrupt wird bei jedem Tick ausgelöst.

MfG Spess

von Jakob (Gast)


Lesenswert?

Mit dem Faktor 16 ist glaube ich mein Problem. Wo mach ich das mit dem
"1Mhz Default Einstellung in den Fuses" ?

von Uwe (de0508)


Lesenswert?

Spess,

unterer einem Tick verstehe ich den Takt nach dem Vorteiler des Timer1.

von Karl H. (kbuchegg)


Lesenswert?

Jakob schrieb:
> Mit dem Faktor 16 ist glaube ich mein Problem. Wo mach ich das mit dem
> "1Mhz Default Einstellung in den Fuses" ?

Alles klar.

Dein Brennprogramm hat einen entsprechenden Eintrag zum Thema Fuses.
Aber sei vorsichtig: mit einer falschen Einstellung kannst du dich aus 
dem µC aussperren.


Zur Erklärung: die Angabe F_CPU ist nur die Information für den 
Compiler, der einige Berechnungen darauf basierend macht (zb _delay_ms). 
Es ist NICHT die Umstellung des µC! Die musst du selber machen. D.h. du 
stellst den µC darauf ein, dass er einen Quarz benutzt und teilst dann 
dem Compiler mittels F_CPU mit, auf welcher Frequenz der µC wirklich 
läuft.
Atmel liefert seine AVR so aus, dass sie auf ca. 1Mhz interenem Takt 
laufen. Wenn du etwas anderes willst (zb einen Quarz benutzen), dann 
musst du an den Fuses eine entsprechende Einstellung vornehmen.
Da dein µC nach wie vor mit 1Mhz läuft, du aber mit 16Mhz gerechnet 
hast, stimmen logischerweise deine errechneten Werte nicht.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?


von spess53 (Gast)


Lesenswert?

Hi

>unterer einem Tick verstehe ich den Takt nach dem Vorteiler des Timer1.

Ich auch.

MfG Spess

von Karl H. (kbuchegg)


Lesenswert?

Da stimmt auch noch was nicht
> Bei 16Mhz und 16Bit braucht der Timer 4,1mS um durch zulaufen

Soweit korrekt, wenn die Fuses erst mal stimmen

> Und für 1mS startet der Zähler bei 49550.

Du verwendest CTC Modus
D.h. da geht es nicht darum, bei welchem Wert der Timer starten muss, 
damit bis zum Überlauf eine bestimmte Zeit vergeht.
Beim CTC Modus, geht es darum, bis zu welchem Wert (von 0 weg) der Timer 
zählen muss, damit bei diesem Wert genau die von dir gewünschte Zeit 
vergangen ist.

Die Berechnung ist ganz einfach.
Da du 16Mhz hast und einen Vorteiler von 1:

In 1 Sekunde würde der Timer bis 16000000 zählen (ignorier mal 
Überläufe).
Wie weit kann er daher in 0.001 Sekunde (= 1Millisekunde) zählen?
1
    1       ....   16000000
2
    0.001   ....    x
3
  ---------------------------
4
        16000000 * 0.001
5
   x = ------------------- = 16000
6
             1

D.h. im CTC Modus lässt du den Timer 16000 Zählschritte machen. Danach 
soll er seinen Interrupt auslösen und wieder bei 0 anfangen.

Bei 0 anfangen macht er alleine, denn genau das macht ja der CTC Modus.
Du brauchst nur noch die 16000 in OCR1A eintragen.
1
   OCR1A = 16000 - 1;

FAQ: Timer

von Jakob (Gast)


Lesenswert?

Vielen Dank für die Antworten, werde mich jetzt erst mal mit den FUSE's 
auseinander setzen. Wenn es geklappt hat oder bei Fragen schreib ich es 
hier rein.

von Jakob (Gast)


Lesenswert?

Hab unter Device Programming den exteren Xtal nun auf 16mhz eingestellt 
die Fuse müssten stimmen...er wartet jetzt die 10 sec zwischen den 
Schaltungen aber die _delay_ms sind jetzt viel zu schnell.

Hab in allen c-files #define F_CPU 16000000UL damit er weiß das er damit 
arbeiten soll, oder reicht es wenn ich es nur im Hauptfile schreibe. ?

von Jakob (Gast)


Lesenswert?

Hab anscheint das Problem gelöst,
1
#include <avr/io.h>
2
#define F_CPU 16000000UL
3
#include <util/delay.h>
4
#include <avr/interrupt.h>
5
#include "timer1-routines.h"
6
#include "Funktionen.h"

vorher
1
#include <avr/io.h>
2
#include <util/delay.h>
3
#include <avr/interrupt.h>
4
#include "timer1-routines.h"
5
#include "Funktionen.h"
6
7
#define F_CPU 16000000UL

ist das so richtig, damit der  merkt das 16Mhz eingestellt sind ?

von Karl H. (kbuchegg)


Lesenswert?

Jakob schrieb:
> Hab anscheint das Problem gelöst,
>
>
1
#include <avr/io.h>
2
> #define F_CPU 16000000UL
3
> #include <util/delay.h>
4
> #include <avr/interrupt.h>
5
> #include "timer1-routines.h"
6
> #include "Funktionen.h"
7
>
>
> vorher
>
1
#include <avr/io.h>
2
> #include <util/delay.h>
3
> #include <avr/interrupt.h>
4
> #include "timer1-routines.h"
5
> #include "Funktionen.h"
6
> 
7
> #define F_CPU 16000000UL
8
>
>
> ist das so richtig, damit der  merkt das 16Mhz eingestellt sind ?

Ja. denn in util/delay.h wird F_CPU bereits benutzt. Daher muss zu 
diesem 'Zeitpunkt' der Wert von F_CPU schon bekannt sein. Ergo sollte 
die Defintion von F_CPU vor util/delay.h stehen.

Der Compiler liest den Programmtext von oben nach unten. So wie du ein 
BUch liest. In einem Krimi geschieht ja auch vorher der Mord und erst 
dann kommt die Auflösung durch den Kommisar.

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.