Forum: Mikrocontroller und Digitale Elektronik Maskieren modern?


von Reiner D. (dollreiner)


Lesenswert?

Schon hundertmal besprochen wahrscheinlich, aber ich finde keine 
überzeugende Antwort.

Warum schreiben die Meisten in AVR-C :

testbyte = PINB & (1 << PINB3)

statt wie früher üblich einfach zu maskieren :

testbyte = PINB & 8

Einser dreimal links weil PINB3 mit 3 vorbelegt,
ist exakt dasselbe wie 2^3=8 ... was soll's also ???

von MaWin O. (mawin_original)


Lesenswert?

Reiner D. schrieb:
> was soll's also ???

Weil es einfacher zu lesen ist?

von Reiner D. (dollreiner)


Lesenswert?

(1 << PINB3) ist einfacher zu lesen als 8  ??

Wenn ich schon weiß, daß das ein shift-Befehl ist, dann werd ich wohl 
auch wissen, was 2^3 ist, oder ?

: Bearbeitet durch User
von Daniel F. (df311)


Lesenswert?

Reiner D. schrieb:
> testbyte = PINB & (1 << PINB3)

ah, in PINB wird das Bit PINB3 gesetzt

>
> statt wie früher üblich einfach zu maskieren :
>
> testbyte = PINB & 8

ah verdammt, welcher Pin wird mit 8 gesetzt? ist das jetzt die 
überarbeitete Generation oder noch die alte?

außerdem kümmert sich der Präprozessor darum, dass (1 << PINB3) zu 8 
wird (oder auch zu 1 oder 2 oder 4 oder was auch immer da mit einer 
neuen µC Familie daher kommen könnte)

von Rahul D. (rahul)


Lesenswert?

Reiner D. schrieb:
> testbyte = PINB & (1 << PINB3)
>
> statt wie früher üblich einfach zu maskieren :
>
> testbyte = PINB & 8

In C hat sich die Shift-Schreibweise durchgesetzt.
Andere Dialekte als AVR-C verwenden die Schreibweise ja auch.

Aber: Mach wie du willst. Das stößt höchtens jemandem sauer auf, der an 
die Shift-Schreibweise gewöhnt ist (als ca. 90% aller C-Programmierer).
Auf jeden Fall besser als magic numbers.

von Reiner D. (dollreiner)


Lesenswert?

Daniel F. schrieb:
> ah verdammt, welcher Pin wird mit 8 gesetzt? ist das jetzt die
> überarbeitete Generation oder noch die alte?

Meinst du mit "Generation" die Menschen ?
Haa ! Da kommen wir zusammen. (Wobei ich alt bin aber auch ständig 
überarbeitet war ...)

Das wäre dann also ein Ratespiel, 2 hoch was gibt denn 8 ??
Erzähl mit doch nicht, daß es irgendjemand gibt, der C programmiert, 
aber nicht die 2-er Potenzen wenigstens bis 256 auswendig kennt ....

Daß der Compiler 8 draus macht ist ja klar, sonst könnte er auf Maschine 
ja nicht maskieren :-)

Ich schlage für die Zukunft vor :

testbyte = PINB & (1 << (PINB3+1))/2 | 0 ;   //wahrscheinlich ein fehler 
drin

von Falk B. (falk)


Lesenswert?

Reiner D. schrieb:
> Schon hundertmal besprochen wahrscheinlich, aber ich finde keine
> überzeugende Antwort.
>
> Warum schreiben die Meisten in AVR-C :
>
> testbyte = PINB & (1 << PINB3)
>
> statt wie früher üblich einfach zu maskieren :
>
> testbyte = PINB & 8
>
> Einser dreimal links weil PINB3 mit 3 vorbelegt,
> ist exakt dasselbe wie 2^3=8 ... was soll's also ???

Weil es direkt lesbar ist, welches Bit (NAME!) gemeint ist, vor allem 
bei den Spezialregistern!  Da muss man icht wissen, welcher Bitnummer 
das ist, das weil der Compiler durch die Headerfiles.
1
DDRA = (1<<PA5) | (1<<PA3);
2
DDRA = 32 | 8;
3
DDRA = 40

Welche Version ist besser verständlich?

Siehe Bitmanipulation

https://www.mikrocontroller.net/articles/Bitmanipulation#Bitmanipulation_beim_MSP430

An sich ist die Version ala MSP430 besser, man spart sich Tiparbeit. Ist 
aber historisch bedingt beim avr gcc nicht so, es sei denn, einer 
konvertiert ALLE Headerfiles!

von Falk B. (falk)


Lesenswert?

Rahul D. schrieb:
> In C hat sich die Shift-Schreibweise durchgesetzt.

Nein. Auf dem avr gcc vielleicht, bei anderen Compilern bzw. CPUs nicht 
unbedingt.

> Auf jeden Fall besser als magic numbers.

Stimmt. Das ist der Hauptgrund. Der Compiler soll schließlich was für 
sein geld machen. Quelltext soll so einfach, eindeutig und 
selbsterklärend wie möglich sein, fast so, wie ein normaler Text 
geschrieben ist.

setLED(GRUEN, ON);

ist deutlich besser als

PORTA |= 16;  // gruene LED einschalten.

von Falk B. (falk)


Lesenswert?


von F. M. (foxmulder)


Lesenswert?

Reiner D. schrieb:
> Das wäre dann also ein Ratespiel, 2 hoch was gibt denn 8 ??
> Erzähl mit doch nicht, daß es irgendjemand gibt, der C programmiert,
> aber nicht die 2-er Potenzen wenigstens bis 256 auswendig kennt ....

Ja ganz toll, vor allem für andere Register zB. Timer, hilft sicher 
extrem beim Debuggen irgendeines komplexen Problems...

testbyte = PINB & 8

ist ja maximal bescheuert, es geht um Pin B3 aber man hat 8 als magic 
number im Code, wirklich super.

testbyte = PINB & (1 << 3)
ist ok
testbyte = PINB & (1 << PINB3)
ist besser

außerdem macht es alleine von der "Nachdenklogik" absolut Sinn, man 
nimmt ein Bit, und schiebt es an den richtigen Platz, versteht jeder.

Besser ist es noch bei den neuen AVRs zB. Atmega 0 series:

PORTB.OUTSET = PIN3_bm;

Jetzt ist es absolut verständlich:
Aha PORTB
aha OUTSET (also Ausgang setzen statt früher DDRB)
aha PIN Nr. 3

: Bearbeitet durch User
von Reiner D. (dollreiner)


Lesenswert?

Mit Verlaub, bitte nicht falsch verstehen :

Ist das nicht ein wenig Kindergarten (oder Arduino ;-) ?

Motiv meiner Frage war : ich werde selbst manchmal gefragt, warum dieser 
komische Shift benutzt wird, und weil mir keine sinnvolle Antwort 
einfällt, hab ich jetzt mal hier gefragt.

Also Antwort : damit auch absolute Anfänger den Code besser lesen 
können.
Ist halt heutzutage so. Früher haben die Leute erst Digitaltechnik 
gelernt, und dann mit dem Programmieren angefangen.

(Wichtig : wie gesagt bitte nicht falsch verstehen. Ich bezeichne damit 
ausdrücklich keinen der hier Antwortenden als Anfänger, Kindergarten 
oder ähnlich !!!)

von Rahul D. (rahul)


Lesenswert?

Falk B. schrieb:
> Nein. Auf dem avr gcc vielleicht, bei anderen Compilern bzw. CPUs nicht
> unbedingt.

Dann schränke ich meine Aussage ein: Die C-Libraries, mit denen ich bis 
jetzt gearbeitet habe, verwenden die Shift-Schreibweise.
(Und damit gilt natürlich auch meine Verallgemeinerung bzgl 90% nicht 
mehr).

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Reiner D. schrieb:
> Ist das nicht ein wenig Kindergarten (oder Arduino ;-) ?

Die Gründe wurden dir genannt.
Wenn du sie nicht einsehen möchtest, ist das dein Problem.

Mit Arduino hat das nichts zu tun, dort wird meist ein anderes Pin 
Benennungsschemata genutzt.

von Oliver S. (oliverso)


Lesenswert?

Reiner D. schrieb:
> Also Antwort : damit auch absolute Anfänger den Code besser lesen
> können.

Nein. Damit jeder, vom Anfänger bis zum Profi, den Code schneller und 
besser lesen kann.

Im Falle der Port-Register mag das auch noch ohne gehen, aber bei allen 
anderen Registern kann niemand mit Magic Numbers etwas anfangen, dagegen 
mit den Bitnamen sehr wohl.

Oliver

: Bearbeitet durch User
von Andreas B. (bitverdreher)


Lesenswert?

Reiner D. schrieb:
> Ist das nicht ein wenig Kindergarten (oder Arduino ;-) ?

Nö, das funktioniert ohne Shift nur deshalb so gut weil die meisten AVR 
Registerbits die Bitnummer im Namen haben.
Jetzt willst Du aber mal z.B. ein Bit maskieren, das nicht die Bitnummer 
im  Namen hat. Dann fängst Du erstmal mit dem Datenblatt an....
Also machen wir es doch lieber einheitlich.

von Wilhelm M. (wimalopaan)


Lesenswert?

Reiner D. schrieb:
> Ist das nicht ein wenig Kindergarten (oder Arduino ;-) ?

Absolut! Weil die Leute nicht in der Lage sind, geeignete Abstraktionen 
einzuführen, wie etwa
1
status_led.on();
2
3
status_led::set<on>(); 
4
5
set(status_led, on);
6
7
const bool status = button::isPressed();

von Reiner D. (dollreiner)


Lesenswert?

Das nehm' ich euch nicht ab, daß ihr :

test = 40

nicht auf den ersten Blick lesen könnt. Ihr seid doch Profis hier.
Und wenn ich das Datenblatt z.B. von einem AVR lese, stehen die 
Bitpositionen aller Register ja sogar grafisch dargestellt drin, also 
"so what" ?



btw : ich habs mal so oder ähnlich gelernt (lesbarkeit..)  :

test = test | 40 ;         // test = test ODER 2^5 ODER 2^3


Ok, ich danke allen für die Beiträge, mitgenommen habe ich, daß die 
Shiftschreibweise eine Konvention ist, die vermutlich eine bessere 
Lesbarkeit erzeugen soll. Meinem "Publikum" gebe ich ggf. dann noch mit, 
daß 2-er Potenzen und Bitmaskierung unverzichtbare Grundlagen 
darstellen, wenn ich merke daß irgend jemand test = 40 nicht lesen kann 
;-)

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Reiner D. schrieb:
> btw : ich habs mal so oder ähnlich gelernt (lesbarkeit..)  :
>
> test = test | 40 ;         // test = test ODER 2^5 ODER 2^3

Und dazu schreibst Du dann einen Kommentar?

Was macht denn
1
test &= 39653678;

Ok, Zeit abgelaufen ...

von Reiner D. (dollreiner)


Lesenswert?

> test &= 39653678;

grins..

von Daniel F. (df311)


Lesenswert?

Reiner D. schrieb:
> Daniel F. schrieb:
>> ah verdammt, welcher Pin wird mit 8 gesetzt? ist das jetzt die
>> überarbeitete Generation oder noch die alte?
>
> Meinst du mit "Generation" die Menschen ?

