Forum: Mikrocontroller und Digitale Elektronik Angabe Frequen in einem Programm


von Klaus (Gast)


Lesenswert?

Hallo
Habe in einem Programm diese Angabe für die Frequenz gefunden:

#define F_CPU 8E6

Habe versucht es umzurechnen in Binär Detimal oder Hexa. Leider komme 
ich so auf Ergebnisse wie 142 ??
Bin zu blöd das zu verstehen.
Nehme immer sonst Angaben wie 8 000 000 oder 16 000 000. Damit kann ich 
Rechnen.
LG Klaus

von Christian H. (netzwanze) Benutzerseite


Lesenswert?

8*10^6

von Veit D. (devil-elec)


Lesenswert?

Hallo,

8E6 ist weder hex noch binär. Das ist eine Abkürzung um nicht hunderte 
Nullen schreiben zu müssen. E steht für Exponent mit Basis 10.
Sprich eine 8 mit 6 Nullen dran.
Sprich 8MHz.

von Praktiker (Gast)


Lesenswert?

Hallo

Das sind halt einige der so typischen "Fallen" welche automatisch 
entstehen wenn, wie bei Programmsprachen, "Nerds" und "Bücher schlaue" 
irgendwelche Anwendungen erstellen.
Hinzu kommt das im Kern viele Programmiersprachen einfach nur alt sind 
und die Darstellung von z.B. "Hoch 6" halt nur vereinfacht mit den 
einfachen ASCII Zeichensatz von Anno 196X (Typenraddrucker, 
Fernschreiber, Schreimaschine...)darstellbar ist.
Und selbst wenn es moderner ist:
Solche "Traditionen" sind halt sehr schwer auszurotten und vererben sich 
leider weiter.
(Ganz anderer Bereich: "Das Licht löschen" ist in vielen Gegenden noch 
üblich obwohl auch dort die elektrische Beleuchtung seit 100 Jahren und 
mehr üblich ist)

Wer noch Taschenrechner vor 30Jahre und mehr nutzen musste ist die 
Darstellung von "hoch" mittels "E" (auch mit 7 Segment Display 
darstellbar) geläufig.
Und das E für "Exponent" also "Hoch diese Zahl nach den E" steht weis 
auch jeder für den die Schulmathematik nicht eine Sache war die nach den 
Abschluss möglichst schnell (oft weil es wie es gelehrt wurde und oft 
noch wird) "vergessen" wurde.

Daher:
Nicht du Klaus bist "dumm" sondern die typischen Lehrmedien bzw. deren 
Autoren die, wenn sie sich nicht ausdrücklich an Kinder wenden, Sachen 
wie 4E6 als Selbstverständlichkeit hinnehmen die jeder automatisch aus 
den Zusammenhang erkennt.
Da gibt es besonders im Bereich der Programmiersprachen (aber bei weiten 
nicht nur dort) noch viel mehr an schlechten Beispielen wo sich 
(angeblich...) hochwertiger Lehrstoff nicht traut mal diese eben doch 
nicht selbstverständlichen Details zu erklären um ja nicht bei seinen 
(im schlechten Sinne) "Nerdkollegen" als dumm dazustehen.

Betriebsblindheit, Angst davor "Selbstverständlichkeiten" auch bei 
Literatur (und ähnlichen) die sich nicht an Einsteiger wendet wenigsten 
noch mal kurz zu erwähnen  bis klar erkennbare Arroganz der Lehrkraft 
und des Autors  ist leider kein Alleinstellungsmerkmal von einigen 
"Spezialisten" in Foren sondern kommt leider auch öfter im Profiumfeld 
vor.

Praktiker

von Rolf M. (rmagnus)


Lesenswert?

Praktiker schrieb:
> Hinzu kommt das im Kern viele Programmiersprachen einfach nur alt sind
> und die Darstellung von z.B. "Hoch 6" halt nur vereinfacht mit den
> einfachen ASCII Zeichensatz von Anno 196X (Typenraddrucker,
> Fernschreiber, Schreimaschine...)darstellbar ist.

Wie würdest du es denn heute in einer "moderneren" Programmiersprache 
darstellen? Als 8⋅10⁶? Wie gibst du das auf einer normalen Tastatur ein?

von Michael U. (amiga)


Lesenswert?

Hallo,

@Praktiker (Gast): Danke für den doch recht nutzlosen Roman...

Diese Art der Exponential-Darstellung nutzen auch Excel, OpenOffice usw. 
auch heute noch genauso. Eine Suche z.B. bei Google nach 8E6 verweist 
zumindest sofort auf "Zahlendarstellung".

Gruß aus Berlin
Michael

von MWS (Gast)


Lesenswert?

Praktiker schrieb:
> Daher:
> Nicht du Klaus bist "dumm" sondern die typischen Lehrmedien bzw. deren
> Autoren die, wenn sie sich nicht ausdrücklich an Kinder wenden, Sachen
> wie 4E6 als Selbstverständlichkeit hinnehmen die jeder automatisch aus
> den Zusammenhang erkennt.

Was ist denn das für ein Blödsinn?
Demzufolge müsste man jedem, der einen Normalenvektor errechnen will, 
alle Voraussetzungen beginnend beim auf's Töpfchen gehen erklären.

Tatsächlich ist (gekonntes) Programmieren eine anspruchvolle 
Angelegenheit, die in vergangenen Zeiten (tm) von Menschen mit 
entsprechender Vorbildung ausgeführt wurde, bei denen die Kenntnis des 
Exponents zur Grundbildung gehörte. Bei Ardruino-verseuchten 
Maker-Wurschtlern gibt's diese Vorbildung eben nicht.

Da drang eine Personengruppe in ein unbekanntes Terrain vor und weis nun 
nicht, dass sich ein steinernes Ding aus Blöcken Treppe nennt.

Ob Klaus dumm ist? Vielleicht. Immerhin kann er nicht mal einen 
Threadtitel richtig schreiben.

Das Threadthema hört sich irgendwie trollig an.
Vielleicht ist der Klaus gar nicht dumm, vielleicht bist Du dumm, der 
auf die Trollerei mit gutmenschenversteherischen Erklärungsversuchen 
hereinfällt.

von Einer K. (Gast)


Lesenswert?

Michael U. schrieb:
> Eine Suche z.B. bei Google nach 8E6 verweist
> zumindest sofort auf "Zahlendarstellung".

Mein Google tut das nicht, da ist die erste Seite geflutet mit:
> Anti-MPO - Single IC (Hu) aus Maus (8E6) - FITC ... - Dianova
und seinen Freunden und Verwandten.
Von Zahlendarstellungen ist da erstmal nix zu sehen.

Könnte auch daran liegen, dass Google weiß, was ich weiß, und auch was 
ich noch nicht weiß, obwohl es in meinen Themenkomplex passt(passen 
könnte).

von Veit D. (devil-elec)


Lesenswert?

MWS schrieb:
> Bei Ardruino-verseuchten Maker-Wurschtlern gibt's diese Vorbildung eben
> nicht.

> Ob Klaus dumm ist? Vielleicht. Immerhin kann er nicht mal einen
> Threadtitel richtig schreiben.

Ohne Worte. Arduino wäre richtig geschrieben.  ;-)

von Eins N00B (Gast)


Lesenswert?

Klaus schrieb:
> Hallo
> Habe in einem Programm diese Angabe für die Frequenz gefunden:
>
> #define F_CPU 8E6
>
> Habe versucht es umzurechnen in Binär Detimal oder Hexa. Leider komme
> ich so auf Ergebnisse wie 142 ??
> Bin zu blöd das zu verstehen.
> Nehme immer sonst Angaben wie 8 000 000 oder 16 000 000. Damit kann ich
> Rechnen.
> LG Klaus

Das erzeugt aber ein double-Literal.
Fällt einem das im Code nicht fürchterlich auf die Füße?

von Axel S. (a-za-z0-9)


Lesenswert?

Klaus schrieb:
> #define F_CPU 8E6
>
> Habe versucht es umzurechnen in Binär Detimal oder Hexa. Leider komme
> ich so auf Ergebnisse wie 142 ??

Netter Versuch. Aber falscher Tag. Freitag ist Trolltag.

von Einer K. (Gast)


Lesenswert?

Eins N00B schrieb:
> Fällt einem das im Code nicht fürchterlich auf die Füße?

Eigentlich gehört an die Frequenz ein UL dran....
würde ich mal sagen......
Denn ein Vorzeichen braucht es doch nicht, und das default int ist 
manchmal zu klein.

