Forum: Mikrocontroller und Digitale Elektronik Zeitmessung mit TIMER Atmega8


von Peter W. (imidin)


Lesenswert?

Hallo,

ich habe einen ATMEGA8 im Einsatz mit einem LCD Display.

Die Anzeige funktioniert ohne Probleme. Jetzt möchte ich gerne eine 
Zeitmessung über zwei Taster realisieren die an den PIN B0 und B1 
hängen.

Soweit ich mich eingelesen habe, sollte das über den TIMER Interupt 
realisiert werden, allerdings komme ich gerade nicht weiter.

Ich habe da ein paar Verständnisprobleme zum Ablauf. Der TIMER soll 
durch die Betätigung von Taster 1 gestartet werden. Danach springt er in 
die ISR und inkremmentiert die variablen. Eigentlich soll er mir während 
dessen schon die variable Sekunde anzeigen. Nachdem der Zähler einmal 
durch ist springt er wieder in die while schleife?

Hier erstmal mein Code:
1
/*
2
 * main.c
3
 *
4
5
6
 */
7
8
//Includes
9
#include <avr/io.h>
10
#include <stdlib.h>
11
#include <stdio.h>
12
#include "lcd-routines.h"
13
#include <avr/interrupt.h>
14
15
//define variables
16
volatile unsigned int millisekunden;
17
volatile unsigned int sekunde;
18
volatile unsigned int minute;
19
volatile unsigned int stunde;
20
21
void TIMER1_interrupt_init(void)
22
{
23
    TCCR1A = (1<<WGM10); // CTC Modus
24
    TCCR1B |= (1<<CS10); // Prescaler 8
25
    // ((1000000/8)/1000) = 125
26
    OCR1A = 125-1;
27
    TIMSK |= (1<<OCIE1A);
28
}
29
30
31
32
void Anzeige(int sekunden)
33
{
34
  char string[20];
35
  itoa( sekunden, string, 10 );
36
  lcd_clear();
37
  lcd_setcursor( 0, 1 );
38
  lcd_string(string);
39
}
40
41
int main(void){
42
    DDRB = 0x00;
43
    PORTB = 0x00;
44
45
    TIMER1_interrupt_init();
46
47
    lcd_init();
48
    lcd_setcursor( 0, 2 );
49
    lcd_string("Taster OFF");
50
51
    while(1)
52
    {
53
      if(!(PINB & (1<<PINB0))) sei();  //Timer starten
54
55
      if(!(PINB & (1<<PINB1))) cli();  //Timer stoppen
56
57
      Anzeige(sekunde);
58
59
    }
60
61
  
62
}
63
64
65
ISR (TIMER1_COMPA_vect)
66
{
67
  millisekunden++;
68
  if(millisekunden == 1000)
69
  {
70
    sekunde++;
71
    millisekunden = 0;
72
    if(sekunde == 60)
73
    {
74
      minute++;
75
      sekunde = 0;
76
    }
77
    if(minute == 60)
78
    {
79
      stunde++;
80
      minute = 0;
81
    }
82
    if(stunde == 24)
83
    {
84
      stunde = 0;
85
    }
86
  }
87
  
88
}

: Bearbeitet durch User
von Dumpf Backe (Gast)


Lesenswert?

Peter W. schrieb:
> mit einem LCD Display.

Also sozusagen mit einem Liquid Crystal Display Display.

Also ein Display das ein Liquid Crystal Display anzeigt?

Neue Technologie?

von Peter W. (imidin)


Lesenswert?

Dumpf Backe schrieb:
> Peter W. schrieb:
>> mit einem LCD Display.
>
> Also sozusagen mit einem Liquid Crystal Display Display.
>
> Also ein Display das ein Liquid Crystal Display anzeigt?
>
> Neue Technologie?

Danke für deine Hilfe. Dein Name ist anscheinend Programm, auch wenn du 
ihn nicht richtig schreiben konntest.

von Jacko (Gast)


Lesenswert?

