Forum: Mikrocontroller und Digitale Elektronik Frequenzmessung (1Hz-20Hz) Wert immer 8mal größer


von Karl (Gast)


Lesenswert?

Hallo.

Ich mache mit MEGA128 eine Frequenzmessung. Die Frequenz kann dabei 
zwischen 1Hz und 20Hz liegen, bleibt aber während der Messung konstant.

Ich habe das mit INT2 und 16bit-Counter ralisiert. FCPU ist 16MHz. 
Cunter-Prescaler setze ich auf 1024.
Hier ist ein Ausschnitt meines Programms:
1
void main(void)
2
{
3
     while(1)
4
     {
5
  uint8_t temp,flag;
6
  
7
          //if 2 rising adge occured  
8
  if(cnt==2)
9
  {
10
       //stop counter
11
       TCCR1B = 0x00;
12
        
13
       temp = SREG;
14
       cnt=0;
15
       cli();
16
17
       //disable INT2
18
       EIMSK &= ~(1<<INT2);
19
20
       //save counter-value
21
       uint16_t Timer;
22
       Timer = (uint16_t)(TCNT1L+((uint16_t)(TCNT1H<<8)));
23
       
24
              //write once to eeprom
25
              if(flag!=5)
26
       {
27
    eeprom_write_block( &Timer, 0x80, sizeof(Timer));
28
    flag=5;
29
       }
30
    
31
              //init TCNT1
32
       TCNT1L = 0;
33
              TCNT1H = 0;
34
        
35
       //pull SREG
36
       SREG = temp;
37
        }
38
39
        if(flag!=5)
40
        {
41
42
  temp = SREG;
43
  cli();
44
  
45
  //enable interrupt
46
  EIMSK |= (1<<INT2);
47
48
  //rising adge
49
  EICRA |= (1<<ISC20)|(1<<ISC21);
50
51
  SREG = temp;
52
        }
53
    }
54
   return 0;    
55
}

hier fange ich die steigenden Falnken, und zähle sie ab.
1
ISR(INT2_vect)
2
{
3
    //start counter
4
    if(TCCR1B!=0x05)
5
    {
6
  GTCCR |= (1<<TSM);
7
      
8
  //Prescaler = 1024
9
  TCCR1B = 0x05;
10
    }
11
   
12
    //count rising edges
13
    cnt ++;
14
}


Die Frequenzwerte speichere ich im EEPROM und schaue sie mir nachher an.
Meiner Idee nach muss die Umrechnung so sein:

zumessenefreq = FCPU/Prescaler/CounterValue
              = 16MHz/1024/Timer

Mein Problem: Meine berechnete Frequenz ist immer 8 mal so groß wie die 
tatsächliche. Eigentlich habe ich alles richtig gemacht. Stimmt evetuell 
der Precsaler im Datenblatt nicht? Kann ich mir fast nicht vorstellen..
Meine FCPU ist 100%tig 16MHz. Prescaler ist auf 1024 gesetzt. Irgendwas 
stimmt trotzdem nicht.

Hab mich mittlerweile bissle verrant in der Sache. Ein Blick von der 
Seite würde evtl. schneller den Bug erkennen.

Sieht jemand was ich falsch gemacht hab? Danke im Voraus...

von Matthias L. (Gast)


Lesenswert?

Lass dir erstmal den Ermittelten COunterwert ausgeben.
Also wieviele Takte werden denn gemessen.
Die Umrechnung erfolgt dann so:


f(1/min) = 937500 / X
f(1/sek) = 15625  / X
X: gemessene Timercount zwischen DENSELBEN Flanken..


Siehe Rechnung im vorletzten Post:
Beitrag "Periodendauer messen und in Frequenz umwandeln"

von Karl (Gast)


Lesenswert?

Du meinst also die umrechung stimmt nicht?

>Lass dir erstmal den Ermittelten COunterwert ausgeben.
Die Werte speichere ich im EEPROM und schaue sie mir nachher an.

>Also wieviele Takte werden denn gemessen
Ich messe zwei steigende Flanken, also ein Takt der zu messenden 
Frequenz.
Dann speichere ich die Werte im EEPROM. Nachdem die ganze Messung vorbei 
ist. Schaue ich EEPROM an und rechne (noch) manuel die zu messende 
Frequenz aus.

>Die Umrechnung erfolgt dann so:
>f(1/sek) = 15625  / X
genau so mach ich doch auch: 15625 = 16MHz/1024