In C gibts vermutlich wenig Alternativen....
Aber in C++/Arduino kann man durchaus mal ein
> Frequenz frq {8_MHz};
einstreuen, wenn man das geschickt vorbereitet..

von MWS (Gast)


Lesenswert?

Veit D. schrieb:
> Ohne Worte. Arduino wäre richtig geschrieben.  ;-)

Ich war schon gespannt, wer zuerst auf meine eingebaute Vorlage 
triggert, LOL.

Hätte aber eher was aus der Richtung der Ardruino-Fangemeinde erwartet.
"Ardruino" ist die meiner Einschätzung nach meistverwendete 
Wortvergewaltigung der Arduino-Jünger, die nicht mal ihren eigenen 
Propheten beim Namen kennen.
Hat manchmal was von Monty Python.

von Einer K. (Gast)


Lesenswert?

MWS schrieb:
> Hätte aber eher was aus der Richtung der Ardruino-Fangemeinde erwartet.
Willst du Stress mit mich?

Vergiss es...
Was du da betreibst ist so ziemlich die dümmste Sorte von Arduino 
Bashing, welche überhaupt möglich ist.
Das ignoriere ich noch nicht einmal!

Es sagt mehr über dich aus, als über die Ardruirno Jünger.

von Hannes (Gast)


Lesenswert?

MWS schrieb:
> Veit D. schrieb:
>> Ohne Worte. Arduino wäre richtig geschrieben.  ;-)
>
> Ich war schon gespannt, wer zuerst auf meine eingebaute Vorlage
> triggert, LOL.
>
> Hätte aber eher was aus der Richtung der Ardruino-Fangemeinde erwartet.
> "Ardruino" ist die meiner Einschätzung nach meistverwendete
> Wortvergewaltigung der Arduino-Jünger, die nicht mal ihren eigenen
> Propheten beim Namen kennen.
> Hat manchmal was von Monty Python.

😂👍

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Arduino Fanboy D. schrieb:

> Eigentlich gehört an die Frequenz ein UL dran.

Warum? Nur, weil das alle so machen?

<util/delay.h> ist vorsätzlich so konzipiert worden, dass man F_CPU in 
einer möglichst natürlichen Schreibweise schreiben kann. Für mich 
gehört es dabei dazu, dass ich bei einem Quarz, der mit "3.6864 MHz" 
beschriftet ist, halt auch "3.6864E6" schreiben kann, statt Nullen 
zählen zu müssen. (Davon abgesehen, dass avr-libc natürlich C-tauglich 
sein muss, behaupte ich mal, dass diese Varianten mit nachgesetzten 
Einheiten in C++ zu der Zeit noch nicht möglich waren. Die erfüllen 
ansonsten mein Lesbarkeits-Kriterium auch.)

Schade nur, dass die Schöpfer von <util/setbaud.h> das nicht 
weitergeführt haben. Dort geht wirklich nur ein integer-Wert.

Das "UL" ist übrigens auch bei Ganzzahlen mittelmäßig überflüssig, denn 
8000000 ist ohnehin schon mal "long" (auf einem AVR), und dass es nicht 
negativ ist, ist offensichtlich. Wer auch immer damit etwas rechnet, 
sollte von sich aus drauf achten, dass er es in der passenden integer 
domain weiter benutzt.

von MWS (Gast)


Lesenswert?

Arduino Fanboy D. schrieb:
> Willst du Stress mit mich?
>
> Vergiss es...
> Was du da betreibst ist so ziemlich die dümmste Sorte von Arduino
> Bashing, welche überhaupt möglich ist.
> Das ignoriere ich noch nicht einmal!

Da isser schon, der Verteidiger aller Ardruinos. ;D
Es ist bezeichnend, dass ein Gutteil der Arduino-Nutzer den Namen nicht 
richtig schreiben, das ist kein Gebashe, sondern Tatsache.
Und klar sagt das etwas über die Nutzer aus, das sind alles Einsteins.
Wenn Du nicht wüsstest, dass ich richtig liege, hättest Du Dir Deine 
Antwort gespart.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Öhem, könntet ihr das vielleicht auf private Kommunikation verlagern?

