Ich habe da mal wieder ein Verständigungsproblem zwischen Mir, dem
Rechner und C.
Ein Pointer ist doch ein Zeiger auf die Stelle der Variable, richtig?
Also wenn ich eine float Variable - 32Bit breit habe und ich sie in eine
Funktion übergeben, ist die "Übergabeadresse" (Wie nennt man das
richtig? Ist das der Pointer?) ja 16Bit breit, richtig?
1
voidMeineFunktion(uint16_t*pvalue){}
Aufrufen tue ich das ganze mit:
1
MeineFunktion(&MeineVariable);
Ist das soweit korrekt?
Dies scheint auch zu funktionieren, nun komme ich aber zu meinem
Problem. In dieser Funktion lese ich auf die Adresse 2x8Bit ein, quasi:
1
voidMeineFunktion(uint16_t*pvalue)
2
{
3
*(p_value)=ReadMSB(1);
4
*(p_value+1)=ReadLSB(1);
5
}
Und hier hakt es... da kommen völlig falsche Werte dabei raus. Kann mir
jemand erklären wo ich anfangen könnte zu suchen?!
Lübsten Dank. :-)
Du hast nur eine Variable, schreibst aber in zwei ...
Deine Funktion "MeineFunktion" geht davon aus, die Anfangsadresse eines
Arrays übergeben zu bekommen, in dem mindestens zwei uint16_t Platz
haben.
1
voidMeineFunktion(uint16_t*pvalue)
2
{
3
*(p_value)=ReadMSB(1);// beschreibt pvalue[0], also das erste Arrayelement
4
*(p_value+1)=ReadLSB(1);// beschreibt pvalue[1], also das zweite Arrayelement
> Ein Pointer ist doch ein Zeiger auf die Stelle der Variable, richtig?
Ja.
> Also wenn ich eine float Variable - 32Bit breit habe und ich sie in eine> Funktion übergeben, ist die "Übergabeadresse" (Wie nennt man das> richtig? Ist das der Pointer?) ja 16Bit breit, richtig?
Nein. Die Breite des Pointers hat mit der Variable nichts zu tun,
sondern mit Deiner CPU und Deinem System.
Außerdem ist ein float 32 bit breit.
> void MeineFunktion(uint16_t *pvalue){}
Wenn der Wert ein float sein soll, dann schreib auch einfach "float *"
hin.
> Ist das soweit korrekt?
Nein, nicht wenn Deine Variable ein float ist.
> void MeineFunktion(uint16_t *pvalue)> {> *(p_value) =ReadMSB(1);> *(p_value+1)=ReadLSB(1);
Befasse Dich ein wenig mit Pointer-Arithmetik. Dein pvalue zeigt auf
uint16_t, d.h. pvalue+1 zeigt nicht etwa, wie Du offenbar annimmst, auf
das nächste Byte im Speicher. Es zeigt auf den nächsten uint16_t.
Also praktisch heißt das, daß in Bytes betrachtet "*(pvalue+1)" ZWEI
Bytes weiterzählt, nicht eines. Weil +1 in Pointerarithmetik nicht um
Bytes weiterzählt, sondern um soviele Bytes, wie der referenzierte
Datentyp in Bytes groß ist. Da ein uint16_t zwei Bytes groß ist, in
diesem Fall um zwei Bytes.
Richtig könnte es so gehen, unter der Annahme, daß ReasMSB/LSB Werte von
0 bis 255 liefern:
1
*(p_value)=(ReadMSB(1)<<8u)|ReadLSB(1);
Was die 1 bei dem Parameter bedeuten soll, weiß ich natürlich nicht, das
sagst Du ja auch nicht.
Johannes S. schrieb:> urra, der nächste Thread warum C Kacke ist :-(
Wieso kacke?! Pointerarithmetik ist doch eines der besten Features von C
überhaupt, zusammen mit Mehrfach-Dereferenzierung.
Johannes S. schrieb:> Hurra, der nächste Thread warum C Kacke ist :-(
Warum das wohl so ist?
Keine Angst vor endlosen Diskussionen ich steige sofort aus dem Thread
aus! Versprochen!
Ach liebsten Dank ich habe verstanden! :-D
Ja wie gesagt, ich dachte das dort ausschließlich die Adresse übergeben
wird - in der Wertbreite des Prozessor. Dies ist aber nicht der Fall.
Ich werde mich da unbedingt nochmal einlesen müssen. Aber ihr habt mir
da schon ziemlich weitergeholfen!
Nop schrieb:> Was die 1 bei dem Parameter bedeuten soll, weiß ich natürlich nicht, das> sagst Du ja auch nicht.
Achso, war ein Copy&Paste Überbleibsel aus dem Code. Das ist von einer
SHT Routine, wo dieses Problem auftrat. So sieht sie richtig aus - mit
der 1 oder 0 gebe ich einen ACK oder noACK an den Sensor:
Nop schrieb:> Johannes S. schrieb:> urra, der nächste Thread warum C Kacke ist :-(>> Wieso kacke?! Pointerarithmetik ist doch eines der besten Features von C
Jepp, wie ein Messer. Damit kann man Brot schneiden aber auch Unfug
mach. Kommt eben auf den Anwender an. Und wieviel Schutz und Regeln und
Überwacher er braucht.
Für auf Nummer sicher gehen : auf jeden Fall Warnungen als Fehler
betrachten.
Rene K. schrieb:> uint8_t SHT_measure(float *p_value
...
> *p_value = (SHT_read_byte(1) << 8u) | SHT_read_byte(1);
Das geht so nicht. float ist ein Fließkommawert mit einer ganz eigenen
Darstellung. Der hat einige Bits für die Mantisse und einige für den
Exponenten, vereinfacht gesagt. Damit möchte man nicht rummmachen, wenn
man sich nicht sehr eingehend mit dem Bitformat befaßt hat.
Also wenn Du nicht wirklich Fließkomma brauchst, sondern nur Highbyte
und Lowbytes in zwei Byteports hast, dann mach das nicht als float,
sondern als uint16_t.
Johannes S. schrieb:> Jepp, wie ein Messer. Damit kann man Brot schneiden aber auch Unfug> mach.
Ack. Und da ich keine stupfen Messer mag.. (:
> Für auf Nummer sicher gehen : auf jeden Fall Warnungen als Fehler> betrachten.
Und obendrein auch noch z.B. CppCheck nehmen. Kostenlos und gut.
Ja, guter Vorschlag. Der Compiler meckert normalerweise erst wenn es
schon fast ein Fehler ist, diese Tools gehen weiter.
Ich bin noch ein Dino der ohne ausgekommen ist (haha), aber nur weil es
das früher nicht gab. Die Zeit die man in solche Fehlerfrüherkennung
steckt holt man aber in der -suche locker wieder raus.
Nop schrieb:> Das geht so nicht. float ist ein Fließkommawert mit einer ganz eigenen> Darstellung. Der hat einige Bits für die Mantisse und einige für den> Exponenten, vereinfacht gesagt. Damit möchte man nicht rummmachen, wenn> man sich nicht sehr eingehend mit dem Bitformat befaßt hat.>> Also wenn Du nicht wirklich Fließkomma brauchst, sondern nur Highbyte> und Lowbytes in zwei Byteports hast, dann mach das nicht als float,> sondern als uint16_t.
Das ist ein Argument, hab ich gleich umgesetzt. Ich brauche die
Fließkommawerte erst später zur Berechnung:
Rene K. schrieb:> typedef union> {> uint16_t i;> float f;> uint8_t c;> } SHTvalue;
Uhhh, also sowas würde ich auch weglassen. Ich verwette Steves Mops, daß
Du Dir nicht exakt im Klaren bist, was genau diese Union tut, sofern Du
sie zur Bitkonversion verwendest.
Du brauchst die Union auch eigentlich nicht. Du kannst auch einen
plain-uint16_t mit dem (float)-cast in einen float umwandeln.
Mit der Union legst du Variablen auf die gleiche Adresse, dein .c
überschreibt also böse dein .i.
Du möchtest eher eine Struktur struct die alle Variablen gleichzeitig
hält?
Nop schrieb:> Rene K. schrieb:>> typedef union>> {>> uint16_t i;>> float f;>> uint8_t c;>> } SHTvalue;>> Uhhh, also sowas würde ich auch weglassen. Ich verwette Steves Mops, daß> Du Dir nicht exakt im Klaren bist, was genau diese Union tut, sofern Du> sie zur Bitkonversion verwendest.>> Du brauchst die Union auch eigentlich nicht. Du kannst auch einen> plain-uint16_t mit dem (float)-cast in einen float umwandeln.> uint16_t x;> float y;>> ...>> y = (float) y;>> das funktioniert.
Oh.. okay... ja gerade bemerkt :-D Union = gleicher Speicherplatz und
Struct = unterschiedlicher Speicherplatz. Gleich mal durchprobiert
beides.
Nop schrieb:> Johannes S. schrieb:>> Du möchtest eher eine Struktur struct die alle Variablen gleichzeitig>> hält?>> Steves Mops dagegen gewettet. ^^
Jep, so war das mal gedacht. :x
Ich muss mich ganz dringend mal in die Variablen einarbeiten! Das hat
mir der heutige Abend gelehrt. Bis dato kam ich ja ganz gut mit den
normalen Zuweisungen und Bearbeitungen zurande. Will man aber etwas
tiefer hinein, kommt man da nicht wirklich drumherum.
Ja, Öl ins Feuer für 'einfache Sachen kann man in C auch kompliziert
machen '.
SHT Dingsda sollte seine Id/Adresse sonstwas und den Zeiger auf das
Ergebnis als Argument bekommen und Fehler als Return Wert is ok.
Fehler addieren geht nur wenn es Bitpos. sind, sonst isses nich
eindeutig.
Gute Nacht.
Rene K. schrieb:> Ich muss mich ganz dringend mal in die Variablen einarbeiten!
Nicht schlimm, das ist der normale Leidensweg in C, deshalb mag ich die
Cortex-M wo debugging einfach und billig möglich ist.
Dein Programm hätte je nach Werten sogar funktioniert, aber das ist noch
böser. Denn ein funktionierendes Programm hat nach Murphy versteckte
Fehler! :-/
Rene K. schrieb:> Bis dato kam ich ja ganz gut mit den> normalen Zuweisungen und Bearbeitungen zurande. Will man aber etwas> tiefer hinein, kommt man da nicht wirklich drumherum.
Also eine gute Faustregel ist, im Normalfall immer bei den Datentypen zu
bleiben, die man gerade hat. Jede Typkonversion bietet Fallen. Gerade in
C, damit muß man sich eingehend befassen. Aber solange man dabei bleibt,
was man gerade hat, ist da auch kein Risiko. Wie mit Frauen. (;
Und Unions sind definitiv für Fortgeschrittene.
Kann gelegentlich seinen Sinn haben, beispielsweise wenn man ein Struct
aus 4 Bytes hat. Da kann man eine Union drüberlegen, wenn man dieses
Struct schnell auf Identität oder auf Null prüfen will. Aber sonst,
Finger weg von Unions.
Johannes S. schrieb:> Hurra, der nächste Thread warum C Kacke ist :-(> Bitte gleich das Schloss dranmachen.
Wenn man einen Hammer verkehrt herum hält und mit dem Stiel auf den
Nagel einschlägt, ist dann das Werkzeug daran schuld?
Soo.. also diese Union Geschichte lässt mich da gerade nicht mehr los
:-D Wenn man darüber nachdenkt, ist das ja eigentlich eine ganz feine
Sache. Wäre es damit auch möglich auch Bit-Weise auf die selbe Variable
zuzugreifen? Weil ich definiere ja einen Speicherbereich z.b. 1 Byte,
diesen Fülle ich ja mit einem Wert, die darauffolgenden Union Member
greifen ja auf diesen Bereich auch zu oder? Also ich meine das so?
1
typedefuinion{
2
uint8_tu_value;
3
4
uint8_tBit0:1;
5
uint8_tBit1:1;
6
uint8_tBit2:1;
7
uint8_tBit3:1;
8
uint8_tBit4:1;
9
uint8_tBit5:1;
10
uint8_tBit6:1;
11
uint8_tBit7:1;
12
}BitValue
Oder muss ich das irgendwie in einem uinion in eine Struct stecken?
Ja, das kannst du mit der Struct so machen.
Nein, was du tun willst, ist nicht eindeutig im Standard definiert.
Nein, für Hardwareregister ist das auch keine gute Lösung.
Wenn du Bitfelder in C anfasst, bist du ganz schnell
im Bereich der "implementation-defined"ness.
Rene K. schrieb:> Wäre es damit auch möglich auch Bit-Weise auf die selbe Variable> zuzugreifen?
Nein, das geht mit Bitfields nicht, weil der C-Standard dem Compiler
nicht vorschreibt, wie herum er die Bits in einem Bitfield legen soll.
Also das zuerst definierte Bit (bit 0) kann je nach Compiler mal das
höchstwertigste oder auch das niederwertigste Bit der Variablen sein.
Oder auch in der Mitte, theoretisch.
Bitfields darf man überhaupt nur über den Namen der Elemente ansprechen,
alles andere ist nicht garantiert. Deswegen sind Bitfields ja auch für
Register-Bitmapping vollkommen verkehrt.
Was aber geht:
Nimm eine Union z.B. aus einem uint32_t und beliebigen anderen
32bit-Datentypen. Dann kannste da irgendwas reinfüllen, etwa einen
float, und dann kannste den uint32 ansprechen. Dessen Bits kriegste mit
Bitmaskierung raus, also etwas wie (my_uint32 & 0x00000001ul) für Bit 0.
Unions sind seit C99 ja für type punning erlaubt und auch das einzige
Mittel dafür, weil pointer based type punning wegen pointer aliasing
nicht erlaubt ist und wegoptimiert werden kann.
Garden schrieb:> Hier mal ein Video zum Thema Pointer in C:>> http://et-tutorials.de/3368/pointer-in-c/
Danke, werde ich mir mal ansehen. So die Grundzüge der Pointer hatte ich
die Nacht noch begriffen :-)
S. R. schrieb:> Nein, für Hardwareregister ist das auch keine gute Lösung.
Nein, der Sinn der mich gerade da verfolgte ist ein Register in einer
ausgelesenen Variable, welches ich ja somit relativ leicht abbilden kann
ohne Bitschieberei.
Danke für die Hinweise :-D
Rene K. schrieb:> void MeineFunktion(uint16_t *pvalue)> {> *(p_value) =ReadMSB(1);> *(p_value+1)=ReadLSB(1);> }
Wah! wenn man einen uint16-pointer inkrementiert, dann wird die Adresse
um 2 Byte erhöht ... Man muss ihn quasi erstmal in einen char-Pointer
casten, wenn man es unbedingt so machen möchte ...
Quasi
*((uint8*) p_value) = ...
*(((uint8*) p_value)+1) = ...
Mampf F. schrieb:> Wah! wenn man einen uint16-pointer inkrementiert, dann wird die Adresse> um 2 Byte erhöht ... Man muss ihn quasi erstmal in einen char-Pointer> casten, wenn man es unbedingt so machen möchte ...>> Quasi> *((uint8*) p_value) = ...> *(((uint8*) p_value)+1) = ...
Ja, falls du den Thread weiter verfolgt hast, wurde das Problem bereits
erklärt, der TS hat es verstanden und implementiert.