z.B
bei 10Hz bekomme ich X=195 entspricht f=80 (also um faktor 8 größer
bei 1Hz  bekomme ich X=1953 entspr. f=8 (also um faktor 8 größer)

Also die Messung an sich scheint zu stimmen, nur habe ich eine 
OFFSET-Steigung = 8

Da sie konstant ist, kann ich sie problemlos rausrechnen, aber mir ist 
ihre ursache nicht ganz klar.

von Johannes M. (johnny-m)


Lesenswert?

> Dann speichere ich die Werte im EEPROM. Nachdem die ganze Messung vorbei
> ist. Schaue ich EEPROM an und rechne (noch) manuel die zu messende
> Frequenz aus.
Du weißt, dass das EEPROM nur eine begrenzte Anzahl Lösch-Schreib-Zyklen 
hat? Wenn bei Messungen permanent Daten dort gespeichert werden, kann 
das recht schnell zu Problemen führen. Ins EEPROM sollte man nach 
Möglichkeit nur schreiben, wenn es unbedingt erforderlich ist.

von Falk B. (falk)


Lesenswert?

@ Johannes M. (johnny-m)

>das recht schnell zu Problemen führen. Ins EEPROM sollte man nach
>Möglichkeit nur schreiben, wenn es unbedingt erforderlich ist.

Ausserdem ist es relativ langsam. Zum Debuggen sollte man lieber den 
SRAM verwenden, ist auch einfacher und logischer.

MfG
Falk

von Johannes M. (johnny-m)


Lesenswert?

Ist cnt volatile? Außerdem: Was machst Du da mit dem SREG? Du weißt gar 
nicht, was man mit solchen Manipulationen am SREG anrichten kann. Es 
kann passieren, dass Du damit dem Compiler ins Handwerk pfuschst. Als 
Hochsprachen-Programmierer hat man am SREG eigentlich nichts zu suchen. 
Einzig das Setzen und Löschen des I-Bits mittels sei() und cli() ist 
gestattet!

von Johannes M. (johnny-m)


Lesenswert?

> TCNT1L = 0;
> TCNT1H = 0;
Und da haben wir vermutlich die Ursache (oder zumindest einen Teil 
davon) für die Fehlfunktion... Das High-Byte des TCNT1 MUSS vor dem 
Low-Byte geschrieben werden. Allerdings bieten sämtliche mir bekannten 
Compiler die Möglichkeit, diese 16-Bit-Register als ganzes anzusprechen. 
Dann nimmt einem der Compiler die Sache mit der Reihenfolge ab. Und das 
sollte man auch nutzen. Also
1
TCNT1 = 0;

EDIT:
Zitat aus einem AVR-Datenblatt:
"To do a 16-bit write, the High byte must be written before the Low 
byte. For a 16-bit read, the Low byte must be read before the High 
byte."

NOCH EIN EDIT:
Auch wenn das obige Problem sicher zu einer Fehlfunktion führen dürfte, 
steht immer noch die Frage im Raum, warum es immer der 8-fache Wert ist, 
der rauskommt. Das ist dadurch, soweit ich das jetzt übersehe, nicht 
erklärbar.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Vieleicht ergibt sich bei 5hz ein anderer Offset... Zwei Beispiele sind 
denk ich keien Garantie das es IMMER *8 ist...
Außerdem würd ich die abfrage ob count==2 ist lieber in den Interupt 
paken!

von Karl (Gast)


Lesenswert?

Danke für die kritische Resonanz.

>Du weißt, dass das EEPROM nur eine begrenzte Anzahl Lösch-Schreib-Zyklen
>hat?
JA, ca 100 000 Zyklen sind garantiert. Ich schreibe ja nicht permanent 
über längere Zeit. Sondern beschreibe nur einmal lediglich 4 Zellen die 
ich später nie wieder brauche.

>Ist cnt volatile?
nein, danke für hinweis

>Außerdem: Was machst Du da mit dem SREG?
kommt aus dem Datenblatt S.124: Sie nennen es atomic write
1
unsigned char sreg;
2
unsigned int i;
3
/* Save global interrupt flag */
4
sreg = SREG;
5
/* Disable interrupts */
6
__disable_interrupt();
7
/* Set TCNTn to i */
8
TCNTn = i;
9
/* Restore global interrupt flag */
10
SREG = sreg;

> TCNT1L = 0;
> TCNT1H = 0;
habe ich total übersehen. Werde sofort korigieren.

Ich vermute, dass der Prescaler nicht richtig gesetzt wird.

von Karl (Gast)


Lesenswert?

>Vieleicht ergibt sich bei 5hz ein anderer Offset... Zwei Beispiele sind
>denk ich keien Garantie das es IMMER *8 ist...

Hier sind meine Messergebnisse:

tatsächliche freq.           TimerWert           16MHz/1024/8/TimerWert
     1Hz                         1953                1,000064Hz
     2Hz                          976                2,00115Hz
     3Hz                          651                3,00019Hz
     4Hz                          480                4,0690Hz
     5Hz                          390                5,0080Hz
     6Hz                          325                6,0096Hz
     10Hz                         195                10,016Hz
     20Hz                          97                20,135Hz

>Außerdem würd ich die abfrage ob count==2 ist lieber in den Interupt
>paken!
Wäre evtl. besser/ klienere Fehler

Leigt bestimmt am Prescaler

von Johannes M. (johnny-m)


Lesenswert?

Karl wrote:
>>Außerdem: Was machst Du da mit dem SREG?
> kommt aus dem Datenblatt S.124: Sie nennen es atomic write
>
1
> unsigned char sreg;
2
> unsigned int i;
3
> /* Save global interrupt flag */
4
> sreg = SREG;
5
> /* Disable interrupts */
6
> __disable_interrupt();
7
> /* Set TCNTn to i */
8
> TCNTn = i;
9
> /* Restore global interrupt flag */
10
> SREG = sreg;
11
>
Das macht aber auch nur dann Sinn, wenn man nicht zu 100% die Kontrolle 
über die Interrupt-Freigabe hat. I.d.R. genügt ein cli() vor und ein 
sei() nach dem 16-Bit-Zugriff. Wie gesagt, in 99,9% der Fälle sollte man 
vom SREG die Finger lassen (was direkte Zugriffe angeht).

> Ich vermute, dass der Prescaler nicht richtig gesetzt wird.
Aha. Wie kommst Du da drauf?

BTW:
> Timer = (uint16_t)(TCNT1L+((uint16_t)(TCNT1H<<8)));
Auch hier ist die Lesereihenfolge nicht gewährleistet. Wenn Du das noch 
nicht geändert hast, dann hol das bitte nach. Nimm auch bei dieser 
Operation das 16-Bit-Register.

von Karl (Gast)


Lesenswert?

Ca ab hier, hat die gemessene Frequenz größeren Fehler, was auf die 
Laufzeiten zurückzuführen ist.

ferq.                   Timer-Wert               berechnet
60Hz                    30                       65,104Hz
100Hz                   14                      139,508Hz

Aber in meinen Anwendung kommen Frequenzen von 0,5Hz-10Hz vor.

von Falk B. (falk)


Lesenswert?

@ Karl (Gast)

>Ca ab hier, hat die gemessene Frequenz größeren Fehler, was auf die
>Laufzeiten zurückzuführen ist.

AUA. Wie kann man mit einem 16MHz!!! uC so einen riesigen Fehler bei 
dermassen niedrigen Frequenzen machen. Unverständlich.

MfG
Falk

von Karl (Gast)


Lesenswert?

>> Ich vermute, dass der Prescaler nicht richtig gesetzt wird.
>Aha. Wie kommst Du da drauf?
Ist eine Vermutung, was sonst kann das sein. Wenn ich konstant statt 
1024 den Wert 128 (1024/8) habe...

>> Timer = (uint16_t)(TCNT1L+((uint16_t)(TCNT1H<<8)));
>Auch hier ist die Lesereihenfolge nicht gewährleistet. Wenn Du das noch
>nicht geändert hast, dann hol das bitte nach. Nimm auch bei dieser
>Operation das 16-Bit-Register.

Aber Timer ist doch 16Bit variable, oder meinst du ich sollte das 
machen:
1
Timer=TCNT1;

von Johannes M. (johnny-m)


Lesenswert?

Karl wrote:
> Aber Timer ist doch 16Bit variable, oder meinst du ich sollte das
> machen:
>
1
> Timer=TCNT1;
2
>
Genau! Es ist auch beim Lesen die Reihenfolge zu beachten (s.o.). Und 
bei der Operation mit Shift und Typkonversion hat der Compiler im 
Prinzip die Freiheit, die Reihenfolge beliebig zu wählen. Es ist deshalb 
nicht gewährleistet, dass die beiden Register in der richtigen 
Reihenfolge gelesen werden.

von Karl (Gast)


Lesenswert?

>AUA. Wie kann man mit einem 16MHz!!! uC so einen riesigen Fehler bei
>dermassen niedrigen Frequenzen machen. Unverständlich.

Danke dir für deine Sachlichkeit und Konstruktivität deines Betrages.

von crazy horse (Gast)


Lesenswert?

Datenblatt:
Default clock source
the device ist shipped with .... and CKDIV8 programmed.
Sollte das dir zu denken geben? Ich glaube ja.

von Karl (Gast)


Lesenswert?

>and CKDIV8 programmed
war tatsächlich programmiert = internal Clockdevision by 8

Das erklärt den OFFSET = 8.
Allerdings sind die Messwerte deutlich ungenauer: Ich bekomme statt 1Hz 
1,3Hz und statt 5Hz 8,7Hz. Dann stimmt meine Frequenz vorne und hinten 
nicht... :)

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Hat der AVR nicht nen ICP interupt?
Ich denk das sit für Frequenzmessung genauer... Oder miß doch eine 
Sekunde lang wieviel Pulse kommen...

