Forum: Mikrocontroller und Digitale Elektronik ATMEGA48A, Problem mit Frequenzmessung per Timer


von Jan (Gast)


Lesenswert?

Hallo,

ich versuche mit einem ATMEGA48A zwei Frequenzen im Bereich von 1MHz zu 
messen (Takt ist 16MHz), die an T0/T1 anliegen. Nachfolgend der Code 
(LCD Ausgabe habe ich weggelassen damit es nicht zu lang wird) - das 
Problem ist das wenn ich das gleiche Signal an beide Eingänge anlege die 
Frequenzmessung von Timer0 richtig ist, Timer1 aber eine zu niedrige 
Frequenz misst. Bin echt am verzweifeln, woran kann das liegen??

volatile unsigned char time;
volatile unsigned short frequencycounter_1, frequencycounter_2;
volatile long frequency_1, frequency_2;

// Timer2 Overflow Interrupt
#pragma vector = TIMER2_OVF_vect
__interrupt void TIMER2_OVF_vect_interrupt(void)
 {
   time++;

   if (time > 48)
    {

      TCCR0B  = 0;
      TCCR1B  = 0;
      TCCR2B  = 0;

      frequency_1 = ((unsigned long)frequencycounter_1 * 256) + TCNT0;

      frequency_2 = ((unsigned long)frequencycounter_2 * 65536) + TCNT1;

      frequencycounter_1 = 0;
      frequencycounter_2 = 0;
      time = 0;

      TCNT0 = 0;
      TCNT1 = 0;
      TCNT2 = 0;
      TCCR0B  |= (1<<CS00 | 1<<CS01 | 1<<CS02);
      TCCR1B  |= (1<<CS10 | 1<<CS11 | 1<<CS12);
      TCCR2B  |= (0<<CS20 | 0<<CS21 | 1<<CS22);
    }
 }

// Timer0 Overflow Interrupt
#pragma vector = TIMER0_OVF_vect
__interrupt void TIMER0_OVF_vect_interrupt(void)
 {
   frequencycounter_1++;
 }


// Timer1 Overflow Interrupt
#pragma vector = TIMER1_OVF_vect
__interrupt void TIMER1_OVF_vect_interrupt(void)
 {
   frequencycounter_2++;
 }

int main(void)
 {
   time = 0;
   frequencycounter_1 = 0;
   frequencycounter_2 = 0;
   frequency_1 = 0;
   frequency_2 = 0;

   __enable_interrupt(); //Interrupt enable

   TIMSK0  |= (1 << TOIE0);
   TIMSK1  |= (1 << TOIE1);
   TIMSK2  |= (1 << TOIE2);
   TCCR0B  |= (1<<CS00 | 1<<CS01 | 1<<CS02);
   TCCR1B  |= (1<<CS10 | 1<<CS11 | 1<<CS12);
   TCCR2B  |= (0<<CS20 | 0<<CS21 | 1<<CS22);

 while(1)
    {

      // [...] Frequenzen auf LCD ausgeben

    }
 }

von Jan (Gast)


Lesenswert?

Hallo,