Das langweilt hier. :(

von MWS (Gast)


Lesenswert?

Jörg W. schrieb:
> Das langweilt hier. :(

Das ist aber auch ein langweiliger Thread, der ein wenig Stimmung 
brauchte. :-)
Ich kann immer noch nicht glauben, dass der ernsthaft und nicht trollig 
ist.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

MWS schrieb:
> Ich kann immer noch nicht glauben, dass der ernsthaft und nicht trollig
> ist.

Naja, es gibt offenbar Leute, die nicht einmal die Grundlagen ihrer 
Programmiersprache (hier: Schreibweisen von numerischen Konstanten) 
beherrschen, aber trotzdem damit anfangen.

Zwar haben wir ein paar Forentrolle ähnlichen Formats, die nicht bis 
Freitag warten können, aber der TE scheint nicht dazu zu gehören.

von MWS (Gast)


Lesenswert?

Jörg W. schrieb:
> Zwar haben wir ein paar Forentrolle ähnlichen Formats, die nicht bis
> Freitag warten können, aber der TE scheint nicht dazu zu gehören.

Eine Suche nach 8E6 macht mir auf duckduckgo als erstes einen 
Taschenrechner auf, in dem dann 8000000 und als Erklärung 8*(10^6) 
steht, bei google muss man mit weiteren Suchbegriffen eingrenzen, aber 
trotzdem ist's einfach, denn man kennt was gesucht wird: eine Zahl.

Es muss ja nicht jeder autodidaktisch eingestellt sein, aber so einen 
einfach rauszufindenden Kleckerkram nicht innerhalb 5 Minuten zu lösen 
und statt dessen einen Thread mit Inhalt "Bin zu blöd" aufzumachen, ist 
schon 'ne Leistung.

Klaus schrieb:
> #define F_CPU 8E6
>
> Habe versucht es umzurechnen in Binär Detimal oder Hexa. Leider komme
> ich so auf Ergebnisse wie 142 ??

Unglaubwürdig auch hier, wieso sollte man in Binär umrechnen? Außerdem 
8E6 als Hex gelesen ist sicher nicht 142.
Ich glaub' also nicht, dass das echt ist.

von Einer K. (Gast)


Lesenswert?

Jörg W. schrieb:
> Warum? Nur, weil das alle so machen?

Vielleicht haben deine "alle" ja auch manchmal einen guten Grund!

Einer der Gründe ist dass ein Takt nie negativ sein kann.
Aus dem Grund ist alleine schon das U angesagt.
Es kostet nichts, und macht dennoch klar, dass man was kapiert hat.

Zudem erlaubt es Warnungen, z.B. bei Vergleichen mit signed Values. Ein 
Tor weniger für Schlampigkeiten.

Es ist ja nicht nur ein F_CPU only Problem.
Das findet sich schon noch häufiger in ganz anderen Ecken.

Bei
> uint32_t takt = 1234567;
brauche ich das nicht.
Das klappt immer, da hast du schon recht.

Bei einem
> #define takt 1234567UL
halte ich das fast für eine Pflicht.
Denn es gibt keine andere Möglichkeit dem define/Literal irgendwas an 
Typeinformation mitzugeben.
Wie gesagt: Es kostet nichts....
(außer, etwas Sorgfalt und Disziplin)

Dem C Menschen wird das nicht so wichtig sein. Aber der C++ Bastler 
kennt immerhin Templates, welche sehr viel mit Typen arbeiten, und auch 
die TypeTraits.
Da kann sich jede Typisierungsschlampigkeit bitter rächen.

von Tschiff (Gast)


Lesenswert?

>Netter Versuch. Aber falscher Tag. Freitag ist Trolltag.
Boah. Die Leute nerven mich, die meinen, jedem Thread nen Trollstempel 
verpassen zu wollen.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Arduino Fanboy D. schrieb:
> Es ist ja nicht nur ein F_CPU only Problem.

Es geht hier aber um F_CPU, und darum, dass F_CPU eine Gleitkommazahl 
sein kann und darf (zumindest für <util/delay.h>). Da ist die Bemerkung, 
dass da ein "UL" dran gehöre, völlig unsinnig.

von Rolf M. (rmagnus)


Lesenswert?

Arduino Fanboy D. schrieb:
> Eins N00B schrieb:
>> Fällt einem das im Code nicht fürchterlich auf die Füße?
>
> Eigentlich gehört an die Frequenz ein UL dran....
> würde ich mal sagen......
> Denn ein Vorzeichen braucht es doch nicht, und das default int ist
> manchmal zu klein.

Man braucht zwar kein Vorzeichen, aber es stört auch nicht. Und ob int 
zu klein ist oder nicht, ist egal. Wenn der Wert größer ist als int, 
wird automatisch ein Typ gewählt, der groß genug ist, sofern vorhanden.
Abgesehen davon kann man an 8E6 kein UL anhängen, weil das eine den Wert 
zu einem double macht, das andere zu einem unsigned long. 8E6UL ist kein 
gültiges Literal.

> In C gibts vermutlich wenig Alternativen....
> Aber in C++/Arduino kann man durchaus mal ein
>> Frequenz frq {8_MHz};
> einstreuen, wenn man das geschickt vorbereitet..

In C könnte man mit einem Makro
1
#define Mhz * 1000000

auch schreiben:
1
#define F_CPU 8 Mhz

Arduino Fanboy D. schrieb:
> Einer der Gründe ist dass ein Takt nie negativ sein kann.
> Aus dem Grund ist alleine schon das U angesagt.

Nö. Ich finde es eine Unsitte, Werte nur deswegen unsigned zu machen, 
weil sie halt nicht negativ sind. Für mich gibt's nur zwei Gründe, einen 
Typ unsigned zu machen:
- Ich brauche den Wertebereich
- Ich will damit Bitgefummel machen, wo das Vorzeichen stört

> Es kostet nichts, und macht dennoch klar, dass man was kapiert hat.

Dass ich kapiert habe, dass Taktraten nicht negativ sein können, muss 
ich niemandem zeigen.

> Zudem erlaubt es Warnungen, z.B. bei Vergleichen mit signed Values. Ein
> Tor weniger für Schlampigkeiten.

Und was habe ich davon?

von Stefan F. (Gast)


Lesenswert?

Praktiker schrieb:
> Wer noch Taschenrechner vor 30Jahre und mehr nutzen musste ist die
> Darstellung von "hoch" mittels "E" (auch mit 7 Segment Display
> darstellbar) geläufig.

Nein, das "e6" bedeutet "mal 10 hoch 6", und nicht "hoch 6". Das war 
quasi schon immer so.

https://de.wikipedia.org/wiki/Gleitkommazahl#Exponentialschreibweise
https://de.wikipedia.org/wiki/Wissenschaftliche_Notation

Beitrag #6759038 wurde von einem Moderator gelöscht.
von Klaus (Gast)


Lesenswert?

Sorry, bin kein Troll auch wenn das einige glauben. Fange gerade mit 
lesen eines Codes an und da sind viele Sachen unverständlich.
Sorry noch mal für die blöde Frage, kommt nicht wieder vor.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Klaus schrieb:
> Fange gerade mit lesen eines Codes an und da sind viele Sachen
> unverständlich.

Naja, die verschiedenen Varianten von numerischen Konstanten gehören nun 
einmal zu den Grundlagen einer Programmiersprache, die solltest du dir 
auf jeden Fall ansehen.

von Stefan F. (Gast)


Lesenswert?

Klaus schrieb:
> Sorry noch mal für die blöde Frage, kommt nicht wieder vor.

Das war keine blöde Frage, sie wirkte auf mich aber eher wie ein 
Vorwurf. Nach dem Motto "Wie kann man nur so einen Müll schreiben, das 
ist doch keine richtige Zahl!"

Daher kamen wohl die Reaktionen.

von Eins N00B (Gast)


Lesenswert?

Jörg W. schrieb:
> Klaus schrieb:
>> Fange gerade mit lesen eines Codes an und da sind viele Sachen
>> unverständlich.
>
> Naja, die verschiedenen Varianten von numerischen Konstanten gehören nun
> einmal zu den Grundlagen einer Programmiersprache, die solltest du dir
> auf jeden Fall ansehen.

Das stimmt, aber kann man das von Anfänger:innen erwarten?
Vielleicht ist ihm ja die ul-Geschichte bewusst, Hex scheint er zu 
kennen, nur die Exponentialschreibweise noch nicht über den Weg 
gelaufen.

Ich programmiere (leider) schon zu lange, um mich noch gut in die Lage 
hineinversetzen zu können, ich kann mir aber vorstellen, dass es eine 
Weile dauert, um überhaupt den Kopf so frei zu haben, sich detailliert 
mit der Sprache auseinanderzusetzen und nicht mehr über einzelne 
Konstrukten brüten zu müssen.

Vielleicht ähnelt das dem, wie es mir gegangen ist, als eich vor einiger 
Zeit mal einen VHDL-Anlauf unternommen habe. Bevor ich das Buch 
aufgemacht habe (übrigens über Empfehlungen hier im Forum ausgewählt ;) 
), wirkte VHDL auch nur wie Salat.

Das ist ein Plädoyer für etwas mehr Verständnis gegenüber den 
Anfänger:innen wie Klaus, deren Fragen für erfahrene Programmierer 
vielleicht unverständlich trivial scheinen.

P.S.: Wer lautstark auf bestimmten Lösungen herumhackt, die vielleicht 
einen selbst auch nicht unbedingt als Zielgruppe haben (Arduino, …), 
lässt mich arg an der fachlichen Kompetenz der Autor:in zweifeln. Auch 
wenn ich ihr/ihm damit vermutlich Unrecht tue.

Manchmal bin ich echt hin- und hergerissen, was ich von diesem Forum 
halten soll …

von Michael U. (amiga)


Lesenswert?

Hallo,

Eins N00B schrieb:
> Jörg W. schrieb:
>> Klaus schrieb:
>>> Fange gerade mit lesen eines Codes an und da sind viele Sachen
>>> unverständlich.
>>
>> Naja, die verschiedenen Varianten von numerischen Konstanten gehören nun
>> einmal zu den Grundlagen einer Programmiersprache, die solltest du dir
>> auf jeden Fall ansehen.
>
> Das stimmt, aber kann man das von Anfänger:innen erwarten?
> Vielleicht ist ihm ja die ul-Geschichte bewusst, Hex scheint er zu
> kennen, nur die Exponentialschreibweise noch nicht über den Weg
> gelaufen.

das eben hat mich etwas verwirrt, die ist (für mich?) nicht so 
ungewöhnlich.
Ich hätte da mehr verwirrt geschaut, ob der Präprozzsor das in der 
gewählten Programmierumgebung wirklich kennt.

Das passiert mir aber auch, wenn im Forum Fragen gestellt werden, wo ich 
denke "Ohmsches Gesetz" oder "Stromkreis" müßte derjenige aber doch 
schonmal gehört haben?

Gruß aus Berlin
Michael

von Eins N00B (Gast)


Lesenswert?

Praktiker schrieb:
> einfachen ASCII Zeichensatz von Anno 196X (Typenraddrucker,
> Fernschreiber, Schreimaschine...)

Nicht zu vergessen: Computertastaturen. Die haben sich auch schon ne 
Weile nicht mehr groß verändert ^^

Rolf M. schrieb:
> Wie würdest du es denn heute in einer "moderneren" Programmiersprache
> darstellen? Als 8⋅10⁶? Wie gibst du das auf einer normalen Tastatur ein?

<8> <⋅ (Layer 4+5)> <1> <0> <Compose> <^ (Layer 3+x)> <6> = 8•10⁶ ; ez

Aber ich bin da wohl Exot …

von Drago S. (mratix)


Lesenswert?


von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Michael U. schrieb:
> Ich hätte da mehr verwirrt geschaut, ob der Präprozzsor das in der
> gewählten Programmierumgebung wirklich kennt.

Du vergisst, dass der Präprozessor nur Texte ersetzt.

Auch sowas wie
1
#define F_CPU  völliger Blödsinn

kann man erstmal schreiben – nur wird vermutlich niemand, der F_CPU 
hinterher verwendet, damit was anfängen können. ;-)

Eins N00B schrieb:
> nur die Exponentialschreibweise noch nicht über den Weg gelaufen

Hmm, macht eigentlich jeder Taschenrechner so.

von Eins N00B (Gast)


Lesenswert?

Michael U. schrieb:
> das eben hat mich etwas verwirrt, die ist (für mich?) nicht so
> ungewöhnlich.
> Ich hätte da mehr verwirrt geschaut, ob der Präprozzsor das in der
> gewählten Programmierumgebung wirklich kennt.
>
> Das passiert mir aber auch, wenn im Forum Fragen gestellt werden, wo ich
> denke "Ohmsches Gesetz" oder "Stromkreis" müßte derjenige aber doch
> schonmal gehört haben?
>
> Gruß aus Berlin
> Michael