Vielleicht habe ich deine Aufgabenstellung nicht ganz verstanden,
aber mir scheint, du machst es sehr umständlich!

Lass doch einfach den Timer und seine Stoppuhr-Register
die Zeit zählen (ms  ss  mm / hh ) - das machst du doch schon
ganz richtig per ms-Interrupt.

Wenn du jetzt die Zeit von Taste 1 (Start) gedrückt, bis
Taste 2 (Stopp) gedrückt anzeigen willst, mach es so:

Programmstart: Stoppuhr-Register = NULL, Zähler Stopp (CS = 0)

Zähler Start: Stoppuhr-Register = NULL, dann
              Zähler Start: CS = passender Prescaler

Zähler Stopp: (CS = 0)
              Stoppuhr-Register bleiben auf dem letzten Wert

Dann kannst du jederzeit (am Besten getaktet von einem
anderen Timer) die Zeitregister auslesen und anzeigen.

Gute Nacht erst mal

Beitrag #5221035 wurde vom Autor gelöscht.
von Peter W. (imidin)


Lesenswert?

So habe meinen Fehler gefunden. Das lcd_clear() hat das Display ständig 
gelöscht und deswegen wurde nichts angezeigt.

Habe noch ein Verständnisproblem. Wenn ich 1msek inkrememntieren will, 
wie muss ich dann meinen Systemclock und alles einstellen


Anbei mein jetziger Code.
1
//Includes
2
#include <avr/io.h>
3
#include <stdlib.h>
4
#include <stdio.h>
5
#include "lcd-routines.h"
6
#include <avr/interrupt.h>
7
8
//define variables
9
volatile unsigned int millisekunden;
10
volatile unsigned int sekunde;
11
volatile unsigned int minute;
12
volatile unsigned int stunde;
13
char line1[15] = "Booting...";
14
15
void TIMER2_interrupt_init(void)
16
{
17
  /*TCCR2 in Modus CTC
18
    Bit       7      6    5    4    3    2      1    0
19
    Name     FOC2  WGM20  COM21  COM20  WGM21  CS22  CS21  CS20
20
    set to    0    0    1    0    1    1    1    1
21
   */
22
   TCCR2 = 0b00101111; // Prescaler 1024
23
24
   TCNT2 = 0; // Initialisiere mit 0
25
   TIMSK |= (1<<OCIE2); //
26
   OCR2 = 70;  //Vergleichswert für Interupt
27
}
28
29
30
31
void Anzeige(int sek, int msek)
32
{
33
  char string_sek[4];
34
  char string_msek[4];
35
36
  itoa( sek, string_sek, 10 );
37
  itoa( msek, string_msek, 10 );
38
39
  lcd_setcursor( 0, 2 );
40
  lcd_string(string_sek);
41
  lcd_setcursor( 2, 2 );
42
  lcd_string(",");
43
  lcd_setcursor( 3, 2 );
44
  lcd_string(string_msek);
45
}
46
47
int main(void){
48
  //Eingänge der Taster B0 und B1
49
    DDRB = 0x00;
50
    PORTB = 0x00;
51
52
    lcd_init();
53
    lcd_setcursor( 0, 1 );
54
    lcd_string(line1);
55
56
    TIMER2_interrupt_init();
57
58
    while(1)
59
    {
60
      //lcd_clear();
61
      if(!(PINB & (1<<PINB0)))
62
        {
63
            lcd_setcursor( 0, 1 );
64
           lcd_string("START          ");
65
            sei();  //Timer starten
66
        }
67
      if(!(PINB & (1<<PINB1)))
68
        {
69
            lcd_setcursor( 0, 1 );
70
            lcd_string("STOPP          ");
71
            cli();  //Timer stoppen
72
        }
73
      Anzeige(sekunde, millisekunden);
74
    }
75
    return 0;
76
}
77
78
79
ISR (TIMER2_COMP_vect)
80
{
81
  millisekunden++;
82
  if(millisekunden == 1000)
83
  {
84
    sekunde++;
85
    millisekunden = 0;
86
    if(sekunde == 60)
87
    {
88
      minute++;
89
      sekunde = 0;
90
    }
91
  }
92
}