na, eine hypothetische überarbeitete Generation des Prozessors, bei dem 
halt aus irgend einem Grund die Register anders belegt sind als bei der 
alten. Gleiche Eigenschaften, aber halt an einem anderen Bit (z.B. 4) 
wegen irgendeinem Padding oder so.
Ist ja wurscht, es geht ums Prinzip - PINB3 ist ein definierte Offset, 
den man ggf. an einer Stelle ändert und alle Vorkommen im Code passen 
sich automatisch an. Ohne dass man nach Vorkommen von "8" suchen muss 
und diese dann einzeln prüfen ob da jetzt was anderes hin kommt oder 
nicht.

von Reiner D. (dollreiner)


Lesenswert?

Punkt für dich.

(Wenn ein Prozessorhersteller wirklich das Kriterium 
Abwärtskompatibilität
so sträflich verletzen würde, aber ok ..)

von Christian M. (christian_m280)


Lesenswert?

F. M. schrieb:
> PORTB.OUTSET = PIN3_bm;

Ah ich kenn das nur so! Aber ich meide auch dieses bescheuerte C. Jetzt 
kommen wieder die Negativer...

Gruss Chregu

von Falk B. (falk)


Lesenswert?

Rahul D. schrieb:
> (Und damit gilt natürlich auch meine Verallgemeinerung bzgl 90% nicht
> mehr).

Verallgemeinerungen sind immer falsch! ;-)

von Reiner D. (dollreiner)


Lesenswert?

Mir ist sogar ein Beispiel eingefallen : Siemens-SPS mit dem 
Durcheinander von MSBF und LSBF .. das könnte sich z.b. ändern. Also wie 
gesagt : Punkt !

von J. T. (chaoskind)


Lesenswert?

Arduino F. schrieb:
> Mit Arduino hat das nichts zu tun, dort wird meist ein anderes Pin
> Benennungsschemata genutzt.

Um noch n bischn Kindergarten nachzureichen :D

Ein Schema, viele Schemata. Genau wie ein Praktikum, mehrere Praktika.

/trolloff

von Falk B. (falk)


Lesenswert?

Reiner D. schrieb:
> Das nehm' ich euch nicht ab, daß ihr :
>
> test = 40
>
> nicht auf den ersten Blick lesen könnt.

Jain. Ich könnte, WILL es aber nicht!

> Ihr seid doch Profis hier.

Aber keine Binärfreaks oder Hex-Freaks wie der gute Josef G.

> Und wenn ich das Datenblatt z.B. von einem AVR lese, stehen die
> Bitpositionen aller Register ja sogar grafisch dargestellt drin, also
> "so what" ?

Und die willst du ALLE im Kopf behalten? Viel Spaß!
Diese Information ist nur INDIREKT wichtig, so wie ein Telefonbuch! 
DIREKT interessiert der BitNAME und dessen Funktion!
Du kannst auch gern die Namen der Register weglassen und direkt die 
Adressen ansprechen. Viel Spaß!

> Ok, ich danke allen für die Beiträge, mitgenommen habe ich, daß die
> Shiftschreibweise eine Konvention ist, die vermutlich eine bessere
> Lesbarkeit erzeugen soll.

Das tut sie auch!

> Meinem "Publikum" gebe ich ggf. dann noch mit,
> daß 2-er Potenzen und Bitmaskierung unverzichtbare Grundlagen
> darstellen, wenn ich merke daß irgend jemand test = 40 nicht lesen kann
> ;-)

Programierer wollen sich, nicht erst heute, nicht mit so einem 
Krümelkram befassen, der DEUTLICH besser vom Compiler verwaltet wird!

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Reiner D. schrieb:
> Ist das nicht ein wenig Kindergarten
Das, was du da veranstaltest, erinnert mich schon ein wenig daran. Wenn 
andere das anders schreiben wollen als du, was ist denn daran 
grundsätzlich schlecht?

Reiner D. schrieb:
> Meinem "Publikum" gebe ich ggf. dann noch mit, daß 2-er Potenzen und
> Bitmaskierung unverzichtbare Grundlagen darstellen, wenn ich merke daß
> irgend jemand test = 40 nicht lesen kann ;-)
Wenn schon einzelne Bits gemeint sind, dann gibt man den Wert nicht 
dezimal mit 40 an, sondern hexadezumal mit 0x28. Da sieht man das 
Bitmuster sofort. Man muss aber trotzdem im Datenblatt nachschaeun, 
welche Funktionen mit den Bits denn nun tatsächlich gesetzt werden. Und 
mit
test = (1<<ABC) | (1<<XYZ); ist softort erkennbar, dass die Funktionen 
ABC und XYZ gemeint sind.

> ich habs mal so oder ähnlich gelernt (lesbarkeit..)  :
> test = test | 40 ;         // test = test ODER 2^5 ODER 2^3
Wofür soll das gut sein? Da steht einfach nur die Codezeile im Kommentar 
in anderen Worten nochmal da. Und sobald etwas geändert wird, ist der 
Kommentar automatisch falsch oder er muss überarbeitet werden. Wenn ich 
aber
test = (1<<ABC) | (1<<XYZ);
abändere in
test = (1<<ABC) | (1<<DEF);
dann muss ich keinen Kommentar ändern und der Code ist immer noch 
leserlich und quasi selbstkommentierend.

: Bearbeitet durch Moderator
von Falk B. (falk)


Lesenswert?

J. T. schrieb:
> Ein Schema, viele Schemata. Genau wie ein Praktikum, mehrere Praktika.

Spaghetto, Spaghetti
Visum, Visa

von Falk B. (falk)


Lesenswert?

Lothar M. schrieb:
> Reiner D. schrieb:
>> Meinem "Publikum" gebe ich ggf. dann noch mit, daß 2-er Potenzen und
>> Bitmaskierung unverzichtbare Grundlagen darstellen, wenn ich merke daß
>> irgend jemand test = 40 nicht lesen kann ;-)
> Wenn schon, dann nicht dezimal, sondern hex 0x28. Da sieht man das
> Bitmuster sofort.

Aber auch nur, wenn es ein Bit pro Nibble ist. Was ist 0xCA? Jaja, man 
kann auch die 16 Nibble auswendig lernen und lesen, ist aber nerviger 
Unsinn.

> dann muss ich keinen Kommentar ändern und der Code ist immer noch
> leserlich und quasi selbstkommentierend.

BINGO!

von Oliver S. (oliverso)


Lesenswert?

Reiner D. schrieb:
> Ok, ich danke allen für die Beiträge, mitgenommen habe ich, daß die
> Shiftschreibweise eine Konvention ist, die vermutlich eine bessere
> Lesbarkeit erzeugen soll. Meinem "Publikum" gebe ich ggf. dann noch mit,
> daß 2-er Potenzen und Bitmaskierung unverzichtbare Grundlagen
> darstellen, wenn ich merke daß irgend jemand test = 40 nicht lesen kann
> ;-)

Ich hoffe mal stark, das dein Publikum keine Schüler sind.

Um es mal für dich etwas deutlicher darzustellen:

UCSR0B |= (1<<TXEN0);
UCSR0B |= 8;

Macht beides das gleiche. Der absolute Anfänger kann mit beiden Zeilen 
nichts anfangen. Jemand mit etwas Erfahrung sieht in der ersten Zeile 
sofort, was passiert, in der zweiten aber nicht.

Das hat überhaupt nichts damit zu tun, ob man Zweierpotenzen im Kopf 
ausrechnen kann oder nicht.

Oliver

von Falk B. (falk)


Lesenswert?

Oliver S. schrieb:
> Das hat überhaupt nichts damit zu tun, ob man Zweierpotenzen im Kopf
> ausrechnen kann oder nicht.

Eben. Man will die Codierung der Bits dort lassen wo sie hingehört. Im 
Compiler. Menschen können mit Namen deutlich mehr anfangen als Nummern. 
TXEN sagt mehr als 8, erst recht wenn es von diesen Codes tausende gibt!

von Klaus H. (hildek)


Lesenswert?

Reiner D. schrieb:
> Also Antwort : damit auch absolute Anfänger den Code besser lesen
> können.

Nöö.
Weil es jeder besser lesen kann.
Weil sofort klar ist, was man beabsichtigt.
Weil man kein Masochist ist.
Weil es völlig egal ist, auf welchem Bit der Pin oder z.B. beim 
TIMSK-Register die Bits TOIE0 und TOIE1 gemappt sind. Die beiden 
letzteren liegen z.B. beim Tinyx5 auf Bit 1 und Bit 2. Das interessiert 
einfach nicht und kann beim nächsten Prozessortyp anders festgelegt 
sein. Und schon muss dein Code intensiv durchforstet und mit dem 
Datenblatt abgeglichen werden, und meiner nicht.
Selbst bei den Ports könnte das sein. Mich interessiert doch, was TOIE0 
und TOIE1 bewirken und nicht auf welchem Bit sie festgelegt sind. Dafür 
hat jemand einmal die Headerfiles erstellt ...

Und wenn man schon direkt Zahlen angeben will, dann wenigstens im 
binären oder gerade noch im hexadezimalen Format, aber ganz bestimmt 
nicht in DEZ!
Klar, man kann schon mal schreiben
1
DDRB = 0; 
2
PORTB = 0xFF;
wenn man alles auf Eingang schalten und die Pullups am Port aktivieren 
will. Aber sonst?

Und wenn man es noch einfacher haben will, dann schaut man die sbit.h 
von Peter D. an. Beitrag "Re: Frage zu Struktur für IO-Port bei AVRs"

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Falk B. schrieb:
> Was ist 0xCA?
1100 1010

Und 0xCA ist garantiert leichter in einzelne Bits umzurechnen als 204.

: Bearbeitet durch Moderator
von Thorsten S. (thosch)


Lesenswert?

Falk B. schrieb:
>> Meinem "Publikum" gebe ich ggf. dann noch mit,
>> daß 2-er Potenzen und Bitmaskierung unverzichtbare Grundlagen
>> darstellen, wenn ich merke daß irgend jemand test = 40 nicht lesen kann
>> ;-)
>
> Programierer wollen sich, nicht erst heute, nicht mit so einem
> Krümelkram befassen, der DEUTLICH besser vom Compiler verwaltet wird!

Nicht nur das, es ist vor allem eine Fehlerquelle weniger!
Wenn ich magic numbers verwende, und etwas ändere, z.B. die LED in einer 
neuen Platinenversion an einem anderen Portbit anschließe, dann muß ich 
mühsam alles durchsuchen und anpassen. Mit der ständigen Gefahr eine 
Stelle zu übersehen oder eine falsche mitzuändern

Mit Portnamen und Pinbezeichnern muß man bei so einer Änderung nur eine 
Stelle in einem Headerfile anpassen.
Außerdem vermeidet man auch gleich im Ansatz die Fehlerquelle, daß 
Kommentar und magic number auseinanderlaufen könnten, was dann umso 
verwirrender wäre.

von Falk B. (falk)


Lesenswert?

Lothar M. schrieb:
>> Was ist 0xCA?
> 1100 1010

Schon klar. Hast du das im Kopf konvertiert oder mit dem 
Windows-Rechner?

> Und 0xCA ist garantiert leichter in einzelne Bits umzurechnen als 204.

Sicher, aber immer noch deutlich schlechter als die direkten Bitnamen.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Falk B. schrieb:
> aber immer noch deutlich schlechter als die direkten Bitnamen.
Ja nun, es gibt darüber hinaus auch noch Situationen, wo mehrere Bits im 
Verband angesprochen werden und Bitmuster wieder sinnvoll sind (z.B. 
wenn am Port ein Lauflicht dargestellt werden soll).