Uff, das hätte ich aus dem Stehgreif auch nicht gewusst. GCC warnt einen 
anscheinend sogar, wenn man Floats im CPP verwendet:
1
#if 4.0 > 2
2
#endif
3
#if 4e0 > 2
4
#endif
1
main.c:4:5: error: floating constant in preprocessor expression

Was ihn natürlich ja aber nicht daran hindert, Suchen und Ersetzen zu 
betreiben (#define halt; ja etwas ungenau).

Ich habe schon einige Programme ohne ein einziges Gleitkommaliteral 
geschrieben, das ist mir so zT auch mal ne Weile nicht begegnet.

Möchtest du darauf hinaus, dass die OPs es in dem Fall nicht haben und 
du dich darüber wunderst?

von Eins N00B (Gast)


Lesenswert?

Jörg W. schrieb:
> Michael U. schrieb:
>> Ich hätte da mehr verwirrt geschaut, ob der Präprozzsor das in der
>> gewählten Programmierumgebung wirklich kennt.
>
> Du vergisst, dass der Präprozessor nur Texte ersetzt.
>
> Auch sowas wie
> #define F_CPU  völliger Blödsinn
>
> kann man erstmal schreiben – nur wird vermutlich niemand, der F_CPU
> hinterher verwendet, damit was anfängen können. ;-)
>
> Eins N00B schrieb:
>> nur die Exponentialschreibweise noch nicht über den Weg gelaufen
>
> Hmm, macht eigentlich jeder Taschenrechner so.

Auf meinem Casio muss ich dafür schon irgendwas umkonfigurieren, wenn 
das überhaupt geht, sonst zeigt der ein kleines ×10 an. (der hat aber 
auch ein Pixeldisplay). Und der ist auch schon über 10 Jahre alt.

Das erste ist Kontextabhängig ^^
1
#define völliger 1
2
#define Blödsinn * 8000000

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Eins N00B schrieb:
> GCC warnt einen anscheinend sogar, wenn man Floats im CPP verwendet:

Du musst zwischen dem Teil des Präprozessors unterscheiden, der einfach 
nur Texte ersetzt und dem, der Bedingungen auswertet. Letzterer kann nur 
mit Ganzzahlen umgehen, um die Entscheidung für die Bedingung zu 
ermitteln. Das war schon immer so und hat nichts mit GCC zu tun.

von Klaus (Gast)


Lesenswert?

So weit alles fast klar. Habe weiter das gefunden. Es stecken die 8MHz 
drin.
1
void timer_init() 
2
  {
3
    TCCR0B = (1<<CS02)|(1<<CS00);                // Prescaler 1024
4
    TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 10ms
5
    TIMSK0 |= 1<<TOIE0;                    // Interrupt erlauben
6
  }
Meine die Zeile mit TCNT0
Das ist das Timer init für Timer 0 also 8 Bit. Stehe die Rechnung nicht.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Klaus schrieb:
> Stehe die Rechnung nicht

Welchen Teil der Rechnung verstehst du denn nicht? Wenn man die 
Typecasts mal weglässt, ist das ja erstmal eine ganz normale Berechnung, 
wie man sie auch auf Papier machen könnte.

Muss für einen eher ältlichen Controller sein, bei moderneren würde man 
lieber CTC (clear timer on compare match) benutzen statt Preload beim 
Überlauf.

: Bearbeitet durch Moderator
von Stefan F. (Gast)


Lesenswert?

Klaus schrieb:
> Stehe die Rechnung nicht.

> // Prescaler 1024

Die 8 MHz werden also erstmal auf 8 kHz herunter geteilt, damit wird der 
Timer getaktet.

> TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 10ms

Zerlegen wir das mal:

> (F_CPU / 1024)

Ergibt die genannten 8 kHz

> 10e-3

Das sind die gewünschten 10ms wo er hin will. Die Formel soll berechnen, 
wie viele Takte in 10ms stattfinden.

> + 0.5

Dient dazu, den Rundungsfehler zu minimieren. Da das Register nur 
Integer Werte kennt, würde der C Compiler die berechnete Zahl immer nur 
abrunden (ist halt so in C). Durch diese Addition kommt man auf eine 
Rundung nach Schulregeln (alles ab 0.5 wird aufgerundet, alles darunter 
wird abgerundet)

Angenommen, die Formel ergibt 123,6. Dann würde der C-Compiler das auf 
123 abrunden. Durch die Addition von 0,5 haben wir aber nun 124,1. Der 
Compiler rundet es auf 124 ab.

123,6 wird zu 124.

Angenommen, die Formel ergibt 123,4. Dann würde der C-Compiler das auf 
123 abrunden. Durch die Addition von 0,5 haben wir aber nun 123,9. Der 
Compiler rundet es auf 123 ab.

123,4 wird zu 123.

Dann bleibt noch das "minus" übrig:
> TCNT0 = -(irgendwas)

Er will dass der Timer nach n Takten überläuft. Deswegen muss man ihn 
mit einem bestimmten Wert vor-laden, damit er nach n Takten die 255 
überschreitet.

Hier wird ausgerechnet, wie weit n vom Überlauf des Timer entfernt ist, 
also 256 - n. Oder einfach -n, was er hier gemacht hat. Dabei kommt das 
gleiche bei heraus. Denn -1 ist mit 255 identisch, und -2 ist mit 254 
identisch, usw. Ergibt sich aus der Darstellung negativer zahlen in den 
Bits.

> (uint8_t)(int16_t)

Das kann ich nicht erklären, ich hätte hier nur "(uint8_t)" geschrieben, 
damit die Fließkommazahl der Formel in 8 Bit Integer umgewandelt wird.

von Rolf M. (rmagnus)


Lesenswert?

Eins N00B schrieb:
> Jörg W. schrieb:
>> Klaus schrieb:
>>> Fange gerade mit lesen eines Codes an und da sind viele Sachen
>>> unverständlich.
>>
>> Naja, die verschiedenen Varianten von numerischen Konstanten gehören nun
>> einmal zu den Grundlagen einer Programmiersprache, die solltest du dir
>> auf jeden Fall ansehen.
>
> Das stimmt, aber kann man das von Anfänger:innen erwarten?

Ich finde, man kann erwarten, dass sie sich dieses Wissen eigenständig 
aneignen. Das ist heute dank Internet leichter denn je, und doch 
scheinen  immer mehr Menschen damit zu kämpfen.

Eins N00B schrieb:
> Uff, das hätte ich aus dem Stehgreif auch nicht gewusst.

Der heißt Stegreif, da es sich nicht etwa um ein flugunfähiges 
geflügeltes Fabelwesen handelt, sondern um einen Reif mit einem Steg. 
;-)

Jörg W. schrieb:
> Klaus schrieb:
>> Stehe die Rechnung nicht
>
> Welchen Teil der Rechnung verstehst du denn nicht?

Das Problem liegt vielleicht in dem - und den Casts. Das ist so nicht 
gerade sehr intuitiv.

von Boomer1337 (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
>> (F_CPU / 1024)
>
> Ergibt die genannten 8 kHz

Ich dachte F_CPU wäre 8E6 und nicht 8000*1024.

Das ist ein Freitagsthread

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Stefan ⛄ F. schrieb:
> Zerlegen wir das mal:
>> (F_CPU / 1024)
>
> Ergibt die genannten 8 kHz

Ist übrigens nicht sonderlich geschickt, denn das bringt mehr oder 
minder große und an dieser Stelle vermeidbare Rundungsfehler ein.

Da die restliche Rechnung ohnehin im Gleitkommabereich erfolgen soll, 
wäre es sinnvoller, das als
1
(F_CPU / 1024.0)

zu schreiben.

von Stefan F. (Gast)


Lesenswert?

Boomer1337 schrieb:
>> (F_CPU / 1024)
>> Ergibt die genannten 8 kHz

> Ich dachte F_CPU wäre 8E6 und nicht 8000*1024.

8e6 sind 8 Mhz. Das durch 1024 geteilt sind 8 kHz.

Wir müssen jetzt aber nicht den Lehrstoff der 6. klasse wiederholen, 
oder?
https://www.lernhelfer.de/schuelerlexikon/physik/artikel/vorsaetze-von-einheiten


Oder zielt dein Einwand auf die Differenz zwischen 1000 und 1024 ab? Die 
hat er so hin geschrieben, weil der Vorteiler die 8 MHz durch 1024 
teilt. Es sind genau genommen 7,8125 kHz, falls es dich beruhigt.

> Das ist ein Freitagsthread

Nach der Erschöpfung schaltet das Hirn irgendwann ab, dagegen kann sich 
niemand wehren. Deswegen wohl immer Freitag.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Stefan ⛄ F. schrieb:
> Es sind genau genommen 7,8125 kHz, falls es dich beruhigt.

Ja, sind es, und genau darum sollte man auch durch 1024.0 teilen und 
nicht einfach nur durch 1024. Bei letzterem kommt nämlich nur 7 raus, 
die dann anschließend mit 10000 multipliziert wird. Der Unterschied ist 
dann zwischen 70000 und 78125, immerhin 10 % zu klein.

Den Rundungsfehler-Ausgleich mit der Addition von 0,5 kann man sich dann 
getrost schenken. ;-)

