W programowaniu AVR bity rejestru są niezmiennie ustawiane przez przesunięcie w lewo 1
do odpowiedniej pozycji bitu - i są usuwane przez uzupełnienie jedynkami.
Przykład: dla ATtiny85 mógłbym ustawić PORTB, b 4 w ten sposób:
PORTB | = (1<<PB4);
lub wyczyść to w ten sposób:
PORTB & = ~ (1<<PB4);
My Pytanie brzmi: Dlaczego jest to zrobione w ten sposób? Najprostszy kod kończy się bałaganem przesunięć bitowych. Dlaczego bity są definiowane jako pozycje bitów zamiast masek.
Na przykład nagłówek IO dla ATtiny85 zawiera to:
#define PORTB _SFR_IO8 (0x18)
# zdefiniować PB5 5
# zdefiniować PB4 4
# zdefiniować PB3 3
# zdefiniować PB2 2
# zdefiniować PB1 1
# zdefiniować PB0 0
Dla mnie o wiele bardziej logiczne byłoby zdefiniowanie bitów jako masek (w ten sposób):
#define PORTB _SFR_IO8 (0x18)
# zdefiniować PB5 0x20
# zdefiniować PB4 0x10
# zdefiniować PB3 0x08
# zdefiniować PB2 0x04
# zdefiniować PB1 0x02
# zdefiniować PB0 0x01
Moglibyśmy więc zrobić coś takiego:
// jako maski bitowe
PORTB | = PB5 | PB3 | PB0;
PORTB & = ~ PB5 & ~ PB3 & ~ PB0;
, aby odpowiednio włączać i wyłączać bity b 5 , b 3 i b 0 . W przeciwieństwie do:
// jako pola bitowe
PORTB | = (1<<PB5) | (1<<PB3) | (1<<PB0);
PORTB & = ~ (1<<PB5) & ~ (1<<PB3) & ~ (1<<PB0);
Kod maski bitowej czyta znacznie wyraźniej: ustaw bity PB5
, PB3
i PB0
. Ponadto wydaje się, że oszczędza operacje, ponieważ bity nie muszą już być przesuwane.
Pomyślałem, że może zostało to zrobione w ten sposób, aby zachować ogólność, aby umożliwić przeniesienie kodu z n -bitowego AVR na m -bitowy (przykład 8-bitowydo wersji 32-bitowej).Ale wydaje się, że tak nie jest, ponieważ #include <avr / io.h>
rozwiązuje się jako pliki definicji specyficzne dla docelowego mikrokontrolera.Nawet zmiana celów z 8-bitowego ATtiny na 8-bitową Atmega (gdzie definicje bitów zmieniają się składniowo z PBx
na PORTBx
, na przykład), wymaga zmian w kodzie.