16x84 opdracht 3 : schakelaar

Deze opdracht beginnen we weer eenvoudig: met een druk op een knop geven we de 16x84 de opdracht om een LED 5 seconden te laten branden. Zo'n schakeling wordt b.v. gebruikt in een trappenhuis: beneden doe je het licht aan, en hopelijk blijft het licht lang genoeg aan om je weg te vinden. Daarna gaat het automatisch weer uit.

schakelaar + LED schema

Het schema is erg eenvoudig: een LED, een schakelaar en twee weerstanden. De onderdelen van het basisschema zijn niet weergegeven.

   include 16c84_10
   include jlib
   pin_a0_direction = output 
   pin_a1_direction = input 
   forever loop
      pin_a0 = high 
      if pin_a1 == low then
         pin_a0 = low 
         delay_1S( 5 )
      end if
   end loop

De eerste regel geeft het soort 16x84 en de klokfrequentie aan. Pas dit aan voor wat je zelf gebruikt. De tweede regel haalt de standaard bibliotheek binnen.

Vervolgdens worden de twee pinnen in de gewenste stand (input of output) gezet.

De hoofdmoot van het programma is weer een loop (lus) die oneindig wordt herhaald. De loop begint met het doven van de LED. De LED is (via een weerstand) aangesloten tussen pin A0 en de +5 Volt voeding, dus we moeten A0 hoog maken om de LED te doven.

Vervolgens kijken we of de schakelaar is ingedrukt. Als dat niet het geval is (ingang A1 is dan hoog) worden de regels tussen then en end if niet uitgevoerd. We zijn dan aan het einde van de loop en beginnen dus weer van voren af aan.

Als de schakelaar wel is ingedrukt (ingang A1 is dan laag) zetten we de LED aan (door uitgang A0 laag te maken). Vervolgens wachten we 5 seconden. Daarna zijn we aan het einde van de if .. then .. end if en ook aan het einde van de loop. We beginnen dus weer vooraan de loop waar de LED wordt gedooft.

opdracht schakel1.jal
Bouw de schakeling en programmeer de 16x84.

Nu gaan we voor het zelfde schema een ander programma maken. De opdracht is nu om met een druk op de knop de LED aan te zetten, en met een volgende druk op de knop de LED weer te doven.

   ...
   var bit stand = high
   forever loop
      pin_a0 = stand 
      if pin_a1 == low then
         stand = ! stand
      end if
   end loop

De regel stand = ! stand keert de waarde van stand om: hoog wordt laag en laag wordt hoog.

opdracht schakel2.jal
Pas het programma aan en probeer het uit. Het effect is dat de LED op halve kracht brand zolang je de schakelaar indrukt. Als je de schakelaar los laat is de LED (willekeurig) aan of uit. Probeer aan de hand van het programma te beredeneren waarom dit zo is.

Om het half branden te voorkomen moeten we nadat we de stand hebben omgekeerd wachten totdat de schakelaar weer is losgelaten voordat we de stand opnieuw gaan omdraaien. De eenvoudigste manier om dit te bereiken is het tussenvoegen van een regel


   while pin_a1 == low loop end loop
Dit is een loop met een voorwaarde. De loop wordt uitgevoerd zolang aan de voorwaarde is voldaan. Deze regel wacht dus net zo lang totdat A1 niet meer laag is. De volgende regel wordt dus pas uitgevoerd als A1 weer hoog is (dus de schakelaar losgelaten).

opdracht schakel3.jal
Bedenk zelf waar deze regel ingevoegd moet worden en probeer het aangepaste programma uit.

Het effect is nu al wat beter: de LED brand niet meer half als we de knop ingedrukt houden. De LED is steeds aan of uit, maar een druk op de knop wisselt niet steeds tussen aan en uit. Dit komt doordat een schakelaar die kontact maakt altijd een aantal keren stuitert voordat het contact definitief is. Als je de schakelaar indrukt maakt hij dus een paar keer contact. Hoeveel keer is iedere keer anders. Dit stuiteren (ook wel denderen genoemd) gaat erg snel, binnen 50mS is het vrijwel altijd voorbij. We moeten het programma dus zo aanpassen dat we nadat de schakelaar contact heeft gemaakt 50mS wachten voordat we gaan kijken of hij misschien al weer losgelaten is. Dan is het gestuiter voorbij en als de schakelaar dan los is dan heeft de gebruiker hem echt losgelaten. Pas het prograam aan door op de juiste plaats een aanroep van de procedure

   delay_1mS( 50 )