von Johannes M. (johnny-m)


Lesenswert?

Input Capture ist tatsächlich die einzig vernünftige Möglichkeit...

von Karl (Gast)


Lesenswert?

>Hat der AVR nicht nen ICP interupt?
Das hat er, kann ich aber nciht nutzen, da mein signal nciht an diesen 
ICP-Pin angeschlossen ist.

>Oder miß doch eine Sekunde lang wieviel Pulse kommen...
Wenn die "Sekunde" nicht stimmt, macht es kein unterschied: Zeitmessen 
für bestimmte Flankenzahl oder umgekehrt..

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

ein simples count++ im interupt ist aber denk ich problemloser als dein 
konstrukt. Außerdem kreigst du so nen Mittelwert und dein Fehler sollte 
kleiner sein ebenso sparrst du dir natürlich die umrechnung

von Johannes M. (johnny-m)


Lesenswert?

Bei so niedrigen Frequenzen ist ein Fehler von 30% aber nicht erklärbar. 
Wie sieht das denn mit der Umrechnung aus? Machst Du die auch im 
Programm oder hast Du bisher nur die Werte von Hand ausgerechnet?

von Karl (Gast)


Lesenswert?

Hab mein programm etwas verbessert, sieht nun so aus:
1
/INT2 definitions
2
//disable interrupts before using taht
3
#define Enable_INT2()  (EIMSK |= (1<<INT2), EICRA |= (1<<ISC20)|(1<<ISC21))       
4
#define Disable_INT2()  EIMSK &= ~(1<<INT2)
5
6
//timer/counter definitions
7
#define Start_Counter1()    (GTCCR |= (1<<TSM), TCCR1B = 0x05 )//Prescaler = 1024
8
#define Stop_Counter1()    TCCR1B = 0x00
9
#define Get_Counter1_Result()  (TCNT1)
10
...
11
12
void main()
13
{
14
...  
15
  timer_value=0;
16
  cli();
17
  Enable_INT2();
18
  sei();
19
}
1
ISR(INT2_vect)
2
{
3
  //start counter
4
  if(TCCR1B == 0x00)
5
  {
6
    Start_Counter1();
7
  }
8
9
  edge_counter ++;
10
11
  if(edge_counter==2)
12
  {
13
    Stop_Counter1();
14
    Disable_INT2();
15
    timer_value=Get_Counter1_Result();
16
    
17
    edge_counter=0;
18
    TCNT1 = 0;
19
  }
20
}