Aber: es ist natürlich in der Rechnung keinerlei Schutz vor völlig 
daneben liegenden Ergebnissen drin. Ob nun 70000 oder 78125 – beide 
Werte passen nicht einmal in einen uint16_t, ganz zu schweigen vom 
Zieltyp uint8_t.

Richtig wäre meiner Meinung nach (ja, es ist Freitag, muss auch erstmal 
drüber nachdenken ;-):
1
void timer_init(void)
2
{
3
    TCCR0B = (1<<CS02)|(1<<CS00);                // Prescaler 1024
4
    TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024.0 / 100.0 + 0.5);  // preload for 10ms = 100 Hz
5
    TIMSK0 |= 1<<TOIE0;                    // Interrupt erlauben
6
}

: Bearbeitet durch Moderator
von Veit D. (devil-elec)


Lesenswert?

Hallo Rolf M.,

du verblüffst mich mit deinen Aussagen.

> Abgesehen davon kann man an 8E6 kein UL anhängen, weil das eine den Wert
> zu einem double macht, das andere zu einem unsigned long. 8E6UL ist kein
> gültiges Literal.

Von 8E6UL war gar keine Rede. Klar das das nicht geht.


> Ich finde es eine Unsitte, Werte nur deswegen unsigned zu machen,
> weil sie halt nicht negativ sind. Für mich gibt's nur zwei Gründe, einen
> Typ unsigned zu machen:
> - Ich brauche den Wertebereich
> - Ich will damit Bitgefummel machen, wo das Vorzeichen stört

Warum soll das eine Unsitte sein? Wenn eine Variable nur für den 
positiven Wertebereich vorgesehen ist, dann mach ich die unsigned. Ohne 
jedes zögern. Außerdem funktioniert mit unsigned der Überlauf. Ich wüßte 
nicht warum unsigned falsch wäre. Im Gegenteil. Wenn eine Rechnung 
temporär einen größeren Wertebereich benötigt um Überläufe während der 
Rechnung zu vermeiden, wird beginnend mit 1UL (oder 1L) multipliziert. 
Dann ist der Wertebereich sichergestellt. Genauso wenn es temporär 
Fließkomma sein muss wird beginnend mit 1.0 multipiziert.


>> Zudem erlaubt es Warnungen, z.B. bei Vergleichen mit signed Values. Ein
>> Tor weniger für Schlampigkeiten.

> Und was habe ich davon?

Na also wenn du das nicht weißt, dann weiß ich auch nicht. Du bist doch 
C++ Programmierer oder nicht? Da sollte man doch jede Möglichkeit einer 
Warnung nutzen um Fehler vermeiden zu können. Bei C++ dreht es sich doch 
im Großen und Ganzen um Datentypsicherheit. Deswegen kann ich deine 
Aussagen leider nicht nachvollziehen.

von Rolf M. (rmagnus)


Lesenswert?

Jörg W. schrieb:
> Stefan ⛄ F. schrieb:
>> Es sind genau genommen 7,8125 kHz, falls es dich beruhigt.
>
> Ja, sind es, und genau darum sollte man auch durch 1024.0 teilen und
> nicht einfach nur durch 1024. Bei letzterem kommt nämlich nur 7 raus,

Nicht wenn F_CPU als 8E6 definiert ist. ;-)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Cross-check:
1
$ avr-gcc -Os -DF_CPU=8E6 -mmcu=atmega328 -Wall -Wextra -S tinit.c
2
$ cat tinit.s
3
        .file   "tinit.c"
4
__SP_H__ = 0x3e
5
__SP_L__ = 0x3d
6
__SREG__ = 0x3f
7
__tmp_reg__ = 0
8
__zero_reg__ = 1
9
        .text
10
.global timer_init
11
        .type   timer_init, @function
12
timer_init:
13
/* prologue: function */
14
/* frame size = 0 */
15
/* stack size = 0 */
16
.L__stack_usage = 0
17
        ldi r24,lo8(5)
18
        out 0x25,r24
19
        ldi r24,lo8(-78)
20
        out 0x26,r24
21
        ldi r30,lo8(110)
22
        ldi r31,0
23
        ld r24,Z
24
        ori r24,lo8(1)
25
        st Z,r24
26
/* epilogue start */
27
        ret
28
        .size   timer_init, .-timer_init
29
        .ident  "GCC: (GNU) 10.2.0"

TCNT1 wird also mit -78 vorgeladen, bei 7,8125 kHz Takt läuft der Timer 
nach 9,984 ms über, was die gewünschten 100 Hz (einigermaßen) ergibt.

Eine ebenso falsche (oder vielleicht richtige?) Rechnung dürfte sich 
nochmal in der Interruptroutine wieder finden.

Aber nochmal: auf einem modernen Controller nimmt man dafür den 
CTC-Modus des Timers, statt dieser Pfriemelei mit dem Vorladen. Sowas 
hat man vor 20 Jahren bei den ersten AVRs machen müssen.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Rolf M. schrieb:

>> Bei letzterem kommt nämlich nur 7 raus,
>
> Nicht wenn F_CPU als 8E6 definiert ist. ;-)

Wo du Recht hast, hast du natürlich Recht. :-)

von Stefan F. (Gast)


Lesenswert?

Jörg W. schrieb:
> Ja, sind es, und genau darum sollte man auch durch 1024.0 teilen und
> nicht einfach nur durch 1024.

Verstehe ich nicht. Ob ich eine Fließkommazahl (die 8E6) durch 1024 oder 
durch 1024.0 teile ändert doch nichts daran, dass der Algorithmus für 
Fließkommazahlen verwendet wird. Oder etwa doch?


Hätte er wie jeder "normale" Mensch
> #define F_CPU 8000000UL
geschrieben, dann wäre es auch für mich klar, dass die 1024 als 
Fließkommazahl geschrieben werden müssen.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Stefan ⛄ F. schrieb:
> Hätte er wie jeder "normale" Mensch
>> #define F_CPU 8000000UL
>
> geschrieben, dann wäre es auch für mich klar wichtig, die 1024 als
> Fließkommazahl auszudrücken.

Genau darauf bezog ich mich (als Idee), Rolf hatte es ja schon 
korrigiert.

Solche Rechnungen im Inneren des Codes sollte man in jedem Falle so 
aufschreiben, dass es eben egal ist, ob der Nutzer 8E6 oder 8000000 da 
hin geschrieben hat.

von Stefan F. (Gast)


Lesenswert?

Ist denn hier jemand der den Sinn des doppelten castens erklären kann?

> (uint8_t)(int16_t) -Fließkommazahl

von Veit D. (devil-elec)


Lesenswert?

Hallo,

@ Stefan:
8000000 / 1024 ist eine Ganzzahlrechnung mit Rundungsfehler

8000000.0 / 1024
oder
8000000 / 1024.0 ist eine Fließkommarechnung

Ich selbst würde es ja so schreiben
1
TCNT0 = static_cast<uint8_t>((F_CPU / 1024.0 / 100) + 0.5);  
2
oder
3
TCNT0 = static_cast<uint8_t>((1.0 * F_CPU / 1024 / 100) + 0.5);

von Rolf M. (rmagnus)


Lesenswert?

Stefan ⛄ F. schrieb:
> Ist denn hier jemand der den Sinn des doppelten castens erklären kann?
>
>> (uint8_t)(int16_t) -Fließkommazahl

