Gewoon een paar punten toevoegen aan het antwoord van Michel Keijzers.
De uitdrukking Wire.read () << 8
neemt de waarde die wordt geretourneerd door Wire.read ()
, wat een int
is, en 8 bits naar links verschuift, wat overeenkomt met vermenigvuldigen met 256. Als u een 32-bits microcontroller gebruikt (bijv. een Arduino Due), is veilig. De meeste Arduino's zijn echter gebaseerd op een 8-bits AVR-chip. Op hen is een int
16-bits en kan deze alleen waarden bevatten tot 32767. Als de beginwaarde groter is dan 127, dan veroorzaakt de bitverschuiving een overloop met gehele getallen met teken, die inC en C ++ zijn ongedefinieerd gedrag (lees: "illegale operatie"). Het is waarschijnlijk dat alles goed verloopt en u het verwachte resultaat krijgt, maar het is onverstandig om ooit te vertrouwen op ongedefinieerd gedrag in C of C ++.
De juiste manier om de verschuiving uit te voeren is om de beginwaarde naar een niet-ondertekend gegevenstype te casten, wat altijd veilig is vs. overflows, en doe dan de shift, zoals in
((uint16_t) Wire.read ()) << 8
Je mag de buitenste haakjes weglaten als je de prioriteiten van je operator kent.
Het tweede punt betreft het reeds genoemde feit dat de volgorde van de reads niet gespecificeerd is. De eenvoudigste oplossing is om één leesopdracht uit te voeren, zoals in:
AcX = (uint16_t) Wire.read () << 8; // lees eerst MSBAcX | = Wire.read (); // dan LSB
Het is vermeldenswaard dat de eerste instructie een impliciete cast heeft van uint16_t
(het resultaat van de verschuiving) naar int16_t
(het type AcX
). Dit kan ook overlopen als de verschoven waarde groter is dan 32767. er treedt echter een overloop op tijdens een conversie in niet ongedefinieerd, maar eerder implementatie gedefinieerd gedrag . Dit betekent dat de auteurs van de compiler moeten beslissen en documenteren wat er gebeurt. Arduino
code wordt meestal gecompileerd door gcc, en de gcc docs documenteren dit gedrag als
• Het resultaat van, of het signaal opgewekt door, het omzetten van een geheel getal naar een getekend geheel getal type wanneer de waarde niet kan worden weergegeven in een object van dat type (C90 6.2.1.2, C99 en C11 6.3.1.3).
Voor conversie naar een type met breedte N, de waarde is gereduceerd modulo 2 ^ N om binnen het bereik van het type te zijn; er wordt geen signaal gegeven.
Dit is precies wat we hier willen, en aangezien het de meest efficiënte keuze is voor een two-complement-architectuur, lijkt het onwaarschijnlijk dat een andere compiler een andere keuze zou maken.