von Ben (Gast)


Lesenswert?

Wenn du eine Millisekunde dekrementieren oder inkrementieren möchtest, 
dann must du einen Timer so mit einem Wert setzen, das der nach einer 
Millisekunde einen Überlauf hat. Der Überlauf löst dann einen Interrupt 
aus und dann kannst du auch Inkrement oder Dekrement oder was auch 
sonnst du damit machen möchtest...
Der Haupttakt spiel dabei keine Rolle. Du mußt nur den Timerwert des 
Zählers den du setzt bevor Überlauf stattfindet, so setzen das es passt.
Bei langsamen Haupttakt näher an 65535 (oder 255 bei 8-Bit Timer, wenn 
8-Bit überhaupt reichen!), bei schnellerem Haupttakt weiter weg von 
65535.
Beispiel bei z.B. 10MHz Haupttakt hast du 100nS bei einem Takt. Wenn du 
nun also 1mS haben möchtest, benötigst du also nach Adam Riese 10000 
Takte.
Dann rechne 65536 - 10000 = 55536. Diese 55536 setzt du jedes mal in der 
Überlauf- Interrupt Routine in die Timer Register H und L. Dann wird der 
Überlaufinterrupt auch immer alle 1mS aufgerufen werden. Nämlich immer 
nach 10000 Takten.

von Reiner_Gast (Gast)


Lesenswert?

Peter W. schrieb:
> //Eingänge der Taster B0 und B1
>     DDRB = 0x00;
>     PORTB = 0x00;

Die Ports sind falsch initialisiert. Hier müssen die internen PullUps 
eingeschaltet werden, sonst floaten die Inputs und die Interupts werden 
zufällig ein- bzw. ausgeschaltet, wenn keine Taste gedrückt wurde.

Leider hast du nicht geschrieben, ob die Taster die Input Pins auf HIGH 
(5V) oder LOW (GND) ziehen. Wenn die internen Pull-Ups benutzt werden, 
dann müssen die gegen GND ziehen

von Peter W. (imidin)


Lesenswert?

Reiner_Gast schrieb:
> Peter W. schrieb:
>> //Eingänge der Taster B0 und B1
>>     DDRB = 0x00;
>>     PORTB = 0x00;
>
> Die Ports sind falsch initialisiert. Hier müssen die internen PullUps
> eingeschaltet werden, sonst floaten die Inputs und die Interupts werden
> zufällig ein- bzw. ausgeschaltet, wenn keine Taste gedrückt wurde.
>
> Leider hast du nicht geschrieben, ob die Taster die Input Pins auf HIGH
> (5V) oder LOW (GND) ziehen. Wenn die internen Pull-Ups benutzt werden,
> dann müssen die gegen GND ziehen

Hallo Reiner,

die Taster sind wie im Tutorial gegen GND angeschlossen. Bislang hab ich 
mit den Tastern keine Probleme. Sie lösen den Interupt aus.

Ben schrieb:
> Beispiel bei z.B. 10MHz Haupttakt hast du 100nS bei einem Takt

Hallo Ben,

was heißt das für mein Bsp mit 1Mhz Systemtakt.

Und heißt es nicht T=1/f ? Also T= 1/1 MHz = 1*10^-6 s = 1 mikrosekunde
Wie muss ich dann den Prescaler wählen und den Vergleichswert für den 
CTC.

von Reiner_Gast (Gast)


Lesenswert?

Peter W. schrieb:
> Und heißt es nicht T=1/f ? Also T= 1/1 MHz = 1*10^-6 s = 1 mikrosekunde
> Wie muss ich dann den Prescaler wählen und den Vergleichswert für den
> CTC.

Der Prescaler muss auf 8 gesetzt werden. OCR2 auf 149 (es wird ab 0 
gezählt)

BTW: Was funktioniert denn genau nicht?

