Kategori: Bitfikling og registere
Treff: 5245

Her fortsetter vi litt på forrige artikkel om bitfikling og lager en binærteller med den samme kretsen.

Knappene skal brukes til å telle opp og ned, samt å nulle ut telleren.

Har laget to enkle programmer, det første bare med en ren binærteller, og det andre inkluderer knappene for å telle opp, telle ned og å resette telleren. 

Vi bruker den samme kretsen som i den første bitfiklings-artikkelen.

Kretsen er bygd opp med en Arduino Nano med 6 lysdioder og tilhørende seriemotstander. I tillegg har vi 3 trykknapper som vi skal bruke senere. Skjemaet er tegnet i Proteus, som angir pinnenummere litt annerledes. IO0, IO1, IO3 osv. tilsvarer D0, D1, D2, og de analoge pinnene angis med AD0, AD1 osv i stedet for A0, A1. For å gjøre oppkoblingen så enkel som mulig, bruker vi de interne pullup-motstandene på knappene slik at knappene kortslutter pinnen direkte mot jord når de aktiveres.

(Høyreklikk og velg "Åpne bilde i ny fane" for større bilde)

I det første programmet I den første programmet tester vi bare lysdiodene, og teller fra 0 til 63 i en for-loop. Dette programmet blir heller ikke -helt- uten Arduino-funksjoner. Vi bruker delay-funksjonen slik at vi rekker å se hva som foregår. Setup-funksjonen blir den samme som i det andre test-programmet.

Program med enkel binær-teller:

void setup() {
//Setter opp de 6 pinnene som utganger
DDRB=0b00111111; //DDRB styrer port B på mikrokontrolleren
//Starter serieporten
Serial.begin(115200);
}

int teller; //Oppretter en telle-variabel

void loop() {
for (teller=0;teller<64;teller++){
PORTB=teller; //Skriver verdien til PORT B
delay(333); //Ca. 3 verdier i sekundet
}
}

Når du kjører programmet over vil du se at det teller binært (totallssystemet) fra 0 til 63, og starter på 0 igjen. Fungerer dette som det skal, er vi klare for å se på knappene.

Som nevnt over har jeg koblet knappene direkte mellom den aktuelle pinnen og jord. Det vil si at knappene blir aktiv lav, altså vi må sjekke om pinnen(e) er 0, eller LOW før vi kjører de kodelinjene vi ønsker.

Kort forklart skal programmet først skrive ut den nåværende verdien av teller til PORT B, sjekke knapp 1, knapp 2 og knapp 3 og eventuelt kjøre et par linjer kode hvis en av knappene er inntrykt. Så starter alt på nytt igjen. Koden for å sjekke om en knapp er trykt inn er litt spesiell, så jeg forklarer den under programmet.

Program (nesten) uten Arduino-funksjoner:

const int debug = 1; //Sett denne til 1 hvis du vil ha utskrift til serieport, 0 hvis ikke
int teller = 0; //Oppretter telle-variabelen vår

void setup() {
DDRB = 0x3f; //Setter bit 0-5 til utganger, angitt heksadesimalt. Tilsvarer 0b00111111
DDRD = 0x00; //Setter hele port D til innganger. Egentlig unødvendig, men greit å vise det i programmet.
PORTD |= 0b01110000; //Aktiverer interne pullup-motstander på pinnene hvor knappene er koblet. (forklares under)
if (debug) Serial.begin(115200); //Starter serieporten hvis debug er satt til 1
}

void loop() {
PORTB = teller; //Skriver verdien til port B
if (!(PIND & (1 << PD4))) { //Sjekker om knapp på PD4 er trykt inn (forklares under)
if (teller < 63) teller++; //Øker telleren med 1 hvis det ikke har kommet for høyt
delay(200); //Liten pause for at det ikke skal telles for fort (og ta unna prell)
}
if (!(PIND & (1 << PD5))) { //Sjekker om knapp på PD5 er trykt inn (forklares under)
if (teller > 0) teller--; //Reduserer telleren med 1 hvis den er større enn 0
delay(200);
}
if (!(PIND & (1 << PD6))) { //Sjekker om knapp på PD6 er trykt inn (forklares under)
teller = 0; //Setter telleren til 0
delay(200);
}
if (debug) {
Serial.print(teller); //Skriver ut telleren desimalt
Serial.print(" 0b"); Serial.print(teller, BIN); //Skriver ut telleren binært
Serial.print(" - PORT D: "); Serial.println(PIND & 0b01110000, BIN); //Skriver ut status på Port D, binært
}
}

 

Jeg lover en del forklaringer i kommentarene i programmet, og det trengs nok.

I setup-funksjonen finner vi denne:  PORTD |= 0b01110000; 
Når bruker pinMode-funksjonen kan vi bruke INPUT_PULLUP som en parameter for å aktivere pullup-motstand på en pinne. Denne linje gjør det samme, men for pinnene PD4, PD5 og PD6 samtidig. Pullup aktiveres ved å skrive en 1'er til den akutelle biten i registeret. Det er viktig at dette gjøres etter at pinnen/porten settes til inngang. Når vi bruker vertikal strek | før = betyr det at vi bruker bitvis eller. Dette gjør vi for å sette bare de bit'ene vi er interessert i, og lar de andre være uforstyrret. Du finner litt mer info om dette under "Operatorer"

Det som kanskje er mest forvirrende er nok sjekkingen av knappene. Som du ser bruker jeg et register som heter PIND der. Det henger sammen med PORTD som er til "skriving" men PIND brukes til lesing, eller sjekking av innholdet på en port. Det første i if'en er utropstegnet før hele uttrykket. Det betyr logisk ikke, eller NOT, altså vi sjekker om uttrykket i parantesen bak er 0. Uttrykket (1 << PD4) brukes for å spesifisere hvilken bit vi er interessert i. Binært blir det 00010000. Deretter bruker vi bitvis OG ( & ) mellom denne verdien og verdien som leses fra PIND.

Hvis ingen knapper er trykt inn vil PIND være 01110000. Bitvis OG mellom 00010000 og 01110000 vil bli større en null, og dermed "feiler" testen. Hvis knappen (PD4) derimot er trykt inn vil PIND være 01100000 (bit 4 er 0), og bitvis OG vil gi 0. Det er enklere å se hvis vi setter verdiene under hverandre:

Knapp åpen:
PIND:      01110000
1<<PD4:    00010000
Bitvis OG: 00010000   

Knapp inntrykt:
PIND:      01100000
1<<PD4:    00010000
Bitvis OG: 00000000

Jeg vet dette er tung materie, og litt vanskelig å "wrap your head around", men det er absolutt en teknikk som er verd å lære seg. Før vi begynte med Arduino i undervisningen brukte vi AVR-kontrollere direkte, og da laget jeg et "cheat-sheet" som kan være greit å bruke til alt dette. Kan lastes ned som PDF-fil her:

Det er bare å spørre i kommentarfeltet hvis du lurer på noe.

Happy hacking!