In onze vorige tutorial hebben we geleerd over het knipperen van een LED met behulp van PIC-microcontroller en hebben we hetzelfde circuit op Perf-bord gebouwd. Vervolgens hebben we PICkit 3, ICSP en MPLAB IPE gebruikt om het programma op ons Perf-bord te dumpen. Nu gaan we in deze tutorial verder met het gebruik van meer pinnen op de PIC-microcontroller. We zullen 7 uitgangen (LED's) en één ingang gebruiken. Voor deze tutorial gebruiken we het oude Perf-bord (hieronder weergegeven) en voegen we bergsticks toe om de vereiste pinnen op het tweede LED-bord te trekken. Aan het einde van deze tutorial zullen we een reeks knipperende LED's genereren met behulp van PIC-microcontroller PIC16F877A en zullen we leren hoe we meerdere in- en uitgangen kunnen gebruiken, wat basisprincipes over 'for'-lus en functieaanroepen.
Het LED-bord is niets anders dan een ander perf-bord, waarop we de LED's zullen solderen met een stroombegrenzende weerstand (zie hieronder). We zullen ook een drukknop toevoegen om de sequentie-LED te laten knipperen.
Schakelschema:
PIC Microcontroller PIC16F877A LED knipperende sequentiecode en werkuitleg:
De volledige code is hieronder gegeven (controleer aan het einde), hier zullen we het regel voor regel doornemen. Deze code zal LED's op een opeenvolgende manier laten gloeien wanneer de drukknop wordt ingedrukt. Bekijk de video aan het einde van de tutorial om de sequenties te begrijpen. Ik zou je aanraden om de output getoond in video te vergelijken met de onderstaande code en het programma te proberen te begrijpen.
Laten we de code regel voor regel bekijken. De eerste paar regels zijn voor het instellen van configuratiebits die in de vorige tutorial zijn uitgelegd, dus ik sla ze voorlopig over. De beste manier om een programma te begrijpen, is door te beginnen met de hoofdfunctie ( void main () ), dus laten we dat doen
TRISB0 = 1; // Geef de MCU de instructie dat de PORTB-pin 0 wordt gebruikt als invoer voor de knop. TRISD = 0x00; // Geef de MCU de instructie dat alle pinnen worden uitgevoerd PORTD = 0x00; // Initialiseer alle pinnen op 0
Het woord TRIS wordt gebruikt om te definiëren of de pin wordt gebruikt als input / output en het woord PORT wordt gebruikt om een pin hoog / laag te maken. De lijn TRISB0 = 1 zal de 0e pin van POORT B als invoer maken. Dit wordt onze drukknop. De regels TRISD = 0x00; PORTD = 0x00; zal alle pinnen van poort D als uitvoer maken en een beginwaarde van LOW aan die pinnen toewijzen.
Omdat we zeiden dat B0 wordt gebruikt als input, zullen we het ene uiteinde van de drukknop verbinden met de pin B0 en het andere uiteinde met de aarde. Tegen die tijd, telkens wanneer we op de knop drukken, wordt de pin tegen de grond gehouden, zoals weergegeven in het bovenstaande aansluitschema. Maar om dit mogelijk te maken, moeten we een pull-up-weerstand gebruiken, zodat de pin hoog wordt gehouden als de knop niet wordt ingedrukt. Een optrekweerstand is zoiets als dit.
Maar onze PIC MCU heeft een interne zwakke pull-up weerstand die op die manier door software kan worden geactiveerd, wat een hoop gedoe bespaart (als er meer knoppen moeten worden aangesloten).
Wat is een zwakke pull-up-weerstand?
Er zijn twee soorten pull-up-weerstanden, de ene is de zwakke pull-up en de andere is de sterke pull-up. De zwakke pull-up weerstanden zijn van hoge waarde en laten daardoor een zwakke stroom door en de sterke pull up weerstanden zijn van lage waarde waardoor er een sterke stroom kan vloeien. Alle MCU gebruiken meestal zwakke pull-up-weerstanden. Om dit in onze PIC MCU te activeren, moeten we ons gegevensblad voor de OPTION_REG (optieregister) bekijken, zoals weergegeven in de onderstaande momentopname.
Zoals getoond, behandelt bit 7 de zwakke pull-up-weerstand. Het moet nul worden gemaakt om het te activeren. Dit wordt gedaan door OPTION_REG <7> = 0 . Dit heeft specifiek betrekking op bit 7 en laat de andere bits op de standaardwaarden staan. Hiermee komen we in onze while-lus, waar het controleert of de knop wordt ingedrukt door if (RB0 == 0) te gebruiken. Als aan de voorwaarde is voldaan, bellen we onze functie met de parameters 1, 3, 7 en 15.
sblink (1); // FUNCTION CALL 1 met parameter 1 sblink (3); // FUNCTION CALL 3 met parameter 3 sblink (7); // FUNCTION CALL 7 met parameter 7 sblink (15); // FUNCTION CALL 4 met parameter 15
Waarom gebruiken we functies?
Functies worden gebruikt om het aantal regels in onze code te verminderen. Dit is wat de meesten van ons zouden hebben geweten. Maar waarom moeten we het aantal regels verminderen, vooral als het gaat om MCU-programmering. De reden is de beperkte ruimte in ons programmageheugen. Als we de code niet correct optimaliseren, is er mogelijk onvoldoende geheugenruimte beschikbaar. Dit is handig als we lange pagina's met codes schrijven.
Elke functie heeft een functie-definitie ( sblink (int get) in ons geval) en een functie-oproep ( sblink (1) in ons geval). Het is optioneel om een functiedeclaratie te hebben, om dit te vermijden heb ik mijn functiedefinitie geplaatst voordat ik de functie in mijn hoofdfunctie aanroep.
Functieparameters zijn de waarde die wordt doorgegeven van de functieaanroep aan de functiedefinitie. In ons geval zijn de integerwaarden (1, 3, 7, 15) de parameters die worden doorgegeven vanuit de functieaanroep en de variabele "get" haalt de waarde van de parameters op in de functiedefinitie om ze te verwerken. Een functie kan meer dan één parameter hebben.
Zodra de functie is aangeroepen, worden de onderstaande regels in de functiedefinitie uitgevoerd.
voor (int i = 0; i <= 7 && RB0 == 0; i ++) {PORTD = haal << i; // LED beweegt naar links Volgorde __delay_ms (50); } for (int i = 7; i> = 0 && RB0 == 0; i--) {PORTD = haal << i; // LED beweegt naar links Volgorde __delay_ms (50); }
Nu lijkt deze regel vreemd te zijn: PORTD = haal << i . Ik zal uitleggen wat hier werkelijk gebeurt.
"<<" is een linker shift-operator die alle bits naar zijn linkerpositie verschuift. Als we nu de functie sblink (int get) aanroepen met parameter '1' als sblink (1), wordt de waarde van 'get' gelijk aan 1, wat in binair getal 0b00000001 is. Daarom zal deze regel zijn als PORTD = 0b00000001 << i .
De waarde van "i" zal variëren van 0 tot 7 aangezien we een 'for-lus' hebben gebruikt voor (int i = 0; i <= 7 && RB0 == 0; i ++). De waarde van 'i' die van 0 tot 7 is, zal het resultaat als volgt veranderen:
Zoals u kunt zien, hebben we één LED per keer ingeschakeld (van links naar rechts) door de rest UIT te houden. De volgende 'for-lus' voor (int i = 7; i> = 0 && RB0 == 0; i--) , zal ook hetzelfde doen, maar deze keer zal de LED achtereenvolgens van rechts naar links AAN gaan, zoals we begonnen van 7 en naar beneden gingen naar 0. We hebben een vertraging van 200 ms gebruikt zodat we kunnen zien dat de LED AAN en UIT gaat.
Als we nu waarde 3 doorgeven in de functie sblink (int get) , wordt de functie sblink (3) uitgevoerd waardoor de waarde van 'get' 0b00000011 wordt, vandaar dat het resultaat op PORTD zal zijn:
Dus nu zullen deze keer twee LED's op elk moment worden ingeschakeld door sblink (3) te gebruiken. Op dezelfde manier zullen voor sblink (7) en sblink (15) drie en vier LED's achter elkaar branden . Zodra dit is voltooid, zorgen we ervoor dat alle LED's branden met behulp van de regel PORTD = 0xFF . Bekijk de video hieronder voor een volledige demonstratie.
Ik hoop dat je de code hebt begrepen en dus hebt geleerd hoe je functies, 'for' en 'while'-lus kunt gebruiken om de gewenste outputs te krijgen. Nu kunt u de code aanpassen om uw verschillende reeks LED's te laten knipperen. Ga je gang, compileer je code en dump deze op je MCU en geniet van de uitvoer. Je kunt het commentaargedeelte gebruiken als je ergens vastloopt. Ik heb hier ook de simulatie- en programmabestanden bijgevoegd.
Dat is het voor nu, in onze volgende tutorial zullen we leren hoe we PIC16F877A-timers kunnen gebruiken in plaats van vertragingsfuncties te gebruiken. Je kunt hier door alle PIC-microcontroller-tutorials bladeren.