Forum: Mikrocontroller und Digitale Elektronik AVR C "Variablenüberlauf"


von swen (Gast)


Angehängte Dateien:

Lesenswert?

Guten Abend,

ich bastel gerade an nem Frequenzzähler (atmega 8mhz, winavr, t0-pin als 
eingang). Läuft soweit so gut. (Abweichung bei 20khz ungefähr 100hz 
(zuviel) gegenüber einem Orginal Frequenzzähler, evtl leichtes 
Timingproblem der Schleife, ist im moment egal, wird später gemacht)

Das Problem:

Sobald es über 65536Hz geht (freq>65536) ist ja nen u_short am "ende". 
kann aber nicht auf long gehen wegen dem UTOA-befehl fürs Display 
(short>char). Gut dacht ich mir, machste halt in diesem Fall (wenn 
Zähler über 64000): Ergebniss/1000 und Anzeige als kHz. Es will aber 
nicht so richtig, es gibt immer nen Überlauf,d.h. die freq-variable 
fängt wieder bei 0 an. Hab auch schon mal die freq-variable als long 
definiert und das Ergebniss an eine u_short variable übergeben (passt ja 
dann rein), geht auch nicht, sobald die Frequenz über 65536Hz ist gibts 
nen überlauf.

Hier der Codeausschnitt (Completter Code im Anhang):
1
else //ab 64000hz = anzeige in kHz
2
    {
3
    lcd_clear();
4
    freq = (256*ztmp + count)/1000; // durch 1000 weil freq unsigned short
5
    utoa( freq, anz, 10 ); // übergabe freq > anz (anz ist char)
6
    set_cursor (0,1); // cursor pos setzen
7
    lcd_string( anz ); // anzeige frequenz
8
    set_cursor (6,1); // cursor pos setzen
9
    lcd_string( "kHz" ); // anz. in kHz
10
    }

mfg swen

von swen (Gast)


Lesenswert?

ne der läuft mit 16mhz...sorry

von Micha H. (mlh) Benutzerseite


Lesenswert?

utoa rauswerfen und stattdessen selber was schreiben. Ich hab's mir 
einmal angeschaut und gleich wieder gelöscht, hat für mich nicht 
getaugt.

Mit der Suche findest Du hier auch was brauchbares.

Nur wegen utoa die Variablenbreite zu beschränken und dann mit 
Hilfskonstrukten drumrumlaborieren ist IMHO keine brauchbare Lösung.

von Stefan E. (sternst)


Lesenswert?

swen schrieb:
> (Abweichung bei 20khz ungefähr 100hz
> (zuviel) gegenüber einem Orginal Frequenzzähler, evtl leichtes
> Timingproblem der Schleife, ist im moment egal, wird später gemacht)

Wenn du per CTC-Mode durch 125 teilen willst, musst du 124 in das 
OC-Register laden, nicht 125.

> Hab auch schon mal die freq-variable als long definiert
>     freq = (256*ztmp + count)/1000; // durch 1000 weil freq unsigned

Der Überlauf passiert auf der rechten Seite in der Klammer. Die Größe 
von freq spielt für diesen Überlauf (generell für die Berechnung auf der 
rechten Seite) nicht die geringste Rolle. Entweder du machst aus ztmp 
ein long, oder du kannst auch aus der "256" ein "256L" machen.

von Stefan E. (sternst)


Lesenswert?

Micha H. schrieb:
> utoa rauswerfen und stattdessen selber was schreiben.

Man könnte auch einfach ltoa nehmen.

von swen (Gast)


Lesenswert?

Stefan Ernst schrieb:
> swen schrieb:

> Wenn du per CTC-Mode durch 125 teilen willst, musst du 124 in das
> OC-Register laden, nicht 125.

> Der Überlauf passiert auf der rechten Seite in der Klammer. Die Größe
> von freq spielt für diesen Überlauf (generell für die Berechnung auf der
> rechten Seite) nicht die geringste Rolle. Entweder du machst aus ztmp
> ein long, oder du kannst auch aus der "256" ein "256L" machen.
>


Jo danke dir, hat geholfen ztemp als long und schon läufts!
hab auch die schleife auf 124 gesetzt, viel mir dann auch wie schuppen 
von den augen. abweichung nun kleiner aber nicht 0. bei 2000khz zeigt er 
2015khz an. hmm. ne idee?

von swen (Gast)


Lesenswert?

hatte noch nen felher gefunden (ein "count = 0;" war zuviel :-))

wenn ich sowohl das comparematch register als auch die schleife auf 124 
setzte komme ich auf 1999khz.. hmm näher aber irgenwie "drüber"

von Karl H. (kbuchegg)


Lesenswert?

Du solltest noch 'runden'

19990 / 1000

