URL: https://www.overclockers.at/coding-stuff/c-bit-field_256379/page_1 - zur Vollversion wechseln!
ich hab folgende union:
Code:typedef union ConfigRegister { struct { bool unused : 1; bool soft_reset : 1; bool alert_pin_select : 1; bool pin_pol : 1; bool therm_alert_mode : 1; uint8_t avg : 2; uint8_t conv : 3; uint8_t mode : 2; bool EEPROM_busy : 1; bool data_ready : 1; bool alert_low : 1; bool alert_high : 1; } bits; uint16_t reg; ConfigRegister() { reg = 0; }; } ConfigRegister;
Code:bool unused = 0b; bool soft_reset = 0b; bool alert_pin_select = 0b; bool pin_pol = 0b; bool therm_alert_mode = 0b; uint8_t avg = 01b; uint8_t conv : 100b; uint8_t mode : 00b; bool EEPROM_busy : 0b; bool data_ready : 1b; bool alert_low : 0b; bool alert_high : 0b;
also das höhere byte scheint um genau 1 bit verschoben zu sein bzw. conv würde am ersten bit des höheren byte starten statt am letzten bit des niedrigen. ich glaube nicht dass es straddling ist, ich habe eine ähnliche union für ein anderes I2C gerät, das problemlos funktioniert:Code:bool unused = 0b; bool soft_reset = 0b; bool alert_pin_select = 0b; bool pin_pol = 0b; bool therm_alert_mode = 0b; uint8_t avg = 01b; uint8_t conv : 010b; uint8_t mode : 00b; bool EEPROM_busy : 1b; bool data_ready : 0b; bool alert_low : 0b; bool alert_high : 0b;
Code:typedef union DataWrite { struct { uint8_t unused0 : 4; uint16_t value : 12; bool unused1 : 1; uint8_t pd_mode : 2; uint8_t unused2 : 2; uint8_t write_mode : 3; } bits; uint8_t content[3]; DataWrite() { memset(content, 0, 3); }; } DataWrite;
Anhand des vorhandenen Codes kann ich jetzt nichts sehen. Meistens liegt es in so einem Bereich an unterschiedlicher Endian/Byte Order.
Wie schaut der restliche Code aus? Wie überprüfst du die Werte?
endianness habe ich schon berücksichtigt, das habe ich im post vergessen zu erwähnen. (ein paar checks sind zwecks lesbarkeit entfernt)
Code:ConfigRegister config_; bool readRegister(uint8_t reg, char *content, unsigned int length) { char buffer[3] = {reg, 0, 0}; if (i2c_->write(address_ << 1, buffer, 1, false) != 0) return false; if (i2c_->read(address_ << 1, buffer+1, 2, false) != 0) return false; // convert to little endian content[0] = buffer[2]; content[1] = buffer[1]; return true; } template <class T> bool readRegister(uint8_t reg, T &content) { char buffer[sizeof(T)]; if (!readRegister(reg, buffer, sizeof(T))) return false; memcpy(&content, buffer, sizeof(T)); return true; }
Code:uint16_t cfg = 0; readConfigRegister(cfg); config_.reg = cfg; bool ready = config_.bits.data_ready;
Code: C++static_assert(sizeof(ConfigRegister::bits) == 2);
ZitatThe following properties of bit fields are undefined:The following properties of bit fields are unspecified:
- The effect of calling offsetof on a bit field
The following properties of bit fields are implementation-defined:
- Alignment of the allocation unit that holds a bit field
- Whether bit fields of type int are treated as signed or unsigned
- Whether types other than int, signed int, unsigned int, and _Bool are permitted
- Whether atomic types are permitted
- Whether a bit field can straddle an allocation unit boundary
- The order of bit fields within an allocation unit (on some platforms, bit fields are packed left-to-right, on others right-to-left)
äääh.. jo. fürst nächste mal weis ichsZitat aus einem Post von VinciCode: C++static_assert(sizeof(ConfigRegister::bits) == 2);
Ups.
also zurück zu & und >> :'(Zitat aus einem Post von Vincihttps://en.cppreference.com/w/c/language/bit_field
die lib kommt schon von mirZitat aus einem Post von Vinci/edit
Hardware-Entwickler können leider auch im Jahr 2020 nicht richtig programmieren. Ich weiß dass so gut wie jeder Hersteller Bitfelder und Unions für Register benutzt. Das ist aber halt alles nicht Standard konform... und das type-punning is übrigens auch UB.
stimmt gar ned, ich hör meistens auf dichZitat aus einem Post von Vinci/edit2
So wie ich die kenn hörst du sowieso nicht auf mich
deshalb auch hier die "Lösung":
#pragma pack(1)
Zitat aus einem Post von wergoralso zurück zu & und >> :'(
Code: C++#define USART_CR_AVG (0b11 << 8)
Code: C++template<uint32_t Mask, uint32_t Value> void set(uint32_t volatile* reg) { reg = ... /* Zur Compilezeit kalkulierter Werte aus Mask & Value */ } int main() { set<USART1_CR_AVG, 2>(USART1->CR); }
Zitat aus einem Post von wergordie lib kommt schon von miraber wenn type punning UB ist, warum gibts dann union, das ist ja genau nix anderes?
sowas werde ich versuchen - die library war eine der ersten wo ich es mit bit fields probiert habe, davor hatte ich immer funktionen die basierend auf der maske die bits gesetzt / gelesen haben.Zitat aus einem Post von VinciCode: C++template<uint32_t Mask, uint32_t Value> void set(uint32_t volatile* reg) { reg = ... /* Zur Compilezeit kalkulierter Werte aus Mask & Value */ } int main() { set<USART1_CR_AVG, 2>(USART1->CR); }
danke, das wusste ich nicht.Zitat aus einem Post von VinciVon einem nicht aktiven (sprich nicht-zuletzt-geschriebenen) Union-Member lesen ist UB.
Das ist auch der Grund weshalb z.B. std::variant (ein fancy Union) in so einem Fall eine Exception wirft.
mache ich, nach dem thread hab ich eh nicht mehr viel lust die zu benutzenZitat aus einem Post von Vinci/edit
Mein Tip bezüglich Bitfelder. Tu so als gäbs die nicht.
was ist der vorteil der non-type parameter in dem fall? wird damit nur sichergestellt dass mask und value zur compilezeit bekannt sind oder bringt das noch mehr?Zitat aus einem Post von VinciCode: C++template<uint32_t Mask, uint32_t Value> void set(uint32_t volatile* reg) { reg = ... /* Zur Compilezeit kalkulierter Werte aus Mask & Value */ }
Zitat aus einem Post von wergorwas ist der vorteil der non-type parameter in dem fall? wird damit nur sichergestellt dass mask und value zur compilezeit bekannt sind oder bringt das noch mehr?
danke
overclockers.at v4.thecommunity
© all rights reserved by overclockers.at 2000-2025