von Peter W. (imidin)


Lesenswert?

Reiner_Gast schrieb:
> BTW: Was funktioniert denn genau nicht?

Also aktuell startet der Timer mit Taster 1 und Stoppt mit Taster 2. 
Leider sind die Zeiten nicht annähernd an der Realität. Und ich bin mir 
gerade gar nicht so sicher wie mein Systemclock ist, da ich ohne 
externen Oszillator arbeite. Vermutlich hat der ATMEGA8 dann 8 MHz mit 
Werkseinstellung?

Ich komme einfach mit der Rechnung nicht klar, wie ich auf 1ms kommen 
kann.

von Peter W. (imidin)


Lesenswert?

Reiner_Gast schrieb:
> Der Prescaler muss auf 8 gesetzt werden. OCR2 auf 149 (es wird ab 0
> gezählt)

Das sieht jetzt schon besser aus. Kannst du mir die Rechnung erklären?

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Peter W. schrieb:
> Vermutlich hat der ATMEGA8 dann 8 MHz mit
> Werkseinstellung?

Ein neuer ATMega8 wird mit dem internen RC Oszillator auf 1MHz gesetzt 
ausgeliefert.
willst du jetzt z.B. im CTC Modus so gut es geht auf 1ms Ticker kommen, 
dann kannst du z.B. den Vorteiler des Timers 2 auf 8 setzen (125 kHz 
Takt am Timer) und ihn dann bis 125 zählen lassen (OCR2 auf 124 setzen). 
Damit tickt der Timer mit 1kHz = 1ms Periode die ISR.

Du kannst auch Timer 1 benutzen, Vorteiler auf 1 setzen und ihn bis 1000 
zählen lassen. Aber Timer 1 ist 'der Beste' im Mega8 und kann auch für 
andere Dinge benutzt werden.
Der interne RC Oszillator ist nicht super genau. Abweichungen von den 
nominellen 1 MHz sind normal und auch von der Umgebungstemperatur 
abhängig.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Peter W. schrieb:
> Das lcd_clear() hat das Display ständig
> gelöscht und deswegen wurde nichts angezeigt.

Ja, das ist eine völlig überflüssige Funktion, die aber leider in jedem 
Beispielcode einem Anfänger förmlich aufgezwungen wird.

In der Praxis überschreibt man einfach den alten mit dem neuen Text.
Damit bei kurzen Zahlen kein Rest stehenbleibt, benutzt man für die 
Ausgabe vorzugsweise sprintf und gibt die maximale Länge der Zahl an.

von Reiner_Gast (Gast)


Lesenswert?

Peter W. schrieb:
> Reiner_Gast schrieb:
>> BTW: Was funktioniert denn genau nicht?
>
> Also aktuell startet der Timer mit Taster 1 und Stoppt mit Taster 2.
> Leider sind die Zeiten nicht annähernd an der Realität. Und ich bin mir
> gerade gar nicht so sicher wie mein Systemclock ist, da ich ohne
> externen Oszillator arbeite. Vermutlich hat der ATMEGA8 dann 8 MHz mit
> Werkseinstellung?
>
> Ich komme einfach mit der Rechnung nicht klar, wie ich auf 1ms kommen
> kann.

Wie oben schon gesagt... das kann daran liegen, dass die Inputs evtl. 
floaten, d.h. keinen definierten Zustand haben, wenn keine Taste 
gedrückt wird. Dann kann zufällig der Timer IRQ ein- oder ausgeschaltet 
werden. D.h. wenn eigentlich der IRQ mit dem taster eingeschaltet wurde, 
können durch die ganzen Ein- und Abschaltungen des IRQ einfach etliche 
IRQ nicht gezählt worden sein... Ich würde das mal prüfen.

Ohne Schaltplan kann ich dir dabei aber nicht weiterhelfen.

von Reiner_Gast (Gast)


Lesenswert?

Peter W. schrieb:
> Vermutlich hat der ATMEGA8 dann 8 MHz mit
> Werkseinstellung?

