Eerst op de PIC16F628A het volgende aansluiten:
- Een LED met 1k serieweerstand tussen pin 17 (PORTA.0) en GND
- Een LED met 1k serieweerstand tussen pin 18 (PORTA.1) en GND
- Een LED met 1k serieweerstand tussen pin 1 (PORTA.2) en GND
- Een (puls)schakelaar tussen pin 6 (PORTB.0) en GND
- Een (puls)schakelaar tussen pin 7 (PORTB.1) en GND
- Een (puls)schakelaar tussen pin 8 (PORTB.2) en GND
- En natuurlijk GND en +5V (denk aan de 100n condensator, zie deel 1)
Zie ook het schema hieronder:
IF...THEN...ELSEIF...ELSE...ENDIF
Eerst gaan we LED1 rechtstreeks met S1 aansturen, LED2 door S2 en LED3 door S3.
Als je een PIC opstart, staan alle pinnen standaard als ingangen ingesteld.
Door, zoals in deel 1, TOGGLE, HIGH en LOW te gebruiken zorgt de PIC Basic compiler ervoor dat deze automatisch uitgangen worden,
maar bij spanning inschakeling zijn het dus ingangen.
Om PORTB.0 dus PORTA.0 aan te laten sturen wil je zoiets:
Als PORTB.0 = laag, want de schakelaars zijn met GND verbonden, dan moet PORTA.0 hoog worden,
want in tegenstelling met deel 1, zitten de LED's nu niet aan +5V, maar aan GND (zie schema).
In PIC Basic schrijf je dan IF PORTB.0 = 0 THEN HIGH PORTA.0.
Maar in deel 1 heb je geleerd om de poorten eerst een naam te geven zoals we ook hier doen in onderstaand programma:
DEVICE 16F628A ;Gebruik een 16F628A type CONFIG INTRC_OSC_NOCLKOUT, WDT_OFF, PWRTE_ON, LVP_OFF, MCLRE_OFF ALL_DIGITAL TRUE ;Alle ingangen digitaal SYMBOL LED1 = PORTA.0 ;Poort A.0 heeft nu de naam LED1 SYMBOL LED2 = PORTA.1 ;Poort A.1 heeft nu de naam LED2 SYMBOL LED3 = PORTA.2 ;Poort A.2 heeft nu de naam LED3 SYMBOL S1 = PORTB.0 ;Poort B.0 heeft nu de naam S1 SYMBOL S2 = PORTB.1 ;Poort B.1 heeft nu de naam S2 SYMBOL S3 = PORTB.2 ;Poort B.2 heeft nu de naam S3 PORTB_PULLUPS ON ;On-chip pull-up weerstanden actief CLEAR ;Wis alle RAM geheugen ;Hoofdprogramma WHILE 1 = 1 ;Oneindige lus IF S1 = 0 THEN HIGH LED1 ;S1 gesloten (=laag) LED1 aan IF S2 = 0 THEN HIGH LED2 ;S2 gesloten (=laag) LED2 aan IF S3 = 0 THEN HIGH LED3 ;S3 gesloten (=laag) LED3 aan WEND END ;Einde programma |
DEVICE, CONFIG, ALL_DIGITAL en CLEAR zijn bekend van deel 1.
Daaronder geven we middels SYMBOL elke poort een naam, zie deel 1, dat is makkelijker met programmeren.
Het hoofdprogramma heeft een oneindige lus door WHILE 1=1, want 1 blijft natuurlijk altijd gelijk aan 1.
Dan IF(= als) S1 = 0 THEN (= dan) HIGH LED1.
Dit betekent zoiets: Als (IF) de schakelaar laag is (S1 = 0), want schakelaar gesloten = verbonden met GND,
dan (THEN) HIGH LED1 (PORTA.0 dus) hoog maken.
Het lijkt verkeerd om, als de schakelaar geopend is, dan is ingang PORTB.0 hoog en als de schakelaar gesloten is dan is PORTB.0 laag.
Dit komt omdat de schakelaar niet met +5V is verbonden maar met GND.
Het is overigens wel mogelijk om de schakelaars met +5V te verbinden maar dan moet er ook een weerstand (10k) worden aangesloten
tussen de poort en GND (dat heet pull-down).
Als je de schakelaars (zoals hier) verbind aan GND zijn deze weerstanden niet nodig
omdat de PIC16F628 op alle PORTB ingangen pull-up weerstanden in de chip zelf heeft.
Die kun je activeren met PORTB_PULLUPS ON, zoals hier ook is gedaan.
Om het eenvoudiger te maken geven we zodadelijk de constanten 0 en 1 een naam, we schrijven dan SYMBOL AAN = 0 en SYMBOL UIT = 1.
Als het programma loopt houdt hij continu de 3 ingangen in de gaten, immers, na WHILE staat er IF S1 = 0, maar die is niet 0,
dus volgende regel, IF S2 = 0, maar S2 is ook niet 0, dus volgende, enz. tot aan WEND, en dan begint hij weer opnieuw S1 te bekijken.
Dit alles gaat zo snel, dat het lijkt of hij alle 3 ingangen tegelijk in de gaten houdt.
Druk je nu op S1 dan wordt de ingang laag, als het programma nu weer langs de regel IF S1 = 0 komt,
dan blijkt dat S1 nu inderdaad 0 is, dus deze regel verder uitvoeren met wat erachter staat, en dat is HIGH LED1.
Maar als je de schakelaar weer loslaat, blijft LED1 branden!
Er staat namelijk nergens een opdracht die LED1 weer uit moet zetten, de regel die hem aan heeft gezet wordt niet meer uitgevoerd,
maar de uitgang is nu eenmaal al aangezet.
Om er voor te zorgen dat de LED weer uitgaat als je de schakelaar loslaat, kun je ELSE (= anders) toevoegen.
Je krijgt dan de zin IF S1 = 0 THEN HIGH LED1 ELSE LOW LED1,
wat zoiets betekent: Als S1 = 0 dan hoog LED1 anders laag LED1.
Wijzig het programma in het onderstaande:
DEVICE 16F628A ;Gebruik een 16F628A type CONFIG INTRC_OSC_NOCLKOUT, WDT_OFF, PWRTE_ON, LVP_OFF, MCLRE_OFF ALL_DIGITAL TRUE ;Alle ingangen digitaal SYMBOL AAN = 0 ;Schakelaar aan = laag (0) SYMBOL UIT = 1 ;Schakelaar uit = hoog (1) SYMBOL LED1 = PORTA.0 ;Poort A.0 heeft nu de naam LED1 SYMBOL LED2 = PORTA.1 ;Poort A.1 heeft nu de naam LED2 SYMBOL LED3 = PORTA.2 ;Poort A.2 heeft nu de naam LED3 SYMBOL S1 = PORTB.0 ;Poort B.0 heeft nu de naam S1 SYMBOL S2 = PORTB.1 ;Poort B.1 heeft nu de naam S2 SYMBOL S3 = PORTB.2 ;Poort B.2 heeft nu de naam S3 PORTB_PULLUPS ON ;On-chip pull-up weerstanden actief CLEAR ;Wis alle RAM geheugen ;Hoofdprogramma WHILE 1 = 1 ;Oneindige lus IF S1 = AAN THEN ;Als S1 aan is dan... HIGH LED1 ;LED1 aan LOW LED2 ;LED2 uit ELSE ;... anders ... LOW LED1 ;LED1 uit ENDIF ;Einde IF...THEN...ELSE blok van S1 IF S2 = AAN THEN HIGH LED2 ;Als S2=AAN dan LED2 aanzetten IF S3 = AAN THEN ;Als S3=AAN dan... HIGH LED3 ;LED3 aanzetten DELAYMS 500 ;Tijd van LED3 aan LOW LED3 ;LED3 uitzetten WHILE S3 = AAN ;Zolang S3 is ingedrukt... WEND ;...in dit lusje blijven wachten ENDIF ;Einde IF...THEN blok van S3 WEND END ;Einde programma |
Om het verwarrende van schakelaar aan = 0 en uit = 1 uit de wereld te helpen geven we de constante 0 en de constante 1 eerst een naam,
dus SYMBOL AAN = 0 en SYMBOL UIT = 1.
AAN is nu dus hetzelde als '0' en UIT heeft nu de waarde '1'.
Nu kun je gewoon schrijven voor "als S1 aan is dan" : IF S1 = AAN THEN,
wat dus hetzelfde is als IF PORTB.0 = 0 THEN, alleen veel duidelijker nu.
Wat nu doet het bovenstaande programma?
![]() |
De eerste IF...THEN behandeld S1. Als S1 AAN is dan LED1 aanzetten en LED2 uitzetten, anders (ELSE, dus als S1 niet aan is) LED1 uitzetten. |
![]() |
De tweede IF...THEN behandeld S2. Als S2 AAN is dan LED2 aanzetten. LED2 moet je dus aanzetten met S2 en uitzetten met S1. |
![]() |
De derde IF...THEN behandeld S3. Als S3 AAN is dan LED3 aanzetten en na 0,5 seconde (DELAYMS 500) automatisch weer uit. WHILE S3 = AAN : WEND is een leeg WHILE - WEND lusje. Deze wacht zolang S3 blijft ingedrukt. Als dit lusje er niet stond dan zal, nadat de DELAYMS tijd is afgelopen, de LED meteen weer aan gaan als de schakelaar wordt vastgehouden, nu moet de schakelaar eerst losgelaten worden, voordat het programma verder gaat. |
Nog even over ENDIF:
Als je met een IF...THEN maar 1 opdracht uit wilt voeren dan kun je dat meteen achter IF...THEN zetten zoals hierboven met S2 is gedaan.
Wil je meer dan 1 opdracht met IF...THEN uitvoeren dan moet je de opdrachten onder IF...THEN zetten,
zoals hierboven met S1 en S3 is gedaan.
Om nu de PIC Basic compiler te laten weten waar het blok eindigt moet na de laatste opdracht van dat IF...THEN blok worden afgesloten met ENDIF.
Een waarheidstabel programmeren met AND, OR en XOR
Stel dat onderstaand waarheidstabel in de PIC geprogrammeerd moet worden.
EN | A | B | UITGANGEN | |||
S1 | S2 | S3 | LED1 | LED2 | LED3 | |
L | L | L | H | L | H | |
L | H | L | H | H | L | |
L | L | H | H | H | L | |
L | H | H | L | L | L | |
H | X | X | L | L | L |
L = Laag als schakelaar is gesloten
X = Don't care (Maakt niet uit of schakelaar aan of uit is, resultaat blijft hetzelfde)
De waarheidstabel geeft het volgende aan:
Een uitgang kan alleen Hoog worden als de ENable schakelaar S1 aan is (Laag is).
Uitgang LED1 is alleen Hoog als schakelaar A (S2) en/of schakelaar B (S3) aan is (Laag is).
Uitgang LED2 is alleen Hoog als schakelaar A (S2) of schakelaar B (S3) aan is (Laag is).
Uitgang LED3 is alleen Hoog als schakelaar A (S2) en schakelaar B (S3) aan zijn (Laag zijn).
Wijzig het programma in het volgende:
DEVICE 16F628A ;Gebruik een 16F628A type CONFIG INTRC_OSC_NOCLKOUT, WDT_OFF, PWRTE_ON, LVP_OFF, MCLRE_OFF ALL_DIGITAL TRUE ;Alle ingangen digitaal SYMBOL AAN = 0 ;Schakelaar aan = laag (0) SYMBOL UIT = 1 ;Schakelaar uit = hoog (1) SYMBOL LED1 = PORTA.0 ;Poort A.0 heeft nu de naam LED1 SYMBOL LED2 = PORTA.1 ;Poort A.1 heeft nu de naam LED2 SYMBOL LED3 = PORTA.2 ;Poort A.2 heeft nu de naam LED3 SYMBOL S1 = PORTB.0 ;Poort B.0 heeft nu de naam S1 SYMBOL S2 = PORTB.1 ;Poort B.1 heeft nu de naam S2 SYMBOL S3 = PORTB.2 ;Poort B.2 heeft nu de naam S3 PORTB_PULLUPS ON ;On-chip pull-up weerstanden actief CLEAR ;Wis alle RAM geheugen ;Hoofdprogramma WHILE 1 = 1 ;Oneindige lus IF S1 = AAN THEN ;Als S1 aan is dan... IF S2 = AAN OR S3 = AAN THEN ;Als S2 en/of S3 aan is dan... HIGH LED1 ;LED1 aan ELSE ;... anders ... LOW LED1 ;LED1 uit ENDIF ;Einde IF...THEN...ELSE blok met OR IF S2 = AAN XOR S3 = AAN THEN ;Als S2 of S3 (niet beide) aan is... HIGH LED2 ;LED2 aan ELSE ;... anders ... LOW LED2 ;LED2 uit ENDIF ;Einde IF...THEN...ELSE blok met XOR IF S2 = AAN AND S3 = AAN THEN ;Als S2 en S3 aan zijn dan... HIGH LED3 ;LED3 aan ELSE ;... anders ... LOW LED3 ;LED3 uit ENDIF ;Einde IF...THEN...ELSE blok met AND ELSE ;... anders ... LOW LED1 ;LED1 uitzetten LOW LED2 ;LED2 uitzetten LOW LED3 ;LED3 uitzetten ENDIF ;Einde IF...THEN...ELSE blok van S1 WEND END ;Einde programma |
Als je goed kijkt zie je dat zowat de hele waarheidstabel met IF's alleen wordt uitgevoerd als schakelaar S1 AAN is,
want anders (ELSE) zet hij alle 3 LED's uit, en dan maakt het niet uit in welke stand S2 en S3 staan.
Om LED's te laten branden moet S1 dus sowieso aan staan.
Stel dat S1 aan is, dan gaat hij dat IF...THEN blok binnen:
![]() |
Daar staat meteen weer een IF...THEN blok, die gaat hij alleen uitvoeren als minimaal 1 van de 2 schakelaars (S2 en/of S3) AAN is, beide mogen dus ook AAN staan. |
![]() |
Dat geldt niet voor het tweede IF...THEN blok, daar staat XOR, dat betekent exclusief of, dus of alleen S2, of alleen S3, maar S2 en S3 mogen niet tegelijk aan zijn, dan gaat LED2 weer uit. |
![]() |
En bij het derde IF...THEN blok moeten juist S2 en (AND) S3 aan zijn om LED3 te laten branden. Is niet aan die voorwaarde voldaan dan zet hij LED3 uit via ELSE. |
Nesten
Je kunt dus een IF...THEN in een andere IF...THEN zetten, dat kun je nog veel vaker doen, dat heet nesten.
Dat nesten kan ook met WHILE - WEND en REPEAT - UNTIL, je kunt dus een WHILE - WEND lus in een andere WHILE - WEND lus plaatsen.
Nogmaals, overzicht is belangrijk
Het wordt al duidelijk waarom het programma gedeelte in een blok, 2 spaties moet inspringen,
of dat nu een IF...THEN, WHILE - WEND of een REPEAT - UNTIL blok is.
Zo is makkelijker te zien welke ENDIF bij welke IF hoort en welke WEND bij welke WHILE.
Ook regels overslaan en REM (remarks) regels schrijven achter de opdrachten zijn belangrijk.
En de variabelen, constanten en poorten namen geven door middel van SYMBOL.
Doe je dat niet dan is het overzicht snel verloren, zie hieronder hetzelfde programma als hierboven,
alleen zonder inspringen, REM en regels overslaan en de poorten geen naam (zoals S1 en LED1) gegeven.
DEVICE 16F628A CONFIG INTRC_OSC_NOCLKOUT, WDT_OFF, PWRTE_ON, LVP_OFF, MCLRE_OFF ALL_DIGITAL TRUE PORTB_PULLUPS ON CLEAR WHILE 1=1 IF PORTB.0=0 THEN IF PORTB.1=0 OR PORTB.2=0 THEN HIGH PORTA.0 ELSE LOW PORTA.0 ENDIF IF PORTB.1=0 XOR PORTB.2=0 THEN HIGH PORTA.1 ELSE LOW PORTA.1 ENDIF IF PORTB.1=0 AND PORTB.2=0 THEN HIGH PORTA.2 ELSE LOW PORTA.2 ENDIF ELSE LOW PORTA.0 LOW PORTA.1 LOW PORTA.2 ENDIF WEND END |
Als je dit programma compileert (Basic omzet naar .HEX bestand voor PIC's) dan werkt het net zo goed, maar als je na een paar maanden iets wilt veranderen is niet snel te zien hoe het programma ook alweer werkt, terwijl het programma in de PIC zelf hierdoor echt niets kleiner is geworden, daarvoor hoef je het dus ook niet te doen.
En dan heeft IF...THEN ook nog het ELSEIF commando.
Stel voor, een PIC-programma die maar 1 LED tegelijk mag laten branden, ook al staan er meer schakelaars aan.
Wijzig het programma hiervoor in het onderstaande:
DEVICE 16F628A ;Gebruik een 16F628A type CONFIG INTRC_OSC_NOCLKOUT, WDT_OFF, PWRTE_ON, LVP_OFF, MCLRE_OFF ALL_DIGITAL TRUE ;Alle ingangen digitaal SYMBOL AAN = 0 ;Schakelaar aan = laag (0) SYMBOL UIT = 1 ;Schakelaar uit = hoog (1) SYMBOL LED1 = PORTA.0 ;Op poort A.0 zit LED1 aangesloten SYMBOL LED2 = PORTA.1 ;Op poort A.1 zit LED2 aangesloten SYMBOL LED3 = PORTA.2 ;Op poort A.2 zit LED3 aangesloten SYMBOL S1 = PORTB.0 ;Op poort B.0 zit S1 aangesloten SYMBOL S2 = PORTB.1 ;Op poort B.1 zit S2 aangesloten SYMBOL S3 = PORTB.2 ;Op poort B.2 zit S3 aangesloten PORTB_PULLUPS ON ;On-chip pull-up weerstanden actief CLEAR ;Wis alle RAM geheugen ;Hoofdprogramma WHILE 1 = 1 ;Oneindige lus IF S1 = AAN THEN ;Als S1 = AAN dan... HIGH LED1 ;LED1 aan LOW LED2 ;LED2 uit LOW LED3 ;LED3 uit ELSEIF S2 = AAN THEN ;... anders, als S2 = AAN dan ... LOW LED1 ;LED1 uit HIGH LED2 ;LED2 aan LOW LED3 ;LED3 uit ELSEIF S3 = AAN THEN ;... anders, als S3 = AAN dan ... LOW LED1 ;LED1 uit LOW LED2 ;LED2 uit HIGH LED3 ;LED3 aan ELSE ;... anders ... (Zijn S1, S2 en S3 uit) LOW LED1 ;LED1 uitzetten LOW LED2 ;LED2 uitzetten LOW LED3 ;LED3 uitzetten ENDIF WEND END |
Als bovenstaand programma wordt uitgevoerd met gewoon IF, dan gebeuren er rare dingen als meer dan 1 schakelaar tegelijk aan wordt gezet.
LED's gaan dan op verschillende helderheid branden omdat ze afhankelijk van de schakelaars heel snel in de ene IF aan,
en in de andere IF weer uit worden gezet, ELSEIF voorkomt dat hier.
Het programma moet het volgende doen:
S1 zet LED1 aan, S2 zet LED2 aan en S3 zet LED3 aan, maar er mag in dit geval maar 1 LED tegelijk branden.
S2 heeft voorrang boven S3, en S1 heeft weer voorrang boven S2.
S1 heeft de hoogste prioriteit, zo wordt dat genoemd.
En S2 heeft een hogere prioriteit dan S3, maar een lagere prioriteit dan S1.
Dat betekent dat als S2 aan staat, dat LED2 brandt.
Als S3 ook aan wordt gezet gebeurt er niets zolang S2 aan blijft staan.
Wordt echter S1 aangezet dan gaat LED2 uit en LED1 aan omdat S1 dus een hogere prioriteit heeft.
ELSEIF voert dus maar 1 blokje van het hele IF...THEN blok tegelijk uit.
Nu IF...THEN behandeld is kan er nog een ontbrekende instructie behandeld worden voor lussen zoals
WHILE-WEND en REPEAT-UNTIL dat eigenlijk bij deel 1 hoorde en dat is het commando BREAK.
Met BREAK kun je voortijdig een lus verlaten:
DEVICE 16F628A ;We gebruiken een 16F628A type CONFIG INTRC_OSC_NOCLKOUT, WDT_OFF, PWRTE_ON, LVP_OFF, MCLRE_OFF ALL_DIGITAL TRUE ;Alle ingangen digitaal SYMBOL AAN = 0 ;Schakelaar aan = laag (0) SYMBOL AantalKnip = 5 ;Het aantal keer dat LED knippert SYMBOL LedTijd = 250 ;mSec, Knipper snelheid LED SYMBOL LED1 = PORTA.0 ;Op poort A.0 zit LED1 aangesloten SYMBOL LED2 = PORTA.1 ;Op poort A.1 zit LED2 aangesloten SYMBOL S1 = PORTB.0 ;Op poort B.0 zit S1 aangesloten DIM LedTeller AS BYTE ;Maak variabele LedTeller aan PORTB_PULLUPS ON ;On-chip pull-up weerstanden actief CLEAR ;Wis alle RAM geheugen ;Hoofdprogramma LedTeller = AantalKnip * 2 ;Stel eerst de teller in REPEAT ;Herhaal onderstaande lus... TOGGLE LED1 ;Knipper LED DELAYMS LedTijd ;Knippersnelheid LED IF S1 = AAN THEN BREAK ;Ook uit de lus springen als S1 = aan DEC LedTeller ;Teller met 1 verlagen UNTIL LedTeller = 0 ;...totdat LedTeller op 0 staat HIGH LED2 ;LED2 aan END ;Einde programma |
Om LED1 5× te laten knipperen moet de lus 10× worden doorlopen, want 5× de LED aan en 5× de LED uit, dus: LedTeller = AantalKnip × 2.
LED1 knippert maximaal 5 keer en springt dan uit de REPEAT - UNTIL lus.
Echter, als nu op S1 wordt gedrukt springt het programma meteen uit de lus (...THEN BREAK) ook al is LedTeller nog niet 0.
Het programma gaat verder bij HIGH LED2, dus LED2 aan zetten.
Let wel even op dat S1 even wordt vastgehouden, omdat het programma de meeste tijd in de DELAYMS tijd zit te wachten en
op dat moment dus niet naar de PORTB.0 kijkt waar S1 op zit aangesloten.
Het direct uit de lus springen als er ook maar heel kort op S1 wordt gedrukt komt later in deze cursus aan bod.
In deel 3 worden ingangen en uitgangen op een betere manier gedefinieerd en wordt wat over het kristal uitgelegd.