von Karl (Gast)


Lesenswert?

>Wie sieht das denn mit der Umrechnung aus? Machst Du die auch im
>Programm oder hast Du bisher nur die Werte von Hand ausgerechnet?

Die Umrechnung mach ich nicht im Programm. d.h. Der Timerwert wird roh 
an PC Übertragen und dort nehme ich die Umrechnung vor.

von Falk B. (falk)


Lesenswert?

@  Karl (Gast)

>>AUA. Wie kann man mit einem 16MHz!!! uC so einen riesigen Fehler bei
>>dermassen niedrigen Frequenzen machen. Unverständlich.

>Danke dir für deine Sachlichkeit und Konstruktivität deines Betrages.

Immer wieder gern geschehn. ;-)

Warum einfach, wenn es auch umständlich geht.

16 Mhz Takt (was hier zumindest Overkill ist).
Prescaler 1024 macht ~16 kHz.

Bei 10 Hz macht das eine Auflösung von 1600 Zählern ~0,6 Promille.
Bei 16 kHz braucht ein 16 Bit-Zähler ca. 65536/16kHz ~4 Sekunden bis zum 
Overflow. Passt.

Alles was man mun tun muss ist die Differenz der Zählerstände zu 
berechnen. Das wars. Im Falle eines Overflows ist die negativ, dann muss 
invertiert werden. Das ist der ganze "Trick".
1
volatile uint16_t delta_t;
2
uint16_t old_time;
3
volatile uint8_t flag;
4
5
void main(void)
6
{
7
  // timer initialisieren
8
9
  TCCR1B = 5;        // Prescaler 1024
10
  
11
  // interrupt initialisieren
12
13
  TIMSK |= (1<<TOIE1);  // Overflow Interrupt ON
14
15
  sei();
16
17
     while(1)
18
     {
19
    while(flag==0);      // Warte auf Messung
20
    flag =0;
21
22
    // hier jetzt die Auswertung/Speicherung des Messwertes delta_t
23
   }
24
}
25
26
// steigende Flanke an INT2
27
28
ISR(INT2_vect)
29
{
30
  uint16_t tmp, tmp1;
31
32
  tmp = TCNT1;
33
  tmp1 = tmp - old_time;
34
  if (tmp1>32768) tmp1 -= 32768;
35
  old_time = tmp;
36
  delta_t = tmp1;
37
  flag = 1;
38
}


@ Läubi Mail@laeubi.de (laeubi)

>Hat der AVR nicht nen ICP interupt?

Ja.

>Ich denk das sit für Frequenzmessung genauer... Oder miß doch eine

Für DIE Frequenzen tuts auch der externe Interrupt.

>Sekunde lang wieviel Pulse kommen...

Tut er nicht, aber er macht es sehr kompliziert und falsch.

MFG
Falk

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Schaut schonmal besser aus.
Ich würde in der ISR nen Flag setzen wenn die Messung abgeschlossen ist, 
und dann in der Mainloop ins EEPRom schreiben und dann eien neue Messung 
starten.

von Gerhard. (Gast)


Lesenswert?

Ich bin der Ansicht dass man den ICP pin auf alle Faelle benutzen 
sollte. Da kann dann prinzipiell nichts daneben gehen;:-)

Fakten:

Timer laeuft mit Prescaler vom 16MHZ Zeitbasis, steuert den 16-Bit Timer 
mit genauem Takt im MHZ Bereich. Bei gewaehlter 1MHz Taktrate also 1us 
Aufloesung der Periodenmessung des Eingangssignal.

Eingangsignal an ICPn anschliessen. (Auf minimalen Jitter achten) Die 
Flankensteilheit des Sihnals muss entsprechend aufbereitet sein.

ICP Interrupt ist fuer fallende oder steigende Flanke des Eingangsignal 
programmiert. (Z.B. Fallende Flanke)

Mit dem Timer overflow Interrupt zaehlen wir die Ueberlaeufe.

Wenn nun das Eingangssignal von H->L geht, wird der jeweilig 
Timerzaehlstand in den capture rgeister uebernommen.

Jetzt haben wir n[start].

Dann warten wir bis der naechste Capture ankommt. Die Periodenmessung 
ist
nun komplett mit n[stop].

Die Periodendauer t =  n[stop] - n[start] * n[overflow] * 65536

(Wegen der 16-bit Variable und captur register braucht man sich nicht um 
uebrlaufe bei der Subtraktion stoeren da die overflows ja mitbeeinzogen 
werden)

Frequenz ist 1/t.

Jetzt auf die gewuenschten Masseinheiten umrechnen - Fertig.

Sorry fuer das Wiederkauen, aber ich dachte es ist vielleicht mal 
nuetzlich sich das in dieser Weise vor den Augen zu halte - Also bitte 
nicht schimpfen.)

Ich mache das bei meinen Programmen mit PIC und AVR immer so, und hat 
bei mir immer problemlos funktioniert. Die Genauigkeit bei den niedrigen 
Frequenzen ist extrem hoch.  Bei 1Mhz Taktfrequenz des TIMER1 kann man 
1Hz auf 0.000001 Hz genau aufloesen.