in te voegen.

opdracht schakel4.jal
Probeer het aangepaste programma uit.

Nu we weten hoe we een schakelaar goed kunnen uitlezen gaan we terug naar de oorspronkelijke opdracht: de tijdschakelaar. Maar nu wordt de tijschakelaar programmeerbaar: we maken het mogelijk om de tijd dat de lamp aan blijft in te stellen.

De tijdschakelaar moet als volgt werken:

De onderdelen van het programma worden nu uitgelegd in de volgorde waarin ze tijdens het uitvoeren worden aangeroepen. In het programma moeten ze echter precies andersom staan, een variable, procedure of functie moet altijd eerste gefinieerd worden voor je hem kunt gebruiken.

   forever loop
      pin_a0 = high
      if pin_a1 == low then
         ingedrukt
      end if
   end loop

In de eeuwige lus zetten we de LED uit en we roepen de procedure ingedrukt aan zodra de knop is ingedrukt.

   procedure ingedrukt is
      pin_a0 = low
      var byte tijd = 0
      while pin_a1 == low loop
         delay_100ms( 1 )
         tijd = tijd + 1
         if tijd == 50 then
            instellen
            knipperen
            return
         end if
      end loop
      delay_1s( tijdsduur )
   end procedure

In de procedure ingedrukt zetten we eerst de LED aan en we declareren de byte tijd waarin we gaan bijhouden hoe lang de knop wordt ingedrukt. Vervolgens gaan we een lus in (while pin_a0 == low loop ...) zolang de knop is ingedrukt. In de lus wachten we 0.1 seconde en dan verhogen we de tijd met 1. Als de tijd nu 50 is (de knop is dan 5 seconden ingedrukt) dan roepen we de procedures instellen en knipperen aan, en we verlaten de procedure (door het return statement). Als de knop eerder wordt losgelaten wachten we de ingestelde tijdsduur voordat we teruggaan naar de eeuwige lus.

   procedure instellen is
      tijdsduur = 0
      var byte pauze = 0
      while pauze < 20 loop
         if pin_a1 != low then
            delay_100ms( 1 )
            pauze = pauze + 1
         else
            pauze = 0
            delay_100ms( 2 )
            tijdsduur = tijdsduur + 1
            while pin_a1 == low loop end loop
         end if
      end loop
   end procedure

In de procedure instellen zetten we eerst de tijdsduur op 0. Vervolgens gaan we een loop in die we pas verlaten als de variable pauze op 20 staat. Pauze geeft aan hoe lang de knop al niet meer ingedrukt is. In de lus kijken we of de schakelaar is ingedrukt. Zo niet dan wachten we 0.1 seconde, pauze wordt met 1 verhoogd en we kijken opnieuw. Is de schakelaar wel ingedrukt dan zetten we pauze op 0, we wachten 0.2 seconde (waarom?), verhogen de ingestelde tijdsduur met 1 en vervolgens wachten we totdat de schakelaar niet meer is ingedrukt.

   var byte tijdsduur = 10
   procedure knipperen is 
      for tijdsduur loop
         pin_a0 = high
         delay_100ms( 5 )
         pin_a0 = low
         delay_100ms( 5 )
         pin_a0 = high
      end loop
   end procedure

De procedure knipperen tenslotte laat de LED net zo vaak knipperen als er seconden zijn ingesteld.

opdracht schakel5.jal
Typ het aangepaste programma in en probeer het uit. Test de timer door de tijd in te stellen op een aantal verschillende waarden en te controleren of de LED inderdaad de ingestelde tijd blijft branden.

Een nadeel van de timer is dat de ingestelde tijd verloren gaat als we de spanning van de timer af halen of als we de 16x84 resetten. (Probeer dit uit.)

We kunnen dit ondervangen door gebruik te maken van de data EEPROM in de 16x84. Hierin is ruimte voor 64 bytes die bewaar blijven als de 16x84 wordt uitgeschakeld of gereset. De EEPROM kunnen we gebruiken met de procedure eeprom_put en de functie eeprom_get. Het aanroepen van gebeurt als volgt:

   eeprom_put( 0, ... )   -- bewaar de waarde ... op eeprom address 0
   ... = eeprom_get( 0 )  -- haal de waarde van eeprom address 0 op

