Beginners mini-cursus (deel 9)

Gegevens opslaan in de interne EEPROM van de PIC

EWRITE Schrijft variabelen naar de interne EEPROM
EREAD Leest variabelen van de interne EEPROM
EDATA Plaatst constanten of strings direct in de interne EEPROM

 

In een EEPROM kun je waarden van variabelen bewaren (= opslaan) en (later) weer terug halen (= uitlezen).
Gegevens opgeslagen in een EEPROM blijven ook bewaard als de voeding wordt uitgeschakeld (of als de stroom uitvalt), zonder gebruik van een back-up batterij (zoals een USB-stick).
Het mooie hiervan is dat je programma zo de waarde van variabelen (van bijvoorbeeld instellingen) weer kan opvragen als de spanning weer wordt ingeschakeld.

Stel dat de PIC bijvoorbeeld het hart van een klok-thermostaat is, dan staan de door de gebruiker ingestelde schakeltijden en (dag en nacht) verwarmingstemperaturen in variabelen (RAM), maar als de stroom uitvalt dan zijn alle instellingen verdwenen.
Door nu de variabelen, die gegevens bevatten die bewaard moeten blijven, op te slaan in een EEPROM geheugen, blijven de instellingen wél behouden, óók na stroom uitval.

De meeste PIC types hebben een EEPROM met een beperkte hoeveelheid geheugen aan boord voor het opslaan van informatie en gegevens.
De grootte van dit geheugen is afhankelijk van het type PIC, maar is nooit apart groot.
Zo heeft de 16F84 maar 64 bytes EEPROM geheugen en de 16F877 heeft ruimte voor 256 bytes.
Om grotere hoeveelheden informatie op te slaan kun je gebruik maken van een externe EEPROM.
Niet alle PIC types hebben overigens een EEPROM aan boord.

De in deze cursus gebruikte 16F628A heeft 128 bytes geheugen.
In 128 bytes geheugen kun je dus 1024 bits (= 128 × 8) opslaan, of 128 BYTE variabelen, of 64 WORD variabelen, of 32 DWORD variabelen, of een mix van dit alles.

Op zich is het met PIC Basic eenvoudig om met de ingebouwde EEPROM te werken.
Toch is het handig als je al wat meer van bits, bytes, words en dwords af weet.
Ikzelf vind het werken met de interne EEPROM in PIC Basic zeer eenvoudig, maar vond dit cursusdeel tot nu toe de lastigste om voor beginners te omschrijven en ben over de beschrijving hier (nog) niet echt tevreden.


EWRITE

Om gegevens naar de interne EEPROM toe te schrijven (= write) is er de instructie EWRITE (= eeprom write).