Habe das mit einer Praezisonszeitbasis auch gemessen und bestaetigen 
koennen. Die Messgenaugikeit sinkt aber mit groesser werderender 
Eingangsfrequenz ab. BEi 10Khz ist das Bild natuerlich nicht mehr so 
schoen.

Ich bin jedenfalls der Ansicht, dass man fuer solche Zwecke unbedingt 
input capture benutzen sollte. Wie gesagt ich will hier niemand auf die 
Fuesse steigen. dachte aber meinen Senf dazu beitragen.

Gruss,
Gerhard

von Karl (Gast)


Lesenswert?

>16 Mhz Takt (was hier zumindest Overkill ist).
Sag mal warum das Overkill ist? Du weißt doch gar nicht was ich sonst 
mit dem µC mache?

>Bei 16 kHz braucht ein 16 Bit-Zähler ca. 65536/16kHz ~4 Sekunden bis zum
>Overflow. Passt.
Habe ich vorher gerechnet, deshalb auch 16Bit zähler

>Alles was man mun tun muss ist die Differenz der Zählerstände zu
>berechnen. Das wars. Im Falle eines Overflows ist die negativ, dann muss
>invertiert werden. Das ist der ganze "Trick".
Meine zu messende Frequenz ist min. 1Hz das heißt t=1s. In dieser Zeit 
erreicht der Zähler bei einer frequenz von 16Mhz/1024 ~ 16kHz max ca. 
16000, da er nur eine sekunde lang mißt, kommt also nicht zum overflow. 
also kein trick...

>Tut er nicht, aber er macht es sehr kompliziert und falsch.
Deine Beiträge sind sehr lustig. :) Sag mal,
Warum ist es kompiziert und falsch Zeit zwischen zwei Flanken zu messen 
und daraus die Frequenz zu errechnen?
4sec eine 1Hz Frequenz zu messen, dass finde ich Overkilled. Aber es ist 
nicht falsch...

von Gerhard. (Gast)


Lesenswert?

Dasa war falsch:

Die Periodendauer t =  n[stop] - n[start] + ( n[overflow] * 65536 )

Sorry,
Gerhard

von Karl (Gast)


Lesenswert?

>Ich bin der Ansicht dass man den ICP pin auf alle Faelle benutzen
>sollte. Da kann dann prinzipiell nichts daneben gehen;:-)

Das Projekt wurde eben nicht so ausgelegt, dass ich ICP nutzen kann. 
Auch ich muss das als gegeben hinnehmen...

>Sorry fuer das Wiederkauen, aber ich dachte es ist vielleicht mal
>nuetzlich sich das in dieser Weise vor den Augen zu halte - Also bitte
>nicht schimpfen.)
Ich schipfe grundsätzlich nicht...

von Gerhard. (Gast)


Lesenswert?

Danke fuer den Hinweis Karl. Da ist im Augenblick guter Rat teuer.

Welcher Pin war das?

Gerhard

von Karl (Gast)


Lesenswert?

>Welcher Pin war das?
PD4

von Gerhard. (Gast)


Lesenswert?

Also ein normaler Port Pin. Da geht es warscheinlich nicht viel 
einfacher wie schon beschrieben. Da bin ich jetzt mit meinen Latein im 
Augenblick auch zu Ende.

Mir kommt vor, die schlechte Genaugigkeit kommt daher dass der TIMER mit 
nur 1953.125 HZ (0.512ms) getaktet wird. Die Aufloesung der Zeitmessung 
ist ja nur 0.512ms. Hast Du schon probiert die Taktrate so weit zu 
erhoehen bis es nicht mehr geht?

Bei dieser Taktrate ist der Unterschied Aufloesungsmaessig nur 0.00452HZ 
bei 1Hz Eingangsfrequenz. Dann stimmen Deine (ungenauen) Messergebnisse.

Gerhard

von Falk B. (falk)


Lesenswert?

@ Karl (Gast)

>>16 Mhz Takt (was hier zumindest Overkill ist).

>Sag mal warum das Overkill ist? Du weißt doch gar nicht was ich sonst
>mit dem µC mache?

Eben deshalb schrieb ich dass es HIER, für diese Messung, Overkill ist. 
Was der uC sonst macht kann einen 16 MHz Takt notwendig machen.

>16000, da er nur eine sekunde lang mißt, kommt also nicht zum overflow.
>also kein trick...

Doch, aber das erklären wir mal später.

>>Tut er nicht, aber er macht es sehr kompliziert und falsch.
>>Deine Beiträge sind sehr lustig. :) Sag mal,
>Warum ist es kompiziert und falsch Zeit zwischen zwei Flanken zu messen
>und daraus die Frequenz zu errechnen?

Das Prinzip ist richtig, deine Umsetzung nicht.

>4sec eine 1Hz Frequenz zu messen, dass finde ich Overkilled. Aber es ist
>nicht falsch...

Du hast mich nicht verstanden. Die 4 Sekunden sind eine Überlaufzeit, 
keine Messzeit.

MfG
Falk

von Karl (Gast)


Lesenswert?

>Mir kommt vor, die schlechte Genaugigkeit kommt daher dass der TIMER mit
>nur 1953.125 HZ (0.512ms) getaktet wird. Die Aufloesung der Zeitmessung
>ist ja nur 0.512ms. Hast Du schon probiert die Taktrate so weit zu
>erhoehen bis es nicht mehr geht?

Du meinst ich sollte die Taktrate des Zählers erhöhen? Ja das könnte ich 
tun. Aber dann wird der Zähler ständig überlaufen, wenn ich niedrige 
Frequenzen messe...