niemand eine Idee? Ich verzweifle daran, schon alles probiert... :(

Gruß,
Jan

von Krapao (Gast)


Lesenswert?

Nur ein Verdacht: Lies mal 16.0.3 External Clock Source im Datenblatt 
und überdenke deine Manipulationen des Prescalers.

von Jan (Gast)


Lesenswert?

Hallo,

was genau meinst Du? Ich setze ja nur jeweils den clock source für alle 
drei Timer, oder was meinst Du?

Gruß,
Jan

von Peter R. (pnu)


Lesenswert?

Wie groß ist der Fehler? Faktor ganzzahlig, im Prozent-Bereich, eine 
oder zwei Schwingungen verpasst, Streuung des Messergebnisses...

von Jan (Gast)


Lesenswert?

Hallo,

ungefähr 10-11% Fehler maximal, allerdings wird er geringer je niedriger 
die Frequenz ist, bei ca. 500kHz ist der Fehler fast 0. Als würde der 
Timer nicht mitkommen... Allerdings ist es ja dasselbe Signal an beiden 
Eingängen, mit 50/50 Tastverhältnis.

Gruß,
Jan

von Peter R. (pnu)


Lesenswert?

Was da nicht mitkommt, ist die Interrupt-Verarbeitung.

Bis der Kontroller dazu kommt, die Int-Befehle abzuarbeiten muss er erst 
aus dem Grundprogramm  aussteigen. Bis dahin können mehrere Taktzyklen 
vergehen, deren Zahl sogar zufällig verschieden sein kann. Auch das 
Nullsetzen des Zählers wird dadurch beeinflusst.

Dazu kommt noch, dass der zuständige Pin das Eingangssignal nicht direkt 
an den Zähler weiterleitet sondern mit dem Kontrollertakt gescannt wird.

Ein Teil Fehlers kann auch dadurch entstehen, dass beim T0 (8bit) mehr 
Lesezeit gebraucht wird als beim T1 (16bit)
Der durch Int-Verhalten entstehende Fehler kann dadurch verringert 
werden, dass man mit Compare-Register arbeitet. Aber die 
Torzeiterzeugung bleibt weiterhin mangelhaft.

Für genaue Zeitmessung keine Kontroller sondern einen normalen Zähler 
als Messzähler und Zeitbasiszähler verwenden.

Hier kommt noch zusätzlich die Zeile if...>48 in der main dazu: die Ints 
der Timer verpfuschen noch den zeitlichenn Ablauf der main, sodass auch 
die Messzeit kräftig gestört wird.

von Jan (Gast)


Lesenswert?

Das Problem tritt aber auch auf, wenn ich time so setze das der 
Timer1-Überlauf nie eintritt. Und im Timer2-Interrupt werden die Timer 
ja zuerst angehalten, und dann erst gelesen und wieder gestartet, also 
sollte dafür genug Zeit sein...

Und es sind nicht nur einige wenige Zähl-Takte, sondern z.B. bei 50ms 
Torzeit 30000 (Timer0) gegen ca. 27000 (Timer1).

Gruß,
Jan

von Peter R. (pnu)


Lesenswert?

Aber die Messzeit wird doch erheblich durch den Programmablauf 
verändert. Einmal misst der Kontroller z.B. 50ms lang, das andere mal 
nur 49ms. Gerade dei Torzeit wird hier in der main bestimmt, und der 
Ablauf der main wird durch die Int's erheblich gestört.

Übrigens : ist der Takt des Kontrollers wirklich 16MHz oder läuft er 
(per fuse) um den Faktor 8 herabgesetzt?

Meiner Meinung nach ist es ein Pfui, Zeitmessungen per Kontroller 
durchzuführen und besonders wenn dabei ein C-Programm auf wenig 
kontrollierte Weise in Maschinensprache übersetzt wird.

von Stefan E. (sternst)


Lesenswert?

Peter R. schrieb:
> Aber die Messzeit wird doch erheblich durch den Programmablauf
> verändert. Einmal misst der Kontroller z.B. 50ms lang, das andere mal
> nur 49ms. Gerade dei Torzeit wird hier in der main bestimmt, und der
> Ablauf der main wird durch die Int's erheblich gestört.

Sorry, aber das ist doch Unsinn. Die Torzeit wird durch einen weiteren 
Timer per Interrupt erzeugt. Die einzige Varianz, die sich da ergibt, 
ist die Latenz dieses Interrupts, also maximal ein paar µS.

von Uwe (Gast)


Lesenswert?

Timer2 hat glaub ich einen Clock Input der zum Zählen in Hardware 
genutzt werden kann. Guck mal ob Timer1 das auch hat, wenn ja nimm die 
zum Zählen und Timer0 als Zeitbasis.

von Jan (Gast)


Lesenswert?

Das mache ich ja, Timer0/1 haben einen externen Eingang, Timer2 nutze 
ich für die Zeit.

Gruß,
Jan

von Peter R. (pnu)


Lesenswert?

Stefan Ernst schrieb:
> Sorry, aber das ist doch Unsinn. Die Torzeit wird durch einen weiteren
> Timer per Interrupt erzeugt. Die einzige Varianz, die sich da ergibt,
> ist die Latenz dieses Interrupts, also maximal ein paar µS.

Der Zeitablauf wird hier durch Zählen innerhalb der main (if..>49..) 
bestimmt.

Dabei wird 50 mal der 1ms-Int beteiligt. Die Streuung durch die 
Int-latency kann da um den Faktor 50 vergrößert werden, da die ints die 
main 50mal unterbrechen.

Wer auf die oben gezeigte Methode Zeitmessungen durchführt, muss eben 
seine Ansprüche auf Genauigkeit herabsetzen. Oder er muss genau wissen, 
was die Messung verfälscht.

von Karl H. (kbuchegg)


Lesenswert?

Jan schrieb:

> Und es sind nicht nur einige wenige Zähl-Takte, sondern z.B. bei 50ms
> Torzeit 30000 (Timer0) gegen ca. 27000 (Timer1).

Was mir aufgefallen ist:
Bei 1Mhz Messfrequenz und 50ms Torzeit sollte der Zählerstand aber 50000 
sein.

Ansonsten:
Das das durch die Interrupt Latenz nicht auf die Schwingung genau ist, 
ist dir ja klar. 3000 Ticks Differenz ist allerdings zu groß für diese 
banale Erklärung. Ich komm allerdings auch nicht drauf, was da los sein 
könnte.

von Karl H. (kbuchegg)


Lesenswert?

Peter R. schrieb:
> Stefan Ernst schrieb:
>> Sorry, aber das ist doch Unsinn. Die Torzeit wird durch einen weiteren
>> Timer per Interrupt erzeugt. Die einzige Varianz, die sich da ergibt,
>> ist die Latenz dieses Interrupts, also maximal ein paar µS.
>
> Der Zeitablauf wird hier durch Zählen innerhalb der main (if..>49..)
> bestimmt.

Nein.
Lies das Programm nochmal.
Er benutzt den Timer 2 als Torwächter. In der ISR werden 49 Aufrufe 
abgezählt und beim 49. Aufruf die Timer 0 und 1 sofort gestoppt. Die 
komplette Steuerung der Torzeit findet im Overflow-Interrupt des Timers 
2 statt.

Das ihm bis zum tatsächlichen Stoppen des Timers ein paar Zyklen zu viel 
einlaufen ist ihm bewusst. Aber das sind keine 3000 Ticks Differenz der 
Messungen von Timer 0 und Timer 1.

von Stefan E. (sternst)


Lesenswert?

Peter R. schrieb:
> Der Zeitablauf wird hier durch Zählen innerhalb der main (if..>49..)
> bestimmt.

Das "if" steht in einem Interrupt, nicht in main.

Peter R. schrieb:
> Dabei wird 50 mal der 1ms-Int beteiligt. Die Streuung durch die
> Int-latency kann da um den Faktor 50 vergrößert werden, da die ints die
> main unterbrechen.

Die Zähler laufen selbstständig und werden nicht per Software wieder auf 
Null gesetzt. Die Latenzen der 50 Interrupts summieren sich daher nicht 
auf. Nur die Latenz des letzten Interrupts schlägt zu Buche.

von Jan (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Jan schrieb:
>
>> Und es sind nicht nur einige wenige Zähl-Takte, sondern z.B. bei 50ms
>> Torzeit 30000 (Timer0) gegen ca. 27000 (Timer1).
>
> Was mir aufgefallen ist:
> Bei 1Mhz Messfrequenz und 50ms Torzeit sollte der Zählerstand aber 50000
> sein.

Ja richtig, das war bei einer kleineren Frequenz. Bei 1MHz ist der 
Fehler sogar noch größer...

Ich komme einfach nicht darauf was da los ist...

Gruß,
Jan

von Karl H. (kbuchegg)


Lesenswert?

> Wer auf die oben gezeigte Methode Zeitmessungen durchführt,
> muss eben seine Ansprüche auf Genauigkeit herabsetzen. Oder
> er muss genau wissen, was die Messung verfälscht.

Darum gehts doch noch gar nicht.
Das Problem ist, dass er signifikant verschiedene Messwerte erhält, je 
nachdem ob er Timer 0 oder Timer 1 benutzt.

Wenn die Werte falsch sind, sollten sie bei beiden Timern gleich falsch 
sein. Sind sie aber nicht. Die Werte laufen auseinander und im Moment 
kann das keiner erklären.

von Karl H. (kbuchegg)


Lesenswert?

Jan schrieb:

> Ja richtig, das war bei einer kleineren Frequenz. Bei 1MHz ist der
> Fehler sogar noch größer...
>
> Ich komme einfach nicht darauf was da los ist...

Hast du im Datenblatt nachgesehen, ob es technologische Beschränken für 
die Maximalfrequenz gibt, mit denen die Timer getaktet werden dürfen?

(Ich kanns mir zwar nicht vorstellen, dass es da zwischen den Timern 
Unterschiede gibt, aber in der Not ist jeder Strohhalm recht)

von Karl H. (kbuchegg)


Lesenswert?

Apropos Strohhalm

Was ist hiermit
1
 while(1)
2
    {
3
4
      // [...] Frequenzen auf LCD ausgeben
5
6
    }

irgendeine Chance, dass sich da ein long/int Fehler in der Ausgabe 
eingeschlichen hat?

von Jan (Gast)


Lesenswert?

Ja, bis 1/2 Taktfrequenz, empfohlen wird nur bis 1/4 zu gehen, aber da 
habe ich bei 16MHz ja noch gut Reserve...

Gruß,
Jan

von Stefan E. (sternst)


Lesenswert?

Jan schrieb:
> Und es sind nicht nur einige wenige Zähl-Takte, sondern z.B. bei 50ms
> Torzeit 30000 (Timer0) gegen ca. 27000 (Timer1).

Und du bist dir wirklich ganz sicher, was die Zuordnung angeht? Es ist 
wirklich Timer1 derjenige, bei dem die Takte fehlen?
Wäre es umgekehrt, kämen zeitweise (und zwar zu lange) in main gesperrte 
Interrupts in Betracht.

von Jan (Gast)


Lesenswert?

Ja, es ist sicher Timer1.

Gruß,
Jan

von Krapao (Gast)


Lesenswert?

> was genau meinst Du? Ich setze ja nur jeweils den clock source für alle
> drei Timer, oder was meinst Du?

"Enabling and disabling of the clock input must be done when T1/T0 has 
been stable for at least one system clock cycle, otherwise it is a risk 
that a false Timer/Counter clock pulse is generated."

Ich würde das wiederholte Disable/Enable im TIMER2_OVF_vect_interrupt 
komplett weglassen.
1
volatile unsigned char time;
2
volatile unsigned short frequencycounter_1;
3
volatile unsigned short frequencycounter_2;
4
volatile unsigned long frequency_1;
5
volatile unsigned long frequency_2;
6
7
// Timer2 Overflow Interrupt
8
#pragma vector = TIMER2_OVF_vect
9
__interrupt void TIMER2_OVF_vect_interrupt(void)
10
{
11
  uint8_t tmp_tcnt0 = TCNT0;
12
  uint16_t tmp_tcnt1 = TCNT1;
13
14
  time++;
15
  if (time > 48)
16
  {
17
    frequency_1 = tmp_tcnt0 + frequencycounter_1 * 256UL;
18
    frequency_2 = tmp_tcnt1 + frequencycounter_2 * 65536UL;
19
    time = 0;
20
    frequencycounter_1 = 0;
21
    frequencycounter_2 = 0;
22
    TCNT0 = 0;
23
    TCNT1 = 0;
24
    TCNT2 = 0; // neue Torzeit starten
25
  }
26
}
27
28
// Timer0 Overflow Interrupt
29
#pragma vector = TIMER0_OVF_vect
30
__interrupt void TIMER0_OVF_vect_interrupt(void)
31
{
32
  frequencycounter_1++;
33
  // Kann frequencycounter_1 überlaufen?
34
}
35
36
// Timer1 Overflow Interrupt
37
#pragma vector = TIMER1_OVF_vect
38
__interrupt void TIMER1_OVF_vect_interrupt(void)
39
{
40
  frequencycounter_2++;
41
}
42
43
int main(void)
44
{
45
  TIMSK0 |= (1 << TOIE0);
46
  TIMSK1 |= (1 << TOIE1);
47
  TIMSK2 |= (1 << TOIE2);
48
  // External clock source on T0 pin. Clock on rising edge.
49
  TCCR0B |= (1<<CS00 | 1<<CS01 | 1<<CS02); 
50
  // External clock source on T1 pin. Clock on rising edge.
51
  TCCR1B |= (1<<CS10 | 1<<CS11 | 1<<CS12);
52
  TCCR2B |= (0<<CS20 | 0<<CS21 | 1<<CS22); // clkT2S/64 (From prescaler)
53
  __enable_interrupt(); //Interrupt enable
54
55
  while(1)
56
  {
57
    unsigned long tmp_frequency_1;
58
    unsigned long tmp_frequency_2;
59
60
    // Interruptfesten Zugriff beachten!
61
    // To Do: Interrupts sperren 
62
    tmp_frequency_1 = frequency_1;
63
    tmp_frequency_2 = frequency_2;
64
    // To Do: Interrupts freigeben
65
66
    // [...] Frequenzen tmp_frequency_X auf LCD ausgeben
67
  }
68
}

>> Und es sind nicht nur einige wenige Zähl-Takte, sondern z.B. bei 50ms
>> Torzeit 30000 (Timer0) gegen ca. 27000 (Timer1).

Diese Zahlen verstehe ich nicht.

F_CPU 16 MHz, 8-Bit Timer2 Prescaler 64 mit Softwaretimer 0..48 ergibt 
eine Torzeitvon 50,176 ms und bei einem 1 MHz exteren Signal an T0/T1 
müssten gelten:

50176 = 0 + 196 * 256UL;
50176 = 50176 + 0 * 65536UL;

von Jan (Gast)


Lesenswert?

Krapao schrieb:
> Ich würde das wiederholte Disable/Enable im TIMER2_OVF_vect_interrupt
> komplett weglassen.

Habe ich ausprobiert, keine Änderung.

>>> Und es sind nicht nur einige wenige Zähl-Takte, sondern z.B. bei 50ms
>>> Torzeit 30000 (Timer0) gegen ca. 27000 (Timer1).
>
> Diese Zahlen verstehe ich nicht.
>
> F_CPU 16 MHz, 8-Bit Timer2 Prescaler 64 mit Softwaretimer 0..48 ergibt
> eine Torzeitvon 50,176 ms und bei einem 1 MHz exteren Signal an T0/T1
> müssten gelten:
>
> 50176 = 0 + 196 * 256UL;
> 50176 = 50176 + 0 * 65536UL;

Wie gesagt da hatte ich mich vertan, das war bei einer kleineren 
Frequenz.
Mit 1MHz wird der Fehlen noch größer.

Gruß,
Jan

von Peter D. (peda)


Lesenswert?

Jan schrieb:
> Und es sind nicht nur einige wenige Zähl-Takte, sondern z.B. bei 50ms
> Torzeit 30000 (Timer0) gegen ca. 27000 (Timer1).

"ca." nützt nichts!

Zeig mal die genauen Werte für verschiedene Frequenzen, dann kann man 
vielleicht eine Regel und daraus den Grund erkennen.

Welchen Compiler benutzt Du überhaupt?


Peter

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.