De syntaxis
 EWRITE Adres, [ Variabele {, Variabele2, Variabele3, ... }

Direct achter het keyword EWRITE schrijf je het adres.
Stel je voor dat je een aantal hokjes hebt waarvan elk hokje één byte (= 8 bits) kan opslaan.
Met Adres geef je dan aan in welk hokje je iets wilt opslaan.
Voor de 16F628A is dat dus een getal van 0 ... 127, omdat dit PIC type een EEPROM heeft die maximaal 128 bytes kan opslaan.

0
00000111
1
11011101
2
11111111
3
11001001
4
00110000
 ... 
 
127
10011011

Adres mag ook een variabele of constantenaam zijn waardoor makkelijker is te onthouden, op welk adres een bepaalde waarde staat.
Door adres 0 bijvoorbeeld 'EE_DagTemperatuur' te noemen en adres 1 'EE_NachtTemperatuur', weet je dat de ingestelde waarde voor de temperatuur 's nachts op EEPROM adres 'EE_NachtTemperatuur' staat (zie zodadelijk voorbeelden).

Na het adres zet je een komma met daarachter tussen blokhaken [  ] de naam van de variabele die in dat hokje moet worden opgeslagen.
Stel dat de BYTE variabele met de naam 'PietJanHein' de waarde 7 heeft en je wilt dit getal in de ingebouwde EEPROM van de PIC bewaren, dan kies je een hoknummer (= het adres dus) waarin je deze waarde wilt bewaren, bijvoorbeeld het allereerste hokje, oftewel adres 0.
Dat schrijf je dan als volgt:

EWRITE 0, [PietJanHein] ;Schrijf de waarde van 'PietJanHein' naar EEPROM adres 0 

Met deze opdracht wordt de waarde die variabele 'PietJanHein' op dat moment heeft naar adres 0 gekopieerd.
De waarde 7 (die 'PietJanHein' hier als voorbeeld heeft) is nu dus ook op EEPROM adres 0 (het allereerste hokje) opgeslagen.
Als de spanning van de PIC wordt uitgeschakeld of uitvalt, is 'PietJanHein' zijn waarde kwijt, maar op EEPROM adres 0 staat nog wél steeds de waarde 7.
Door er nu voor te zorgen dat, als de PIC opnieuw wordt opgestart, de variabele 'PietJanHein' eerst weer de waarde op EEPROM adres 0 uitleest, heeft 'PietJanHein' weer de (voorbeeld)waarde 7.

Overigens kan elke EWRITE opdracht tot 10mSec tijd per adres innemen, dit ligt aan de PIC, niet aan Positron PIC Basic.


EREAD

Om de met EWRITE weggeschreven gegevens naar de EEPROM van de PIC weer terug te lezen (= read) in een variabele is er de instructie EREAD (= eeprom read).
Met EREAD lees je welke waarde er op het opgegeven adres staat.

De syntaxis
 Variabele = EREAD Adres 

Direct achter het keyword EREAD schrijf je het adres van welk "hokje" je de waarde wilt hebben.
Voor de 16F628A is dat dus een getal van 0 ... 127, omdat dit PIC type een 128 bytes EEPROM heeft.
Adres mag ook een variabele of constantenaam zijn (zie zodadelijk voorbeelden).

Vóór de EREAD instructie komt de variabelenaam waar de waarde van het opgegeven EEPROM adres naar toe moet worden gekopieerd:

JanKlaas = EREAD 0      ;Lees waarde op EEPROM adres 0 en geef aan 'JanKlaas'    

Als eerder bijvoorbeeld de waarde 7 op adres 0 was opgeslagen met EWRITE, dan heeft de variabele 'JanKlaas' na uitvoering van bovenstaand programmaregel deze waarde (als voorbeeld 7 dus) overgenomen.

Het lezen van de EEPROM met EREAD neemt vrijwel geen tijd in.
Dit in tegenstelling tot schrijven naar EEPROM met EWRITE, dat tot 10mSec per adres kan duren.


Voorbeelden
Met het volgende voorbeeld is een LED aan en uit te zetten met een druktoets.
Als de spanning wordt uitgeschakeld en dan weer wordt ingeschakeld zal de PIC nog steeds weten of de LED aan of uit stond, vóórdat de spanning werd uitgeschakeld.
De LED zit hier (met serieweerstand van 1k) tussen PORTA.2 en GND, en schakelaar S1 zit tussen PORTB.0 en GND.

DEVICE 16F628A                ;Gebruik een 16F628A type
CONFIG INTRC_OSC_NOCLKOUT, WDT_OFF, PWRTE_ON, LVP_OFF, MCLRE_OFF
ALL_DIGITAL TRUE              ;Alle ingangen digitaal

;Algemene constanten
SYMBOL AntiDender   = 10      ;mSec: Tegen contactdender

;Logische constanten
SYMBOL AAN  = 0               ;Geinverteerd AAN
SYMBOL UIT  = 1               ;Geinverteerd UIT

;Poortnamen
SYMBOL LED  = PORTA.2         ;LED wordt aan/uit gezet door toetsindruk
SYMBOL S1   = PORTB.0         ;Poort B.0 heet nu S1 waarmee de LED wordt getoggled

;        76543210
PORTA = %00000000             ;PORTA uitgangen bij opstart een laag niveau
TRISA = %11111011             ;PORTA.2 is een uitgang voor de LED
TRISB = %11111111             ;PORTB allen ingangen

PORTB_PULLUPS ON              ;On-chip pull-up weerstanden actief voor toets 
CLEAR                         ;Wis alle RAM geheugen


;Hoofdprogramma
LED = EREAD 0                 ;Lees EEPROM of de LED aan of uit was bij stroomuitval 

WHILE 1 = 1                   ;Oneindige lus
  WHILE S1 = UIT : WEND       ;Wacht op een toetsindruk
  DELAYMS AntiDender          ;Tegen contactdender bij indrukken van de toets

  LED = ~LED                  ;Toggle de LED
  EWRITE 0, [LED]             ;Schrijf naar EEPROM of de LED aan of uit is

  WHILE S1 = AAN : WEND       ;Wacht tot toets wordt losgelaten
  DELAYMS AntiDender          ;Tegen contactdender bij loslaten van de toets
WEND

END

Voordat het programma in een oneindige WHILE ... WEND lus komt, wordt eerst de EEPROM op adres 0 uitgelezen en de waarde aan PORTA.2 (met de naam 'LED') gegeven:

LED = EREAD 0 

Dan wacht het programma op een toetsindruk, en zodra daar opgedrukt is wordt de poort 'LED' getoggled (is LED aan dan LED uitzetten, anders LED aanzetten).
Meteen daarna wordt de nieuwe stand in de EEPROM op adres 0 gezet.

EWRITE 0, [LED]

Het getal direct achter EWRITE geeft het (vanaf...) adres aan.

In bovenstaand voorbeeld is 'LED' geen variabele maar een poort (PORTA.2).
Ook dat is dus mogelijk.


In het volgende voorbeeld is een teller met HD44780 display gemaakt om de werking van EWRITE en EREAD beter te laten zien.
Sluit hiervoor pulstoetsen aan op PORTB.0 en PORTB.1 en op de overige PORTB poorten een HD44780 display volgens de standaard van PIC Basic (zie cursus deel 4).

DEVICE 16F628A                ;Gebruik een 16F628A type
CONFIG INTRC_OSC_NOCLKOUT, WDT_OFF, PWRTE_ON, LVP_OFF, MCLRE_OFF
ALL_DIGITAL TRUE              ;Alle ingangen digitaal

;Logische constanten
SYMBOL AAN          = 0       ;Geinverteerd AAN 

;EEPROM adresnamen 
SYMBOL EE_Teller    = 0       ;Adres 0 van de EEPROM bevat de tellerstand

;Poortnamen
SYMBOL ToetsHoger   = PORTB.0 ;Deze pulstoets laat de teller omhoog lopen
SYMBOL ToetsLager   = PORTB.1 ;Deze pulstoets laat de teller omlaag lopen

;Variabelen declareren
DIM Teller          AS BYTE   ;Deze variabele bevat de tellerwaarde 

;        76543210 
PORTA = %00000000             ;PORTA uitgangen bij opstart een laag niveau
TRISA = %11110011             ;PORTA.2 en A.3 zijn uitgangen voor de LED's
TRISB = %11111111             ;Deze regel mag eventueel weggelaten worden

PORTB_PULLUPS ON              ;On-chip pull-up weerstanden actief (voor de schakelaars) 
CLEAR                         ;Wis alle RAM geheugen
DELAYMS 500                   ;LCD stabilisering 


;Hoofdprogramma
Teller = EREAD EE_Teller      ;Lees EEPROM met adres 'EE_Teller' en geef waarde aan 'Teller'

CLS                           ;Display allereerste keer wissen
PRINT "Teller:"               ;Zet tekst op het display

WHILE 1 = 1                   ;Oneindige lus
  PRINT AT 1, 9, DEC3 Teller  ;De waarde in 3 cijfers op het display zetten 

  DELAYMS 250                 ;Telsnelheid

  WHILE (PORTB & 3) = 3 : WEND ;Wacht hier in deze lus tot een toets wordt ingedrukt
  DELAYMS 1                   ;Antidender

  IF ToetsHoger = AAN AND Teller < 255 THEN INC Teller ;'Teller' met 1 verhogen
  IF ToetsLager = AAN AND Teller >   0 THEN DEC Teller ;'Teller' met 1 verlagen

  ;Als 'Teller' ongelijk is aan waarde in EEPROM, dan de nieuwe tellerstand in EEPROM zetten 
  IF Teller <> EREAD EE_Teller THEN EWRITE EE_Teller, [Teller]
WEND                          ;Terug naar WHILE

Om de EEPROM instructies makkelijker in de voorbeeldprogramma's terug te vinden zijn ze hier geel gemarkeerd.

Als het programma voor het eerst wordt gestart geeft het display niet 0, maar 255 aan.
De adressen van een lege (gewiste) EEPROM zijn namelijk niet gevuld met nulletjes (00000000), wat je zou verwachten, maar met eentjes (11111111).
Dit geldt overigens voor alle PROM's, (UV)EPROM's en EEPROM's.

Stel met de up/down toetsen op PORTB.0 en PORTB.1 de teller in op een willekeurige waarde.
Schakel de spanning uit en even later weer in.
Je zult zien dat het display nu de tellerstand weer heeft, die het had toen de spanning werd uitgeschakeld.

In de praktijk is de EEPROM echter niet geschikt om continu waarden naar toe te schrijven, omdat er maar een beperkt aantal maal naar de EEPROM mag worden geschreven (1.000.000 maal volgens de datasheet van de PIC, zodadelijk meer hierover).
Een (snelle) teller die continu naar het EEPROM geheugen schrijft zal de EEPROM geen goed doen.


EDATA

Als je een PIC programmeert met een PIC programmer (zoals de Wisp628, Wisp648 of Galva-Wisp), dan is de EPROM nog helemaal leeg (alle bits '1') als je het programma van de PIC voor de eerste maal start.
EDATA biedt de mogelijkheid om ook de ingebouwde EEPROM van de PIC met een PIC programmer te programmeren.
Zo kun je dus meteen al bepaalde waarden in de EEPROM zetten.
Zodra de PIC dan is geprogrammeerd, is de EEPROM meteen al goed ingesteld met "fabrieksinstellingen" (= default instellingen).
De gebruiker kan dan later zelf in een toepassing deze waarden eventueel weer wijzigen.

Stel dat de PIC het hart is van een thermostaat.
Als je de PIC nu programmeert, kun je met EDATA alvast de "fabrieksinstelling" op 20°C zetten.
Meteen nadat je de PIC geprogrammeerd hebt, staat de thermostaat al op 20°C ingesteld.
Zou je EDATA niet gebruiken, dan zou de thermostaat na het programmeren op 255°C staan, waardoor je deze handmatig op 20°C terug moet zetten.
Als je een serieproduct maakt (honderden thermostaten), dan zou je dit bij elke thermostaat apart moeten doen, onbegonnen werk.
Met EDATA gebeurt dit dus automatisch.

Of zoals het vorige voorbeeld met de teller, die in te stellen is van 0 t/m 255.
Als je de PIC net geprogrammeerd hebt, staan alle EEPROM adressen op 255 (= %11111111), waardoor de teller begint met tellerstand 255.
Met EDATA kun je nu de ingebouwde EEPROM van de PIC zo programmeren dat deze meteen nadat de PIC geprogrammeerd is, de teller start met de tellerstand op 0, of welke waarde dan ook.

De syntaxis
 EDATA Constante {, Constante2, Constante3, ... }  

 
EDATA plaats je ergens in je programma, maakt niet uit waar, het mag dus ook helemaal onderaan zijn.

Met EDATA kun je echter geen adres opgeven.
De lijst met constante waarden die achter EDATA staat, begint altijd vanaf adres 0.
Zo plaatst EDATA 20 het getal 20 op EEPROM adres 0.
En met EDATA 33, 118 komt 33 op EEPROM adres 0 en de waarde 118 op EEPROM adres 1.

Om alle 128 geheugenplaatsen (EEPROM adressen 0 ... 127) van de 16F628A te vullen met fabrieksinstellingen (= default data), moet er dus een hele tabel in de programmalisting worden opgenomen.
Maar dat is niet erg, want EDATA neemt geen programmageheugen in (wat EWRITE en EREAD wel doen) omdat EDATA alleen tijdens het programmeren van de PIC zelf wordt gebruikt (door de PIC programmer) om de EEPROM van voorinstellingen te voorzien.

EDATA is dan ook geen instructie, maar geeft aan de compiler door dat alles wat achter EDATA is opgegeven, tijdens PIC programmeren naar de interne EEPROM van de PIC moet worden geschreven.
Het PIC programma zelf kan dan met EREAD deze waarden weer uitlezen en eventueel met EWRITE deze waarden wijzigen.

DEVICE 16F628A                ;Gebruik een 16F628A type
CONFIG INTRC_OSC_NOCLKOUT, WDT_OFF, PWRTE_ON, LVP_OFF, MCLRE_OFF
ALL_DIGITAL TRUE              ;Alle ingangen digitaal

;Logische constanten
SYMBOL AAN          = 0       ;Geinverteerd AAN 

;EEPROM adresnamen 
SYMBOL EE_Teller    = 0       ;Adres 0 van de EEPROM bevat de tellerstand

;Poortnamen
SYMBOL ToetsHoger   = PORTB.0 ;Deze pulstoets laat de teller omhoog lopen
SYMBOL ToetsLager   = PORTB.1 ;Deze pulstoets laat de teller omlaag lopen

;Variabelen declareren
DIM Teller          AS BYTE   ;Deze variabele bevat de tellerwaarde 

EDATA 0                       ;Plaats het getal 0 op het allereerste EEPROM adres (adres 0)

;        76543210 
PORTA = %00000000             ;Alle PORTA uitgangen uit (laag maken)
TRISA = %11110011             ;PORTA.2 en A.3 zijn uitgangen voor de LED's
TRISB = %11111111             ;Deze regel mag eventueel weggelaten worden

PORTB_PULLUPS ON              ;On-chip pull-up weerstanden actief (voor de schakelaars) 
CLEAR                         ;Wis alle RAM geheugen
DELAYMS 500                   ;LCD stabilisering 


;Hoofdprogramma
Teller = EREAD EE_Teller      ;Lees EEPROM met adres 'EE_Teller' en geef waarde aan 'Teller'

CLS                           ;Display allereerste keer wissen
PRINT "Teller:"               ;Zet tekst op het display

WHILE 1 = 1                   ;Oneindige lus
  PRINT AT 1, 9, DEC3 Teller  ;De waarde in 3 cijfers op het display zetten 

  DELAYMS 250                 ;Telsnelheid

  WHILE (PORTB & 3) = 3 : WEND ;Wacht hier in deze lus tot een toets wordt ingedrukt
  DELAYMS 1                   ;Antidender

  IF ToetsHoger = AAN AND Teller < 255 THEN INC Teller ;'Teller' met 1 verhogen
  IF ToetsLager = AAN AND Teller >   0 THEN DEC Teller ;'Teller' met 1 verlagen

  ;Als 'Teller' ongelijk is aan waarde in EEPROM, dan de nieuwe tellerstand in EEPROM zetten 
  IF Teller <> EREAD EE_Teller THEN EWRITE EE_Teller, [Teller]
WEND                          ;Terug naar WHILE

Nogmaals de teller maar nu met EDATA toegevoegd.
De teller zal nu de allereerste keer, net nadat de PIC geprogrammeerd is, starten met de waarde 000, omdat EDATA 0 in het programma is toegevoegd.
Verander je de waarde met toets 'ToetsHoger' naar bijvoorbeeld 17 en schakel je de spanning uit (of reset de PIC), dan zal bij opnieuw inschakelen van de spanning EDATA niet opnieuw de teller op 0 zetten, maar zal de teller weer met 17 verder gaan.


Een WORD constante (een getal groter dan 255 en kleiner dan 65536) past natuurlijk niet in één EEPROM adres en wordt daarom verdeeld over twee adressen (2 bytes).
En een DWORD en FLOAT over vier adressen (4 bytes).
Dit gebeurt met LSB first (= Least Significant Byte eerst).

EDATA 1000 zal verdeeld worden over twee adressen.
In de EEPROM komt dit als 232, 3.
Hoezo 232 en 3?
Wel, de 3 op EEPROM adres 1 is de MSB waarde van 1000 (3 × 256 = 768).
De LSB is de restwaarde van het getal 1000 en staat op EEPROM adres 0 (1000 - 768 = 232).


Stel, je schrijft "ergens" (bovenin, onderin, middenin, maakt niet uit waar) in je programma:

EDATA 7, $FF, 256, %11000011, "ABC"

Om te laten zien hoe dit in het EEPROM geheugen komt pakken we de hokjes er weer even bij:

0
00000111
1
11111111
2
00000000
3
00000001
4
11000011
 5
01000001
6
01000010
 7
01000011

 
EDATA begint dus altijd vanaf adres 0.
De eerste waarde is 7 (binair 00000111) en wordt dus naar adres 0 geschreven.
Op adres 1 komt het hexadecimale getal FF oftewel binair 11111111 (decimaal 255).
(Hexadecimale waarden zijn in PIC Basic herkenbaar doordat ze beginnen met het dollarteken '$').

Dan volgt een waarde die groter is dan één byte, namelijk 256.
Deze is dus groter dan 255 en past niet op één adres.
Daarom wordt een WORD waarde over twee adressen verdeelt (LSB first).
De waarde 256 wordt binair geschreven als 00000001 00000000.
LSB first betekent dat de meest rechtse byte (de LSB) van een getal eerst naar het geheugen wordt geschreven en daarna het MSB, de linkse byte (MSB is in het voorbeeld 00000001).
Hierdoor komt op adres 2 het LSB (= Least Significant Byte) en op adres 3 het MSB (= Most Significant Byte).

Op adres 4 komt 11000011 (oftewel 195).
En op de adressen 5, 6 en 7 komen achtereenvolgens de ASCII codes van A, B en C (= 65, 66 en 67).


Als je het keyword EDATA op meerdere plaatsen in je programma plaatst, telt de compiler gewoon verder met de adressen.
Dus het vorige voorbeeld:

EDATA 7, $FF, 256, %11000011, "ABC" 

kan ook als volgt worden geschreven:

EDATA 7, $FF
EDATA 256, %11000011 
EDATA "ABC"

De eerste EDATA die de compiler in het programma tegenkomt begint dus op adres 0, maar de volgende EDATA begint in dit geval niet opnieuw op adres 0, maar telt verder waar de vorige EDATA is opgehouden.
In bovenstaand voorbeeld plaatst de eerste EDATA de waarde 7 op adres 0 en de waarde 255 (hexadecimaal FF) op adres 1.
De tweede EDATA schrijft de opgegeven waarden niet opnieuw naar adres 0, maar gaat nu verder op adres 2, ook als EDATA op een andere plek verderop in het programma staat geschreven.
En de derde EDATA gaat weer verder vanaf het eerstvolgende adres waar de tweede EDATA is opgehouden.

Meer info over EDATA


Voorbeelden

Pincode slot, variant 1
Onderstaand programma is een codeslot.
Deze werkt met een display en een toetsenbordje met twaalf toetsen (0 t/m 9, * en #) op één poort met RCIN (zie cursus deel 5).
Vanwege de 50 regels limiet van de LITE versie van PIC Basic (demo versie) kunnen in dit voorbeeld maar 5 toetsen worden gebruikt (1 t/m 5).
Bij de volledige PIC Basic versie kunnen alle twaalf toetsen gebruikt worden (SELECT CASE uitbreiden).


Vergeet de 220E (= 220Ω) weerstand niet.

De EEPROM wordt hier gebruikt om te onthouden hoe vaak een foute code (= cijfercombinatie) is ingetoetst.
Als er drie keer een verkeerde code is ingetoetst blokkeert de PIC en is dan nooit meer aan de praat te krijgen (de PIC zal dan dus opnieuw met de PIC programmer moeten worden geprogrammeerd).
Het display geeft dan alleen maar PIC geblokkeerd aan, verder doet de PIC niets meer, ook niet als de spanning er even van af is gehaald.

DEVICE 16F628A                ;Gebruik een 16F628A type
CONFIG INTRC_OSC_NOCLKOUT, WDT_OFF, PWRTE_ON, LVP_OFF, MCLRE_OFF
ALL_DIGITAL TRUE              ;Alle ingangen digitaal

;Variabelen declareren
;WORD
DIM ToetsCode         AS WORD ;Hierin komt de door de gebruiker getoetste code in te staan
DIM Weerstand         AS WORD ;WORD! Bevat de door RCIN gemeten toetsen weerstandswaarde
;BYTE
DIM Cijfer            AS BYTE ;Bevat de ingedrukte cijfertoets na meting met RCIN en SELECT
DIM BD1               AS BYTE ;Byte Dummy 1

EDATA 0                       ;Bij het programmeren van de PIC, EEPROM adres 0 op 0 zetten


;Pincode programma
CLS                           ;Display wissen bij opstart

WHILE 1 = 1
  CLEAR ToetsCode             ;Begin met een frisse toetscode ('ToetsCode' op 0 zetten dus)

  IF EREAD 0 >= 3 THEN        ;Als aantal pogingen (op EEPROM adres 0) 3x of meer is, dan...
    PRINT AT 1, 1, "PIC geblokkeerd" ;Tekst plaatsen dat PIC geblokkeerd is
    END                       ;Beeindig programma (= blokkeer PIC, PIC gaat niet verder)
  ENDIF

  FOR BD1 = 1 TO 4            ;Code intoetsen, bestaande uit 4 cijfers
    WHILE PORTA.1 = 1 : WEND  ;Wacht steeds op een toetsindruk
 
    HIGH PORTA.1              ;Condensator ontladen op de toetsen poort (PORTA.1)
    DELAYMS 1                 ;Even wachten zodat condensator helemaal leeg is
    Weerstand = RCIN PORTA.1, HIGH ;Geef RC oplaadtijd aan WORD variabele 'Weerstand'

    SELECT Weerstand          ;We gaan de variabele met de naam 'Weerstand' testen
      CASE <  31: Cijfer = 1  ;Is 'Weerstand' kleiner dan 31, dan is het toets 1, anders
      CASE <  94: Cijfer = 2  ;Is 'Weerstand' kleiner dan 94, dan is het toets 2, anders
      CASE < 159: Cijfer = 3  ;...enz
      CASE < 228: Cijfer = 4
      CASE < 300: Cijfer = 5
    END SELECT

    IF ToetsCode = 0 THEN CLS ;Display alleen wissen bij eerste toetsindruk
    PRINT "*"                 ;Plaats een * op het display i.p.v. het cijfer
    ToetsCode = (ToetsCode * 10) + Cijfer ;Schuif digits 1 plaats naar links en Cijfer erbij

    WHILE PORTA.1 = 0 : WEND  ;Wacht tot toets wordt losgelaten
  NEXT

  DELAYMS 700                 ;Nog even **** laten zien
  
  IF ToetsCode = 2553 THEN    ;Als ingetoetste code gelijk is aan vereiste code dan...
    IF EREAD 0 > 0 THEN EWRITE 0, [0];Is EEPROM adres 0 niet 0, dan 0 maken (teller op 0)
    PRINT AT 1, 1, "Code OK"
    END                       ;Normaal kan hier naar de rest van een programma worden gegaan
  ELSE                        ;...anders... (is er dus een foute code ingetoetst)
    BD1 = 1 + EREAD 0         ;Tellerstand van EEPROM adres 0 verhogen en in dummy zetten
    EWRITE 0, [BD1]           ;Daarna waarde van dummy 'BD1' weer wegschrijven naar EEPROM
    PRINT AT 1, 1, "Code onjuist"
    IF BD1 < 3 THEN PRINT AT 2, 1, "Nog ", DEC (3 - BD1), " pogingen"
  ENDIF
WEND

Er mag dus maar drie keer achter elkaar een foute code worden ingetoetst.
Het programma slaat steeds het aantal keer dat een foute code is ingedrukt op in de EEPROM.
Als je dit met het normale (RAM) geheugen zou bijhouden, dan zou je na twee keer een foute code, de spanning eraf kunnen halen en dan de PIC opnieuw kunnen starten waardoor de teller die het aantal foutief ingetoetste codes bijhoudt, weer op 0 staat.
Als je het met de EEPROM bijhoudt kan de gebruiker de spanning er wel even af halen, maar de PIC blijft onthouden hoe vaak er fout is ingedrukt.
Nadat de goede code is ingetoetst, wordt de "foute code"-teller (op EEPROM adres 0) natuurlijk wel weer op 0 gezet.

De PIC houdt dus met zijn EEPROM bij hoe vaak er een foute code is ingetoetst.
Als dit getal groter of gelijk is aan drie, dan wordt de PIC geblokkeerd door het programma steeds meteen te beëindigen met END.

Omdat een lege (gewiste) EEPROM op al zijn adressen 255 (= 11111111) heeft staan, wordt adres 0 (het enige EEPROM adres dat in bovenstaand programma wordt gebruikt) op 0 gezet tijdens PIC programmeren met EDATA 0.

In de WHILE ... WEND lus wordt WORD variabele 'ToetsCode' op 0 gezet met CLEAR ToetsCode.


Daarna wordt bekeken wat de waarde op EEPROM adres 0 is.
EEPROM adres 0 houdt in dit programma bij hoe vaak er een foute code is ingetoetst:

IF EREAD 0 >= 3 THEN        ;Als aantal pogingen (op EEPROM adres 0) 3x of meer is, dan... 
  PRINT "PIC geblokkeerd"   ;Tekst plaatsen dat PIC geblokkeerd is
  END                       ;Beeindig programma
ENDIF

Als dat getal 3 (of groter) is (>=) dan is er dus 3 keer achter elkaar een foute code ingetoetst en wordt de PIC geblokkeerd met END.
Ook al zal de gebruiker de spanning er af halen, na het opnieuw starten van de PIC zal er alleen PIC geblokkeerd op het display verschijnen, verder doet de PIC niets meer.
De PIC zal dus met een PIC programmer opnieuw moeten worden geprogrammeerd.


Als de PIC niet geblokkeerd is wordt er in de FOR ... NEXT lus een cijfercombinatie ingetoetst.
De FOR ... NEXT lus voert zijn lus 4 keer uit, omdat het codenummer uit 4 cijfers bestaat.
Zodoende kunnen 4 cijfers worden ingetoetst.
De ingetoetste code wordt opgeslagen in de WORD variabele 'ToetsCode'.
Het programma laat de cijfers niet zien, maar plaatst er asterisks (= *, sterretjes) voor in de plaats.


Na het intoetsen wordt de ingetoetste code vergeleken met de vaste code (hier als voorbeeld '2553' ):

  IF ToetsCode = 2553 THEN    ;Als ingetoetste code gelijk is aan vereiste code dan...
    IF EREAD 0 > 0 THEN EWRITE 0, [0];Is EEPROM adres 0 niet 0, dan 0 maken
    PRINT AT 1, 1, "Code OK"
    END                       ;Normaal kan hier naar de rest van een programma worden gegaan 

Als de ingetoetste code (die in de variabele 'ToetsCode' staat) gelijk is aan 2553 (de vereiste pincode), dan is de goede code ingetoetst.
Er wordt dan bekeken of de teller op EEPROM adres 0 groter is dan 0 (voor het geval er eerst 1 of 2 keer een foute code is ingetoetst) en als dat zo is, dan wordt de teller op EEPROM adres 0 weer op 0 gezet met EWRITE 0, [0].
Daarna wordt op het display Code OK gezet en zal het programma (in dit voorbeeld tenminste) beëindigen.
In een volledig programma kun je vanaf hier naar het begin van de rest van je programma springen, waardoor dat hoofdprogramma pas start, als eerst de juiste pincode is ingetoetst.


Anders ... (de teller op EEPROM adres 0 is dus kleiner dan 3, maar de toetscode is niet juist):

  ELSE                        ;...anders... (is er dus een foute code ingetoetst)
    BD1 = 1 + EREAD 0         ;Tellerstand van EEPROM adres 0 verhogen en in dummy zetten
    EWRITE 0, [BD1]           ;Daarna waarde van dummy 'BD1' weer wegschrijven naar EEPROM 
    PRINT AT 1, 1, "Code onjuist"
    IF BD1 < 3 THEN PRINT AT 2, 1, "Nog ", DEC (3 - BD1), " poging(en)"
  ENDIF

Eerst wordt EEPROM adres 0 uitgelezen, met 1 verhoogt en de uitkomst in dummyvariabele 'BD1' geplaatst.
In de regel daarna wordt de verhoogde waarde van 'BD1' weer naar EEPROM adres 0 weggeschreven.

Het móet wel via een dummy want EWRITE 0, [1 + EREAD 0] kan niet.
Als voorbeeld is hier de EREAD opdracht rechtstreeks in de EWRITE opdracht geplaatst, dit kan dus niet, het moet via een (dummy) variabele.
Je krijgt hier van de compiler niet altijd een foutmelding, maar het verwachte resultaat blijft uit.

Daarna wordt er nog Code onjuist op het display gezet en wordt op de tweede regel afgebeeld, hoeveel pogingen er nog kunnen worden gedaan.
Dit wordt bereikt door de waarde, die nog steeds in dummyvariabele 'BD1' staat, van de 3 pogingen af te halen, oftewel: (3 - BD1).

Alles tezamen zorgt ervoor dat alleen als er een foute code is ingetoetst, er naar EEPROM wordt geschreven.
Als meteen bij de eerste poging de goede code wordt ingetoetst (wat meestal zo zal zijn), wordt er dus nooit iets naar de EEPROM geschreven, wat de EEPROM ten goede komt.


Pincode slot, variant 2
Onderstaand voorbeeld is een ander soort codeslot, zonder display.
Het voordeel van deze is dat je de pincode eventueel kunt wijzigen en opslaan in de EEPROM, nadat je eerst de goede (oude) pincode hebt ingetoetst.
Bovendien kun je (als daar behoefte aan is) de code uitbreiden tot wel meer dan 100 cijfers (de constante 'AantalDig' aanpassen, en bij EDATA de defaultcode opgeven), bij het vorige pincode slot programma kan de pincode maximaal 4 cijfers lang zijn.


Vergeet niet de 10k pull-up weerstand.

Hier worden acht toetsen aangesloten op PORTB.0 t/m PORTB.7.
Vier toetsen moeten in een bepaalde volgorde worden ingetoetst om uitgang PORTA.2 hoog te maken.
Als er drie keer een verkeerde code is ingetoetst gaat de rode LED branden, blokkeert de PIC en is dan (net als het vorige voorbeeld) nooit meer aan de praat te krijgen (de PIC zal dan dus opnieuw met de PIC programmer moeten worden geprogrammeerd).
De PIC houdt met zijn EEPROM bij wat de pincode is en hoe vaak er een foute pincode is ingedrukt.
Als dit getal groter is dan drie, dan wordt de PIC geblokkeerd door het programma steeds meteen te beëindigen met END.

Als de goede cijfercombinatie is ingetoetst, dan is de pincode eventueel te wijzigen in een andere pincode en wordt deze automatisch opgeslagen in de EEPROM met EWRITE.

Om met deze 8 PORTB ingangen een 10-cijferig toetsenbordje te kunnen bedienen, kun je toets 9 en 10 parallel aansluiten op een andere toets, die ook niet bij de juiste code hoort.
Denk ook aan de losse pull-up weerstand van 10k aan PORTA.1.
Het heeft weinig nut om PORTA_PULLUPS ON in het programma te schrijven, de PIC16F628A heeft nu eenmaal geen ingebouwde pull-up weerstanden op PORTA, alleen maar op PORTB.

DEVICE 16F628A                ;Gebruik een 16F628A type
CONFIG INTRC_OSC_NOCLKOUT, WDT_OFF, PWRTE_ON, LVP_OFF, MCLRE_OFF, DATA_CP_ON
ALL_DIGITAL TRUE              ;Alle ingangen digitaal

;Algemene constanten
SYMBOL AantalDig    = 4       ;Het aantal cijfers (digits) waaruit een code bestaat

;EEPROM adresnamen
SYMBOL EE_FouteCode = 0       ;Op EEPROM adres 0 wordt het aantal foute codes bijgehouden

;Poortnamen
SYMBOL LED_Groen    = PORTA.2 ;Groene LED brandt als de goede code is ingedrukt
SYMBOL LED_Rood     = PORTA.3 ;Rode LED brandt bij foute code of als de PIC geblokkeerd is

;Variabelen declareren
;ARRAY
DIM ToetsCode[AantalDig + 1] AS BYTE ;Array variabele declareren met aantal cijfers per code
;BYTE
DIM Toets             AS BYTE ;Toetsnummer teller van FOR...NEXT lussen
DIM BD1               AS BYTE ;Byte Dummy 1

;        76543210              Default pincode is: PORTB.7, B.0, B.7, B.5
EDATA   0,_                   ;Teller "foute code ingetoetst" op 0
        %10000000,_           ;Eerste toetsindruk is S8 (128 = PORTB.7)
        %00000001,_           ;Tweede toetsindruk is S1 (  1 = PORTB.0)
        %10000000,_           ; Derde toetsindruk is S8 (128 = PORTB.7)
        %00100000             ;Vierde toetsindruk is S6 ( 32 = PORTB.5)

;        76543210
PORTA = %00000000             ;Alle PORTA poorten laag
TRISA = %11110011             ;PORTA.2 en A.3 zijn uitgangen voor de LED's

PORTB_PULLUPS ON              ;On-chip pull-up weerstanden actief voor de toetsen 
DELAYMS 300                   ;Stabilisering signalen

GOTO PincodeInvoer            ;Spring over de subroutine


;SUBROUTINES
CodeIntoetsen:
FOR Toets = 1 TO AantalDig    ;Code intoetsen
  WHILE PORTB = 255 : WEND    ;Wacht op een toetsindruk op 1 van de PORTB poorten
  DELAYMS 5                   ;Tegen contactdender bij indrukken van een toets

  ToetsCode[Toets] = ~PORTB   ;Sla ingetoetste code (geinverteerd) op in arrayvariabele
  LED_Rood = 0                ;Rode LED uitzetten (mocht die branden door een foute code)

  WHILE PORTB < 255 : WEND    ;Wacht tot toets wordt losgelaten
  DELAYMS 5                   ;Tegen contactdender bij loslaten van de toets
NEXT
RETURN


PincodeInvoer:
IF EREAD EE_FouteCode > 3 THEN;Meer dan 3x fout ingetoetst is de PIC geblokkeerd 
  LED_Rood = 1                ;Zet rode LED aan (PIC geblokkeerd)
  END                         ;Eindig uitvoering van het programma
ENDIF

GOSUB CodeIntoetsen           ;Spring naar de subroutine voor code intoetsen

;Controleren en vergelijken van de ingetoetste cijfers met code in EEPROM
FOR Toets = 1 TO AantalDig    ;Controleer cijfer voor cijfer
  IF ToetsCode[Toets] <> EREAD Toets THEN ;Als er een foute code is ingedrukt dan...
    BD1 = 1 + EREAD EE_FouteCode ;FouteCode teller EEPROM verhogen en even in dummy zetten
    EWRITE EE_FouteCode, [BD1];Dummy BD1 met verhoogde teller in EEPROM schrijven
    LED_Rood = 1              ;Rode LED aanzetten ten teken foute code ingetoetst
    BREAK                     ;Verlaat FOR...NEXT lus voortijdig
  ENDIF
NEXT

IF Toets > AantalDig THEN     ;Als vorige FOR...NEXT geheel is doorlopen, dan is code goed
  LED_Groen = 1               ;Groene LED aanzetten
  IF EREAD EE_FouteCode > 0 THEN EWRITE EE_FouteCode, [0] ;EEPROM teller weer op 0 zetten

  IF PORTA.1 = 0 THEN         ;Als PORTA.1 laag is, dan... (is een nieuwe code in te voeren)
    GOSUB CodeIntoetsen       ;Typ nieuwe code in
    FOR Toets = 1 TO AantalDig;Alle cijfers van de nieuwe code schrijven in EEPROM
      EWRITE Toets, [ToetsCode[Toets]] ;...de nieuwe toetscode in EEPROM opslaan
    NEXT
  ENDIF

  END                         ;Normaal kan hier naar de rest van een programma worden gegaan
ENDIF

GOTO PincodeInvoer

Je kunt met een PIC programmer niet alleen naar het EEPROM schrijven, maar deze ook uitlezen.
Een slimme gebruiker zou op die manier toch nog achter de in de EEPROM opgeslagen pincode kunnen komen door de PIC EEPROM via de PIC programmer terug te lezen in de PC/laptop en zo op het beeldscherm de pincode kunnen achterhalen.
Door achter CONFIG de "fuse-setting" DATA_CP_ON te zetten, kan een PIC programmer niet meer in het EEPROM geheugen van de PIC kijken.

DATA_CP_ON: DATA Code Protect ON (= Code beveiliging van EEPROM ingeschakeld).

Bericht voor degenen met een Wisp628 of Galva-Wisp programmer:
Wegens een fout in BumbleBee V3.00 is deze "fuse" niet met BumbleBee te programmeren, wél met XWisp en XWisp2.
Heb je XWisp en XWisp2 niet geinstalleerd dan kun je DATA_CP_ON uit de CONFIG verwijderen.
De pincode in de EEPROM is dan echter wel door anderen met een PIC programmer te achterhalen.
Meer info over XWisp2 bij het Galva-Wisp PIC programmer artikel.


Om uitgang PORTA.2 hoog te krijgen (groene LED gaat branden), moet na elkaar PORTB.7, B.0, B.7 en B.5 laag worden gemaakt.
Deze volgorde is de default volgorde (de eigenlijke pincode dus) en wordt met behulp van EDATA in de EEPROM geprogrammeerd.

Foute pincode teller 
Eerste pincode cijfer 
Tweede pincode cijfer 
Derde pincode cijfer 
Vierde pincode cijfer 

opgeslagen op EEPROM adres 0, default = 0
opgeslagen op EEPROM adres 1, default = 128 
opgeslagen op EEPROM adres 2, default = 1
opgeslagen op EEPROM adres 3, default = 128
opgeslagen op EEPROM adres 4, default = 32

(= PORTB.7)
(= PORTB.0)
(= PORTB.7)
(= PORTB.5)

Als deze (of andere) PORTB poorten in een andere volgorde laag worden gemaakt, zal nadat 4 keer een PORTB poort laag is gemaakt, de rode LED gaan branden, ten teken dat een foute code is ingetoetst.
Ook hier kun je dus maximaal 3 keer een foute, 4 cijferig getal intoetsen, want als de vierde poging ook fout is, blokkeert de PIC, forever...
De enige mogelijkheid om de PIC dan weer aan de praat te krijgen is hem opnieuw programmeren.


Als de juiste volgorde wordt ingetoetst (B.7 - B.0 - B.7 - B.5), dan gaat de groene LED branden en wordt EEPROM adres 0 (die het aantal fout ingetoetste codes telt) weer op 0 gezet als dit adres een grotere waarde bevat dan 0:

IF Toets > AantalDig THEN     ;Als vorige FOR...NEXT geheel is doorlopen, dan is code goed  
  Groene_LED = 1              ;Groene LED aanzetten
  IF EREAD EE_FouteCode > 0 THEN EWRITE EE_FouteCode, [0] ;EEPROM teller weer op 0 zetten

 


Is PORTA.1 laag (S9 ingedrukt houden) op het moment dat het laatste cijfer van de pincode wordt ingetoetst, dan is de pincode van de PIC te wijzigen.
Er is dan een nieuwe, 4 cijferig pincode in te toetsen op de poorten B.0 t/m B.7, die daarna opgeslagen wordt in de EEPROM.
Denk er aan dat PORTA.1 al laag moet zijn vóórdat het laatste cijfer van de juiste (oude) pincode is ingetoetst.
Is het laatste cijfer ingetoetst, dan mag ook S9 (op PORTA.1) weer los worden gelaten, en kan meteen met het intoetsen van de nieuwe code worden begonnen.

  IF PORTA.1 = 0 THEN         ;Als PORTA.1 laag is, dan... (is een nieuwe code in te voeren)
    GOSUB CodeIntoetsen       ;Typ nieuwe code in
    FOR Toets = 1 TO AantalDig;Alle cijfers van de nieuwe code schrijven in EEPROM
      EWRITE Toets, [ToetsCode[Toets]] ;...de nieuwe toetscode in EEPROM opslaan
    NEXT
  ENDIF

  END                         ;Normaal kan hier naar de rest van een programma worden gegaan

Als PORTA.1 laag is, wordt eerst met GOSUB naar een stukje programma gesprongen, die de invoer van een 4-cijferig pincode regelt.
Dat is dezelfde subroutine waarmee de oude pincode is ingetoetst.
Hiermee wordt dus ook de nieuwe, gewijzigde pincode ingetoetst.
Als het programma weer terugkomt uit de subroutine 'CodeIntoetsen', staat de (nieuwe) ingetoetste pincode in de array 'ToetsCode'.
De FOR ... NEXT lus die daarna komt, slaat de nieuwe pincode cijfer voor cijfer op in de EEPROM, op de EEPROM adressen 1 t/m 4.


Pincode slot, variant 3
Het volgende programma is een variant van het vorige codeslot programma.
Hier worden ook acht toetsen aangesloten op PORTB.0 t/m PORTB.7.
Vier toetsen moeten in een bepaalde volgorde worden ingetoetst om PORTA.2 hoog te maken.
Als er meer dan drie keer een foute pincode is ingetoetst blokkeert de PIC minimaal 1 uur en pas daarna kan er weer een pincode worden ingetoetst.
De PIC houdt met zijn EEPROM bij wat de pincode is en hoe vaak er een foute pincode is ingedrukt.
Als dit getal groter is dan drie, dan wordt de PIC niet voor eeuwig geblokkeerd, maar geblokkeerd voor 1 uur door het programma een uur te laten wachten.
Wordt in die wachttijd de spanning uitgeschakeld en even later weer ingeschakeld dan moet er opnieuw weer een heel uur worden gewacht.
Na meer dan drie keer een foute pincode moet de PIC dus verplicht minimaal 1 uur lang voedingsspanning hebben, zonder onderbrekingen!
Als het uur voorbij is, krijgt de gebruiker maar 1 poging om het nog eens te proberen.
Is de pincode dan weer fout, dan moet opnieuw minimaal een uur worden gewacht.
Is de pincode nu goed, dan kan bij een volgende keer wel weer drie maal een foute poging worden gedaan, voordat de PIC weer voor een uur blokkeert.

Vanwege de 50 regel limiet in PIC Basic LITE, is het met onderstaand programma niet mogelijk om de pincode te wijzigen.
Mensen die de volledige PIC Basic versie hebben, kunnen het vorige en het onderstaande programma combineren tot een groter programma, dat dan zowel de uur wachten optie heeft alsook de mogelijkheid tot wijzigen van de pincode.

DEVICE 16F628A                ;Gebruik een 16F628A type
CONFIG INTRC_OSC_NOCLKOUT, WDT_OFF, PWRTE_ON, LVP_OFF, MCLRE_OFF
ALL_DIGITAL TRUE              ;Alle ingangen digitaal

;Algemene constanten
SYMBOL AantalDig    = 4       ;Het aantal cijfers (digits) waaruit een code bestaat

;EEPROM adresnamen
SYMBOL EE_FouteCode = 0       ;Op EEPROM adres 0 wordt het aantal foute codes bijgehouden

;Poortnamen
SYMBOL LED_Groen    = PORTA.2 ;Groene LED brandt als de goede code is ingedrukt
SYMBOL LED_Rood     = PORTA.3 ;Rode LED brandt bij foute code of als de PIC geblokkeerd is

;Variabelen declareren
;ARRAY
DIM ToetsCode[AantalDig + 1] AS BYTE ;Array variabele declareren met aantal cijfers per code
;BYTE
DIM Toets             AS BYTE ;Toetsnummer teller van FOR...NEXT lussen
DIM BD1               AS BYTE ;Byte Dummy 1

;        76543210              Default pincode is: PORTB.7, B.0, B.7, B.5
EDATA   0,_                   ;Teller "foute code ingetoetst" op 0
        %10000000,_           ;Eerste toetsindruk is S8 (128 = PORTB.7)
        %00000001,_           ;Tweede toetsindruk is S1 (  1 = PORTB.0)
        %10000000,_           ; Derde toetsindruk is S8 (128 = PORTB.7)
        %00100000             ;Vierde toetsindruk is S6 ( 32 = PORTB.5)

;        76543210
PORTA = %00000000             ;Alle PORTA poorten laag
TRISA = %11110011             ;PORTA.2 en A.3 zijn uitgangen voor de LED's

PORTB_PULLUPS ON              ;On-chip pull-up weerstanden actief voor de toetsen
CLEAR                         ;Wis alle RAM geheugen
DELAYMS 300                   ;Stabilisering signalen


;Pincode programma
WHILE 1 = 1
  IF EREAD EE_FouteCode > 3 THEN;Meer dan 3x fout ingetoetst is de PIC geblokkeerd 
    LED_Rood = 1              ;Zet rode LED aan (PIC geblokkeerd)
    FOR BD1 = 1 TO 60         ;Een uur wachten door 60 maal 1 minuut te wachten
      DELAYMS 60000           ;60.000 mSec = 1 minuut
    NEXT

    ;Uur is voorbij
    EWRITE EE_FouteCode, [3]  ;EEPROM nu niet op 0, maar op 3, zo is maar 1 poging mogelijk
    LED_Rood = 0              ;Rode LED weer uitzetten
  ENDIF

  FOR Toets = 1 TO AantalDig  ;Code intoetsen
    WHILE PORTB = 255 : WEND  ;Wacht op een toetsindruk op 1 van de PORTB poorten
    DELAYMS 5                 ;Tegen contactdender bij indrukken van een toets

    ToetsCode[Toets] = ~PORTB ;Sla ingetoetste code (geinverteerd) op in arrayvariabele
    LED_Rood = 0              ;Rode LED uitzetten (mocht die branden door een foute code)

    WHILE PORTB < 255 : WEND  ;Wacht tot toets wordt losgelaten
    DELAYMS 5                 ;Tegen contactdender bij loslaten van de toets
  NEXT

  ;Controleren en vergelijken van de ingetoetste cijfers met code in EEPROM
  FOR Toets = 1 TO AantalDig  ;Controleer cijfer voor cijfer
    IF ToetsCode[Toets] <> EREAD Toets THEN ;Als er een foute code is ingedrukt dan...
      BD1 = 1 + EREAD EE_FouteCode ;FouteCode teller EEPROM verhogen en even in dummy zetten
      EWRITE EE_FouteCode, [BD1];Dummy BD1 met verhoogde teller in EEPROM schrijven
      LED_Rood = 1            ;Rode LED aanzetten ten teken foute code ingetoetst
      BREAK                   ;Verlaat FOR...NEXT lus voortijdig
    ENDIF
  NEXT

  IF Toets > AantalDig THEN   ;Als vorige FOR...NEXT geheel is doorlopen, dan is code goed
    LED_Groen = 1             ;Groene LED aanzetten
    IF EREAD EE_FouteCode > 0 THEN EWRITE EE_FouteCode, [0] ;EEPROM teller weer op 0 zetten

    GOTO HoofdProgramma       ;Spring naar de rest van het programma
  ENDIF
WEND


HoofdProgramma:
;Hier kan het hoofdprogramma worden geschreven.
;Deze zal pas worden uitgevoerd als de pincode goed is ingevoerd.
END

Als voorbeeld is ook hier de default pincode in de EEPROM: PORTB.7 - B.0 - B.7 - B.5.
DATA_CP_ON zelf bij CONFIG plaatsen als je daar behoefte aan hebt.

Als je het programma wilt testen, ga je natuurlijk niet steeds een uur wachten.
Om te testen wijzig je de tijd door DELAYMS 60000 te veranderen in DELAYMS 600.
Zodoende duurt de blokkade nog maar 60 × 600 = 36000 mSec, oftewel 36 seconden.
Is je programma helemaal goed getest en werkt het zoals je wilt, dan natuurlijk niet vergeten om de tijd weer langer te maken.


Het voorbeeld van de digitaal naar analoog omzetter van cursus deel 7 is hier uitgebreid met de interne EEPROM.
Startte het voorbeeld in deel 7 steeds op met de spanning op 0V, in onderstaand voorbeeld start de spanning op met de spanning die het had toen de PIC werd uitgeschakeld, door de spanning steeds op te slaan in de EEPROM.


Het schema van cursus deel 7 blijft ongewijzigd.

 

DEVICE 16F628A                ;Gebruik een 16F628A type
CONFIG INTRC_OSC_NOCLKOUT, WDT_OFF, PWRTE_ON, LVP_OFF, MCLRE_OFF
ALL_DIGITAL TRUE              ;Alle ingangen digitaal

;Logische constanten
SYMBOL HOOG         = 1       ;Hoog signaal
SYMBOL LAAG         = 0       ;Laag signaal

;Algemene constanten
SYMBOL WijzigSpeed  = 10      ;Snelheid van wijzigen van PWM bij hoger en lager toetsen 

;EEPROM adresnamen 
SYMBOL EE_Spanning  = 0       ;Adres 0 van de EEPROM bevat de spanning

;Poortnamen
SYMBOL PWM_Signaal  = PORTA.6 ;Op deze poort komt het PWM signaal
SYMBOL ToetsHoger   = PORTB.0 ;Toets om spanning te verhogen
SYMBOL ToetsLager   = PORTB.1 ;Toets om spanning te verlagen

;Variabele declareren
DIM Spanning        AS BYTE   ;Deze variabele bevat de spanning (als PWM duty waarde)

EDATA 0                       ;Na het PIC programmeren spanning op 0V

;        76543210
PORTA = %00000000             ;Maak alle poorten van PORTA laag
TRISA = %10111111             ;PORTA.6 is uitgang voor het PWM signaal

PORTB_PULLUPS ON              ;Pull-up activeren voor de toetsen
CLEAR                         ;Wis alle RAM geheugen
DELAYMS 500                   ;LCD stabilisering 


;Hoofdprogramma
CLS                           ;Wis display
Spanning = EREAD EE_Spanning  ;Lees spanning in van vorige keer om mee op te starten

WHILE 1 = 1                   ;Oneindige lus
  ;Als op 'ToetsHoger' wordt gedrukt en spanning is nog niet maximaal, dan deze verhogen 
  IF ToetsHoger = LAAG AND Spanning < 250 THEN INC Spanning
  ;Als op 'ToetsLager' wordt gedrukt en spanning is nog niet nul, dan deze verlagen 
  IF ToetsLager = LAAG AND Spanning > 0   THEN DEC Spanning 

  ;Reken PWM waarde om naar spanning en plaats dit op het display 
  PRINT AT 1, 1, DEC2 Spanning / 25, ",", DEC2 Spanning * 4, " Volt"

  ;Stuur naar poort 'PWM_Signaal', de grootte 'Spanning' als een PWM signaal
  PWM PWM_Signaal, Spanning, WijzigSpeed

  IF ToetsLager = HOOG AND ToetsHoger = HOOG THEN  ;Beide toetsen losgelaten dan...
    IF Spanning <> EREAD EE_Spanning THEN EWRITE EE_Spanning, [Spanning]
  ENDIF
WEND

EDATA 0 zorgt ervoor dat de schakeling opstart met 0V.
Dit geldt dus alleen voor de allereerste keer, direct nadat de PIC geprogrammeerd is.
De keren daarna start de PIC op met de spanning die het had toen de schakeling werd uitgeschakeld.

In het hoofdprogramma wordt vóór de oneindige lus de variabele 'Spanning' eerst weer op de waarde gebracht die het had toen de PIC werd uitgeschakeld door Spanning = EREAD EE_Spanning.
De naam 'EE_Spanning' heeft bij SYMBOL de waarde 0 gekregen, dus eigenlijk staat hier Spanning = EREAD 0.

Onderin het programma wordt de spanning alleen in de EEPROM opgeslagen als de insteltoetsen 'ToetsLager' en 'ToetsHoger' niet bedient zijn, anders zou elk verhoging- of verlaging stapje in de EEPROM worden opgeslagen, wat de levensduur van de EEPROM verkort.
Nu wordt het pas opgeslagen als de spanning goed is ingesteld en de toetsen zijn losgelaten.
Bovendien wordt er alleen naar EEPROM geschreven als de waarden verschillen (IF Spanning <> EREAD EE_Spanning).
Er staat immers: Als 'Spanning' kleiner of groter (< >) is dan de waarde in de EEPROM, dan EWRITE EE_Spanning, [Spanning] (de nieuwe spanningsinstelling in de variabele 'Spanning' opslaan op EEPROM adres 'EE_Spanning').

In dit voorbeeld wordt alleen adres 0 gebruikt, maar als in een groter programma veel EEPROM adressen voor diverse instellingen en waarden worden gebruikt, is het overzicht snel verloren als je directe adressering gebruikt.
Bij SYMBOL wordt daarom opgegeven dat de waarde van de spanning ligt opgeslagen op EEPROM adres 0, met de naam 'EE_Spanning'.
Verder in het programma plaatsen we dan niet meer direct het adres achter de instructies EREAD en EWRITE, maar geven we de naam 'EE_Spanning' op.
Hierdoor is het meteen duidelijk dat we het EEPROM adres hebben, dat de spanning bevat.
Om aan te geven dat de naam een EEPROM adres is, begin ik de naam met "EE_".
Overigens is 'EE_Spanning' gewoon een naam.
Het had dus ook EREAD Adres_Spanning of EREAD PietJanHein kunnen zijn.
Ik heb er een gewoonte van gemaakt om namen van EEPROM adressen altijd te beginnen met 'EE_'.
'PietJanHein' werkt dus, maar aan 'EE_PietJanHein' kan ik zien dat het een EEPROM adres betreft.

In een groter programma is:

IF EREAD 0 > 250 AND EREAD 22 = 1 THEN EWRITE 5, [0]

niet echt duidelijk, omdat niet meteen te zien is, wat de EEPROM adressen 0, 22 en 5 precies bevatten.
Door de adressen eerst namen te geven, is het een stuk duidelijker en het kost geen geheugenruimte extra in de PIC.
Als je namelijk gaat compileren, dan plaatst de compiler (de PC/laptop dus) eerst alle adreswaarden weer voor de namen in de plaats.
Onderstaand programmadeel doet dus precies hetzelfde als bovenste programmadeel, maar neemt toch niet meer geheugen in van de PIC.

;EEPROM adresnamen
SYMBOL EE_Spanning  = 0    ;EEPROM adres 0  bevat de spanningswaarde
SYMBOL EE_Stroom    = 5    ;EEPROM adres 5  bevat de stroombegrenzingswaarde
SYMBOL EE_Gebruiker = 22   ;EEPROM adres 22 bevat gebruikersinstellingen

IF EREAD EE_Spanning > 250 AND EREAD EE_Gebruiker = 1 THEN EWRITE EE_Stroom, [0]

 


De overeenkomst tussen EDATA en EWRITE is dat ze allebei waarden naar de interne EEPROM toe schrijven.
EDATA doet dat echter maar één keer, en dat is alleen tijdens het programmeren van de PIC zelf.
De waarden achter EDATA worden dan ook door de PIC programmer in de EEPROM geschreven en niet door de PIC.
EWRITE schrijft naar het EEPROM als de PIC zijn programma uitvoert.
De waarden van EWRITE worden door de PIC zelf in zijn EEPROM geschreven.

Je kunt niet aan het begin van een programma EWRITE gebruiken om de EEPROM op een default waarde in te stellen, want steeds als je dan de PIC opnieuw start, zal opnieuw de default waarde worden ingesteld.
EDATA doet dat niet.
Alleen de eerste keer, tijdens het PIC programmeren, zal de default waarde worden ingesteld.
Als het programma in de PIC later deze waarde wijzigt, blijft het deze nieuwe waarde behouden en zal niet meer door EDATA worden overschreven, ook niet als je de PIC opnieuw opstart of reset.

DEVICE 16F628A                ;Gebruik een 16F628A type
CONFIG INTRC_OSC_NOCLKOUT, WDT_OFF, PWRTE_ON, LVP_OFF, MCLRE_OFF
ALL_DIGITAL TRUE              ;Alle ingangen digitaal

;Logische constanten
SYMBOL AAN          = 0       ;Geinverteerd AAN
SYMBOL UIT          = 1       ;Geinverteerd UIT

;Algemene constanten
SYMBOL AntiDender   = 5       ;mSec: Tijd tegen contactdender van toetsen
SYMBOL VerstelSpeed = 300     ;mSec: Snelheid dat op/neer telt bij vasthouden toets

;EEPROM adresnamen
SYMBOL EE_TempInstel= 0       ;EEPROM adres: Bevat het adres waar de ingestelde temp. staat

;Poortnamen
SYMBOL Hoger        = PORTB.0 ;Deze toets verhoogt de temperatuurinstelling
SYMBOL Lager        = PORTB.1 ;Deze toets verlaagt de temperatuurinstelling

;Variabelen declareren
DIM TempInstel      AS BYTE   ;Variabele waarin de ingestelde temperatuur staat

EDATA 20                      ;Bij PIC programmeren defaultwaarde 20 op EEPROM adres 0 zetten

;        76543210
TRISA = %11111111             ;PORTA allen ingangen
TRISB = %11111111             ;PORTB allen ingangen

PORTB_PULLUPS ON              ;On-chip pull-up weerstanden actief voor toets 
CLEAR                         ;Wis alle RAM geheugen
DELAYMS 500                   ;LCD stabilisering


;Hoofdprogramma
TempInstel = EREAD EE_TempInstel ;Lees vanuit de EEPROM wat de ingestelde temperatuur is 

CLS
PRINT "Temp:   ", 223, "C"    ;Plaats alvast tekst op het display

WHILE 1 = 1                   ;Oneindige lus
  CURSOR 1, 7                 ;Plaats cursor alvast op eerste regel, zevende positie
  IF TempInstel < 10 THEN PRINT " " ;Nulonderdrukking (zie cursus deel 4)
  PRINT DEC TempInstel        ;Plaats ingestelde temperatuur op het display

  DELAYMS VerstelSpeed        ;Bepaalt snelheid van instelling bij vasthouden toets

  IF Hoger = AAN AND TempInstel < 32 THEN ;Toets ingedrukt EN temp. nog niet maximaal, dan...
    INC TempInstel                        ;...temperatuurinstelling verhogen
  ELSEIF Lager = AAN AND TempInstel > 5 THEN ;Toets ingedrukt EN temp.nog niet minimaal, dan
    DEC TempInstel                        ;...temperatuurinstelling verlagen
  ELSEIF TempInstel <> EREAD EE_TempInstel THEN ;Als instelling ongelijk is aan EEPROM, dan
    EWRITE EE_TempInstel, [TempInstel]    ;...(nieuwe) instelling opslaan
  ENDIF
WEND

END

Op EEPROM adres 0 wordt de ingestelde temperatuur bewaard.
Door adressen namen te geven is het makkelijker te onthouden.
Met SYMBOL geven we de naam 'EE_TempInstel' de waarde 0.
Hierdoor is EREAD EE_TempInstel dus hetzelfde als EREAD 0.

Als na stroomuitval de PIC weer wordt opgestart leest het eerst adres 'EE_TempInstel' van de EEPROM uit en plaatst de waarde weer (terug) in de variabele 'TempInstel'.
Dit zal een waarde tussen 5°C en 32°C zijn.
Als de PIC zelf echter wordt geprogrammeerd (met de PIC programmer), dan is de EEPROM nog leeg en zal de waarde nog 255°C zijn.
Dit is opgelost met EDATA die de default waarde (de fabrieksinstelling) op 20 zet zodat, direct nadat de PIC geprogrammeerd is, het programma al "voorgeprogrammeerd" is op 20°C.

Met bovenstaand voorbeeld is de temperatuur van een thermostaat in te stellen van 5°C t/m 32°C.
Toets 'Lager' verlaagt de temperatuur met 1°C zolang deze hoger is dan 5°C.
Toets 'Hoger' verhoogt de temperatuur met 1°C zolang deze lager is dan 32°C.
Als een toets wordt vastgehouden verstelt de temperatuurinstelling automatisch.


Het voorbeeld van de temperatuurinstelling loopt in een lus.
Als je EWRITE zomaar zonder voorwaarden in de lus zou zetten, wordt er continu (dezelfde waarde) naar de EEPROM geschreven, de EEPROM "slijt" hierdoor.
Daarom wordt in het voorbeeldprogramma EWRITE pas uitgevoerd als de ingestelde temperatuur ongelijk is aan de temperatuurwaarde die is opgeslagen in de EEPROM.
Nadat de ingestelde temperatuur in de EEPROM is opgeslagen, is deze weer gelijk aan de waarde in de variabele 'TempInstel' en zal er de volgende keer dus niet opnieuw naar de EEPROM worden geschreven, maar pas dan, als 'TempInstel' weer door de gebruiker is gewijzigd.

Ook wordt in bovenstaande voorbeeld pas naar de EEPROM geschreven als de toetsen zijn losgelaten, anders zou bij verstelling van de temperatuur van bijvoorbeeld 18°C naar 22°C, achtereenvolgens 19, 20, 21 en 22 naar de EEPROM worden geschreven, wat nutteloos is en de EEPROM onnodig zou belasten.
Zoals het voorbeeld is geschreven wordt de waarde 18 in één keer verandert in 22, dus zonder die tussenwaarden.

Het uitlezen van de EEPROM is wél onbeperkt.
Je mag deze dus zo vaak uitlezen (met EREAD) als je wilt.
Dat gebeurt ook in bovenstaand voorbeeld.
Bij elke lusloop wordt steeds weer de EEPROM uitgelezen om deze te vergelijken met de variabele 'TempInstel': ...ELSEIF TempInstel <> EREAD EE_TempInstel THEN...


Beperking
Er zit dus een beperking aan het aantal keer dat je mag schrijven naar een EEPROM.
Volgens de datasheet van de PIC zijn gegevens een miljoen maal betrouwbaar op te slaan.
Daarna wordt goede opslag niet meer gegarandeerd (de EEPROM van de PIC is dan eigenlijk "op").
Het aantal 1.000.000 lijkt veel, maar als je EWRITE in een lus zet, waardoor steeds weer (dezelfde) waarden naar de EEPROM worden geschreven, zit je er zo aan.
Daarom goed uitkijken waar en hoe je de instructie EWRITE in je programma neer zet.
De PIC zelf kan overigens "maar" 100.000 keer worden geprogrammeerd.

Het beste kun je instellingen met gewone (RAM) variabelen doen.
Als de instelling eenmaal goed is, kun je de waarde van de variabele in de EEPROM opslaan.
In diverse projecten van deze site wordt gebruik gemaakt van het EEPROM:


Het electrisch klapraam onthoudt met zijn EEPROM of het raam open of dicht is.
Hierdoor weet de PIC na een spanningsuitval nog steeds de stand van het klapraam.
Stel dat je per dag 2 keer het raam open en dicht doet, dan wordt er dus 4 keer naar het EEPROM geschreven (2 keer open en 2 keer dicht).
Dan wordt er per jaar (4 × 365 dagen =) 1460 keer naar het EEPROM geschreven.
En aangezien er 1.000.000 keer naar toe mag worden geschreven zal de EEPROM van de PIC minimaal (1.000.000 ÷ 1460 =) 684 jaar mee moeten gaan.


De nummermelder zet zijn telefoonnummers ook in het EEPROM geheugen.
Elk cijfer van een telefoonnummer staat op een eigen adres.
Een telefoonnummer bestaat uit 10 cijfers.
Een PIC16F628A heeft 128 bytes geheugen, een rekensommetje laat zien dat er op deze manier 128 bytes ÷ 10 cijfers = 12,8 oftewel 12 complete telefoonnummers passen in de interne EEPROM van een 16F628A.
(Ik weet het, je kunt de nummers op een zuiniger manier opslaan, maar daar gaat het hier nu niet om, bovendien wordt in de ongebruikte bits van elk cijfer bijgehouden of het telefoonnummer al is bekeken, e.d.).
De nummermelder kan dus 1.000.000 keer 12 telefoonnummers opslaan, dat zijn 12.000.000 telefoonnummers, voordat de EEPROM onbetrouwbaar wordt.


In het posterproject ligt de servostand van de spiegel in geopende toestand opgeslagen in EEPROM.
Bij het openen van de spiegel wordt eerst de stand vanuit EEPROM in de (RAM) variabele 'SpiegelOpen' gelezen.
Afhankelijk van de variabele zet de servomotor de spiegel in een bepaalde hoek (ongeveer 45°).
Met de RC5 afstandsbediening is het via de spiegel geprojecteerde beeld op de muur naar links of rechts te positioneren door de spiegelhoek iets te verdraaien.
De hoek wordt dus gewijzigd met de variabele 'SpiegelOpen'.
Pas als de beamer wordt uitgezet, sluit de spiegel zich en wordt gekeken of de hoek van de geopende spiegel is gewijzigd en zo ja, wordt dán pas de nieuwe waarde opgeslagen in EEPROM.

SYMBOL EE_SpiegelHoek = 20  ;EEPROM adres waar geopende spiegelstand in staat

DIM SpiegelOpen AS WORD     ;Deze variabele bevat (ook) de geopende spiegelstand

;EEPROM adres 'EE_SpiegelHoek' vergelijken met variabele 'SpiegelOpen'
IF EREAD EE_SpiegelHoek <> SpiegelOpen THEN ;Spiegelstand gewijzigd? Zo ja dan...
  EWRITE EE_SpiegelHoek, [SpiegelOpen]      ;...nieuwe stand opslaan in EEPROM
ENDIF

Als de spiegelstand niet is gewijzigd, zal er niet naar de EEPROM worden geschreven.
En tijdens het wijzigen van de spiegelstand, wordt de waarde in de variabele steeds gewijzigd, (nog) niet die in de EEPROM.
Pas als de spiegel wordt gesloten, wordt de gewijzigde waarde opgeslagen in de EEPROM.
Hierdoor wordt voorkomen dat er vaak naar de EEPROM wordt geschreven, wat de EEPROM ten goede komt.


De PIC van de draadloze 10 kanalen ontvanger heeft 10 uitgangen waarmee 10 verschillende dingen draadloos kunnen worden bedient met een normale (Philips) TV-afstandsbediening met het RC5 protocol.
De gebruiker kiest eerst een systeem (VCR2, SAT, DCC) dat nog vrij is op zijn afstandsbediening.
Door daarna op een toets van de afstandsbediening te drukken, deze ingedrukt te houden en dán pas de spanning op de PIC te zetten, neemt de PIC het systeem over dat het op dat moment van de afstandsbediening ontvangt.
De PIC slaat het ontvangen systeemnummer (een 5 bits waarde) meteen op in zijn EEPROM.
Vanaf dan zijn de 10 uitgangen van de PIC te bedienen met de toetsen 0 t/m 9 van de afstandsbediening, maar reageert de PIC alleen op deze toetsen als de afstandbediening is ingesteld op het systeem, dat ook in de EEPROM van de PIC is opgeslagen.
Staat de afstandsbediening in bijvoorbeeld systeem TV (om de TV te bedienen) dan reageert de PIC niet op de toetsen 0 t/m 9.
Zo kunnen meerdere PIC's onafhankelijk van elkaar met dezelfde afstandsbediening bedient worden.


Het aantal keer dat er naar een EEPROM wordt geschreven geldt overigens voor elk adres apart.
Als je 500.000 keer naar adres 0 en 500.000 keer naar adres 1 schrijft, geldt dit als 500.000 keer (per adres) en telt dus niet als 1.000.000 keer.


Meerdere variabelen met één EWRITE instructie in de EEPROM plaatsen kan ook, deze moet je dan scheiden door er komma's tussen te plaatsen.
De adresopgave achter EWRITE is dan een "vanaf" adres:

EWRITE 5,  [DagTemperatuur, NachtTemperatuur, PietJanHein, KlaasJan] ;4 bytes schrijven
EWRITE 14, [SpiegelOpen, SpiegelDicht]                               ;2 words schrijven 

Stel dat de vier variabelen van de eerste regel allen BYTE variabelen zijn, dan plaatst deze regel de waarde van variabele 'DagTemperatuur' op EEPROM adres 5,
van 'NachtTemperatuur' op adres 6,
van 'PietJanHein' op adres 7 en
van 'KlaasJan' op adres 8.

Stel dat de twee variabelen 'SpiegelOpen' en 'SpiegelDicht' van de tweede regel WORD variabelen zijn, dan staat de waarde van 'SpiegelOpen' op adres 14 en 15 (LSB first), omdat een WORD uit 16 bits bestaat en de EEPROM nu eenmaal maar maximaal 8 bits per adres op kan slaan.
'SpiegelDicht' komt hierdoor automatisch op adres 16 en 17.
Op adres 16 staat dan het LSB en op adres 17 het MSB van de WORD variabele 'SpiegelDicht'.


Als een WORD variabele wordt gebruikt om een waarde uit de EEPROM te lezen, dan worden automatisch twee achtereenvolgende adressen gelezen.
Als een DWORD of FLOAT variabele wordt gebruikt om een waarde uit de EEPROM te lezen, dan worden automatisch vier achtereenvolgende adressen gelezen.
Voorbeeld:

PietJanHein = EREAD 5     ;Lees (vanaf) EEPROM adres 5 

Als 'PietJanHein' hier eerder in het programma als BYTE variabele is gedeclareerd (AS BYTE), dan leest bovenstaand voorbeeld de waarde van EEPROM adres 5 en kopieert de waarde in 'PietJanHein'.
Is 'PietJanHein' echter als WORD variabele gedeclareerd (AS WORD), dan leest het bovenstaand voorbeeld de adressen 5 en 6 en kopieert de waarde naar 'PietJanHein'.
En is 'PietJanHein' een DWORD of een FLOAT variabele (32 bits), dan wordt met bovenstaand voorbeeld de gezamenlijke waarde (LSB first) van de adressen 5, 6, 7 en 8 naar 'PietJanHein' geschreven.

Is er echter een situatie waarin een 8 bits waarde in een WORD variabele moet worden gelezen, dan kun je gebruik maken van LOWBYTE en HIGHBYTE:

DIM PietJanHein AS WORD         ;'PietJanHein' gedeclareerd als WORD variabele (16 bits) 

PietJanHein.LOWBYTE  = EREAD 5  ;Lees van EEPROM adres 5 alleen de LSB
PietJanHein.HIGHBYTE = 0        ;Maak de MSB van 'PietJanHein' 0

Door LOWBYTE wordt alleen de LSB ingelezen in 'PietJanHein'.
Eventueel kun je het MSB van 'PietJanHein' wissen met HIGHBYTE = 0.
 

Of als er een situatie is waarin een 8 bits waarde in een DWORD variabele moet worden gelezen, dan kun je gebruik maken van BYTE0, BYTE1, BYTE2 en BYTE3:

DIM PietJanHein AS DWORD        ;'PietJanHein' gedeclareerd als DWORD variabele (32 bits) 

PietJanHein.BYTE0 = EREAD 5     ;Lees van EEPROM adres 5 alleen de LSB
PietJanHein.BYTE1 = 0           ;Maak de tweede byte van 'PietJanHein' 0
PietJanHein.BYTE2 = 0           ;Maak de derde  byte van 'PietJanHein' 0
PietJanHein.BYTE3 = 0           ;Maak de vierde byte van 'PietJanHein' 0

Door BYTE0 wordt alleen de LSB ingelezen in 'PietJanHein'.

In het geval er met een DWORD variabele wordt gewerkt bestaat er ook nog LOWWORD en HIGHWORD.
Onderstaand voorbeeld is dus hetzelfde als bovenstaande:

DIM PietJanHein AS DWORD        ;'PietJanHein' gedeclareerd als DWORD variabele (32 bits) 

PietJanHein.BYTE0    = EREAD 5  ;Lees van EEPROM adres 5 alleen de LSB
PietJanHein.BYTE1    = 0        ;Maak de tweede byte van 'PietJanHein' 0
PietJanHein.HIGHWORD = 0        ;Maak de derde en vierde byte van 'PietJanHein' 0

Later wordt het gebruik van BYTE0, BYTE1, BYTE2, BYTE3, LOWBYTE, HIGHBYTE, LOWWORD en HIGHWORD beter uitgelegd, omdat dit hoofdstuk over de EEPROM gaat en niet over bit en byte bewerking.


Als er een vergelijking is tussen een variabele en een EEPROM adres, dan wordt het aantal bytes dat wordt gelezen vanuit de EEPROM automatisch aangepast aan de grootte van de variabele (byte, word of dword/float).
Een voorbeeld zal het duidelijker maken:

DIM JanKlaas AS BYTE         ;'JanKlaas' gedeclareerd als BYTE variabele

IF JanKlaas <> EREAD 12 THEN ;Als 'JanKlaas' ongelijk is aan EEPROM adres 12, dan...

Omdat de variabele 'JanKlaas' hier als BYTE is gedeclareerd, wordt er ook maar vergeleken met één byte van de EEPROM, in dit geval EEPROM adres 12.


In het volgende voorbeeld is 'JanKlaas' als WORD (= 2 bytes) gedeclareerd:

DIM JanKlaas AS WORD         ;'JanKlaas' gedeclareerd als WORD variabele

IF JanKlaas <> EREAD 12 THEN ;Als 'JanKlaas' ongelijk is aan EEPROM adres 12 en 13  

Nu wordt WORD variabele 'JanKlaas' vergeleken met een WORD waarde in het EEPROM.
In dit geval is de IF ... THEN regel precies hetzelfde als het eerste voorbeeld, en toch worden nu twee EEPROM adressen gelezen, namelijk adres 12 (LSB) én adres 13 (MSB), puur omdat de variabele waarmee het EEPROM moet worden vergeleken hier als WORD is gedeclareerd.


In het volgende voorbeeld is 'JanKlaas' als DWORD (= 4 bytes) gedeclareerd:

DIM JanKlaas AS DWORD        ;'JanKlaas' gedeclareerd als DWORD variabele

IF JanKlaas <> EREAD 12 THEN ;Als 'JanKlaas' ongelijk is aan EEPROM adres, dan...   

Nu wordt DWORD variabele 'JanKlaas' vergeleken met een DWORD waarde in het EEPROM.
In dit geval is de IF ... THEN regel nog steeds hetzelfde als het eerste voorbeeld, en toch worden nu vier EEPROM adressen gelezen, namelijk de adressen 12, 13, 14 én 15, omdat de variabele waarmee het EEPROM moet worden vergeleken hier als DWORD is gedeclareerd.


Nog één voorbeeld
Een teller met hierin EDATA, EWRITE en EREAD in verwerkt:

DEVICE 16F628A                ;Gebruik een 16F628A type
CONFIG INTRC_OSC_NOCLKOUT, WDT_OFF, PWRTE_ON, LVP_OFF, MCLRE_OFF
ALL_DIGITAL TRUE              ;Alle ingangen digitaal

;Logische constanten
SYMBOL AAN          = 0       ;Geinverteerd AAN 

;Algemene constanten 
SYMBOL MaxSnelheid  = 20      ;Hoe lager dit getal, hoe sneller de teller kan

;EEPROM adresnamen 
SYMBOL EE_Teller    = 0       ;Adres 0 van de EEPROM bevat de tellerstand

;Poortnamen
SYMBOL ToetsHoger   = PORTB.0 ;Deze pulstoets laat de teller omhoog lopen
SYMBOL ToetsLager   = PORTB.1 ;Deze pulstoets laat de teller omlaag lopen

;Variabelen declareren
DIM Teller          AS BYTE   ;Deze variabele bevat de tellerwaarde 
DIM TelSnelheid     AS BYTE   ;Deze variabele heeft de telsnelheid
DIM Tijd            AS WORD   ;Houdt de tijd bij als een toets is ingedrukt

EDATA 50                      ;Als de PIC geprogrammeerd is, is de default waarde 50

;        76543210 
PORTA = %00000000             ;Alle PORTA uitgangen uit (laag maken)
TRISA = %11110011             ;PORTA.2 en A.3 zijn uitgangen voor de LED's
TRISB = %11111111             ;Deze regel mag eventueel weggelaten worden

PORTB_PULLUPS ON              ;On-chip pull-up weerstanden actief (voor de schakelaars) 
CLEAR                         ;Wis alle RAM geheugen
DELAYMS 500                   ;LCD stabilisering 


;Hoofdprogramma
Teller = EREAD EE_Teller      ;Lees EEPROM adres 'EE_Teller' en geef waarde aan 'Teller' 

CLS                           ;Display allereerste keer wissen
PRINT "Teller:"               ;Zet tekst op het display

WHILE 1 = 1                   ;Oneindige lus
  PRINT AT 1, 9, DEC3 Teller  ;De waarde in 3 cijfers op het display zetten 

  Tijd = TelSnelheid
  WHILE (PORTB & 3) < 3       ;Zolang 1 van de toetsen is ingedrukt in de lus blijven... 
    IF Tijd = 0 THEN BREAK    ;...maar als tijd voorbij is ook uit de lus springen
    DELAYMS 5
    DEC Tijd
  WEND

  IF TelSnelheid >= MaxSnelheid THEN TelSnelheid = TelSnelheid - 3 ;Steeds sneller tellen 

  IF (PORTB & 3) = 3 THEN     ;Beide toetsen niet bedient dan...
    TelSnelheid = 100         ;'TelSnelheid' weer langzaam
    ;Alleen als tellerstand is gewijzigd, dan de nieuwe tellerstand opslaan in EEPROM:
    IF Teller <> EREAD EE_Teller THEN EWRITE EE_Teller, [Teller]
  ENDIF

  WHILE (PORTB & 3) = 3 : WEND ;Zolang geen van de toetsen is ingedrukt, in de lus blijven...

  IF ToetsHoger = AAN AND Teller < 100 THEN   ;Als toets is ingedrukt EN teller niet maximaal
    INC Teller                                ;...de teller met 1 verhogen
  ELSEIF ToetsLager = AAN AND Teller > 0 THEN ;Als toets is ingedrukt EN teller niet minimaal
    DEC Teller                                ;...de teller met 1 verlagen
  ENDIF
WEND                          ;Terug naar WHILE

Nadat de PIC geprogrammeerd is met de PIC programmer zal, zodra de PIC wordt opgestart, de teller meteen 50 aangeven.
Dit is nu de functie van EDATA, het instellen van default waarden (= "fabrieks instellingen").
EDATA is dus alleen van nut tijdens het programmeren van de PIC zelf en de hiermee opgegeven waarden komen niet in het programmageheugen, maar in het EEPROM geheugen van de PIC.
De default waarde 50 is dus een startwaarde, want daarna kan deze met EWRITE worden gewijzigd.

Deze teller kan tellen tussen 0 en 100.
Als je een up- of downtoets vasthoudt, zal de teller steeds sneller gaan tellen.


In sommige PIC datasheets van Microchip wordt aanbevolen om een naar EEPROM weggeschreven waarde direct er na te controleren of dit goed is gegaan en zoniet, dan nogmaals de waarde naar EEPROM te schrijven.
Het is aan de programmeur (jij dus) of je dat in je programma bouwt (ikzelf doe deze extra controle zelden).
In PIC Basic gaat dat bijvoorbeeld zo:

IF Teller <> EREAD EE_Teller THEN  ;Alleen als tellerstand is gewijzigd, dan...
  REPEAT
    EWRITE EE_Teller, [Teller]     ;...de nieuwe tellerstand opslaan in EEPROM
  UNTIL EREAD EE_Teller = Teller   ;Schrijf naar EEPROM totdat waarde er goed in is
ENDIF

In bovenstaand voorbeeld wordt de tellerstand in de variabele 'Teller' opgeslagen in EEPROM als deze is gewijzigd (oftewel ongelijk is aan die in de EEPROM).
Door de REPEAT ... UNTIL lus blijft het programma naar de EEPROM schrijven als de waarde niet goed door de EEPROM is overgenomen.
Nadeel is dat als de interne EEPROM ooit defect raakt, het hele programma zichzelf ophangt (vastloopt, crashed) omdat het continu in de REPEAT ... UNTIL lus zal blijven.

Je zou nog een teller mee kunnen laten lopen dat ervoor zorgt dat er steeds maximaal (bijvoorbeeld) 10 pogingen mogen worden ondernomen.

IF Teller <> EREAD EE_Teller THEN  ;Alleen als tellerstand is gewijzigd, dan...
  Pogingen = 10                    ;Maximaal 10 pogingen
  REPEAT
    EWRITE EE_Teller, [Teller]     ;De nieuwe tellerstand opslaan in EEPROM
    DEC Pogingen                   ;Verlaag variabele 'Pogingen' met 1
    IF Pogingen = 0 THEN           ;Als 'Pogingen' 0 is (waarschijnlijk de EEPROM defect)
      LED = ON                     ;(Normaal mag 'Pogingen' overigens nooit de 0 bereiken!) 
      BREAK                        ;Spring uit de REPEAT...UNTIL lus
    ENDIF
  UNTIL EREAD EE_Teller = Teller   ;Schrijf naar EEPROM totdat waarde er goed in zit 
ENDIF

Dit is maar een voorbeeld.
Het hangt helemaal van de toepassing af of je het belangrijk vindt dat je het toepast.
Het neemt wel allemaal PIC programmageheugen in.

In het voorbeeld zal de LED gaan branden als na 10 pogingen de EEPROM de waarde van 'Teller' nog steeds niet goed heeft overgenomen.
De (rode) LED geeft dan aan dat de EEPROM defect is.

 


PIC Basic cursus deel 10 gaat over willekeurige getallen genereren.