Mal schuen ob die Werte besser werden

von Gerhard. (Gast)


Lesenswert?

Ich wuerde die Overflows in Kauf nehmen. Die kann ja der Overflow 
Interrupt mitzaehlen und dann brauchst Du Dich nicht mehr darum 
kuemmern.

Gerhard

von crazy horse (Gast)


Lesenswert?

solange es nicht 2 overflows gibts, braucht man sich gar nicht drum 
kümmern. Auch eine Korrekturrechnung ist nicht erforderlich, rechnet man 
mit unsigned int.
ISR:
{
static unsigned int Messwert_bak;
unsigned int temp;
Messwert=temp-Messwert_bak;
Messwert_bak=temp;
}

Bsp:
alter Zählerstand 0x1000, Int bei 0x2000 -> Messwert 0x1000
alter Zählerstand 0xFF00, Int bei 0x0F00 (Overflow) -> Messwert 0x1000

von Gerhard. (Gast)


Lesenswert?

Karl:

So stelle ich mir das vor:

t[us] - 32 Bit UNSIGEND INTEGER
T1,T2 - 16 Bit UNSIGNED INTEGER (Unbedingt beachten)
n[overflow] - 8 oder 16 bit UNSIGNED INTEGER

t[us] =  (int32) (T2 - T1) + (int32) (n[overflow] * 65536) * (int32) 
Taktrate(us)

Kuemmere Dich nicht um das Sign Bit der Rechnung T2-T1. Das geht aber 
nur richtig mit 16-bit Speichervariablen. Zum Beispiel wenn T1 = 55535 
und T2 = 1000, dann ist der Unterschied (T2-T1) = 11001
            T1                      T1
T[total] = 55535 - 65536 = 10001 + 1000 = 11001 (Lauft ueber, also nicht 
negativ)

Gerhard

von Karl (Gast)


Lesenswert?

@Falk
Es wird interessant wie ein Krimi...

>Das Prinzip ist richtig, deine Umsetzung nicht.
Ok, gehehn wir meine und deine Umsetztung systematisch durch:

im main machst du die Timer und Interrruptinitialisierung
Dann aktivierst du interrupts und wartest du bis ne steigende Flanke 
kommt
1
volatile uint16_t delta_t;
2
uint16_t old_time;
3
volatile uint8_t flag;
4
5
void main(void)
6
{
7
  // timer initialisieren
8
  TCCR1B = 5;        // Prescaler 1024
9
  
10
  // interrupt initialisieren
11
  TIMSK |= (1<<TOIE1);  // Overflow Interrupt ON
12
13
  sei();
14
15
  while(1)
16
  {
17
    while(flag==0);      // Warte auf Messung
18
    flag =0;
19
20
    // hier jetzt die Auswertung/Speicherung des Messwertes delta_t
21
   }
22
}

In ISR lädst du timerergebnis, ziehst eine unreferenzierte old_time ab 
und das ergebnis prüfst du auf overflow. OK.
Nun schiebst du timerergebnis in time_old. und delta_t ist 
zeitmessung.OK verstehe. Aber ne Frage: wo wird der Timer gestartet
Und verlierst du nicht die allererste Flanke wenn old_time 
unreferenziert ist, beim ersten durchlauf?
1
// steigende Flanke an INT2
2
ISR(INT2_vect)
3
{
4
  uint16_t tmp, tmp1;
5
6
  tmp = TCNT1;
7
  tmp1 = tmp - old_time;
8
  if (tmp1>32768) tmp1 -= 32768;
9
  old_time = tmp;
10
  delta_t = tmp1;
11
  flag = 1;
12
}

Was ich mache ist:
Warte auf Falnke -> Starte Timer -> Ist zweite Falnke da? -> Stoppe 
Timer
Im Timer ist nun meine Zeit zwischen zwei steigenden Falnken.

Was ist konkret schlecht an meiner Umsetztung?

von Falk B. (falk)


Lesenswert?

@ Karl (Gast)

>zeitmessung.OK verstehe. Aber ne Frage: wo wird der Timer gestartet

Im Main und läuft dann immer ohne Pause.

>Und verlierst du nicht die allererste Flanke wenn old_time
>unreferenziert ist, beim ersten durchlauf?

Ja. Aber bei kontinuierlichen Messungen kein Problem.

>Was ich mache ist:
>Warte auf Falnke -> Starte Timer -> Ist zweite Falnke da? -> Stoppe
>Timer
>Im Timer ist nun meine Zeit zwischen zwei steigenden Falnken.

>Was ist konkret schlecht an meiner Umsetztung?

Schau dir dein Program an. Viel zu kompliziert. Das SREG ist für dich 
als C-Programierer zu 99,999% tabu. Ist auch vollkommen unnötig. Das 
Sperren und wideranschalten der Interrupts ist unnötig, ausserdem muss 
dazu kein cli() gemacht werden.
Und, der letzte und grösste Fehler. Du stoppst den Zähler nicht in der 
ISR sondern in der Main-Schleife. Das bringt zusätzliche, unnötige, 
variable Verzögerungen, die das Messergebnis verfälschen.