Ganz ungünstig ist dann, wenn der Programmierer nur diese 
"Bitschieberei" kennt und die Bitreihenfolge z.B. bei einem Multiplexer 
nur partiell oder sogar "verkehrt herum" auscodiert:
1
ADMUX = (1<<ADMUX0)|(1<<ADMUX2);
2
ADMUX = (1<<ADMUX0)|(0<<ADMUX1)|(1<<ADMUX2)|(0<<ADMUX3);
Da kehrt sich dieser "selbstdokumentierende" Vorteil ins Negative, wo es 
doch dezimal ganz leicht zu lesen wäre:
ADMUX = 5;

: Bearbeitet durch Moderator
von Daniel S. (supernova01)


Lesenswert?

J. T. schrieb:
> Um noch n bischn Kindergarten nachzureichen :D

Geht auch anders herum. Einzahl von Spaghetti ...

Ich würde die genutzten Pins und Register eh immer alle mit defines 
ersetzen:

#define HC595_PIN_CLK       PIN3
#define HC595_PIN_CLK_PORT  PORTB
#define HC595_PIN_CLK_DIR   DDRB

#define HC595_PIN_CLK_INIT  HC595_PIN_CLK_DIR  |=  (1<<HC595_PIN_CLK)
#define HC595_PIN_CLK_HIGH  HC595_PIN_CLK_PORT |=  (1<<HC595_PIN_CLK)
#define HC595_PIN_CLK_LOW   HC595_PIN_CLK_PORT &=~ (1<<HC595_PIN_CLK)

So hat man immer schnell alle Möglichkeiten den Code für alles 
anzupassen.

Man kann das natürlich noch durch Makros ausbauen.

Klar hat man viele Defines, muss man mögen

#define PIN_SET(pinnr, port)    port|=(1<<pinnr)
#define PIN_RESET(pinnr, port)  port&= ~(1<<pinnr)

#define HC595_PIN_CLK_HIGH  PIN_SET(HC595_PIN_CLK,HC595_PIN_CLK_PORT)
#define HC595_PIN_CLK_LOW   PIN_RESET(HC595_PIN_CLK,HC595_PIN_CLK_PORT)

Man kann das ja wirklich bis ins unendliche treiben:

#define ATM_PIN_ON    |=
#define ATM_PIN_ON    &=~

#define PIN_SET(pinnr, port)    port ATM_PIN_ON (1<<pinnr)
#define PIN_RESET(pinnr, port)  port ATM_PIN_OFF (1<<pinnr)

#define HC595_PIN_CLK_HIGH  PIN_SET(HC595_PIN_CLK,HC595_PIN_CLK_PORT)
#define HC595_PIN_CLK_LOW   PIN_RESET(HC595_PIN_CLK,HC595_PIN_CLK_PORT)

Gibt ja so viele Möglichkeiten. Ich finde es immer ganz gut wenn jeder 
Pin eindeutig zu finden ist, und zu jeden Pin gehören die entsprechenden 
Register...

DS

: Bearbeitet durch User
von Thorsten S. (thosch)


Lesenswert?

Die 4-Bit Binär vs. Hex-Darstellung eines Nibbles dürften die meisten 
Entwickler genauso einfach im Kopf parat haben, wie sie z.B. 
Widerstands-Beschriftungen, seien es Farbringe oder Ziffern auf SMDs, 
nicht mehr "buchstabieren", sondern auf einen Blick als Wort lesen.

Solche Fähigkeit hilft ungemein beim Debuggen, wenn man auf die Schnelle 
Registerinhalte aufschlüsseln möchte.
Dennoch käme ich nie auf die Idee magic numbers in meinen Programmen zu 
verwenden, ganz gleich in welcher Programmiersprache auch immer.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Lothar M. schrieb:
> dann muss ich keinen Kommentar ändern und der Code ist immer noch
> leserlich und quasi selbstkommentierend.

Selbst ein:
test = (1<<ABC) | (1<<XYZ) | (0<<DEF);
macht unter dem Aspekt Sinn.
Wenn es auch auf den ersten Blick überflüssig erscheint.

Reiner D. schrieb:
> test = test | 40 ;         // test = test ODER 2^5 ODER 2^3

Grundregel:
Der Code sagt klar und deutlich "was getan wird".
Der Kommentar sagt "warum es so getan wird"

Dein Beispiel ist ein klarer Verstoß dagegen.

Thorsten S. schrieb:
> Dennoch käme ich nie auf die Idee magic numbers in meinen Programmen zu
> verwenden, ganz gleich in welcher Programmiersprache auch immer.

Grundsätzlich: Zustimmung!

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

:
> Warum schreiben die Meisten in AVR-C :
>
> testbyte = PINB & (1 << PINB3)
>


Du kannst ja stattdessen auch
1
testbyte = PINB & (1 << PIND3)

oder
1
testbyte = PINB & (1 << PCINT0_vect_num)

schreiben.

Das ist zwar semantisch Blödsinn, funktioniert aber trotzdem.

Oder
1
testbyte = PINB * 3;

Auch das ist Blödsinn, der Compiler hindert Dich aber nicht daran.

Das dieser Mist möglich ist (sprich: nicht zur Compilezeit aufgedeckt
werden kann), liegt daran, dass hier Typsicherheit fehlt.

PINB ist leider als `volatile uint8_t` definiert. Und `(1<<PINB3)`
hat den Typ `int`. Und ist damit implizit konvertierbar. Weiterhin sind
sowohl `uint8_t` als auch `int` arithmetische Typen. Damit ist also
etwa auch `*` oder `/` dafür definiert, was bei der hier angesprochenen
Verwendung natürlich keinen Sinn macht.

PINB oder DDRB oder PORTB.OUTSET ist erstmal nur eine Ansammlung
von Bits, sonst nichts. Solche Bitmuster möchte man gerne mal schieben,
aber eher selten als Ganzzahl interpretieren. Und wenn doch, dann sollte
der Code das klar aussagen.

AUßerdem ist natürlich auch
1
TCCR0A = (1 << CS00)

falsch und sollte deswegen gar nicht erst compilieren.

Leider kannst Du das mit C nicht erreichen.

Wobei natürlich
das von Dir oben angesprochene "Arduino"-Kindergartenzeug C++ ist. Und
in C++ ist das recht elegant möglich.

von Rahul D. (rahul)


Lesenswert?

Falk B. schrieb:
> Verallgemeinerungen sind immer falsch! ;-)

Das ist mein Spruch! ;)

Klaus H. schrieb:
> Und wenn man schon direkt Zahlen angeben will, dann wenigstens im
> binären oder gerade noch im hexadezimalen Format, aber ganz bestimmt
> nicht in DEZ!

Bei Timer-Registern, die eine Anzahl angeben, ist eine Dezimalangabe 
schon sinnvoll.

Lothar M. schrieb:
> Da kehrt sich dieser "selbstdokumentierende" Vorteil ins Negative, wo es
> doch dezimal ganz leicht zu lesen wäre:
> ADMUX = 5;

Für jede Ausgangseinstellung ein Makro definieren?!

von Falk B. (falk)


Lesenswert?

Wilhelm M. schrieb:
> AUßerdem ist natürlich auch
> TCCR0A = (1 << CS00)
>
> falsch und sollte deswegen gar nicht erst compilieren.
>
> Leider kannst Du das mit C nicht erreichen.

Das ist falsch. Wenn man die Register als struct/union mit Bitfeldern 
anlegt, geht das sehr wohl.

Beitrag "Re: XC8 compiler - Register schreiben wie bei Atmel"

> das von Dir oben angesprochene "Arduino"-Kindergartenzeug C++ ist. Und
> in C++ ist das recht elegant möglich.

Geht auch im ollen C.

von Falk B. (falk)


Lesenswert?

Rahul D. schrieb:
> Klaus H. schrieb:
>> Und wenn man schon direkt Zahlen angeben will, dann wenigstens im
>> binären oder gerade noch im hexadezimalen Format, aber ganz bestimmt
>> nicht in DEZ!
>
> Bei Timer-Registern, die eine Anzahl angeben, ist eine Dezimalangabe
> schon sinnvoll.

Sicher, das ist aber eine Minderheit.

> Lothar M. schrieb:
>> Da kehrt sich dieser "selbstdokumentierende" Vorteil ins Negative, wo es
>> doch dezimal ganz leicht zu lesen wäre:
>> ADMUX = 5;
>
> Für jede Ausgangseinstellung ein Makro definieren?!

Nein

von Wilhelm M. (wimalopaan)


Lesenswert?

Falk B. schrieb:
> Wilhelm M. schrieb:
>> AUßerdem ist natürlich auch
>> TCCR0A = (1 << CS00)
>>
>> falsch und sollte deswegen gar nicht erst compilieren.
>>
>> Leider kannst Du das mit C nicht erreichen.
>
> Das ist falsch. Wenn man die Register als struct/union mit Bitfeldern
> anlegt, geht das sehr wohl.

Abgesehen davon, dass bitfields stark implementation-defined sind,
mach mal bitte ein Beispiel mit folgendem fiktivem Register:
1
struct R1 {
2
    uint8_t a:1;
3
    uint8_t b:2;
4
    uint8_t c:3;
5
    uint8_t d:4;
6
};

von Falk B. (falk)


Lesenswert?

Wilhelm M. schrieb:
> Abgesehen davon, dass bitfields stark implementation-defined sind,

Sind sie, kann man aber trotzdem machen. Wir auch so gemacht.

> mach mal bitte ein Beispiel mit folgendem fiktivem Register:

Hab ich dir schon gezeigt. Folge meinem Link, dort ist ein Dokument von 
TI, wie sie es im Code Composer Studio machen, zumindest für die C2000er 
Serie.

von MaWin O. (mawin_original)


Lesenswert?

Das ist wieder mal ein sehr schönes triviales Thema, zu dem jeder seine 
eigene ganz feste und ganz richtige Meinung haben kann.

Wenn du lieber 8 statt 1<<3 schreibst und das für genau so lesbar 
hältst, dann mache es halt so.
Ich mache es nicht so, denn ich halte es für schwer verständlich das 
dritte Bit mit 8 zu beschreiben, obwohl es natürlich vollkommen richtig 
ist.

Vieeeeel viel wichtiger als Neulingen einen ganz bestimmten "richtigen" 
Stil beizubringen, ist zu vermitteln, dass ein konsistenter Stil viel 
wichtiger ist!
Wenn ich eine Änderung in einem Stück Code mache, wo Masken alle als 
Zahl ohne Shift dargestellt sind, dann halte ich mich daran und füge 
neue Masken auch als Zahl ohne Shift ein. Konsistenz ist viel wichtiger 
als irgendeine Stil-Religion, weil Konsistenz die mentale Last absenkt. 
Darunter fallen dann auch so Dinge wie Formatierungsstil.

von Wilhelm M. (wimalopaan)


Lesenswert?

Falk B. schrieb:
> Wilhelm M. schrieb:
>> Abgesehen davon, dass bitfields stark implementation-defined sind,
>
> Sind sie, kann man aber trotzdem machen. Wir auch so gemacht.
>
>> mach mal bitte ein Beispiel mit folgendem fiktivem Register:
>
> Hab ich dir schon gezeigt. Folge meinem Link, dort ist ein Dokument von
> TI, wie sie es im Code Composer Studio machen, zumindest für die C2000er
> Serie.

Was Du meinst ist folgendes, oder?
1
#include <stdint.h>
2
3
enum E1 {a, b, c, d};
4
enum E2 {e, f, g, h};
5
6
struct R1 {
7
    uint8_t a:1;
8
    enum E1 b:2;
9
    uint8_t c:3;
10
    uint8_t d:4;
11
};
12
struct R2 {
13
    uint8_t w:4;
14
    enum E2 x:3;
15
    uint8_t y:2;
16
    uint8_t z:1;
17
};
18
19
struct R1 r1;
20
struct R2 r2;
21
22
int main(void) {
23
    r1.b = e; // Ups
24
    r2.x = a;
25
}

