Forum: Mikrocontroller und Digitale Elektronik Periodendauermessung mit Timer


von Mark Z. (lyams)


Lesenswert?

Hallo,
Also ich erklär mal was ich vor habe.
Ich erzeuge mit einem Atmega32 ein Rechtecksignal mit einer Frequenz von 
momentan 400kHz.
Dieses Signal sende ich an einen zweiten µC ebenfalls einen Atmega32 und 
möchte dort mit dem Timer/Counter die Periodendauer messen.

Das geschieht indem ich den Timer laufen lasse und bei jeder fallenden 
Flanke einen Interrupt auslöse. In der dann folgenden ISR lese ich den 
Timerwert aus und speichere diesen.
Das mache ich mit 5 Timerwerten und mache mir diese dann an ein paar 
leds sichtbar.
Soweit so gut, allerdings entsprechen die Timerwerte nicht meinen 
Erwartungen.

Bei der oben genannten Frequenz von 400kHz(T=2500ns) und einem Timertakt 
von 16MHz(externer Quarz; Periodendauer=62,5ns) ergibt sich somit 
rechnerisch ein Timerwert von 40.

Wider Erwarten zählt mein Timer jedoch bis 77

hier mal noch der Code

Sender:
1
#ifndef F_CPU
2
#define F_CPU 16000000UL
3
#endif
4
5
#include <avr/io.h>
6
#include <util/delay.h>
7
8
void Init_Timer1()
9
{
10
  TCCR1A = (1<<COM1A0) | (1<<WGM11) | (1<<WGM10);  
11
  TCCR1B = (1<<WGM12) | (1<<WGM13) | (1<<CS10);
12
  OCR1A = 19;
13
  TIFR = 0xFF;
14
}
15
16
17
int main(void)
18
{
19
//   DDRA = (1<<PA0);
20
//   DDRB = (1<<PB3);
21
  DDRD = (1<<PD5);
22
  Init_Timer1();
23
    while(1)  
24
    {
25
    
26
    }
27
}
28
29
30
Empfänger:
31
32
#ifndef F_CPU
33
#define F_CPU 16000000UL
34
#endif
35
36
#include <avr/io.h>
37
#include <avr/interrupt.h>
38
39
#include <util/delay.h>
40
41
uint8_t u8_timerwert[5];      
42
uint8_t x=0;
43
44
 void Interrupt_init()
45
 {
46
                                 
47
MCUCR |= ((1<<ISC11)|(0<<ISC10)|(1<<ISC01)|(0<<ISC00));    
48
   GICR |= (1<<INT0);                      
49
   TCCR1A = 0x00;
50
   TCCR1B = (0<<CS12) | (0<<CS11) | (1<<CS10);         
51
  sei();
52
 }
53
 
54
 void Ausgabe()
55
 {
56
 uint8_t j;
57
 while(1)
58
 {
59
     PORTA=0xff;  //Startsignal - alle LED an            
60
     _delay_ms(2000);
61
  for (j=0; j<5; j++)          
62
  {
63
  if (u8_timerwert[j]<84)  //Arrayelemente vergleichen und ausgegeben    
64
    {
65
    PORTA=0b00000101;        
66
    _delay_ms(1000); //Verzögerungen zur besseren Sichtbarkeit      
67
    PORTA=0x00;
68
    _delay_ms(500);
69
    }
70
    else
71
    {
72
    PORTA=0b00000001;
73
    _delay_ms(1000);
74
    PORTA=0x00;
75
    _delay_ms(500);
76
    }
77
   }
78
     
79
 }
80
}
81
82
 ISR(INT0_vect)
83
 {    
84
  x=TCNT1L;          //Counterwert speichern              
85
  TCNT1L=0x00;          //reset Lowteil des Registers            
86
  static uint8_t i=0;  //Hilfsvariable          
87
  if (i>0)          //ersten Wert verwerfen              
88
  {      
89
  u8_timerwert[i-1]=x;  //Arrayelemente mit Timerwerten beschreiben  
90
  }  
91
  i++;
92
  if (i==6)          //Abbruchbedingung              
93
  {    
94
  GICR &= (0<<INT0);  //ISR deaktivieren
95
    Ausgabe();              
96
  }
97
                
98
  
99
}
100
 
101
int main(void)
102
{
103
  DDRA=0xff;                //PORTA -> Ausgang
104
  Interrupt_init();
105
  while(1)
106
  {
107
     
108
  }
109
}
Wäre schön wenn jemand ne Idee hätte woran es liegt, ich probier jetzt 
nochmal ne niedrigere Frequenz und aktiviere dann den Prescaler, weil 
ich vermute es könnte auch daran liegen dass der Hase dort im Pfeffer 
sitzt;)

Vielen Dank im Voraus!

von Karl H. (kbuchegg)


Lesenswert?

Mark Ziegler schrieb:

> Bei der oben genannten Frequenz von 400kHz(T=2500ns) und einem Timertakt
> von 16MHz(externer Quarz; Periodendauer=62,5ns) ergibt sich somit
> rechnerisch ein Timerwert von 40.


Es bedeutet aber auch, dass alle 40 Takte ein AUfruf dieser ISR
erfolgt
1
 ISR(INT0_vect)