Mit meiner Methode hat man fast die selbe Auflösung und Genauigkeit wie 
mit der Input Capture Funktion, weil die Verzögerung von der Flanke bis 
zum Auslesen des Zählers konstant ist. Die einzige Unschärfe im 
Zeitverhalten ist die variable Verzögerung des Interrupts. Wenn nämlich 
zufällig ein Befehl mit mehr als 1 Takt Länge ausgeführt wird, muss der 
Interrupt solange warten. Das bringt aber worst case drei Takte 
Unsicherheit (bei einem Befehl mit 4 Takten). Naja, und ein anderer 
Interrupt darf natürlich nicht aktiv sein, sonst geht das schief, klar.

Mfg
Falk

von Karl (Gast)


Lesenswert?

>Schau dir dein Program an. Viel zu kompliziert. Das SREG ist für dich
>als C-Programierer zu 99,999% tabu. Ist auch vollkommen unnötig.

Habe ich schon längst verbessert, hast du es übersehen?:
1
void main()
2
{
3
...  
4
  timer_value=0;
5
  cli();
6
  Enable_INT2();
7
  sei();
8
}
9
10
ISR(INT2_vect)
11
{
12
  //start counter
13
  if(TCCR1B == 0x00)
14
  {
15
    Start_Counter1();
16
  }
17
18
  edge_counter ++;
19
20
  if(edge_counter==2)
21
  {
22
    Stop_Counter1();
23
    Disable_INT2();
24
    timer_value=TCNT1;
25
    
26
    edge_counter=0;
27
    TCNT1 = 0;
28
  }
29
}


>Und, der letzte und grösste Fehler. Du stoppst den Zähler nicht in der
>ISR sondern in der Main-Schleife. Das bringt zusätzliche, unnötige,
>variable Verzögerungen, die das Messergebnis verfälschen.

Stimmt nicht. Ich mach das in ISR. Wenn 2 Flanken gezählt wurden, stoppe 
ich den Zähler und deaktiviere INT2. Im main aktiviere ich nur den 
INterrupt für steigende Flanke.

von Karl (Gast)


Lesenswert?

>Naja, und ein anderer
>Interrupt darf natürlich nicht aktiv sein, sonst geht das schief, klar.

Ist aber in meinem Programm der Fall. Aber die Externen Interrupts haben 
allerdings den Vorrang. Haben sie trotzdem einen Einfluss?

Deine Funktion habe ich getestet und beim Debuggen sehe ich, dass 
delta_t fast immer 0 ist. Dabei sollte delta_t den TimerWert zwischen 
zwei flanken darstellen.. Seltsam

von Karl H. (kbuchegg)


Lesenswert?

Karl wrote:

> Deine Funktion habe ich getestet und beim Debuggen sehe ich, dass
> delta_t fast immer 0 ist. Dabei sollte delta_t den TimerWert zwischen
> zwei flanken darstellen.. Seltsam

<Zitat>
1
void main(void)
2
{
3
  // timer initialisieren
4
  TCCR1B = 5;        // Prescaler 1024
5
  
6
  // interrupt initialisieren
7
  TIMSK |= (1<<TOIE1);  // Overflow Interrupt ON
8
9
  sei();
10
11
  while(1)
12
  {
13
    while(flag==0);      // Warte auf Messung
14
    flag =0;
15
16
    // hier jetzt die Auswertung/Speicherung des Messwertes delta_t
17
   }
18
}
</Zitat>

Hast du einen Overflow Handler für Timer 1?
Solange du nicht mehr als 65536 Timer Ticks zur Messung
benötigst (auch bei freilaufendem Timer) brauchst du die
Overflows nicht zu berücksichtigen. Durch die unsigned
Rechnung gleicht sich das von alleine aus.
Daher ist auch das
  if (tmp1>32768) tmp1 -= 32768;
in so einem Fall nicht notwendig.

von Falk B. (falk)


Lesenswert?

@ Karl (Gast)

>>Naja, und ein anderer
>>Interrupt darf natürlich nicht aktiv sein, sonst geht das schief, klar.

>Ist aber in meinem Programm der Fall. Aber die Externen Interrupts haben
>allerdings den Vorrang. Haben sie trotzdem einen Einfluss?

Aber sicher! Stell dir vor, ein anderer Interrupt wird aktiv, egal 
welcher. Der Prozessor fängt an ihn zu bearbeiten. Nur einen Takt später 
kommt dein Externer Interrupt. Da der AVR keine verketteten Interrupts 
von Hause aus kann, muss dein Externer Interrupt warten, bis der gerade 
begonnene Interrupt zu Ende ist. Das verfälscht die Messung natürlich um 
EINIGES! Abhilfe schafft hier in Grenzen ein Verketten von Interupts per 
Software, sprich, der erste Befehl ist ein sei() in JEDER anderen 
Interruptroutine, ausser im externen Interrupt. Damit sind alle anderen 
Interrupts unterbrechenbar. Doch die Zeitverzögerung ist dann immer noch 
gross, je nach Compileroverhead 4....50 Takte.

>zwei flanken darstellen.. Seltsam

In der Tat.

MfG
Falk

von Gerhard. (Gast)


Lesenswert?

Ist es wirklich unmoeglich, die Hardware zu modifizieren, dass der ICP 
capture Eingang verwendet werden koennte? Das ware sooo viel einfacher.

Mir kommt vor, dass es wirklich verflixt schwierig scheint, das 
Zeitintervallmessen richtig zum Funktionieren zu bringen. Bei der 
Capture methode verliert man auch keine Genaugigkeit, da der TIMER 
Zaehlerstand von der Hardware direkt uebernommen wird.

Vielleicht is es Zeit das Skalpell aus der Schublade zu holen und an der 
Platine zu operieren. Zwei Schnitte + Draht und Loetkolben: Fertig - und 
das Problem gehoert der Vergangenheit an;:-)