von Falk B. (falk)


Lesenswert?

Wilhelm M. schrieb:
> Was Du meinst ist folgendes, oder?

Enums werden dabei nicht verwendet. Die Bits bzw. Bitgruppen werden per 
Name angesprochen. Damit kann man nicht die falschen Bits im falschen 
Register ansprechen.

von DSGV-Violator (Gast)


Lesenswert?

Och nööööö, das haben wir doch erst im Februar durchgekaut:
Beitrag "Re: Bits in Register setzten (Schreibweise)"

von Falk B. (falk)


Lesenswert?

Beitrag "Re: XC8 compiler - Register schreiben wie bei Atmel"
https://www.mikrocontroller.net/attachment/289053/spraa85d_Programming_TMS320xx280xx_C_C__.pdf

Seite 11
1
/********************************************************************
2
* User's source file
3
********************************************************************/
4
// Access registers without a bit field definition (.all, .bit not used)
5
SciaRegs.SCIHBAUD = 0;
6
SciaRegs.SCILBAUD = 1;
7
// Write to bit fields in SCI-A SCICTL1
8
SciaRegs.SCICTL1.bit.SWRESET = 0;
9
SciaRegs.SCICTL1.bit.SWRESET = 1;
10
SciaRegs.SCIFFCT.bit.ABDCLR = 1;
11
SciaRegs.SCIFFCT.bit.CDC = 1;
12
// Poll (i.e., read) a bit
13
while(SciaRegs.SCIFFCT.bit.CDC == 1) { }
14
// Write to the whole SCI-B SCICTL1/2 registers (use .all)
15
ScibRegs.SCICTL1.all = 0x0003;
16
ScibRegs.SCICTL2.all = 0x0000;

von Wilhelm M. (wimalopaan)


Lesenswert?

Falk B. schrieb:
> Wilhelm M. schrieb:
>> Was Du meinst ist folgendes, oder?
>
> Enums werden dabei nicht verwendet. Die Bits bzw. Bitgruppen werden per
> Name angesprochen. Damit kann man nicht die falschen Bits im falschen
> Register ansprechen.

Ok,geht also nicht in C

von Dergute W. (derguteweka)


Lesenswert?

Falk B. schrieb:
> J. T. schrieb:
>> Ein Schema, viele Schemata. Genau wie ein Praktikum, mehrere Praktika.
>
> Spaghetto, Spaghetti
> Visum, Visa

Basilikum, Basilika

scnr,
WK

von Falk B. (falk)


Lesenswert?

Wilhelm M. schrieb:
>> Enums werden dabei nicht verwendet. Die Bits bzw. Bitgruppen werden per
>> Name angesprochen. Damit kann man nicht die falschen Bits im falschen
>> Register ansprechen.
>
> Ok,geht also nicht in C

Das war gar nicht dein Argument! Sondern daß man falsche Bits in 
falschen Registern ansprechen kann!

"AUßerdem ist natürlich auch

TCCR0A = (1 << CS00)

falsch und sollte deswegen gar nicht erst compilieren.

Leider kannst Du das mit C nicht erreichen."

: Bearbeitet durch User
von Thorsten M. (pappkamerad)


Lesenswert?

Reiner D. schrieb:
> Also Antwort : damit auch absolute Anfänger den Code besser lesen
> können.
> Ist halt heutzutage so. Früher haben die Leute erst Digitaltechnik
> gelernt, und dann mit dem Programmieren angefangen.

Eigentlich ist es genau andersrum. Mit einem
PORTB = PORTB & (1 << PORTB3);
kann ein absoluter Anfänger nichts anfangen. er versteht das Shiften 
nicht und weiß auch nicht, was PORTB3 ist. Er weiß nichtmal ob es eine 
Variable, ein Define oder sonst etwas ist. Da ist die Version mit der 8 
einfacher für das Grundverständnis, da weiß man sofort, dass es um eine 
Zahl geht, in dem Beispiel dem Port also ein Wert zugewiesen wird. Das 
Rückrechnen auf den Pin ist relativ intuitiv. Für den Profi ist aber das 
schnelle Lesen und Vermeiden von unnötigem Nachdenken und Verringerung 
der Gefahr etwas falsch zu verstehen so wertvoll, dass es sich für ihn 
lohnt, das 1er-Shiften als Konvention zu lernen und er weiß auch sofort, 
was das PORTB3 wohl beinhaltet.

von DSGV-Violator (Gast)


Lesenswert?

>>> Ein Schema, viele Schemata. Genau wie ein Praktikum, mehrere Praktika.
>>
>> Spaghetto, Spaghetti
>> Visum, Visa
>
> Basilikum, Basilika

Atlas  - Atlanten
Thomas - Thomaten

viele Bauteile von murata - ein Bauteil von mura
ein K                     - viele Kata
ein bla                   - viel blabata

SCNR

von J. S. (jojos)


Lesenswert?

nur sind typsichere C++ Registerdefinitionen nicht Standard bei den 
Hersteller SDK, für einen STM32H7 möchte ich das nicht selber schreiben.
STM hat ja schon ewig gebraucht den 'register' storage specifier zu 
entfernen.

von Daniel S. (supernova01)


Lesenswert?

Wilhelm M. schrieb:
> TCCR0A = (1 << CS00)
>
> falsch und sollte

Falk B. schrieb:
> Geht auch im ollen C.

Nö, abgesehen davon dass es doppelt gemoppelt ist, fehlt da das 
Semikolon.

DS

: Bearbeitet durch User
von MaWin O. (mawin_original)


Lesenswert?

Thorsten M. schrieb:
> er versteht das Shiften nicht

Muss er ja auch gar nicht sofort verstehen. Oder verstehst du immer 
sofort alles?

> und weiß auch nicht, was PORTB3 ist.

Was soll das denn anderes sein als Pin 3 auf PORTB?

von Stefan F. (Gast)


Lesenswert?

Reiner D. schrieb:
> testbyte = PINB & (1 << PINB3)

Ich weiß nicht wo du das her hast. Solche Zeilen würdest du bei mir nur 
in temporärem Code für Experimentierbretter finden, wo z.B. ein Taster 
mit B3 beschriftet ist.

In sinnvollen Programmen würde eher so etwas stehen:

> if PINB & (1<<ENDSCHALTER) {...}

Oder

> UCSR3B = (1<<RXEN3) | (1<<TXEN3) | (1 << RXCIE3);

Da sieht man an den Namen der Bits ungefähr, worum es in der Zeile geht.

Reiner D. schrieb:
> Wenn ich schon weiß, daß das ein shift-Befehl ist, dann werd ich wohl
> auch wissen, was 2^3 ist, oder ?

Wetten, dass du das mit 32 Bit Registern nicht so einfach im Kopf 
hinbekommst? Ich vermute stark, dass du dir die Kopf-Rechnerei bei der 
Programmierung von ARM Controllern ganz schnell abgewöhnst.

von Klaus S. (kseege)


Lesenswert?

Reiner D. schrieb:
> Warum schreiben die Meisten in AVR-C :
> testbyte = PINB & (1 << PINB3)

Bisher hatte ich mir immer eingebildet, das käme der Portabilität 
zugute. Bei Pin3 von PortB ists wohl nicht so notwendig, aber bei
UART0CONTROL = UART0CONTROL | (1<<TXEnable)
erlaubt es dem Hersteller, das Bit mal an einer anderen Stelle zu 
plazieren und in einem geänderten Headerfile anders anzugeben. Der 
Programmierer muß dann nichts ändern, das Headerfile richtets.

Aber nachdem hier über 50 Posts nur über Schönheit diskutieren, ist 
meine Sicht der Dinge wohl ziemlich abartig. Beauty rules!

Gruß Klaus (der soundsovielte)

von Falk B. (falk)


Lesenswert?

Stefan F. schrieb:
>> testbyte = PINB & (1 << PINB3)
>
> Ich weiß nicht wo du das her hast. Solche Zeilen würdest du bei mir nur
> in temporärem Code für Experimentierbretter finden, wo z.B. ein Taster
> mit B3 beschriftet ist.
>
> In sinnvollen Programmen würde eher so etwas stehen:
>
>> if PINB & (1<<ENDSCHALTER) {...}

Besser so. Mehr Kapselung, mehr Übersicht.
1
#define ENDSCHALTER (PINB & (1<<3))
2
#define LED_WARNING_ON  PORTC |= (1<<2)
3
4
if (ENDSCHALTER) foo(); else LED_WARNING_ON;

: Bearbeitet durch User
von MaWin O. (mawin_original)


Lesenswert?

Falk B. schrieb:
> Mehr Kapselung, mehr Übersicht.

nö.

von Falk B. (falk)


Lesenswert?

MaWin O. schrieb:
>> Mehr Kapselung, mehr Übersicht.
>
> nö.

Doch!

von M. K. (sylaina)


Lesenswert?

Reiner D. schrieb:
> Wenn ich schon weiß, daß das ein shift-Befehl ist, dann werd ich wohl
> auch wissen, was 2^3 ist, oder ?

Ja, aber woher weißt du was die 8 bedeutet? Mit (1 << PB3) weißt du, 
dass das 3. Bit hier gesetzt werden soll. Und das ganze ganz ohne in das 
Datenblatt zu schaun. OK, bei simplen Port-Routinen gehts vielleicht 
auch noch mit den Magic Numbers aber was ist z.B. wenn du denn ADC eines 
Atmegas starten willst? Meinst du nicht, dass ein "ADCSRA |= (1 << 
ADSC);" einfacher zu verstehen ist als ein "ADCSRA |= 64;"? (und ja, die 
64 musste ich jetzt echt im Datenblatt nachlesen)

von MaWin O. (mawin_original)


Lesenswert?

Falk B. schrieb:
> Doch!

Keine Ahnung, wie du darauf kommst, dass mehr Kapselung grundsätzlich zu 
mehr Übersicht führt. Aber das ist doch offensichtlicher Unsinn. Schaue 
dir den Grenzfall an: Wenn ich alles immer weiter rekursiv kapsele, dann 
soll das der Übersicht helfen? Das Gegenteil ist der Fall.

Bei

>if (ENDSCHALTER) foo(); else LED_WARNING_ON;

sehe ich überhaupt nicht mehr, was passiert. Das kann natürlich in 
einigen Fällen hilfreich sein. In anderen aber auch wieder nicht.

Und warum nicht so, wenn Kapselung doch die Übersicht verbessert?

#define REAKTION_AUF_ENDSCHALTER if (ENDSCHALTER) foo(); else 
LED_WARNING_ON

REAKTION_AUF_ENDSCHALTER;

von Wilhelm M. (wimalopaan)


Lesenswert?

Falk B. schrieb:
> Wilhelm M. schrieb:
>> Ok,geht also nicht in C
>
> Das war gar nicht dein Argument! Sondern daß man falsche Bits in
> falschen Registern ansprechen kann!
>
> "AUßerdem ist natürlich auch
>
> TCCR0A = (1 << CS00)
>
> falsch und sollte deswegen gar nicht erst compilieren.
>
> Leider kannst Du das mit C nicht erreichen."

Dieses Beispiel und das mit dem fiktiven Register zeigt genau, was ich 
meine. Und diese Art von Typ-Sicherheit kann man in C eben nicht 
erreichen.

von Wilhelm M. (wimalopaan)


Lesenswert?

