mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik 70000 / 5 = 888


Autor: auf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich programmiere einen ATMega644 in C mit dem AVR-Studio. Dabei
fiel mir auf, dass eine Rechenoperation irgendwie nicht das tut, was sie
tun soll.
#include <avr/io.h>
#include <util/delay.h>
#include <avr\interrupt.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>

int main(void)
{

long pulstimerwert = 0;

  while(1)
  {

OCR1A = pulstimerwert / 5;

  }
}

Wenn ich das Ganze nun genauer anschaue, stelle ich fest, dass sobald
pulstimerwert (pulstimerwert ist immer restlos durch 5 teilbar) über die
65535 (16 bit) kommt, die Division kein richtiges Ergebnis mehr
liefert – 70000 / 5 = 888.

Ich habe schon verschiedene andere Datentypen ausprobiert – int, uint,
long, uint16_t, uint32_t – und die Rechenoperation auf eine Zwischen-
variable gelegt … Jedoch irgendwie will dieses Problem nicht
verschwinden.

Suche und bing konnten mir bis jetzt keine Lösung liefern und jetzt in
Mein C-Programm mit Assembler rumzuflicken will ich nicht.
Ich würde das Ganze nun versuchen mit einer Fallunterscheidung
für pulstimerwert zu lösen, ich hoffe aber, dass jemand von euch weiß
was ich hier falsch mache und eine bessere Lösung hat /:

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das einzige was ich an diesem Beispiel erkennen kann, ist dass 
pulstimerwert 0 ist, und das Problem folglich nicht auftritt.

Es bringt nichts, einen Code zu posten bei dem das Problem garantiert 
nicht auftreten kann. Der Code muss schon noch den Fehler enthalten, 
sonst wird das nichts.

Ansonsten: Welche Version vom WinAVR?

Autor: auf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>.<

Im Laufe des Programms werden pulstimerwert Werte zwischen 400 und
300000 zugewiesen. Je nachdem was der User auswählen wird. Ich habe
"long pulstimerwert = 0;" nur angegeben, um zu zeigen wie die Variable
derzeit deklariert ist.

AVR-Studioversion: 4.16

Autor: Benedikt K. (benedikt) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
auf schrieb:
> Im Laufe des Programms werden pulstimerwert Werte zwischen 400 und
> 300000 zugewiesen.

Das ist aus dem von dir geposteten Codeteil nicht ersichtlich. Wenn du 
Hilfe willst, dann poste einen Code, der von jedem compilierbar ist, und 
der den Fehler zeigt. Nur so können andere das Problem nachvollziehen 
und den Fehler suchen.

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Glaskugel an

Der vom Benutzer gewählte Wert wird irgendwo auf 16 Bit (implizit) 
gecasted und noch ein wenig "abgerundet".

Glaskugel aus