ergibt nun mal 19kHz und nicht 20kHz, auch wenn 20kHz der Wahrheit viel 
näher kommen würde.

   freq = (256L*ztmp + count + 500)/1000; //weil freq unsigned short

von Karl H. (kbuchegg)


Lesenswert?

Stefan Ernst schrieb:

> Der Überlauf passiert auf der rechten Seite in der Klammer. Die Größe
> von freq spielt für diesen Überlauf (generell für die Berechnung auf der
> rechten Seite) nicht die geringste Rolle. Entweder du machst aus ztmp
> ein long, oder du kannst auch aus der "256" ein "256L" machen.

Da diese Thematik im Forum des öfteren vorkommt, habe ich einen FAQ 
dafür begonnen

http://www.mikrocontroller.net/articles/FAQ#Datentypen_in_Operationen

von Swen (Gast)


Angehängte Dateien:

Lesenswert?

Karl heinz Buchegger schrieb:
> Du solltest noch 'runden'
>
> 19990 / 1000
>
> ergibt nun mal 19kHz und nicht 20kHz, auch wenn 20kHz der Wahrheit viel
> näher kommen würde.
>
>    freq = (256L*ztmp + count + 500)/1000; //weil freq unsigned short

Wie meinst du das mit runden? also wenn ich 19,99kHz runde dann ergibt 
das bei mir 20Khz, so wars in der Schule...;-)
du meins sicher, das die nachkommastellen rigoros abgeschitten werden, 
wenn sie an "ztmp" übergeben werden, hmm stimmt.

Warum brauche ich 256L (ich denk mal d.h. als Long definiert)? Mit 
"normal" 256 läufts, hab doch ztmp als long definiert (steht weiter 
oben). ->Wenn ich nun aber 256L nehme und ztmp wieder als unsigned 
chart, hab ich trotzdem nen Überlauf, beides zus. geht natürlich auch

Hab die Schleife noch mal angepasst. Nun ist der Fehler: rund 50hz 
zuviel bei 200kHz. Nimmt bei geringeren frequenzen stetig ab (4 Hz zu 
viel bei 20Khz, 2Hz zuviel bei 10Khz, fehlerfrei bis 2Khz). D.h. die 
Schleife ist nenn Tick zu langsam, obwohl mathematisch korrekt

Muss ich da noch ein bissl Handoptimieren, oder sind das 
Unzulänglichkeiten/Toleranzen in der Codeababarbeitung? Ist ja kein 
Assembler

Schleifenausschnitt
1
while(1)
2
{
3
  if (s==125){ //schleife mit 1hz 16mhz/1024/125/125 = 1,0000 hz
4
  count = TCNT0;
5
  s=0;
6
  ztmp = z;
7
  z= 0;
8
  TCNT0 = 0;
9
    if (ztmp<250) //1 -  64000Hz (256*250)
10
    {
11
    lcd_clear();
12
    freq = 256L*ztmp + count;
13
    utoa( freq, anz, 10 ); // übergabe freq > anz
14
    set_cursor (0,1); // cursor pos setzen
15
    lcd_string( anz ); // anzeige frequenz
16
    set_cursor (6,1); // cursor pos setzen
17
    lcd_string( "Hz" ); // anz. in Hz
18
    }
19
    else //ab 64000hz = anzeige in kHz
20
    {
21
    lcd_clear();
22
    freq = (256*ztmp + count)/1000; //weil freq unsigned short
23
    utoa( freq, anz, 10 ); // übergabe freq > anz
24
    set_cursor (0,1); // cursor pos setzen
25
    lcd_string( anz ); // anzeige frequenz
26
    set_cursor (6,1); // cursor pos setzen
27
    lcd_string( "kHz" ); // anz. in kHz
28
    }
29
  }
30
}
31
return 0;
32
}

mfg
swen