J. S. schrieb:
> nur sind typsichere C++ Registerdefinitionen nicht Standard bei den
> Hersteller SDK, für einen STM32H7 möchte ich das nicht selber schreiben.
> STM hat ja schon ewig gebraucht den 'register' storage specifier zu
> entfernen.

Tja, das ist zwar traurig, aber nicht mein Problem.

Sowas macht man mit einem Skript, was die ganzen templates aus den 
XML-Beschreibungen der µC automatisch erzeugt.

von Wilhelm M. (wimalopaan)


Lesenswert?

Stefan F. schrieb:
> Wetten, dass du das mit 32 Bit Registern nicht so einfach im Kopf
> hinbekommst? Ich vermute stark, dass du dir die Kopf-Rechnerei bei der
> Programmierung von ARM Controllern ganz schnell abgewöhnst.

s.a. hier:

Wilhelm M. schrieb:
> Was macht denn
> test &= 39653678;
>
> Ok, Zeit abgelaufen ...

von Dergute W. (derguteweka)


Lesenswert?

Falk B. schrieb:
> MaWin O. schrieb:
>>> Mehr Kapselung, mehr Übersicht.
>>
>> nö.
>
> Doch!

Ohhh!

von Wilhelm M. (wimalopaan)


Lesenswert?

Stefan F. schrieb:
> Oder
>
>> UCSR3B = (1<<RXEN3) | (1<<TXEN3) | (1 << RXCIE3);
>
> Da sieht man an den Namen der Bits ungefähr, worum es in der Zeile geht.

Es geht dabei zwar auch um Lesbarkeit, aber auch um Sicherheit. Ich 
möchte einfach, dass ein Unsinn wie
1
UCSR3A = (1<<RXEN3) | (1<<TXEN3) | (1 << RXCIE3);

oder
1
TCCR0A = (1 << CS00);

nicht kompiliert.
Denn Programme, die deswegen nicht kompilieren, sind gute Programme ;-)

von Wilhelm M. (wimalopaan)


Lesenswert?

Dergute W. schrieb:
> Falk B. schrieb:
>> MaWin O. schrieb:
>>>> Mehr Kapselung, mehr Übersicht.
>>>
>>> nö.
>>
>> Doch!
>
> Ohhh!

Leicht abgewandelt, aber auch nett.

von Falk B. (falk)


Lesenswert?

MaWin O. schrieb:
> Falk B. schrieb:
>> Doch!
>
> Keine Ahnung, wie du darauf kommst, dass mehr Kapselung grundsätzlich zu
> mehr Übersicht führt. Aber das ist doch offensichtlicher Unsinn. Schaue
> dir den Grenzfall an: Wenn ich alles immer weiter rekursiv kapsele,

Wo soll das Rekursion sein? Das sind einfahe Abstraktionsebenen, auch 
HAL genannt (Hardware Abstraction Layer).

> dann
> soll das der Übersicht helfen? Das Gegenteil ist der Fall.
>
> Bei
>
>>if (ENDSCHALTER) foo(); else LED_WARNING_ON;
>
> sehe ich überhaupt nicht mehr, was passiert.

Das WILL ich in den meisten Fällen auch gar nicht! Bei der Anwendung von 
Sensoren und Aktoren will ich mich um die Hardwaredetails NICHT mehr 
kümmern. Das mache ich einmalig, wenn ich die HAL aufschreibe. Ebenso in 
Funktionen. Wie die INTERN arbeiten, intertessiert beim AUFRUF/Anwendung 
NICHT! Im Gegenteil.

> Das kann natürlich in
> einigen Fällen hilfreich sein. In anderen aber auch wieder nicht.
>
> Und warum nicht so, wenn Kapselung doch die Übersicht verbessert?
>
> #define REAKTION_AUF_ENDSCHALTER if (ENDSCHALTER) foo(); else
> LED_WARNING_ON
>
> REAKTION_AUF_ENDSCHALTER;

Man kann alles übertreiben. Du bist ein Meister darin.
Wasser ist lebensnotwendig. 10l auf einmal "getrunken" tödlich, aka 
Ertrinken (oder Wasservergiftung).

Ich bin ja kein Softwerker, aber soviel hab selbst ich Hardwaredepp mal 
vor vielen Jahren im Studium gelernt.

von Ron-Hardy G. (ron-hardy)


Lesenswert?

Dergute W. schrieb:
> Falk B. schrieb:
>> MaWin O. schrieb:
>>>> Mehr Kapselung, mehr Übersicht.
>>>
>>> nö.
>>
>> Doch!
>
> Ohhh!
wer's nicht kennt...
https://www.youtube.com/watch?v=OL8Eh2XLp80

von Falk B. (falk)


Lesenswert?

Wilhelm M. schrieb:
> Dieses Beispiel und das mit dem fiktiven Register zeigt genau, was ich
> meine. Und diese Art von Typ-Sicherheit kann man in C eben nicht
> erreichen.

Man kommt nahe genug ran, auch wenn es nicht der reinen Lehre 
entspricht. Sieg der Praxis über die Theorie.

von Wilhelm M. (wimalopaan)


Lesenswert?

Falk B. schrieb:
> Wilhelm M. schrieb:
>> Dieses Beispiel und das mit dem fiktiven Register zeigt genau, was ich
>> meine. Und diese Art von Typ-Sicherheit kann man in C eben nicht
>> erreichen.
>
> Man kommt nahe genug ran, auch wenn es nicht der reinen Lehre
> entspricht.

Dann stimmst Du mir ja endlich zu, dass es nicht wirklich geht, sondern 
nur so ungefähr ;-)

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
>> Ok, Zeit abgelaufen ...

Da habe ich auch noch einen kleine "Test" versteckt, den aber wegen der 
aufwendigen Rechnerei schon bei 8 Bit ebenfalls keiner gesehen hat, als

ich schrieb:
> Und 0xCA ist garantiert leichter in einzelne Bits umzurechnen als 204.
204 ist nämlich nicht 0xCA, sondern 0xCC. Das sieht man aber eben ohne 
viel Umrechnerei auf keinen Fall. Deshalb nimmt man für Bitmuster (wenn 
sie schon als "Magic Number" hingeschrieben werden sollen) immer 
Hex-Zahlen.

von Falk B. (falk)


Lesenswert?

Wilhelm M. schrieb:
> Dann stimmst Du mir ja endlich zu, dass es nicht wirklich geht, sondern
> nur so ungefähr ;-)

JA, drucks dir aus und häng es an die Wand! ;-)

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Lothar M. schrieb:
> Deshalb nimmt man für Bitmuster (wenn
> sie schon als "Magic Number" hingeschrieben werden sollen) immer
> Hex-Zahlen.

Oder man nimmt Bitmuster, wenn man Bitmuster verwenden will!
0b0100111

von MaWin O. (mawin_original)


Lesenswert?

Arduino F. schrieb:
> Oder man nimmt Bitmuster, wenn man Bitmuster verwenden will!

Ja. Super praktisch. Besonders für 32-Bit-Maschinen!
1
REG |= 0b00000000000010000000000000000000;

Gibt kaum was Lesbareres! Man sieht sofort auf einen Blick, welches Bit 
gesetzt wird!