Autor: GB (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
  while(1)
  {

OCR1A = (uint) (pulstimerwert / 5); // <- expliziter Cast, da OCR1A ein 
                                    //  16-Bit-Register ist

  }

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Frage ist eher, wo der Wert zugewiesen wird.
Und die Version!!!!!

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
GB schrieb:
>
>   while(1)
>   {
> 
> OCR1A = (uint) (pulstimerwert / 5); // <- expliziter Cast, da OCR1A ein
>                                     //  16-Bit-Register ist
> 
>   }
> 

das ist nicht der springende Punkt. Der springende Punkt ist: was steht 
eigentlich in pulstimer tatsächlich für ein Wert drinnen. Auch wenn der 
OP denkt, dass da 70000 drinnenstehen sollte, muss das ja nicht so sein.

(70000 - 65536) / 5 = 892

verdächtig nache an den angegebenen 888. Die Vermutung liegt also nahe, 
dass er bei irgendeiner Eingabe- oder Rechenaktion von den gewünschten 
32 Bit irgendwo die oberen 16 Bit verloren hat.

Autor: horst (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Unter Linux mit gcc reicht es nicht, math.h zu inkludieren, um die 
mathematische Bibliothek einzubinden. Da braucht der linker noch ein 
-lm.

Ist das beim AVR-Studio vielleicht ähnlich?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
horst schrieb:

> Ist das beim AVR-Studio vielleicht ähnlich?

ist ähnlich. Aber er braucht ja nichts aus der math Library. Das ist 
eine popelige long Division.

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
libm.a wird hier nicht benötigt, da es nicht um Floatingpoint-Arithmetik 
geht.

Das ist reine Integer-Arithmetik, die braucht keine Libraries. Und auch 
kein math.h

Autor: Dirk Broßwick (sharandac)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

über das Problem bin ich letzten auch schon gestolpert. Das Problem ist 
wahrscheinlich das er die 5 durch die du teilen möchtest als 16Bit nimmt 
und dein long auf 16bit castet. Schreibe mal einfach hinter der 5 ein 
kleines l.
OCR1A = pulstimerwert / 5l;

kann sein das du das Ergebnis aber noch casten musst.

CA Dirk

Autor: auf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Pulstimerwert hat sich aus einer Summe von multiplizierten Int-Werten.
generiert. Das war der Fehler >.<
Zumindest funktioniert es nun wie es soll.

Danke fürs Schubsen in die richtige Richtung und das nächste Mal gebe
ich nicht nur den Teil an Informationen mit an wo ich denke, dass das
das alleinige Problem sein könnte >.<

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dirk Broßwick schrieb:

> über das Problem bin ich letzten auch schon gestolpert. Das Problem ist
> wahrscheinlich das er die 5 durch die du teilen möchtest als 16Bit nimmt
> und dein long auf 16bit castet.

Wenn eine der beiden Seiten "long" ist wird in "long" gerechnet.

Autor: Dirk Broßwick (sharandac)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@A. K.

sorry, habe es eben nochmal getestet ... und siehe da es stimmt, du hast 
recht. Und habe auch festgestellt das das Beispiel bei mir richtig 
rechnet.
AVR-GCC 4.2.4. Das Problem bei mir war wenn ich zwei Zahlen vergleichen 
wollte.

z.b.
if ( ByteCounter < ( 1024l*1024l ) )
   printf_P( PSTR(" %ld kBytes "), ByteCounter / 1024 );

und
if ( ByteCounter < ( 1024*1024 ) )
   printf_P( PSTR(" %ld kBytes "), ByteCounter / 1024 );

Das erste Beispiel geht, das zweite nicht. ByteCounter ist vom Typ long.

CA

Autor: Läubi .. (laeubi) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
if ( ByteCounter < ( 1024*1024 ) )
Standardmäßig werden Konstanten als int (bei gcc = 16bit) gerechnet.
Erst am schluß wird die Konstante dann auf long erweitert und daher geht 
dir vorher schon was verloren bevor der Compiler das auf long erweitern 
kann.

Autor: Dirk Broßwick (sharandac)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Läubi,

ja, das habe ich dann nach dem intensiven lesen meiner C Lektüre auch 
gelesen. Zum damaligen Zeitpunkt als ich angefangen habe C zu lernen war 
mir das nur völlig unklar.

CA

Autor: Benedikt K. (benedikt) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
C hat viele gemeine Regeln, wann wie umgewandelt und gerechnet wird, vor 
allem was das Vorzeichen angeht. So Sachen wie unsigned*signed sind 
extrem gemein, und sollten daher so gut es geht vermieden werden, wenn 
man sich nicht besonders damit auskennt (ich bin da selbst schon 
merhmals drauf reingefallen).

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Benedikt K. schrieb:
> C hat viele gemeine Regeln, wann wie umgewandelt und gerechnet wird, vor
> allem was das Vorzeichen angeht. So Sachen wie unsigned*signed sind
> extrem gemein, und sollten daher so gut es geht vermieden werden, wenn
> man sich nicht besonders damit auskennt (ich bin da selbst schon
> merhmals drauf reingefallen).

Ja, signed / unsigned kann immer wieder zu Überraschungen führen. Das 
hat wohl jedem schon mal graue Haare eingebracht. (Ein ganzes Büschel 
links hinten geht auf dieses Konto)

Aber agesehen davon ist die simple Grundregel die:

In C ist es völlig irrelevant wie ein Ergebnis verwendet wird. Der 
Compiler berücksichtigt für eine Operation immer nur die Datentypen der 
an der Operation beteiligten Operanden, niemals den Datentyp der 
letztendlich das Ergebnis aufnehmen wird. Der kleinere der beiden 
Datentypen wird auf den größeren hoch gehoben. Ist einer der beiden ein 
Floating Point Typ, wird es der andere auch.
Was noch? Ach ja: Bei signed / unsigned gewinnt immer unsigned (wobei es 
immer spannend ist: Was geschieht mit einem ev. negativen Vorzeichen im 
signed)

Damit lassen sich die Mehrzahl aller Probleme analysieren und bei Bedarf 
bereinigen.

Autor: Dirk Broßwick (sharandac)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
> Benedikt K. schrieb:
>> C hat viele gemeine Regeln, wann wie umgewandelt und gerechnet wird, vor
>> allem was das Vorzeichen angeht. So Sachen wie unsigned*signed sind
>> extrem gemein, und sollten daher so gut es geht vermieden werden, wenn
>> man sich nicht besonders damit auskennt (ich bin da selbst schon
>> merhmals drauf reingefallen).
>
> Ja, signed / unsigned kann immer wieder zu Überraschungen führen. Das
> hat wohl jedem schon mal graue Haare eingebracht. (Ein ganzes Büschel
> links hinten geht auf dieses Konto)

Ich habe zwar nicht so viele Haare, aber die grauen gehen definitiv auf 
das Konto von C.
Aber zurück zum Problem. Ich selber kann es nicht so wirklich 
nachvollziehen. Es wäre mit Sicherheit mal interessant wie der restliche 
Code aussieht.

CA Dirk

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dirk Broßwick schrieb:

> Aber zurück zum Problem. Ich selber kann es nicht so wirklich
> nachvollziehen.

Das ist längst gelöst (15:25).

Autor: Dirk Broßwick (sharandac)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Äh ... mal wieder verpeilt ... lang genau nach meinem und vor deinem 
Beitrag :-)

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.