Vielleicht wollte der Urheber einen Zwischenwert generieren, in dessen 
Wertebereich das Ergebnis auf jeden Fall rein passt. Ich wüsste aber 
nicht, wozu das nötig sein könnte.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Stefan ⛄ F. schrieb:
> Ist denn hier jemand der den Sinn des doppelten castens erklären kann?
>
>> (uint8_t)(int16_t) -Fließkommazahl

Nö :)
1
(gdb) p (unsigned char)-(8E6 / 1024.0 / 100.0 + 0.5)
2
$1 = 178 '\262'
3
(gdb) p (unsigned char)(int)-(8E6 / 1024.0 / 100.0 + 0.5)
4
$2 = 178 '\262'

Der AVR-GDB ist auch der Meinung, dass beide Ausdrücke das Gleiche 
ergeben. ;-)

(uint8_t oder int16_t sind keine native data types, die kennt der 
Debugger nicht ohne geladenes ELF-File, daher hier die native types 
benutzt.)

von Klaus (Gast)


Lesenswert?

So wie es bei euch klingt, vergessen wir mal ganz schnell diese komische 
Rechnung. Wenn ihr als Profis für manche Ausdrücke keine Erklärung hat, 
was soll ich da als Anfänger sagen.
Nach der Rechnung soll es ein Timer mit 8 Bit füe 10 mS wwerden. Der CTC 
ist gut da für geeignet. Da werde ich mal sehen ob ich einen für die 
10ms und 1ms zusammen bekomme.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Klaus schrieb:
> Nach der Rechnung soll es ein Timer mit 8 Bit füe 10 mS wwerden.

Nicht nach der Rechnung, aber nach dem Kommentar neben der Rechnung. ;-)

> Der CTC
> ist gut da für geeignet.

Ja.

> Da werde ich mal sehen ob ich einen für die
> 10ms und 1ms zusammen bekomme.

Einer reicht, alle 1 ms. Bei jedem 10. Aufruf werden halt die Aktionen 
für 10 ms dann auch noch mit abgearbeitet.

Die allerersten AVRs hatten diesen CTC-Modus noch nicht. Um dort einen 
regelmäßigen Timer-Interrupt zu erreichen, der sich nicht durch die 
volle Zählweite abbilden lässt (in diesem Falle von 1:256, da es ein 
8-Bit-Timer ist), musste man mit dieser Preload-Krücke arbeiten: das 
Zählerregister wurde auf einen bestimmten Wert vorgeladen, um den 
Zeitabstand bis zum Überlauf-Interrupt zu verkürzen. Im 
Überlauf-Interrupt selbst wurde dann als erstes der Preload-Wert neu 
geladen, danach die restlichen Aktionen ausgeführt. Damit hat man aber 
natürlich zusätzlich noch die Zeit für die Interruptannahme, und wenn 
gerade ein anderer Interrupt aktiv ist, bekommt man Jitter, weil der 
Preload erst später erfolgt. Daher ist das eher eine Krücke, und alle 
waren froh, dass es in den späteren Controllern dann CTC gab.

Der Preload-Wert ist "256 - <Wert, der für Timeout nötig ist>", aber die 
256 kürzt sich bei der Rechnung raus (ganzzahliger Überlauf), daher 
steht da oben dann einfach nur ein negatives Vorzeichen vor dem 
berechneten Ausdruck.

von Klaus (Gast)


Lesenswert?

Habe mit den Timer angesehen und versuche etwas für 8 MHz und 16 MHz auf 
1ms zu machen. Bitte mal anschauen:
für 16MHz
1
void timer_init()      // Timer 8 Bit, 16MHz, 1ms
2
    {                      // Timer 0 konfigurieren
3
    TCCR0A = 0;                // CTC Modus
4
    TCCR0B = (1<<WGM01)|(1<<CS01)|(1<<CS00);  // Prescaler 64
5
    TCNT0=1;
6
    OCR0A=249;
7
    TIMSK0|=(1<<OCIE0A);            // Interrupt erlauben
8
    }
und für 8MHz
1
void timer_init()      // Timer 8 Bit, 8MHz, 1ms
2
    {                      // Timer 0 konfigurieren
3
    TCCR0A = 0;                // CTC Modus
4
    TCCR0B = (1<<WGM01)|(1<<CS01)|(1<<CS00);  // Prescaler 64
5
    TCNT0=1;
6
    OCR0A=124;
7
    TIMSK0|=(1<<OCIE0A);            // Interrupt erlauben
8
    }
und die ISR
1
ISR (TIMER0_COMPA_vect)        // wait1=1ms,
2
    {  
3
    }
Die Zeiten müssten damit stimmen

von Rainer V. (a_zip)


Lesenswert?

Vielleicht könnte man gerade auch als Anfänger mal einen Blick in 
"einfache" Assembler-Listings werfen. Da findet man z.B. so etwas:

; cpu clock in hertz
.equ F_CPU = 16000000

...und mit etwas weiterer Suche könnte man auch Initialisierungsroutinen 
für die serielle Schnittstelle finden, wo sowohl der Teiler für eine 
gewünschte Baudrate berechnet wird, als auch der tatsächliche zum 
zulässigen Fehler. Macht natürlich der Assembler, den man dann auch mit 
einer entsprechenden Fehlermeldung abbrechen lassen kann!
Gruß Rainer

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Rainer V. schrieb:
> mal einen Blick in "einfache" Assembler-Listings werfen

Warum willst du jetzt jemanden, der gerade mit C anfängt, auch noch mit 
Assembler verwirren?

Klaus schrieb:
> OCR0A=249;

Das ist halt genau das, was der andere Code versucht, automatisch zu 
berechnen.
1
OCR0A = (uint16_t)(F_CPU / 64.0 /* prescaler 64 */ / 1000 /* Hz = 1 ms */);

Das kommt übrigens auf 125 für 8 MHz. Ich denke, du hast einen 
off-by-one Fehler drin bei dir.

von Hannes (Gast)


Lesenswert?


von Rainer V. (a_zip)


Lesenswert?

Jörg W. schrieb:
> Warum willst du jetzt jemanden, der gerade mit C anfängt, auch noch mit
> Assembler verwirren?

Na weil er ja offensichtlich kein "theoretischer" C-ler werden will. Er 
hat halt eine konkrete Maschine und da halte ich es ganz sicher für 
nötig, mal in die untere Programmierebene abzusteigen. Schon allein die 
Port-Pin's, die es ja letztlich machen müssen, schaut man sich doch 
nicht in "C" an...aber ich glaube, mit etwas Willen geht es natürlich 
ganz ohne Assembler...genau so wie mich im Berufsleben der erste Dokter 
der E-Technik damit überraschte, dass er nicht in der Lage war, eine 
vernünftige Lötstelle zu produzieren. Und das nicht als gepflegte 
Attitüde...nein, man hatte sofort eine Höllenangst, dass der Mensch sich 
mit dem Griff zum Lötgerät, eigenhändig aus der Welt schaffen würde :-)
Gruß Rainer

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Rainer V. schrieb:

>> Warum willst du jetzt jemanden, der gerade mit C anfängt, auch noch mit
>> Assembler verwirren?
>
> Na weil er ja offensichtlich kein "theoretischer" C-ler werden will.

Du hast eine reichlich verschobene Vorstellung von der Welt.

> Er
> hat halt eine konkrete Maschine und da halte ich es ganz sicher für
> nötig, mal in die untere Programmierebene abzusteigen.

Das geht auch mit C ganz prima. Habe ich mehr als 15 Jahre beruflich 
gemacht. Irgendwelches Assembler-Gefummel zu pflegen, hätte mir wohl 
kein Kunde bezahlen wollen.

Dass man, wenn man auf der Ebene arbeitet, den Assemblercode lesen 
können muss, halte ich übrigens für unbestritten. Dafür muss man sich 
aber nicht mit dem Assembler selbst herum schlagen. Auf Compilerebene 
hat man halt Gleitkommazahlen, bei denen man sich nicht in jedem 
Rechenschritt überlegen muss, ob der Ausdruck jetzt über- oder 
unterlaufen kann und welchen Verlust an Genauigkeit man gerade mit dem 
aktuellen Schritt eingeht, den man dann durch irgendwelche Skalierungen 
(die wieder überlaufen könnten) wieder ausgleicht. Nichtsdestotrotz: im 
Compilat steht, siehe oben, am Ende eine 8-Bit-Konstante. (Als Kontrolle 
kann man die sich natürlich vorher mit dem Taschenrechner ermitteln, 
aber auch der rechnet mit Gleitkommazahlen.)

> aber ich glaube, mit etwas Willen geht es natürlich
> ganz ohne Assembler...

