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
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.
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
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?
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
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.
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).
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. ;-)
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?
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.
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..
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.
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.
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. 😂👍
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.
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.
Öhem, könntet ihr das vielleicht auf private Kommunikation verlagern? Das langweilt hier. :(
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.
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.
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.
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.
>Netter Versuch. Aber falscher Tag. Freitag ist Trolltag.
Boah. Die Leute nerven mich, die meinen, jedem Thread nen Trollstempel
verpassen zu wollen.
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.
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?
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.
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.
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.
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.
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 …
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
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 …
Mathematik, Folge 1, Natürliche Zahlen https://www.br.de/grips/faecher/grips-mathe/01-natuerliche-zahlen-nachlesen102.html
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.
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?
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
|
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.
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.
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
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.
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.
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
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.
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.
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
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.
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. ;-)
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.
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. :-)
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.
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.
Ist denn hier jemand der den Sinn des doppelten castens erklären kann?
> (uint8_t)(int16_t) -Fließkommazahl
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); |
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.
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.)
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.
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.
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
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
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.
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
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?
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
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
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
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
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. :-)
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.
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.
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.
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.
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 | }
|
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
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.
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.
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.
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
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.
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.