von Stefan F. (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Ich möchte einfach, dass ein Unsinn wie
> ... nicht kompiliert.
Geht leider nicht. In den Headern von STM32 werden Fehler allerdings 
recht wirksam vermieden, indem die Namen der Bit-Konstanten mit dem 
Namen des Register beginnen.

von Falk B. (falk)


Lesenswert?

Stefan F. schrieb:
> indem die Namen der Bit-Konstanten mit dem
> Namen des Register beginnen.

Was den Quältext schon arg aufbläht und geschwätzig macht. Naja.

von Wilhelm M. (wimalopaan)


Lesenswert?

Stefan F. schrieb:
> Wilhelm M. schrieb:
>> Ich möchte einfach, dass ein Unsinn wie
>> ... nicht kompiliert.
> Geht leider nicht.

Das gilt eben nur für C, in C++ geht es.

von MaWin O. (mawin_original)


Lesenswert?

Wilhelm M. schrieb:
> Das gilt eben nur für C, in C++ geht es.

Die Leute mögen aus irgendwelchen nicht nachvollziehbaren Gründen gerne 
die Schmerzen dieser antiquierten Sprache.

von Stefan F. (Gast)


Lesenswert?

Lothar M. schrieb:
> 204 ist nämlich nicht 0xCA, sondern 0xCC. Das sieht man aber eben ohne
> viel Umrechnerei auf keinen Fall. Deshalb nimmt man für Bitmuster (wenn
> sie schon als "Magic Number" hingeschrieben werden sollen) immer
> Hex-Zahlen.

Wenn du Binärzahlen gefordert hättest, ergäbe der Satz sehr viel mehr 
Sinn.

von Stefan F. (Gast)


Lesenswert?

MaWin O. schrieb:
> REG |= 0b00000000000010000000000000000000;
>
> Gibt kaum was Lesbareres! Man sieht sofort auf einen Blick, welches Bit
> gesetzt wird!

Hier wäre der pöhse Shift Operator hilfreich.

von MaWin O. (mawin_original)


Lesenswert?

Stefan F. schrieb:
> Hier wäre der pöhse Shift Operator hilfreich.

Teufelszeug! Wer soll das denn bitte verstehen? Was, wenn der Anfänger 
nicht weiß, was shiften ist?

von Thorsten M. (pappkamerad)


Lesenswert?

MaWin O. schrieb:
> Thorsten M. schrieb:
>> er versteht das Shiften nicht
>
> Muss er ja auch gar nicht sofort verstehen. Oder verstehst du immer
> sofort alles?

Als Anfänger sieht man den Wald vor lauter Bäumen nicht, da ist soetwas 
nicht hilfreich. Mir scheint die Meisten hier haben überhaupt keine 
Ahnung mehr, wieviel "Beiwissen" für das Verständnis von Programmcode 
erforderlich ist.
Siehe:

>> und weiß auch nicht, was PORTB3 ist.
>
> Was soll das denn anderes sein als Pin 3 auf PORTB?

Ein Anfänger versteht vielleicht, dass mit Pin 3 irgendwas gemacht 
werden soll, aber nicht, dass der Mechanismus dahinter einfach ein 
zugewiesener Zahlenwert ist.

von Stefan F. (Gast)


Lesenswert?

Ist eigentlich ganz einfach. Schreibe die Zahl in einem gut lesbaren 
Format hin, wenn es um die Zahl geht.

Aber wenn eine bestimmte Funktion gemeint ist, dann nenne sie beim 
Namen.

3 ist keine gute Bezeichnung für den Endschalter. Und 4 ist keine gute 
Bezeichnung für den Status des Empfangspuffers. Ich will nicht wissen 
was mit der 4 los ist, sondern was mit dem Empfangspuffer ist. Also 
schreibe ich das auch so im menschen-lesbarer Form hin. Das ist 
schließlich der Sinn jeder Programmiersprache.

von MaWin O. (mawin_original)


Lesenswert?

Thorsten M. schrieb:
> Mir scheint die Meisten hier haben überhaupt keine
> Ahnung mehr, wieviel "Beiwissen" für das Verständnis von Programmcode
> erforderlich ist.

Das ist mir sehr wohl bewusst.

Genau so könntest du aber argumentieren, dass das Konzept von Zahlen 
ansich viel zu kompliziert ist. Woher soll der Anfänger das denn bitte 
alles wissen? Es könnte sein, dass er noch in den Kindergarten geht.

Irgendwo muss man halt die Messlatte anlegen.
Und irgendwelche Dinge werden immer gelernt werden müssen.

Ich sehe da auch überhaupt kein Problem, wenn ein Anfänger erst einmal 
eine Woche zu 5 Programmzeilen recherchiert, bis er sie wirklich 
verstanden hat.

> Ein Anfänger versteht vielleicht, dass mit Pin 3 irgendwas gemacht
> werden soll, aber nicht, dass der Mechanismus dahinter einfach ein
> zugewiesener Zahlenwert ist.

Was soll es denn sonst sein? Ein Mettbrötchen?
Ein Rechner kennt nur Zahlen. Deshalb heißt er so. Das sollte doch 
Allgemeinwissen sein.

von DSGV-Violator (Gast)


Angehängte Dateien:

Lesenswert?

Stefan F. schrieb:
> MaWin O. schrieb:
>> REG |= 0b00000000000010000000000000000000;
>>
>> Gibt kaum was Lesbareres! Man sieht sofort auf einen Blick, welches Bit
>> gesetzt wird!
>
> Hier wäre der pöhse Shift Operator hilfreich.

oder man kommentiert dieses machwerk wie es jeder angehender 
Programmierer in der Grundschule gelernt hat.

Alternativ kann man sich mit einem gescheiten editor behelfen der 
mitzählt in welcher Spalte grad der Cursor steht.

von MaWin O. (mawin_original)


Lesenswert?

DSGV-Violator schrieb:
> oder man kommentiert dieses machwerk wie es jeder angehender
> Programmierer in der Grundschule gelernt hat.
>
> Alternativ kann man sich mit einem gescheiten editor behelfen der
> mitzählt in welcher Spalte grad der Cursor steht.

lol, nein.
Einfach nur nein.
nein.

von DSGV-Violator (Gast)


Lesenswert?

MaWin O. schrieb:

>> mitzählt
>
> lol, nein.
> Einfach nur nein.
> nein.

Wer das Zählen nicht ehrt, ist den Computer nicht wert ....

von Jens G. (jensig)


Lesenswert?

Arduino F. schrieb:
> Oder man nimmt Bitmuster, wenn man Bitmuster verwenden will!
> 0b0100111

MaWin O. schrieb:
> Arduino F. schrieb:
>> Oder man nimmt Bitmuster, wenn man Bitmuster verwenden will!
>
> Ja. Super praktisch. Besonders für 32-Bit-Maschinen!
> REG |= 0b00000000000010000000000000000000;
>
> Gibt kaum was Lesbareres! Man sieht sofort auf einen Blick, welches Bit
> gesetzt wird!

Also auf seiner 7bit-Maschine geht das schon noch ...

von Gerhard O. (gerhard_)


Lesenswert?

Jens G. schrieb:
> Arduino F. schrieb:
>> Oder man nimmt Bitmuster, wenn man Bitmuster verwenden will!
>> 0b0100111
>
> MaWin O. schrieb:
>> Arduino F. schrieb:
>>> Oder man nimmt Bitmuster, wenn man Bitmuster verwenden will!
>>
>> Ja. Super praktisch. Besonders für 32-Bit-Maschinen!
>> REG |= 0b00000000000010000000000000000000;
>>
>> Gibt kaum was Lesbareres! Man sieht sofort auf einen Blick, welches Bit
>> gesetzt wird!
>
> Also auf seiner 7bit-Maschine geht das schon noch ...

Da würde ich aber trotzdem HEX vorziehen. Da hat man einen 
Gruppenanhaltspunkt. 0x00080000 ist da leichter als die vielen Nullen zu 
klassifizieren.

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


Lesenswert?

Falk B. schrieb:
> Auf dem avr gcc vielleicht, bei anderen Compilern bzw. CPUs nicht
> unbedingt.

Meine Vermutung ist, dass das damit zusammenhängt, wie die SBI- und 
CBI-Befehle beim AVR gestrickt worden sind: sie benutzen eine Bitnummer. 
Wenn man nun daraus eine Bitmaske machen muss, braucht man den 
Schiebebefehl, auch im Assemblercode.

AVR-C hat das von da geerbt, zumal SBI und CBI anfangs noch nicht vom 
Compiler selbst generiert werden konnten, sondern über inline-Makros 
produziert worden sind.

Der Rest der Welt benutzt schon immer vorzugsweise gleich Bitmasken für 
das Benennen von Bits (kann man prima sehen, wenn man sich irgendwelche 
UNIX-Quellen ansieht).

Hätte man beim AVR natürlich auch haben können: es gibt keinen Grund, 
warum der Assembler nicht hätte in der Lage sein sollen, ein
1
   SBI PORTC, 8

selbst so umzurechnen, dass er im Opcode die Bitnummer 3 generiert (und 
sich über einen Semantikfehler erbricht, wenn jemand versucht, mehr als 
ein Bit im Operanden zu setzen).

Hat man aber nicht. Also schieben wir beim AVR fröhlich die Bitnummern 
herum. :-)

: Bearbeitet durch Moderator
von (prx) A. K. (prx)


Lesenswert?

Daniel F. schrieb:
> außerdem kümmert sich der Präprozessor darum, dass (1 << PINB3) zu 8
> wird

Eine nicht ausrottbare Fehlinformation. Der Präprozessor rechnet nur in 
#if Statements und macht hier aus (1 << PINB3) lediglich (1 << 3). Die 
Rechnung erfolgt im Compiler selbst.

von (prx) A. K. (prx)


Lesenswert?

Über Geschmacksfragen kann man trefflich streiten. Ich auch. ;-)

Ein Charme von sowas wie
   (1 << REGx_CB1)
und auch dem von manchen als absurd angesehenen
   (0 << REGx_CB1)
ist, dass man im Quelltext danach suchen kann.

Ersetzt man die Bitangabe durch eine benannte Maske, spart man zwar 
Schreibarbeit, aber die zweite Variante fällt durch ersatzloses Fehlen 
der Bitbezeichnung auf. Und etwas, was fehlt, widersetzt sich jeder 
Suche im Quelltext. Man findet also jene Stellen, in denen es auf 1 
gesetzt wird, nicht aber jene, in denen es auf 0 gesetzt wird.

Für die Maske empfiehlt sich deshalb also sowas wie
   REGx_CB1_ENABLE
   REGx_CB1_DISABLE
und hat dann halt jedes Bit zweimal im Header.

Bei der Variante, alle Bits eines Registers im Kopf zusammenzurechnen 
und als Ergebnis in Hex, Oktal oder Dezimal in den Quelltext zu werfen, 
empfehle ich ersatzweise Programmierung in INTERCAL. Das ist 
konsequenter. Ist dir das zu doof, nimm APL. Damit habe ich angefangen, 
weiss also, was Write-Only-Code ist.

: Bearbeitet durch User
von Εrnst B. (ernst)


Lesenswert?

Seitenweise Diskussion über Shifts, und noch keiner hat das _BV - Makro 
aus der avr-libc ins Spiel gebracht?

https://www.nongnu.org/avr-libc/user-manual/group__avr__sfr.html
1
PORTB |= _BV(PB3);
2
if (bit_is_set(PINB, PB4)) { ...

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


Lesenswert?

Wobei ich sowas wie "bit_is_set()" eher als historischen Ballast 
bezeichnen würde. ;-)

von Wilhelm M. (wimalopaan)


Lesenswert?

Jörg W. schrieb:
> Wobei ich sowas wie "bit_is_set()" eher als historischen Ballast
> bezeichnen würde. ;-)

Genau.
Vor allem, weil
1
bit_is_set(PORTB, 10);

überhaupt compiliert.

von (prx) A. K. (prx)


Lesenswert?

MaWin O. schrieb:
> REG |= 0b00000000000010000000000000000000;

Bei C++14 hatte man den Geistesblitz, das so schreiben zu dürfen:
  REG |= 0b0000'0000'0000'1000'0000'0000'0000'0000;

: Bearbeitet durch User
von Ralf D. (doeblitz)


Lesenswert?

Stefan F. schrieb:
> MaWin O. schrieb:
>> REG |= 0b00000000000010000000000000000000;
>>
>> Gibt kaum was Lesbareres! Man sieht sofort auf einen Blick, welches Bit
>> gesetzt wird!
>
> Hier wäre der pöhse Shift Operator hilfreich.

ACK. Oder durch Gruppierung (ab C++14, leider nicht mit der 
GCC-Extension):
1
REG |= 0b0000'0000'0000'1000'0000'0000'0000'0000;

von Jens G. (jensig)


Lesenswert?

(prx) A. K. schrieb:

  REG |= 0b0000'0000'0000'1000'0000'0000'0000Ä0000;

Das waren bestimmt Deutsche, die den Geistesblitz hatten ...

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


Lesenswert?

(prx) A. K. schrieb:

> Bei C++14 hatte man den Geistesblitz, das so schreiben zu dürfen:
>   REG |= 0b0000'0000'0000'1000'0000'0000'0000'0000;

Bei C23 wurde das übernommen.

Der Unterstrich wurde nicht benutzt, weil er ein gültiges 
Präprozessorsymbol ist und ja auch tatsächlich in der Form benutzt wird 
(beispielsweise in Systemen zur Internationalisierung von Texten).

von (prx) A. K. (prx)


Lesenswert?

Jens G. schrieb:
> Das waren bestimmt Deutsche, die den Geistesblitz hatten ...

Mit der von mir früher verwendeten US ASCII Tastatur wäre mir das nicht 
passiert. ;-)

Um das andere Ende ebenso zu würdigen: Vor Jahrzehnten verwendete ich 
eine Tastatur, die auf eigene (private!) Anfrage in kundenspezifischem 
Layout produziert wurde. Gibts sowas heute noch, und bezahlbar? Ich 
würde liebend gerne einen halben Meter Abstand zwischen Shift|CAPS Lock 
und A einbauen. Besser noch, Shift Lock ganz weglassen. Die Kappe 
rauszureissen ist leider nur eine Teillösung, weil bei Notebooks nicht 
anwendbar.

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

J. S. schrieb:
> nur sind typsichere C++ Registerdefinitionen nicht Standard bei den
> Hersteller SDK, für einen STM32H7 möchte ich das nicht selber schreiben.

Das sehe ich auch so.

Wenn der Toolchain/SDK-Hersteller eine C++-Template-Bibliothek mit allen
Schikanen anbietet, nehme ich die natürlich gerne. Gibt es für jedes
Register ein C-Struct mit Bitfeldern, ist das ebenso in Ordnung, auch
wenn es vielleicht nicht ganz so idiotensicher ist. Beschränken sich die
Header-Files – wie in der AVR-Libc – auf Makros für die Registeradressen
und Bitnummern, kann ich auch damit sehr gut leben.

Wichtig ist lediglich, dass diese Header-Files von anderen bereits
ausgiebig getestet und gedebugt sind, alles andere ist nice-to-have.
Wenn mein Programm nicht läuft, möchte ich die Fehler schließlich nur in
meinem eigenen Code und nicht in den kilometerlangen, unausgereiften
Header-Files suchen müssen.

Es mag zwar nett sein, wenn der Compiler für das Setzen des CS00-Bits in
TCCR0A eine Fehlermeldung ausgibt, es ist aber auch kein nennenswerter
Nachteil, wenn er es nicht tut. Mir ist ein solcher Fehler noch nie
passiert, was aber überhaupt nicht daran liegt, dass ich ein perfekter
Programmierer bin. Der Grund dafür liegt vielmehr darin, dass ich bei
der Initialisierung bspw. eines Timers das Datenblatt aufschlage und
darin schrittweise alle betroffenen Register mit den jeweiligen Bits
durchgehe. Da jedem Register ein eigener Abschnitt gewidmet ist, kommt
da überhaupt nichts durcheinander.