opdracht schakel6.jal
Pas het programma zo aan dat de tijdsduur wordt opgehaald uit en (na wijziging) wordt opgeslagen in byte 0 van de EEPROM.

Een heel andere toepassing van het zelfde schema is een codeslot: de LED gaat alleen branden als je op een bepaalde manier op de knop drukt. We gebruiken een code van 8 posities, iedere positie is een korte of lange druk op de knop voor respectievelijk een 0 en een 1.

We moeten eerst bepalen hoe lang 'kort op een knop drukken' precies is. Het volgende programma laat de LED alleen branden als de knop korter dan de ingestelde vertraging (hier 50mS) indrukt.

   forever loop
      pin_a0 = high
      if pin_a1 == low then
         delay_1mS( 50 )
         if pin_a1 == high then
            pin_a0 = low
            delay_1S( 1 )
         else
            while pin_a1 == low loop end loop
         end if
      end if
   end loop

opdracht schakel7.jal
Probeer het programma uit en bepaal wat een goede vertraging is voor 'kort op de knop drukken'.

Een goed codeslot mag niet snel te kraken zijn door alle mogelijke combinaties uit te proberen. Dit kan bereikt worden door het aantal combinaties erg groot te maken (maar dan wordt de code erg lang en dus moeilijk te onthouden). Een andere manier is er voor te zorgen dat er na een foute code een tijd gewacht moet worden voor de volgende code geprobeerd kan worden. Als je zelf altijd de juiste code ingeeft heb je hier geen last van, maar iemand die alle codes moet uit proberen is erg lang bezig.

We moeten er ook op letten dat het programma begrijpt wanneer je met het intypen van de code begint, anders is het vrijwel onmogelijk om het slot nog open te krijgeni nadat er een paar ker op de knop gebdrukt is. We doen dit door als er enige tijd niet op de knop wordt gedrukt weer van voren af aan te beginnen.

   var byte code
   forever loop
      pin_a0 = high
      if pin_a1 == low then
         lees_code( code )
         if code == 0b_0000_1111 then
            pin_a0 = low
            delay_1s( 5 )
         else
            delay_1s( 10 )
         end if
      end if
   end loop

De eeuwige loop van het codeslot programma is simpel. Eerst zetten we de LED uit. Als de knop is ingedrukt laten we de procedure lees_code de code inlezen die wordt ingedrukt. Het resultaat vergelijken we met de geheime code. Als de gelezen code goed is zetten we de LED aan en wachten 5 seconden totdat we verder gaan met de loop. Als de gelezen code niet de goede is wachten we 10 seconden voordat we weer klaar staan om een code te lezen. De geheime code is hier 0b_0000_1111, dus vier keer kort en vier keer lang.

      procedure lees_code( byte out x ) is
      for 8 loop
   
         var byte n = 0
         while pin_a1 == high loop
            delay_1mS( 5 )
            n = n + 1
            if n == 255 then
               x = 0
               return
            end if
         end loop
   
         delay_1ms( 200 )
         x = x << 1
         if pin_a1 == low then
            x = x + 1
         end if
   
         while pin_a1 == low loop 
         end loop
   
      end loop
   end procedure

De procedure lees_code bestaat uit een for lus die we 8 keer doorlopen. In de lus wachten we eerst tot de schakelaar wordt ingedrukt. Als dit echter te lang op zich laat wachten keert de procedure lees_code terug met het voor x de waarde 0. Hiervoor zorgt het eerste stukje met de while lus. Het is dus niet verstandig om als geheime code 0b_0000_0000 te nemen!

Het tweede deel komt pas aan bod als de schakelaar is ingedrukt. We wachten eerst even om kort en lang indrukken van elkaar te onderscheiden. Is de schakelaar daarna nog steeds ingedrukt, dan schuiven we een 1 rechts in x. Is de schakelaar niet meer ingedrukt dan schuiven we x alleen 1 positie op, het laagste bitje wordt dan 0.

Het derde deel zorgt ervoor dat we wachten met verder gaan totdat de schakelaar weer is losgelaten, anders zou het programma denken dat de schakelaar al een volgende keer was ingedrukt.

opdracht schakel8.jal
Programmeer het codeslot. Vervang de code door je eigen geheime code en probeer of je de LED aan kunt krijgen.