Bei der AVR-Programmierung werden Registerbits immer durch Verschieben von a 1
nach links an die entsprechende Bitposition gesetzt - und durch ein Einsenkomplement derselben gelöscht.
Beispiel: Für einen ATtiny85 könnte ich PORTB, b 4 wie folgt einstellen :
PORTB |= (1<<PB4);
oder löschen Sie es so:
PORTB &= ~(1<<PB4);
Meine Frage ist: Warum wird es so gemacht? Der einfachste Code ist ein Durcheinander von Bitverschiebungen. Warum werden Bits als Bitpositionen anstelle von Masken definiert?
Der E / A-Header für den ATtiny85 enthält beispielsweise Folgendes:
#define PORTB _SFR_IO8(0x18)
#define PB5 5
#define PB4 4
#define PB3 3
#define PB2 2
#define PB1 1
#define PB0 0
Für mich wäre es viel logischer, die Bits stattdessen als Masken zu definieren (wie folgt):
#define PORTB _SFR_IO8(0x18)
#define PB5 0x20
#define PB4 0x10
#define PB3 0x08
#define PB2 0x04
#define PB1 0x02
#define PB0 0x01
Also könnten wir so etwas machen:
// as bitmasks
PORTB |= PB5 | PB3 | PB0;
PORTB &= ~PB5 & ~PB3 & ~PB0;
um die Bits b 5 , b 3 und b 0 ein- bzw. auszuschalten. Im Gegensatz zu:
// as bit-fields
PORTB |= (1<<PB5) | (1<<PB3) | (1<<PB0);
PORTB &= ~(1<<PB5) & ~(1<<PB3) & ~(1<<PB0);
Der bitmask Code liest viel deutlicher: gesetzte Bits PB5
, PB3
und PB0
. Darüber hinaus scheint es Operationen zu sparen, da die Bits nicht mehr verschoben werden müssen.
Ich dachte, vielleicht wurde dies auf diese Weise getan, um die Allgemeinheit zu bewahren und den Portierungscode von einem n- Bit-AVR auf ein m- Bit (Beispiel 8-Bit auf 32-Bit) zu ermöglichen. Dies scheint jedoch nicht der Fall zu sein, da es #include <avr/io.h>
sich um Definitionsdateien handelt, die für den Ziel-Mikrocontroller spezifisch sind. Selbst das Ändern von Zielen von einem 8-Bit-ATtiny zu einem 8-Bit-Atmega (bei dem sich die Bitdefinitionen beispielsweise syntaktisch von PBx
zu ändern PORTBx
) erfordert Codeänderungen.
_BV(b)
anstelle von(1<<b)
macht die Dinge unnötig chaotisch. Ich definiere normalerweise Bit-Mnemonik mit_BV()
z#define ACK _BV(1)
.