In dem sehr unwahrscheinlichen Fall, dass mir dieser Fehler dennoch
unterläuft, merke ich das recht schnell, da der Timer nicht so läuft,
wie ich es gerne hätte. Dann muss ich eben die paar Code-Zeilen, in
denen er initialisiert wird, noch einmal genau unter die Lupe nehmen. Da
ist eine Sache von maximal 10 Minuten, dann ist der Fehler behoben.

Würde ich stattdessen versuchen, das genannte Problem (das eigentlich
gar keines ist) durch das Schreiben einer C++-Template-Bibliothek zu
lösen, würde ich erst einmal viele Tage an Aufwand darin versenken, und
die Wahrscheinlichkeit, dabei einen Fehler zu machen, wäre wegen der
vielen Codezeilen um mehrere Größenordnungen höher. Wenn mein Programm
dann nicht läuft, muss ich den Fehler nicht nur in meinem Anwendungscode
suchen, sondern auch in den selbstgeschriebenen Header-Files, was um ein
Vielfaches aufwendiger ist.

Dieser ganze Zusatzaufwand würde sich für mich nie auch nur ansatzweise
amortisieren. Er lohnt sich – wenn überhaupt – nur dann, wenn (bspw. in
einer größeren Entwicklergruppe) einer die Arbeit übernimmt und viele
andere davon profitieren.

Wenn ich dann für das nächste Projekt keinen AVR, sondern einen ARM,
einen ESP32 oder irgendeinen anderen Mikrocontroller einsetze möchte,
geht das ganze Gefrickel wieder von vorne los.

Die in der Praxis tatsächlich auftretenden Fehler und Probleme wie bspw.
Missverständnisse beim Lesen des Datenblatts oder Vergessen, ein
bestimmtes Bit in einem Register zu setzen, werden auch mit noch so
vielen C++-Templates nicht vermieden.

Deswegen lasse ich mir das Verfahren, mit dem Register mit Werten belegt
werden, einfach vom Hersteller der Toolchain vorgeben. Damit bin ich
noch nie schlecht gefahren.


Zum eigentlichen Thema:

Statt
1
testbyte = PINB & 8;

würde ich (wie schon einige andere in diesem Thread)
1
testbyte = PINB & 1<<FUNKTION_DES_PINS;

scheiben (FUNKTION_DES_PINS durch einen zur Anwendung passenden Namen
ersetzen) .

Magic Numbers sind fast immer schlecht. Eine Magic Number entsteht meist
durch eine Berechnung oder durch die Nummerierung einer bestimmten, mit
der Nummer verbundenen Funktion oder Eigenschaft (bspw. bei Bitnummern
in I/O-Registern, Kommando-IDs oder Errorcodes).

Die Berechnung einer Konstanten sollte man, wenn möglich, im Quellcode
zu Dokumentationszwecken stehen lassen. Der Compiler übernimmt die
Auswertung gerne. Deswegen ist in obigem Fall 1<<3 schon einmal besser
als einfach nur 8. Erst recht ist 1<<31 besser als 2147483648 (in diesem
Fall sogar weniger Tipparbeit).

Nummern, mit denen eine bestimmte Funktion oder Eigenschaft verbunden
ist, sollten einen entsprechenden Namen bekommen, üblicherweise mittels
#define, enum oder der Initialisierung einer const-Variable.

von Wilhelm M. (wimalopaan)


Lesenswert?

Yalu X. schrieb:
> Würde ich stattdessen versuchen, das genannte Problem (das eigentlich
> gar keines ist)

Anscheinend doch: sonst würden wir hier nicht drüber reden bzw. ich 
finde es toll, dass das für Dich nie ein Problem ist.

von Jens G. (jensig)


Lesenswert?

Wilhelm M. schrieb:
> Yalu X. schrieb:
>> Würde ich stattdessen versuchen, das genannte Problem (das eigentlich
>> gar keines ist)
>
> Anscheinend doch: sonst würden wir hier nicht drüber reden bzw. ich
> finde es toll, dass das für Dich nie ein Problem ist.

Naja, das Thread-Thema paßt in die Kategorie "Nennen Sie mir Ihre Lösung 
- ich mache ein Problem daraus" ...

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wilhelm M. schrieb:
> Yalu X. schrieb:
>> Würde ich stattdessen versuchen, das genannte Problem (das eigentlich
>> gar keines ist)
>
> Anscheinend doch: sonst würden wir hier nicht drüber reden

Wer außer dir sieht darin noch ein Problem?

Ich glaube nicht einmal, dass du selber darin ein ernsthaftes Problem
siehst. Oder ist dir ein Fehler der Art

Wilhelm M. schrieb:
> TCCR0A = (1 << CS00)

tatsächlich schon einmal unterlaufen?

Ist es nicht eher so, dass du rein aus Interesse, die diesbezüglichen
Möglichkeiten von C++ zu erkunden, viel Zeit für eine Lösung investiert
hast, die du jetzt durch die übertriebenen Darstellung der Schwere des
Problems aufzuwerten versuchst?

Nicht, dass du mich falsch verstehst:

Ich finde deine Lösung gut¹ und bin prinzipiell für alles dankbar, was
Fehler (auch wenn es nur seltene sind) bereits zur Compilezeit
aufdecken. Nur scheint bei diesem konkreten Problem der Aufwand für
dessen Lösung den Nutzen bei Weitem zu übersteigen, sonst würde sie von
den kommerziellen Toolchain-Entwicklern standardmäßig angeboten. Auch
bei den Open-Source-Entwicklern scheint so etwas keine Priorität zu
genießen, sonst hätte sich sicher längst eine Gruppe zusammengefunden,
die das in einer AVR-Libc++ umsetzt.

──────────────
¹) auch wenn ich sie nicht im Detail kenne, weil du ja nicht viel
   darüber veröffentlichst

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


Lesenswert?

Naja, Bitfield-structs für IO-Register gibt es ja durchaus (und die 
lösen das potenzielle Setzen von Bits in falschen Registern genauso), 
beispielsweise bei vielen Cortex-M. Beim AVR hatten wir das mal beim 
ATmega128RFA1 (und Nachfolgern) gemacht, aber dort hat das seitens Atmel 
und dann Microchip keiner aufgegriffen.

von Klaus H. (hildek)


Lesenswert?

Yalu X. schrieb:
> Oder ist dir ein Fehler der Art
>
> Wilhelm M. schrieb:
>> TCCR0A = (1 << CS00)
>
> tatsächlich schon einmal unterlaufen?

Bei Verwendung des Shiftoperators hätte man vermutlich die Möglichkeit, 
dem Compiler eine Fehlererkennung beizubringen. Auch bei aufmerksamen 
Kontrollieren seines Codes hat man die Chance, zu erkenne, dass CS00 
eben nicht im TCCR0A-Register vorhanden ist. Mit
1
TCCR0A = 1;
wird dasselbe bewirkt und da wäre die Chance nicht vorhanden, weder für 
den Programmierer (oder steht im Kommentar, dass man CS00 meinte?) noch 
für den Compiler.
Und es ging ja ursprünglich um das Thema Shiftoperator oder Magic 
Number.

Thorsten M. schrieb:
> Ein Anfänger versteht vielleicht, dass mit Pin 3 irgendwas gemacht
> werden soll, aber nicht, dass der Mechanismus dahinter einfach ein
> zugewiesener Zahlenwert ist.

Wie an vielen Stellen: das muss man eben lernen! Wer ein richtiger 
Anfänger ist, hat noch viele andere Baustellen. Und wer nur Anfänger im 
Programmieren von µCs ist, der hat in Minuten verstanden, warum man das 
hier übersichtlicher so schreibt. Es sind doch prinzipiell nur die 
beiden Schreibweisen zu verstehen:
1
PORTB |= (1<<PB0);  // B0 setzen
2
// und
3
PORTB &= ~(1<<PB0); // B0 löschen

Etwas gefährlicher ist dies (bin auch schon selber auf mich 
reingefallen):
1
WDTCR |= (1<<WDP2) | (0<<WDP1) | (1<<WDP0);
Man führt alle Bits auf um bei Überarbeitung schnell mal auf eine andere 
Kombination ändern zu können, sieht aber die '0' bei WDP1 nicht auf 
Anhieb ...
Es wäre auch vom Compiler erkennbar, dass in der Zeile (0<<WDP1) nichts 
bewirkt.

von Bauform B. (bauformb)


Lesenswert?

Namen statt magic numbers sollte man auch nicht übertreiben. Gerade will 
ich eine RTC von Hand stellen und die Eingabe irgendwo begrenzen; RTCs 
kennen ja nur Jahre von 0 bis 99.
1
  utc = mktime (&temp);
2
  if (utc > 4070908799) { // 2098-12-31 23:59:59
3
    utc = 4070908799;
4
  } else {
5
    if (utc < source_date_epoch ()) {
6
      utc = source_date_epoch ();
7
    }
8
  }
Würde irgendwer diese magic number per Präprozessor ausrechnen lassen? 
Nachdem dies die einzige Verwendung im ganzen Programm ist, lohnt sich 
doch nicht einmal ein einziges #define? Eher würde ich sie zur Laufzeit 
ausrechnen...

von Thorsten M. (pappkamerad)


Lesenswert?

Klaus H. schrieb:
> Wie an vielen Stellen: das muss man eben lernen! Wer ein richtiger
> Anfänger ist, hat noch viele andere Baustellen.

Das sag ich ja grad. Es ist ja wirklich nicht schwierig das zu lernen. 
Aber es ist eben nicht wie der TE meinte eine Hilfe für Anfänger, 
sondern eine Vereinfachung für Fortgeschrittene. Und die Frage kam ja 
scheinbar aus dem Kontext der Ausbildung.


MaWin O. schrieb:
> Genau so könntest du aber argumentieren, dass das Konzept von Zahlen
> ansich viel zu kompliziert ist. Woher soll der Anfänger das denn bitte
> alles wissen?

Deswegen fängt man in der Mathematik nicht mit Differentialgleichungen 
an, sondern mit dem Zählen. Bei 0 oder 1 gehts los, weiß ich nicht mehr.

Aber du hast mich wohl sowieso falsch verstanden. Ich argumentiere ja 
gar nicht gegen das Konstrukt, siehe oben, sondern möchte hier nur 
verdeutlichen, dass das auch etwas ist, das man erst lernen muss und im 
Sinne der Lehre an geeigneter Stelle einbauen sollte und nicht einfach 
sagt, das versteht sich ja von selbst.

>> Ein Anfänger versteht vielleicht, dass mit Pin 3 irgendwas gemacht
>> werden soll, aber nicht, dass der Mechanismus dahinter einfach ein
>> zugewiesener Zahlenwert ist.
>
> Was soll es denn sonst sein? Ein Mettbrötchen?
> Ein Rechner kennt nur Zahlen. Deshalb heißt er so. Das sollte doch
> Allgemeinwissen sein.