2
 {    
3
  x=TCNT1L;          //Counterwert speichern              
4
  TCNT1L=0x00;          //reset Lowteil des Registers            
5
  static uint8_t i=0;  //Hilfsvariable          
6
  if (i>0)          //ersten Wert verwerfen              
7
  {      
8
  u8_timerwert[i-1]=x;  //Arrayelemente mit Timerwerten beschreiben  
9
  }  
10
  i++;
11
  if (i==6)          //Abbruchbedingung              
12
  {    
13
  GICR &= (0<<INT0);  //ISR deaktivieren
14
    Ausgabe();              
15
  }
16
                
17
  
18
}

und die wird höchst wahrscheinlich nicht einen Durchlauf in 40 Takten 
zustande bringen. Durch den Funktionsaufruf zwingst du den Compiler alle 
Register zu sichern. Und schon alleine das kostet dir einige Taktzyklen.

Halte dich an die einfache Regel: man macht keine derartigen Ausgaben in 
einer ISR. Leg das Zeug in die Hauptschleife, die ISR setzt ein Flag, 
wenn es Zeit ist die Ausgabe zu starten und gut ists. Dann ist deine ISR 
kurz
1
volatile uint8_t i=0;  //Hilfsvariable          
2
3
ISR(INT0_vect)
4
{    
5
   x=TCNT1L;          // Counterwert speichern              
6
   TCNT1L=0x00;       // reset Lowteil des Registers            
7
8
  u8_timerwert[i] = x;  // Arrayelemente mit Timerwerten beschreiben  
9
10
  i++;
11
  if( i == 5 )
12
  {
13
    GICR &= ~(1<<INT0);  //ISR deaktivieren
14
    i = 0;
15
  }                
16
}

und du kannst die Timinganforderungen erfüllen, dass die ISR nicht mehr 
als 40 Takte brauchen darf.

PS: Warum verwendest du nicht den Input Capture? Genau dafür ist der 
gedacht. Und die Interrupt Latenz wirst du damit auch automatisch los.

von Mark Z. (lyams)


Lesenswert?

Hallo Karl Heinz,
Danke schonmal für die schnelle Antwort.
Ich bin noch sehr unerfahren, daher diese Dummheiten;)
Die Laufzeit der ISR hab ich versucht mit dem Oszilloskop zu messen, und 
kam auf etwa 1,2µs.
Die Ausgabe mache ich ja erst nachdem der Interrupt dereits deaktiviert 
wurde.
Aber du hast mit dem Flag natürlich recht und es ist auch eleganter.

Zur Sache mit dem Input Capture muss ich mich nochmal belesen. danke 
auch für diesen Tipp.

Ich habs übrigens grad mit einer Frequenz von 125kHz probiert und 
empfängerseitig mit 2MHz, damit läufts wie erwartet - 16 Timerwerte.

Also liegt der Verdacht ja nahe dass es vorher zu zeitkritisch war.

Danke nochmals, ich werd mich dann nach dem Abendessen mal dran machen.

von Karl H. (kbuchegg)


Lesenswert?

Mark Ziegler schrieb:
> Hallo Karl Heinz,
> Danke schonmal für die schnelle Antwort.
> Ich bin noch sehr unerfahren, daher diese Dummheiten;)
> Die Laufzeit der ISR hab ich versucht mit dem Oszilloskop zu messen, und
> kam auf etwa 1,2µs.
> Die Ausgabe mache ich ja erst nachdem der Interrupt dereits deaktiviert
> wurde.

Das ändert aber nichts daran, dass du durch den Funktionsaufruf den 
Compiler zwingst Vorlauf und Nachlaufcode in die ISR einzubauen, der 
auch seine Zeit dauert.

von Mark Z. (lyams)


Lesenswert?

Auf den guten Tipp von Karl Heinz hab ich jetzt versucht die Input 
Capture Unit zu verwenden um mein eingehendes Signal zu verarbeiten. 
scheinbar will mir dies aber noch nicht so recht gelingen.
Hat jemand eine Idee wo es noch hakt, denn die Timerwerte sind nicht wie 
erwartet.

code:

#ifndef F_CPU
#define F_CPU 16000000UL
#endif

#include <avr/io.h>
#include <avr/interrupt.h>

#include <util/delay.h>

uint8_t u8_timerwert[5];
uint8_t x=0;

 void Interrupt_init()
 {
  GICR |= (1<<INT1);
  TIMSK |= (1<<TICIE1);
        TCCR1A = 0x00;
   TCCR1B |= (0<<ICNC1) | (0<<ICES1) | (1<<CS11);
  sei();
 }

 void Ausgabe()
 {
   uint8_t j;
   while(1)
   {
     PORTA=0xff;
     _delay_ms(2000);
     for (j=0; j<5; j++)
     {
       if (u8_timerwert[j]<8)
       {
         PORTA=0b00000101;
         _delay_ms(1000);
         PORTA=0x00;
         _delay_ms(500);
       }
       else
       {
         PORTA=0b00000001;
         _delay_ms(1000);
         PORTA=0x00;
         _delay_ms(500);
       }
     }

   }
 }
volatile uint8_t i=0;
 ISR(TIMER1_CAPT_vect)
 {

  if (i>0)
  {
          u8_timerwert[i-1]=ICR1L;
  }
  i++;
  if (i==6)
  {
    GICR &= (0<<INT1);
    Ausgabe();
  }
  TCNT1=0x00;

}

int main(void)
{
  DDRA=0xff;
  Interrupt_init();
  while(1)
  {

  }
}

Die Ausgabe muss noch in den Hauptteil.

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.