Ja. Kaum zu glauben, ARM Cortex-M wurde ausdrücklich so entworfen, dass 
man selbst die Initialisierung und Vektortabelle komplett in C 
ausdrücken kann. Die spinnen sicher alle bei ARM, oder?

von Veit D. (devil-elec)


Lesenswert?

Hallo,

wenn casten dann bitte 8 Bit.  ;-)
1
OCR0A = (uint8_t)(F_CPU ...);

Der Fehler bei Klaus steckt in der falschen Bitzuweisung. Das WGM01 Bit 
steht in TCCR0A und nicht in TCCR0B. Dann klappt das auch. Vorausgesetzt 
die Interrupts sind generell freigegeben. Wenn man das alles inkl. 
Formel  korrigiert kommt man auf
1
const uint16_t freq = 1000; // [Hz]
2
void set_Timer0();
3
4
int main(void)
5
{         
6
  configTimer0();  
7
  sei();  
8
  
9
  while(1)
10
  { } 
11
}  
12
13
void configTimer0()              
14
{
15
  TCCR0A = _BV(WGM01);              // CTC
16
  TCNT0 = 0;                               
17
  OCR0A = (F_CPU / 64 / freq) - 1;  // Compare Value
18
  TIMSK0 = _BV(OCIE0A);             // enable Compare Match A
19
  TCCR0B = _BV(CS01) | _BV(CS00);   // Prescaler 64 und Timer starten
20
} 
21
22
ISR(TIMER0_COMPA_vect)   
23
{  
24
                  
25
}

: Bearbeitet durch User
von Rainer V. (a_zip)


Lesenswert?

Jörg W. schrieb:
> Dass man, wenn man auf der Ebene arbeitet, den Assemblercode lesen
> können muss, halte ich übrigens für unbestritten. Dafür muss man sich
> aber nicht mit dem Assembler selbst herum schlagen.

Ich denke, da beißt die Maus keinen Faden ab, aber irgendwann biste dann 
doch schon beim Portx.y ... egal ob in Ass. oder Hochsprache...man kann 
offensichtlich nie zu wenig voraussetzen. Im Gegenteil, man wundert sich 
eher immer wieder, wie viel weniger man nun doch vorraussetzen muß!
Gruß Rainer

von Veit D. (devil-elec)


Lesenswert?

Hallo,

ihr übertreibt ja total. Man muss dafür weder Assembler schreiben noch 
lesen können. Man kann sich alle Werte notfalls auch auf der seriellen 
ausgeben lassen.

Eine kleine Verbesserung sei noch erlaubt
1
void configTimer0(const uint16_t f)              
2
{
3
  TCCR0A = _BV(WGM01);              // CTC
4
  TCNT0 = 0;                               
5
  OCR0A = (F_CPU / 64 / f) - 1;     // Compare Value
6
  TIMSK0 = _BV(OCIE0A);             // enable Compare Match A
7
  TCCR0B = _BV(CS01) | _BV(CS00);   // Prescaler 64 und Timer starten
8
}

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Veit D. schrieb:
> Man muss dafür weder Assembler schreiben noch lesen können.

Müssen nicht, aber siehe oben: es ist gewiss sinnvoll, sich mal 
anzusehen, was nach der Rechnung mit dem Compiler heraus gekommen ist.

Deine Verbesserung kann nach hinten los gehen: der Gleitkommausdruck 
wird jetzt u. U. zur Laufzeit berechnet statt vom Compiler. Mit der 
globalen Konstante (genauer: unveränderlichen Variable) für die 
Zielfrequenz kann der Compiler das sehr sicher zur Compilezeit 
berechnen. Wenn man sie als Parameter an die Funktion übergibt, hängt es 
davon ab, ob er während des Compilierens auch feststellen kann, dass der 
Aufruf mit einer tatsächlichen Konstante erfolgte¹. Das "const" im 
Parameter erzwingt dies keineswegs, das verhindert nur, dass man den 
Wert innerhalb der Funktion modifizieren kann.

Ich würde bei deiner vorhergehenden Version bleiben.

¹ Durch Ansehen des erzeugten Assemblercodes kann man natürlich genau 
das verifizieren. ;-)

Jörg W. schrieb:
> Ich denke, du hast einen off-by-one Fehler drin bei dir.

Diese Bemerkung ziehe ich zurück. Am Ende noch 1 zu subtrahieren ist 
korrekt – habe jetzt nochmal ins Datenblatt geschaut (Veit hat das ja 
bei sich auch so stehen).

: Bearbeitet durch Moderator
von Veit D. (devil-elec)


Lesenswert?

Hallo,

sollte eine Komfortvariante werden. ;-)  Aber stimmt schon, so einen 
Intervall-Timer ändert man nie wieder. Ich hoffe Klaus kann damit was 
anfangen und darauf aufbauen.

Danke fürs verifizieren.  :-)

von Klaus (Gast)


Lesenswert?

Klaus schrieb:
> void timer_init()      // Timer 8 Bit, 8MHz, 1ms
>     {                      // Timer 0 konfigurieren
>     TCCR0A = 0;                // CTC Modus
>     TCCR0B = (1<<WGM01)|(1<<CS01)|(1<<CS00);  // Prescaler 64
>     TCNT0=1;
>     OCR0A=124;
>     TIMSK0|=(1<<OCIE0A);            // Interrupt erlauben
>     }

Sorry, habe euren Text nicht ganz verstanden, einer sagt 125 der andere 
124. Was stimmt den? Laut Berechnung müsste es 124 sein.

von Klaus (Gast)


Lesenswert?

Stimmt du hast recht, bin im Rgister verruscht muss sein

TCCR0A = (1<<WGM01);                // CTC Modus
TCCR0B = (1<<CS01)|(1<<CS00);  // Prescaler 64

Beitrag #6760202 wurde von einem Moderator gelöscht.
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Klaus schrieb:
> einer sagt 125 der andere 124. Was stimmt den?

Lies doch bitte nicht nur punktuell, sondern den kompletten Thread.

Ich hatte mich korrigiert. Eins weniger ist richtig, steht ja auch im 
Datenblatt der AVRs.

Beitrag #6760214 wurde von einem Moderator gelöscht.
Beitrag #6760407 wurde von einem Moderator gelöscht.
Beitrag #6760503 wurde von einem Moderator gelöscht.
Beitrag #6760507 wurde von einem Moderator gelöscht.
von Klaus (Gast)


Lesenswert?

Müsst ihr euch immer streiten. Es gibt genügend andere Probleme.
Also ein anderes Problem um rechnisch auf 1ms zu kommen.
Gegeben:
Frequenz 8 000 000 HZ (8 MHz)
Prescaler 64
Rechnung:
   Timer 8 Bit x Vorteiler   256 x 64   16384
t= ----------------------- = -------- = ------- = 0,002048 s = 2,048 ms
      Systemtakt              8000000   8000000
Wie komme ich jetzt mit der Rechnung per Fuss auf die 124 als 
Einstellung für die Einstellung?
Die Berechnung vorher hat das Programm im Netz gemacht.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

ich habe mir mal den Spass gemacht eine automatische Berechnung zu 
erstellen.
Man ist eben nur in der [ms] Einheit und in 8Bit innerhalb 1...16ms 
eingeschränkt. Funktioniert, aber so ganz glücklich bin ich mit der 
doppelt vorhandenen Formel nicht. Einmal muss ich den passenden 
Prescaler suchen und dann muss ich damit den Compare Wert berechnen. Mit 
constexpr kann ich aber nur einen Rückgabewert haben. Wer da eine Idee 
hat, nur zu.
1
/* 
2
 Arduino Mega2560
3
 Timer0: CTC, Mode 2
4
 17.07.2021
5
 Berechnet und konfiguriert an Hand der ISR Aufrufperiode alle notwendigen Timer0 Parameter automatisch.
6
*/
7
8
const uint16_t isrPeriode = 1; // [ms] Maximum 16ms
9
void configTimer0(const uint32_t periode);  
10
constexpr uint16_t calculateTimer0Prescaler(const uint16_t periode);
11
constexpr uint8_t calculateTimer0CompareValue(const uint16_t periode, const uint16_t prescaler);
12
constexpr uint8_t calculateTimer0ClockSelectBits (const uint16_t prescaler);
13
14
// umrechnen, ansonsten geht die Genauigkeit verloren
15
constexpr float tCPU {1.0/(F_CPU/1000000)};  // F_CPU [µs] (16MHz = 0,0625µs)
16
 