von Swen (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> freq = (256L*ztmp + count + 500)/1000; //weil freq unsigned short

upps, mann sollte auch 256L an die richtige stelle setzen. muss 
natürlich "unten" hin. sorry, dann klappts auch


Swen schrieb:

>     if (ztmp<250) //1 -  64000Hz (256*250)
>     {
>     lcd_clear();
>     freq = 256*ztmp + count;
>     utoa( freq, anz, 10 ); // übergabe freq > anz
>     set_cursor (0,1); // cursor pos setzen
>     lcd_string( anz ); // anzeige frequenz
>     set_cursor (6,1); // cursor pos setzen
>     lcd_string( "Hz" ); // anz. in Hz
>     }
>     else //ab 64000hz = anzeige in kHz
>     {
>     lcd_clear();
>     freq = (256L*ztmp + count)/1000; //weil freq unsigned short
>     utoa( freq, anz, 10 ); // übergabe freq > anz
>     set_cursor (0,1); // cursor pos setzen
>     lcd_string( anz ); // anzeige frequenz
>     set_cursor (6,1); // cursor pos setzen
>     lcd_string( "kHz" ); // anz. in kHz

von Karl H. (kbuchegg)


Lesenswert?

Swen schrieb:
> Karl heinz Buchegger schrieb:
>> Du solltest noch 'runden'
>>
>> 19990 / 1000
>>
>> ergibt nun mal 19kHz und nicht 20kHz, auch wenn 20kHz der Wahrheit viel
>> näher kommen würde.
>>
>>    freq = (256L*ztmp + count + 500)/1000; //weil freq unsigned short
>
> Wie meinst du das mit runden? also wenn ich 19,99kHz runde dann ergibt
> das bei mir 20Khz, so wars in der Schule...;-)

Das interessiert aber deine Berechnung nicht.
Eine Integer Division liefert keine Kommastellen.

Wenn du keine Vorkehrungen triffst, um bei einer Integer Division ein 
gerundetes Ergebnis zu erhalten, dann wird auch nichts gerundet.

> Hab die Schleife noch mal angepasst. Nun ist der Fehler: rund 50hz
> zuviel bei 200kHz. Nimmt bei geringeren frequenzen stetig ab (4 Hz zu
> viel bei 20Khz, 2Hz zuviel bei 10Khz, fehlerfrei bis 2Khz). D.h. die
> Schleife ist nenn Tick zu langsam, obwohl mathematisch korrekt

Das ist jetz durch deine Messmethode zu erklären.
Für das was du machst, gibt es einen eigenen Timermodus: Input Capture. 
Damit kannst du Zyklengenau messen unabhängig davon, wie lange dein 
Programm zur Auswertung braucht.

Die nächste Annahme, die du triffst:
Dein Quarz hat exakt 16000000 Hz. Die hat er aber nicht!

von Swen (Gast)


Lesenswert?

OK, aufm quarz steht 16,000 Mhz, da ist natürlich mit Abweichungen 
unterhalb 1Khz anzunehmen, wenn man das so deutet, wie es draufsteht. da 
muss wohl ein genauers (16,000000 Mhz :) )Quarz her, wenn es denn sowas 
gibt.

also mit input capture könnte man es ja mal probieren., was dann aber 
die unzulänglicheiten des quartzes aber nicht ausgleichen kann?!

von Swen (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Das ist jetz durch deine Messmethode zu erklären.
> Für das was du machst, gibt es einen eigenen Timermodus: Input Capture.
> Damit kannst du Zyklengenau messen unabhängig davon, wie lange dein
> Programm zur Auswertung braucht.

ich hab da mal nen thread ausgegraben, da gehts genau darum (input 
capture)

Beitrag "Input Capture Pin (ICP) auslesen ( Frequenz messen)"

da hast du freundlicherweise mal den code optimiert. nun lese ich aber 
was von max 75khz. weil das capture interrupt permanent reinschiesst. 
das wäre natürlich blöde.. ich brauch noch bissl cputime für weitere 
aufgaben.

mein plan: nen frequenzgenerator (max038 oder sowas) mit dem avr 
koppeln, d.h. ich stelle die frequenz mittels tasten am display ein, der 
misst dann die permanet die frequenz (darum der freqcounter) und regelt 
dann darauf ein und nach. da brauch ich ja noch kapazitäten.

von Karl H. (kbuchegg)


Lesenswert?

Swen schrieb:

>
> Beitrag "Input Capture Pin (ICP) auslesen ( Frequenz messen)"
>
> da hast du freundlicherweise mal den code optimiert. nun lese ich aber
> was von max 75khz. weil das capture interrupt permanent reinschiesst.
> das wäre natürlich blöde.. ich brauch noch bissl cputime für weitere
> aufgaben.

Grundsätzlich gibt es 2 verschiedene Messmethoden

a) Man baut ein 'Tor'
   Dieses Tor lässt man zb 1 Sekunde offen stehen, und zählt wieviele
   Pulse in dieser Zeit am Tor sichtbar sind.

b) man misst die Zeit von einer steigenden Flanke des Signals bis
   zur nächsten steigenden Flanke

Geht man nun davon aus, dass man Zeiten nicht beliebig genau messen 
kann, dann kann man sich mal fragen, wie sich eigentlich der Messfehler 
in den beiden Methoden verhält.
Und da sieht man etwas Interessantes: In Methode a) wird der Messfehler 
mit steigender Frequenz kleiner. Bei Methode b) hingegen wird der 
Messfehler mit fallender Frequenz kleiner.

Es gibt daher einen Punkt, über dem man mit Methode a) besser bedient 
ist und darunter mit Methode b)

Zudem ist man in beiden Fällen extrem gut beraten, wenn man die Messung 
erst dann starten lässt, wenn das Signal gerade eine Flanke macht.

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.