Intro til Atmel Studio 6.1 - Bits&bytes Vi har nå vært igjennom et bittelite prosjekt med en LED og en knapp, husker du at vi måtte gjøre noe spessielt med PORTB i loopen vår så den skulle fortsette å være pulled-up mens vi blinket LED av og på? vel det er jo litt teit å måtte gå å huske på alle disse tingene hver gang man skal forandre på en port? ikkesant? Så i denne artiklen vil jeg fortelle litt om andre måter å gjøre ting som dette på, vi var jo såhvidt innom litt maskering på slutten av forrige prosjekt så vi kan fortsette litt derifra. Maskere betyr jo i utgangspunktet å legge noe over det vi ikke er interesert i og vi skal her bruke det en god del. 1. Byte En byte består av 8 bit, disse gies oftest nummer fra 0 til 7. (litt viktig å huske på når vi teller) En byte kan inneholde 256 forskjellige kombinasjoner. (0-255) 2. Bit En bit er en bit er en bit, den kan være høy eller lav. 3. Bit operasjoner C har flere funksjoner for å gjøre morsomme ting med bits, i forrige prosjekt var vi såhvidt innom bitvis AND operasjonen (&) vi har flere operasjoner for å leke med bits og de vil jeg kose litt med her: 3.1 & AND (OG) Flott operasjon for å maskere ut brytere med ikkesant? enkelt forklart så fungere den på dette viset: 1 & 1 = 1 0 & 1 = 0 1 & 0 = 0 0 & 0 = 0 Vi ser derfor at den eneste måten vi får 1 på er hvis begge argumentene er 1, hvis vi da flytter dette over til en byte: 10101010 & 00001111 = 00001010 Ser du hva vi har gjort her? Da vet du hvordan & fungerer! Kunne vi brukt dette i vårt forrige prosjekt til å slå LED av?
JAaa, det kunne vi gjort ved å AND'e PORTB med en byte hvor den bitten vi ønsker å slå av er den eneste som er lav, slik: PORTB = PORTB & 0b11101111; Hvis du ser av først eksempelet over så vil alle bittene holde seg slik de var utenom den 5 bitten (PORTB5) som må bli lav! 3.2 OR (ELLER) ELLER operasjonen er ikke det motsatte av OG! så er det sagt, det er en annen operasjon som vil gi høy dersom den ene eller den andre er høy. Eller fungerer på dette viset: 1 1 = 1 0 1 = 1 1 0 = 1 0 0 = 0 Og her ser vi at alle resultatene utenom dersom begge er lave blir høy, og hvis vi flytter dette over til en byte: 10101010 00001111 = 10101111 Så ser vi at vi nå med den andre byten har tvunget de 4 siste bitene høye, uavhengig om de var høye eller lave fra før, dette kunne vi brukt til å slå på LED i forrige prosjekt uten å måtte huske på å holde pull-up høy hele tiden ikkesant? slik kunne vi skrevet da: PORTB = PORTB 0b00100000; Et lite triks her er at dette også kan skrives slik :"PORTB = 0b00100000;" i C så finnes det mange finurligheter for å skrive det samme på forskjellige måter, jeg vil fortsette å bruke den lange varianten her for at ting skal være enkelt å forstå. 3.3 ~ NOT (IKKE) Denne er litt spesiel, det eneste denne gjør er å gjøre om ALLE bitene til det motsatte, denne trenger bare 1 argument slik: ~10101010 = 01010101 Ser du hva den gjør? bra, siden vi liker å bruke 1 til å indikere hvilken bit vi jobber med (jeg gjør hvertfall det ) så kan vi jo bruke denne til å finpusse litt på hvordan vi slo av LED i forrige prosjekt, der vi brukte 0 til å maskere LED. Så vi gjør om den da til: PORTB = PORTB & ~0b00100000; Ble pent ikkesant? I C så blir dette gjort mest for at det skal være lett for andre å forstå DITT program, så hvordan du ønsker å gjøre ting er opp til deg, men om andre også skal forstå ditt program så er det lurt å gjøre ting på en standard måte.
3.4 ^ XOR (XELLER) Eksklusiv ELLER er en avart av OR (ELLER) hvor den gir høy KUN dersom BARE den ene eller den andre er høy, sannhetstabellen ser slik ut: 1 ^ 1 = 0 0 ^ 1 = 1 1 ^ 0 = 1 0 ^ 0 = 0 Og her blir resultatet på byte testen vår : 10101010 ^ 00001111 = 10100101 Men hva skjer her da? her ser vi at dersom vi tester mot 0 så skjer det ingenting, men mot 1 så snur vi ting opp ned! På fagspråket heter det at vi Flipper bittene. Hmm, dette kan jo sikkert være til nytte i vårt forrige prosjekt... JAAA vi kan jo faktisk korte ned halve loop en vår med denne. Til nå så har vi forandret litt på programmet vårt og det burde se sånn ca slik ut: #define F_CPU 16e6 #include <avr/io.h> #include <util/delay.h> int main(void) DDRB = 0b00100000; PORTB = 0b00010000; while(1) if(!(pinb & 0b00010000)) //PORTB = 0b00110000; PORTB = PORTB 0b00100000; //PORTB = 0b00010000; PORTB = PORTB & ~0b00100000; Så tenker vi oss litt om hvordan vi kan bruke denne flip operasjonen, vi tar en ole brumm tenkestund, og kommer frem til: PORTB = PORTB ^ 0b00100000;
Denne vil slukke LED om den er høy og tenne LED om den er lav, så vi skriver om programmet vårt slik: Hehey! Dette begynner å bli avansert! eller? 3.5 << LEFT SHIFT (VENSTRE SKIFT) Venstre shift eller flytt er en hendig liten operasjon som ganske enkelt flytter alle bitter i en byte mot venstre x antall steg slik: Eller #define F_CPU 16e6 #include <avr/io.h> #include <util/delay.h> int main(void) DDRB = 0b00100000; PORTB = 0b00010000; while(1) if(!(pinb & 0b00010000)) //PORTB = 0b00110000; //PORTB = PORTB 0b00100000; PORTB = PORTB ^ 0b00100000; //PORTB = 0b00010000; //PORTB = PORTB & ~0b00100000; // 00000001 << 1 = 00000010 00000001 << 4 = 00010000 Dette kan brukes både til å faktisk flytte en bit i en byte, men det kan også brukes for å tydeliggjøre programmet ditt slik: Her skal vi gjøre akkurat det samme som i XOR eksempelet, vi tar PORTB = PORTB ^ 0b00100000; Og gjør den om til: PORTB = PORTB ^ (1 << 5); For det var jo den 5te bitten vi jobbet med ikkesant? Vi kunne likegjerne nå gi denne et navn : #define LED 5 PORTB = PORTB ^ (1 << LED); Og vi kunne brukt den korte varianten: #define LED 5 PORTB ^= (1 << LED);
Nå begynner ting å bli avansert nå! Neida, disse tingene her er absolutt barnemat, kansje litt mye å forstå i begynnelsen men dette er melk og brød i programmeringsverden. 3.6 >> RIGHT SHIFT (HØYRE SKIFT) Motsatt av LEFT SHIFT, flytter bittene x antall plasser mot høyre slik: 10101010 >> 3 = 00010101 Enkelt ikkesant! Legg merke til en ting som gjelder for både LEFT og RIGHT SHIFT er at det flytter IKKE det som faller ut på siden over til andre siden, de bare faller ut og blir borte i det tomme intet!! 4. MACRO Frem til nå har vi gjort ting veldig avansert, Nå skal vi prøve å gjøre ting enklere. Vi har nå lært hvordan vi manipulerer bits og bytes slik vi vil med å bruke AND, OR, XOR, NOT, LEFT SHIFT og RIGHT SHIFT. Vi skal nå lage oss et par makroer i C, makroer er ganske enkelt en slags funksjon som kan gjøre ting vi vil den skal gjøre, dette kan være avansert men vi skal ta det små steg av gangen. Den første macroen vi skal lage er en som skal sette den bitten vi vil høy. Vi tenker tilbake på bitmanipulasjonen og husker OR operasjonen. Eller: Byte Byte Byte (1<<BIT) En makro lages med #define uttrykket i førsten av programmet vårt sammen med andre #define, Når vi lager en slik makro i C er det standard at navnet vi gir den er i STOR SKRIFT, så vi kaller den SETBIT og sier ifra at den skal motta 2 variabler BYTEN og BITTEN. Deretter må vi fortelle hva vi vil at den skal gjøre med de, og slik blir det: #define SETBIT(BYTEN, BIT) (BYTEN = (1<<BIT)) Vi lager samtidig en CLRBIT macro : #define CLRBIT(BYTEN, BIT) (BYTEN &= ~(1<<BIT)) Du husker hvordan vi gjorde dette ikkesant? Og siden vi er så godt i gang så lager vi FLPBIT. #define FLPBIT(BYTEN, BIT) (BYTEN ^= (1<<BIT))
Da har vi noen flotte verktøy til å sette en bit høy elle lav, eller bare flippe den fra den ene til den andre uavhengig. Men hadde det ikke vært fint med en måte å sjekke en bit på også da? Vi kaster oss over CHKBIT. #define CHKBIT(BYTEN, BIT) (BYTEN & (1<<BIT)) Her ser du at vi bare AND er det er ikke noe = i den linjen ;) Så, nå har vi laget verktøyene, la oss bruke et par av de: #define F_CPU 16e6 #define SETBIT(BYTEN, BIT) (BYTEN = (1<<BIT)) #define CLRBIT(BYTEN, BIT) (BYTEN &= ~(1<<BIT)) #define FLPBIT(BYTEN, BIT) (BYTEN ^= (1<<BIT)) #define CHKBIT(BYTEN, BIT) (BYTEN & (1<<BIT)) #include <avr/io.h> #include <util/delay.h> int main(void) DDRB = 0b00100000; PORTB = 0b00010000; while(1) if(!(chkbit(pinb,4))) //////PORTB = 0b00110000; ////PORTB = PORTB 0b00100000; // Set //PORTB = PORTB ^ 0b00100000; // Flip FLPBIT(PORTB,5); //////PORTB = 0b00010000; ////PORTB = PORTB & ~0b00100000;// Clear // Nå har vi vært igjennom hvordan man manipulerer bits og bytes, hvordan man bygger en enkel macro og hvordan man kan bruke de. Nå skal vi bygge om hele vårt lille blinke med knappen program til å bruke alle hjelpemidler AVR studio har å hjelpe oss med. Og slik blir da hele programmet vårt seende ut :
#define F_CPU 16e6 ////////////////////////////////////////////////////////////////////////// #define SETBIT(BYTEN, BIT) (BYTEN = (1<<BIT)) #define CLRBIT(BYTEN, BIT) (BYTEN &= ~(1<<BIT)) #define FLPBIT(BYTEN, BIT) (BYTEN ^= (1<<BIT)) #define CHKBIT(BYTEN, BIT) (BYTEN & (1<<BIT)) ////////////////////////////////////////////////////////////////////////// #define LED PORTB5 #define KNAPP PORTB4 #define ER_KNAPP_LAV!CHKBIT(PINB,4) ////////////////////////////////////////////////////////////////////////// #include <avr/io.h> #include <util/delay.h> int main(void) SETBIT(DDRB,LED); SETBIT(PINB,KNAPP); while(1) if(er_knapp_lav) FLPBIT(PORTB,PORTB5); //LED = PORTB5 som utgang //KNAPP = PORTB4 slå på pull-up Nå ser det ganske så anderledes ut hva?, jeg har laget et par macroer til bare sånn for å vise at det går, ser du ER_KNAPP_LAV macroen, genial hva. La også inn noen // linjer for å skille ting litt fra hverandre. Ok tingen er den at i Atmel Studio som i arduino IDE så er det en MASSE ferdige definisjoner, ting som DDRB, PORTB virker jo ganske åpenlyst ikkesant, men det er faktisk definisjoner for bit også som du ser så definerer jeg LED som PORTB5, jeg kunne selvfølgelig bare skrevet 5 men da hadde jo hele poenget mitt her vært borte. Vi kommer mye mer tilbake til alle definisjoner når vi kommer til analog converter og andre mer avanserte ting senere.
5. Avslutning Da godtfolk har jeg kommet til enden av dagens lektyre, jeg har her sammenfattet barnelærdommen av bits & bytes og manipulasjon av disse, jeg har i det hele forsøkt å holde meg til vårt første eksempel og arbeidet ut ifra dette, det har nok gitt av eksempelet at det er noen ting som da ikke kommer med som burde ha vært med. 5.1 Sette flere navngitte bits samtidig Dette er noe som veldig ofte brukes når flere bit skal settes samtidig, gjerne i et register men kan også brukes fritt til andre ting. La oss gjøre et tenkt eksempel, vi ønsker oss bit 0,1,2 og 3 i PORTB som utganger og trenger da å skrive de høye, selvfølgelig går det fint å gjøre dette på 4 linjer med SETBIT, men som proffe programmerere vi skal bli (host) så gjøres dette oftere slik: DDRB &= (1<<PORTB0) (1<<PORTB1) (1<<PORTB2) (1<<PORTB3); Kan også se slik ut : DDRB &= (1<<PORTB0) (1<<PORTB1) (1<<PORTB2) (1<<PORTB3);