Ich weiß nicht, wie es bei den ATMEGA8 ist, aber bei allen ANTINY84/85 
und ATMEGA328/644 war die Werkseinstellung immer der interne 8MHz Oszi 
und der Teiler durch 8 durch die Fuse CLK/8 gesetzt... Das macht dann 1 
MHz Takt

Da schaust du mit dem AVR Studio am besten mal nach, wie die Fuses 
eingestellt sind.

Je nach dem, was dabei rauskommt, berechnet sich auch der Prescale und 
der Wert für OCR2, das hat Matthias S. ja schon gut beschrieben.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Reiner_Gast schrieb:
> Ich weiß nicht, wie es bei den ATMEGA8 ist, aber bei allen ANTINY84/85
> und ATMEGA328/644 war die Werkseinstellung immer der interne 8MHz Oszi
> und der Teiler durch 8 durch die Fuse CLK/8 gesetzt... Das macht dann 1
> MHz Takt

Das ist beim alten Mega8 noch etwas anders. Der hat einen internen RC 
Oszillator, den man auf 1, 2, 4 oder 8 Mhz fusen kann. Genau wie bei den 
neueren AVR ist er aber werksseitig konservativ auf 1MHz eingestellt.

von Peter W. (imidin)


Lesenswert?

Peter W. schrieb:
> ISR (TIMER2_COMP_vect)
> {
>   millisekunden++;
>   if(millisekunden == 1000)
>   {
>     sekunde++;
>     millisekunden = 0;
>     if(sekunde == 60)
>     {
>       minute++;
>       sekunde = 0;
>     }
>   }

Ich habe mir jetzt mal ein Oszilloskop besorgt und würde ganz gerne an 
PB3 als Ausgang immer den Takt der Millisekunden ausgeben. Ich weiß grad 
gar nicht wie ich am besten die Ausgabe realisiere. Ich müsste eine ms 
PB3 auf 0 und dann eine ms wieder auf 1 legen, damit ich am Oszi den 
Takt erkennen kann.

Hat jemand einen Tipp zur Realisierung?

von Christopher J. (christopher_j23)


Lesenswert?

Einen Pin togglen kann man ganz leicht mittels XOR-Operator, d.h. 
konkret einfach
1
PORTB ^= ( 1 << PB3 );
in die ISR packen.

von Peter W. (imidin)


Lesenswert?

Und wie funktioniert das? Also wie trifft er die Entscheidung zu Low 
oder High. Hatte mir jetzt überlegt mit dem Modulo Operator nach Gerade 
oder Ungerade zu unterscheiden und dann den Port Low oder High zu 
schalten.

von Christian S. (roehrenvorheizer)


Lesenswert?

Hallo,

in die ISR kannst Du schreiben:

PORTB ^= ( 1 << PB3 );

Dies ändert den Zustand des Pins. Natürlich zuvor als Ausgang 
einstellen.

MfG

von Dietrich L. (dietrichl)


Lesenswert?

Peter W. schrieb:
> Und wie funktioniert das?

Der Exclusiv-Oder Operator invertiert genau das Bit oder die Bits, die 
Du in der Maske ausgewählt hast.
Wenn du wissen willst, wie der µC das genau macht, dann musst du im 
Maschinencode des Compilers nachschauen.


> Also wie trifft er die Entscheidung zu Low
> oder High. Hatte mir jetzt überlegt mit dem Modulo Operator nach Gerade
> oder Ungerade zu unterscheiden und dann den Port Low oder High zu
> schalten.

Das geht natürlich auch, ist aber umständlicher.

Übrigens ergibt dieses toggeln jede Millisekunde eine Frequenz von 500Hz 
- nicht das du dich beim messen wunderst...)

von Peter W. (imidin)


Angehängte Dateien:

Lesenswert?

Prima das funktioniert. Anbei das Ergebnis. Damit zählt die Zeitmessung 
auch annähernd korrekt.

Warum brauch der Ausgang so lange bis er auf 0 geht?

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.