17
int main (void)
18
{ 
19
  DDRB = _BV(PB7);    // Pin 13, Messpin
20
  configTimer0(isrPeriode); 
21
  sei(); 
22
  
23
  while(1)
24
  { } 
25
} 
26
 
27
28
// ****** Funktionen ****** //
29
// sucht den bestmöglichen Prescaler 
30
constexpr uint16_t calculateTimer0Prescaler(const uint16_t periode)
31
{  
32
  const uint16_t usedPrescaler[] = {1, 8, 64, 256, 1024};  // verfügbare Prescaler
33
  uint16_t prescaler {0};
34
  const uint8_t maxCMR {255};  // max. Compare Match Register Wert
35
  uint32_t newCMR {0};         // passender errechneter Compare Match Register Wert
36
  
37
  if (periode >= 1 && periode <= 16)  // [ms] Gültigkeitskontrolle
38
  {  
39
    for (auto &pre : usedPrescaler)
40
    {
41
      newCMR = static_cast<uint32_t>(1000.0*periode/tCPU/pre-1+0.5);  // CMR Berechnung und runden
42
      if (newCMR <= maxCMR) // Gültigkeitskontrolle
43
      {
44
        prescaler = pre;
45
        break;    // gültigen Prescaler gefunden, Suche abbrechen
46
      }  
47
    } 
48
  }  
49
      
50
  return prescaler;
51
} 
52
53
// berechnet den bestmöglichen CMR Wert 
54
constexpr uint8_t calculateTimer0CompareValue(const uint16_t periode, const uint16_t prescaler)
55
{ 
56
  // Periodendauer ISR Aufruffrequenz mit Faktor 1000 anpassen, ms -> µs
57
  //                            µs      ms    µs  
58
  return static_cast<uint8_t>(1000.0*periode/tCPU/prescaler-1+0.5);  // CMR Berechnung und runden);
59
} 
60
61
62
constexpr uint8_t calculateTimer0ClockSelectBits (const uint16_t prescaler)
63
{
64
  uint8_t cs {0};
65
    switch (prescaler) { 
66
      case    1 : cs = _BV(CS10);              break;
67
      case    8 : cs = _BV(CS11);              break;
68
      case   64 : cs = _BV(CS11) | _BV(CS10);  break;
69
      case  256 : cs = _BV(CS12);              break;
70
      case 1024 : cs = _BV(CS12) | _BV(CS10);  break;
71
      default :   cs = 0;
72
    }  
73
  return cs;
74
}
75
76
void configTimer0(const uint16_t periode)              
77
{
78
  TCCR0B = 0;
79
  TCCR0A = 0;
80
  TIMSK0 = 0; 
81
  TCNT0  = 0; 
82
  TCCR0A |= _BV(WGM01);                                                         // CTC                              
83
  OCR0A   = calculateTimer0CompareValue(periode, calculateTimer0Prescaler(periode));  // Compare Value
84
  TIMSK0 |= _BV(OCIE0A);                                                        // enable Compare Match A Interrupt
85
  TCCR0B |= calculateTimer0ClockSelectBits(calculateTimer0Prescaler(periode));  // Prescaler 64 und Timer starten
86
} 
87
88
ISR(TIMER0_COMPA_vect)   
89
{  
90
  PINB = _BV(PB7);                  
91
}

von Veit D. (devil-elec)


Lesenswert?

Hallo Klaus,

du hast doch im Manual für jeden Timer Modus eine Formel stehen. Diese 
stellst du auf die gesuchte Größe um.

Tipp. Wenn du erstmal mit Frequenzen rechnest und nicht mit 
Periodendauer gehts leichter. Dann geht das Formel umstellen erstmal 
einfacher.

: Bearbeitet durch User
von Klaus (Gast)


Lesenswert?

Im Kapitel 11 (8 Bit Timer) des datenblatts steht unter CTC die folgende 
Formel: (Hoffe das korrekt angezeigt wird)
1
            f(CLK)
2
f(OC) = ------------------
3
        2 x N x (1+OCRnA)

Nach der Erklärung im DB steht das folgende für
1
N - Prescaler (64)
2
f (OC) - gesuchte Frequenz (1000 Hz - 1 ms)
3
f (CLK) - Frequenz IC / Prescaler (64)

umstellung nach OCRnA
Als Ergenbis bekomme ich 15,8 ? Das stimmt wohl nicht. Habe ich die 
Bezeichnungen falsch zugeordnet?

: Bearbeitet durch Moderator
Beitrag #6760715 wurde von einem Moderator gelöscht.
von MWS (Gast)


Lesenswert?

Klaus schrieb:
> Als Ergenbis bekomme ich 15,8 ? Das stimmt wohl nicht. Habe ich die
> Bezeichnungen falsch zugeordnet?

Das kann man im Kopf rechnen, wenn auf dem Strich 8000000 steht und 1000 
rauskommen soll, dann muss unterm Strich 8000 stehen. Das kann man durch 
zweimal 64 teilen und schon kommt 62,5 raus. Also unbrauchbar, weil's 
nicht auf geht.

Da schaust Du Dir evtl. die falsche Formel, bzw. den falschen Modus an, 
wobei CTC prinzipiell richtig ist. Der Mode ist richtig, bei dem im 
Teiler nur einmal N benutzt wird, dann geht's mit Top = 124 (125 - 1) 
auch auf.

von Klaus (Gast)


Lesenswert?

Das Kapitel im DB ist 11.7.2 mit Angabe der Formel

Klaus schrieb:
>             f(CLK)
> f(OC) = ------------------
>         2 x N x (1+OCRnA)
 Dabei ist
1
N - Prescaler
2
f(OCRnA) - f bei der der ISR auslösen soll
3
f(clk) - Taktgeschwindigkeit
4
OCRnA - Wert mit der Timer geladen werden muss

und die Umstellung dazu
1
             f(clk)             8 000 000
2
(1+OCRnA) = ----------------- = ----------- = 62,5
3
             f(OCRnA) x 2 x N   1000 x 2 64

Umstellung der Formel stimmt jetzt, aber wieso nicht 124?

: Bearbeitet durch Moderator
Beitrag #6760828 wurde von einem Moderator gelöscht.
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Klaus schrieb:
> Umstellung der Formel stimmt jetzt, aber wieso nicht 124?

Dein "2 x" im Nenner bezieht sich auf die Frequenz, die sich am 
OCxy-Ausgang ergibt, wenn du bei jedem Erreichen der Zählschwelle den 
OCxy-Ausgang umschalten lässt ("Waveform generation" wird das da auch 
genannt). Wenn du aber auf die Interrupt-Frequenz guckst, brauchst du 
diesen Teiler 2 nicht, denn es wird ja bei jedem Erreichen der 
Zählschwelle ein Interrupt ausgelöst.

: Bearbeitet durch Moderator
von Veit D. (devil-elec)


Lesenswert?

Hallo,

der Modus ist schon richtig und die Formel auch. Der Knackpunkt ist die 
Bedeutung von Zielfrequenz und dessen Periodendauer. Wenn der Pin OCR0A 
mit 1kHz takten soll, dann stimmt 62,5. Dafür gilt die Formel.
Du möchtest aber keinen 1kHz Takt am Timerpin erzeugen sondern eine ISR 
Aufruffrequenz von 1ms. Die 1ms ist deine Periodendauer. Deswegen 
Ergebnis mal 2 nehmen oder praktischerweise das durch 2 weglassen.

Das wird dir klarer wenn du ein Taktsignal aufmalst und die 
Periodendauer einzeichnest. Dann markierst du die Stellen an denen dafür 
notwendigerweise die ISR auslösen muss, wenn angenommen die ISR/OCR0A 
ein Takt erzeugen soll. Ist blöd zu erklären, deswegen aufmalen, dann 
sollte es Klick machen.

von Spess53 (Gast)


Lesenswert?

Hi

Der OC-Interrupt wird aber bei jedem Flankenwechsel ausgelöst, also muss 
die Frequenz 500 Hz und nicht 1000 Hz sein. Damit kommst du bei deiner 
Rechnung auf 125. Und die ergeben 125-1=124.

MfG Spess

Beitrag #6760853 wurde von einem Moderator gelöscht.
Beitrag #6761024 wurde von einem Moderator gelöscht.
Beitrag #6761069 wurde von einem Moderator gelöscht.
von Klaus (Gast)


Lesenswert?

Danke für eure Hilfe

Beitrag #6761394 wurde von einem Moderator gelöscht.
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.