Holla. PortA ist schonmal nicht einfach eine Zahl im Compilersinn. 
Sondern eine Adresse, die dann dank gemapptem Speicher gleich noch ein 
Hardwareregister ist. Der Compiler dereferenziert automatisch, das 
Beschreiben hat dann auch noch eine Auswirkung auf die Umwelt. Je nach 
Anwendung sogar auf ein Mettbrötchen. Für die Profis sind die 
Implikationen alle klar. Um sofort zu sehen, was ein Hardwareregister, 
was eine Variable, was eine Funktion, was eine Konstante ist, muss das 
Hirn erst auf Programmiersyntax eingestellt werden. Das geht nicht in 
Minuten und nicht in Tagen.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Bauform B. schrieb:
> Namen statt magic numbers sollte man auch nicht übertreiben.
>
>  if (utc > 4070908799) { // 2098-12-31 23:59:59
>
> Würde irgendwer diese magic number per Präprozessor ausrechnen lassen?

Nein, aber einen Namen kannst du ihr ja trotzdem geben, bspw. mit
1
#define UTC_2098_12_31_23_59_59 4070908799

von MaWin O. (mawin_original)


Lesenswert?

Thorsten M. schrieb:
> Holla. PortA ist schonmal nicht einfach eine Zahl im Compilersinn.
> Sondern eine Adresse, die dann dank gemapptem Speicher gleich noch ein
> Hardwareregister ist.

1<<PA3 sollte man also deiner Meinung nach 8 schreiben, weil das weniger 
abstrakt ist.

Wie müsste man dann PORTA genau schreiben?
Adresse hartcodieren?
So?
*((volatile uint8_t *)0x1234)
Schließlich muss das ja verständlicher sein, deiner Argumentation nach.

Bizarr.

von Thorsten M. (pappkamerad)


Lesenswert?

MaWin O. schrieb:
> Bizarr.

Ja, man sollte nie mit einem falschen Mawin diskutieren.
Mein Fehler.

von MaWin O. (mawin_original)


Lesenswert?

Thorsten M. schrieb:
> Ja, man sollte nie mit einem falschen Mawin diskutieren.

Außer Ad-hominem keine weiteren Argumente mehr?
Case closed, würde ich sagen.

von Wilhelm M. (wimalopaan)


Lesenswert?

Yalu X. schrieb:
> Bauform B. schrieb:
>> Namen statt magic numbers sollte man auch nicht übertreiben.
>>
>>  if (utc > 4070908799) { // 2098-12-31 23:59:59
>>
>> Würde irgendwer diese magic number per Präprozessor ausrechnen lassen?
>
> Nein, aber einen Namen kannst du ihr ja trotzdem geben, bspw. mit
>
>
1
> #define UTC_2098_12_31_23_59_59 4070908799
2
>

Würde dafür eine constexpr-Funktion nehmen: das kann der Compiler ganz 
gut zur Compilezeit selbst ausrechnen.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Bauform B. schrieb:
> Würde irgendwer diese magic number per Präprozessor ausrechnen lassen?

Das ist unmöglich!
Der Präprozessor kann zwar rechnen, aber man bekommt die berechneten 
Zahlen nicht aus dem Präprozessor in den Code.

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


Lesenswert?

Wilhelm M. schrieb:
> Würde dafür eine constexpr-Funktion nehmen: das kann der Compiler ganz
> gut zur Compilezeit selbst ausrechnen.

Das geht auch mit jeder beliebigen "static" Funktion. Der Compiler 
sieht, dass sie nur einmal benutzt wird und inlinet sie, auch ganz ohne 
"constexpr". Aber es spricht absolut nichts dagegen, dass per 
Präprozessor-Makro zu machen (außer das C++ den Präprozessor nicht mag 
;-).

von Steve van de Grens (roehrmond)


Lesenswert?

>> Würde irgendwer diese magic number per Präprozessor ausrechnen lassen?

Yalu X. schrieb:
> Nein, aber einen Namen kannst du ihr ja trotzdem geben, bspw. mit
> #define UTC_2098_12_31_23_59_59 4070908799

Ich würde bei der Benamung möglichst noch einen Schritt weiter gehen, um 
anzuzeigen, was es mit diesem Datum auf sich hat. Etwa so:

#define UTC_GERMAN_UNITY_DAY 654951600

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wilhelm M. schrieb:
> Würde dafür eine constexpr-Funktion nehmen: das kann der Compiler ganz
> gut zur Compilezeit selbst ausrechnen.

Diese Funktion wird man aber wohl selber schreiben müssen, oder geht das
auch irgendwie mit Funktionen aus der Standardbibliothek?

Für den AVR, um den es hier wohl primär geht und für den es keine
C++-Standardbibliothek gibt, müsste die Funktion auf jeden Fall selber
geschrieben und natürlich wie jede anderen Funktion erst einmal
ausgiebig getestet werden.

Ich glaube, in diesem konkreten Fall würde ich die Regel, Berechnungen
möglichst durch den Compiler erledigen zu lassen, ausnahmsweise brechen.

von Reiner D. (dollreiner)


Lesenswert?

Nochmal der Versuch einer Zusammenfassung.

Ich denke, die jeweiligen programmiersprachlichen Ausdrücke werden je 
nach persönlicher Historie so oder so benutzt und bewertet.

Ihr hier seid mehrheitlich junge Leute denke ich, die in moderner Zeit 
mit ausgereiften Tools, IDEs und komplexen Compilern digital 
sozialisiert wurden.

Ich hab das Controller-Programmieren auf dem 8048 gelernt, in Maschine, 
das Entwicklungssystem und der Emulator so groß wie ein Küchenschrank. 
Damals gabs nichts anderes ;-)

Daraus resultieren verschiedene Vorgehensweisen. Es würde mir nicht 
einfallen, irgend etwas mit einem Controller zu tun, ohne vorher den 
Absatz im Datenblatt gelesen zu haben.

(Da bin ich sehr bei yalu : "dass ich bei der Initialisierung bspw. 
eines Timers das Datenblatt aufschlage und darin schrittweise alle 
betroffenen Register mit den jeweiligen Bits durchgehe. ").

Libs sind mir ein Gruselding, ich will verstehen was da läuft.

--------
btw. Was mir auffällt, sind einige Beiträge mit sehr unsachlichen 
Inhalten.
Was soll das in einem technischen Forum ? Vorschlag : Beiträge mit 
persönlichen Schmähungen werden nur akzeptiert, wenn sie in korrekter 
Syntax geschrieben sind ;-)

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


Lesenswert?

Reiner D. schrieb:
> Ihr hier seid mehrheitlich junge Leute denke ich

:-))

von MaWin O. (mawin_original)


Lesenswert?

Reiner D. schrieb:
> Ihr hier seid mehrheitlich junge Leute denke ich

Ja, selbstverständlich. Wir sind alle sehr jugendlich, dynamisch, 
äußerst attraktiv und unsere volle Haarpracht bestätigt das nur noch.

räusper

von (prx) A. K. (prx)


Lesenswert?

Reiner D. schrieb:
> Ihr hier seid mehrheitlich junge Leute denke ich, die in moderner Zeit
> mit ausgereiften Tools, IDEs und komplexen Compilern digital
> sozialisiert wurden.

Manchmal umschleicht mich das Gefühl, dass die Tools vor 4 Jahrzehnten 
ausgereifter waren. Allerdings waren die Compiler nicht so komplex.

von Wilhelm M. (wimalopaan)


Lesenswert?

Yalu X. schrieb:
> Wilhelm M. schrieb:
>> Würde dafür eine constexpr-Funktion nehmen: das kann der Compiler ganz
>> gut zur Compilezeit selbst ausrechnen.
>
> Diese Funktion wird man aber wohl selber schreiben müssen, oder geht das
> auch irgendwie mit Funktionen aus der Standardbibliothek?
1
constexpr auto secondsSinceEpoch(const std::chrono::year_month_day& d) {
2
    const auto t =  sys_days{d}.time_since_epoch();
3
    return std::chrono::duration_cast<std::chrono::seconds>(t).count();    
4
}

> Für den AVR, um den es hier wohl primär geht und für den es keine
> C++-Standardbibliothek gibt, müsste die Funktion auf jeden Fall selber
> geschrieben und natürlich wie jede anderen Funktion erst einmal
> ausgiebig getestet werden.

Aus praktischen Gründen macht man Datumsarithmetik auf Basis des 
julianischen Datums. Dann ist es auch für den AVR schnell geschrieben:
1
constexpr uint32_t julianDay(const uint16_t year, const uint8_t month, const uint8_t day) {
2
    const size_t a = (14 - month)/12;
3
    const size_t y = year+4800-a;
4
    const size_t m = month + 12*a - 3;
5
    return day + (153*m+2)/5 + uint32_t{y}*365 + y/4 - y/100 + y/400 - 32045;
6
}
7
8
constexpr auto secondsSinceEpoch(const uint16_t year, const uint8_t month, const uint8_t day) {
9
    return (julianDay(year, month, day) - julianDay(1970, 1, 1)) * 24u * 3600u;
10
}

Benutzung:
1
int main() {
2
    constexpr auto v1 = secondsSinceEpoch(2098y/12/31) - 1 ;
3
    constexpr auto v2 = secondsSinceEpoch(2098, 12, 31) - 1;
4
}

Dies ist natürlich nur so dahingerotzt. Im echten Leben gehen natürlich 
die 3-stelligen Parameterliste mit identischen Datentypen gar nicht. 
Sondern man muss hier sinnvollerweise eine Klasse Date einführen so 
ähnlich wie in der stdlib für std::year_month_date. So kann man nicht 
lassen.
Hier sei es gestattet um das Prinzip zu zeigen.

Die Umrechnungen findet man unter: 
http://www.tondering.dk/main/index.php/calendar-information

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

Wilhelm M. schrieb:
> constexpr uint32_t julianDay(const uint16_t year, const uint8_t month,  const 
uint8_t day) {
>     const size_t a = (14 - month)/12;
>     const size_t y = year+4800-a;
>     const size_t m = month + 12*a - 3;
>     return day + (153*m+2)/5 + uint32_t{y}*365 + y/4 - y/100 + y/400 - 32045;
> }

Wenn ich jetzt gehässig wäre, würde ich sagen: Du hast zwar die magische
Konstante 4070908799 erfolgreich eliminiert, dafür aber drei neue (4800,
153 und 32045) eingeführt ;-)

> constexpr auto v1 = secondsSinceEpoch(2098y/12/31) - 1 ;

Du hast (warum auch immer) die Uhrzeit (23:59:59) weggelassen und
wolltest das mit dem -1 am Ende kompensieren. Dann hättest du das Datum
aber auf den 1.1. des Folgejahres setzen müssen:
1
constexpr auto v1 = secondsSinceEpoch(2099y/1/1) - 1 ;

Hättest du den Timestamp bspw. hier

  https://www.epochconverter.com/

aus der Eingabe von Datum und Uhrzeit berechnen lassen und mit dem
Ergebnis (4070908799) im Programm eine Konstante mit einem
aussagekräftigen Namen initialisiert, wäre das nicht passiert.

von Wilhelm M. (wimalopaan)


Lesenswert?

Yalu X. schrieb:

>> constexpr auto v1 = secondsSinceEpoch(2098y/12/31) - 1 ;
>
> Du hast (warum auch immer) die Uhrzeit (23:59:59) weggelassen und
> wolltest das mit dem -1 am Ende kompensieren. Dann hättest du das Datum
> aber auf den 1.1. des Folgejahres setzen müssen:
>
>
1
> constexpr auto v1 = secondsSinceEpoch(2099y/1/1) - 1 ;
2
>

Wunderbar!
Du hast also sofort erfasst, was diese Zeile sollte. Und den 
offensichtlichen Fehler direkt erkannt. Es ist ja kein Fehler der 
Funktion, sondern seiner Anwendung.

Die RTC kann auch sicher bis Jahr 99, Monat 12, Tag 31 zählen, oder?
Dann wäre ja sogar Deine Korrektur falsch gewesen.

Ein Fehler in dem Literal 4070908799 wäre wohl länger verborgen 
geblieben.

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.