Gerhard

von Johannes M. (johnny-m)


Lesenswert?

> Aber die Externen Interrupts haben allerdings den Vorrang.
Es gibt bei den AVRs keine Interrupt-Prioritäten im engeren Sinne und im 
Prinzip sind Interrupt Handler nicht durch andere Interrupts 
unterbrechbar (außer man gibt im Handler explizit die 
Interrupt-Bearbeitung frei). Die "Vorrangstufen" nach der Vektortabelle 
beziehen sich lediglich auf zeitgleich auftretende Interrupts (bzw. auf 
den Fall, dass bei einer globalen Freigabe mehrere lokal freigegebene 
Interrupts bereits anstehen, also deren Flags gesetzt sind).

von Karl (Gast)


Lesenswert?

OK. Ich hab jezt die Lösung von Falk implementiert. Bis jetzt liefert 
sie bessere Ergebnisse.

Damit andere Interrupts das Ergebniss nicht verfälschen schalte ich INT2 
und Counter jedesmal ein und aus.

Vielleicht noch paar Worte zu meiner Anwendung. Im Grunde benutze ich 
AT90USB (identisch zu MEGA128 bis auf USB). Ich sende per USB ein 
Commando z.B GET_FREQUENCY (indem ich auf ein Button drücke), dann 
werden die INT2 und Zähler gestartet.
1
main()
2
{
3
...
4
else if(received_command == COMM_GET_FREQUENCY)
5
  {
6
    last_command = COMM_GET_FREQUENCY;
7
    
8
    cli();
9
      
10
    TCCR1B = 5;        // Prescaler 1024
11
        
12
    TIMSK1 |= (1<<TOIE1);  // Overflow Interrupt ON
13
14
    Enable_INT2();
15
    sei();
16
  }
17
}
PC wartet 1,5s lang und fragt dann die Daten ab. Die Daten werden via 
USB übertragen, danach werden die INT2 und Zähler ausgeschaltet.
1
ISR(ENDPOINT_INTERRUPT)
2
{
3
...
4
if(is_interrupt_EP_INT_IN())
5
  {
6
    Usb_select_endpoint(EP_INT_IN);
7
    Usb_ack_in_interrupt();
8
    
9
    Usb_write_byte(MSB(delta_t));
10
    Usb_write_byte(LSB(delta_t));
11
        
12
    for(i=0;i<EP_SIZE_1-2;i++) 
13
    {
14
      Usb_write_byte(1);
15
    }
16
          
17
    Usb_send_in();
18
    //INT2 OFF
19
    Disable_INT2();
20
    //CNTR1 OFF
21
    TCCR1B = 0;   
22
      TIMSK1 = 0; 
23
  }
24
}

USB Interrupt und der INT2 scheinen sich nicht zu behindern. Allerding 
bekomme ich bei aller ersten Messung immer 0. Wenn ich frequenz erhöhe 
oder verringere muss ich ebenfalls 2x auf den Button klicken bis der 
richtige Wert angezeigt wird.

von Karl (Gast)


Angehängte Dateien:

Lesenswert?

>Ist es wirklich unmoeglich, die Hardware zu modifizieren, dass der ICP
>capture Eingang verwendet werden koennte? Das ware sooo viel einfacher.

no way... siehe bild

von Falk B. (falk)


Lesenswert?

@ Karl (Gast)

>OK. Ich hab jezt die Lösung von Falk implementiert. Bis jetzt liefert
>sie bessere Ergebnisse.

Schön zu hören ;-)

>Damit andere Interrupts das Ergebniss nicht verfälschen schalte ich INT2
>und Counter jedesmal ein und aus.

Das bringt nix. Mein Posting nochmal lesen.

>USB Interrupt und der INT2 scheinen sich nicht zu behindern. Allerding

Du machst in einem INTERRUPT so viele USB Operatioen? Ob das ne gute 
Idee ist?

>Dateianhang: Leiterplatte.PNG (251,4 KB, 13 Downloads)

Bildformate!!!

>>capture Eingang verwendet werden koennte? Das ware sooo viel einfacher.

>no way... siehe bild

Wo ist das Problem?

Mfg
Falk

von Karl (Gast)


Lesenswert?

>Bildformate!!!
schon gut. ich habs doch extra komprimiert und als PNG hochgeladen.. ??

>Du machst in einem INTERRUPT so viele USB Operatioen? Ob das ne gute
>Idee ist
Das geht in ordnung, kommt vom Atmel. Das sind alles USB-Operationen, da 
USB zeitkritisch ist, muss ich unbedingt auf jeden möglichen INterrupt 
reagieren.

>>capture Eingang verwendet werden koennte? Das ware sooo viel einfacher.
>Wo ist das Problem?
niergends. Ich sagte ich kann die hardware nicht ändern um ICP zu 
nutzen. Das ist alles.

Danke für die tolle Hilfe, ich werde nun die Frequenzmessung ausgibig 
testen und falls ich Fehler entdecke und nicht weiter komme melde ich 
mich.

von Andreas K. (a-k)


Lesenswert?

> schon gut. ich habs doch extra komprimiert und als PNG hochgeladen.. ??

Eben. Zeichnung => PNG, Bild => JPG.

von Gerhard. (Gast)


Lesenswert?

OK Karl - Du hast mich ueberzeugt;-)

Freut mich dass es auf diese Weise auch geht - Ist gut zu wissen.

Gruss,
Gerhard

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.