16x84 assignment 3 : switch

This assignment starts very simple: a push on a button instructs the 16x84 to light a LED for 5 seconds.

schakelar + LED schema

The circuit is very simple: a LED, a switch and two resistors. And of course the parts from the basic hardware, which are not shown here.

   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

The first line identifies the kind of 16x84 and the clock frequency. The second line includes the standard library.

Next the two pins which are used are put in the correct direction (input or output).

The main part of the program is an eternal loop. The loop start by switching the LED off. The LED is connected between pin A0 and the +5 Vcc, so we must make pin A0 high.

Next we check whether the switch is pressed. When that is not the case (input A1 will be high) the lines between then and end if are not executed. The loop is finished and starts again.

When the sitch is pressed (input A1 is low) we turn on the light (pin B0 low) and we delay for 5 seconds. After that the loop is finished and we start again. At the begin of the loop the LED is switched off again.

work switch1.jal
Build the circuit and program the 16x84.

Now we will use the same circuit for a different purpose: one push of the button must switch the LED on, the next push must switch it back off etc.

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

The line led = ! led inverts the value of led: high becomes low and low becomes high.

work switch2.jal
Modify the program and try it. The effect is that while the button is pushed the LED lights with half intensity. When the button is released the LED is randomly on or off. Try to reason why the program behaves this way.

The program was constantly toggeling between on and off while the button was pressed. To prevent this we should toggle the value only when the button is released again. This can simply be achieved by inserting a line


   while pin_a1 == low loop end loop
This is a loop whith a condition. The loop (which contains no statements) will be executed as long as the condition remains true. Hence the next line will only be executed when A1 is high again (when the button is released).

work switch3.jal
Put the line in your program and try it.

The effect is somewhat better now: no half-burning any more while the button is pressed. But still the pressing of the button does not always toggle between on and off. This is caused by contact bounce: when the button is pushed the contacts close, open again, close etc. in a rapid succession, before they finally close. This bouncing takes a small time, 50 ms seems to be a safe upper limit. So we must adapt the program so that after the first closure we wait 50 ms before we check again so see whether the button has been released again. Adapt the program by putting a delay

   delay_1mS( 50 )
at the right place.

work switch4.jal
Change your program and try it.

Now that we known how to detect a single button press we go back to our original goal: the timer. The next step is to make it possible to configure the time that the LED is on.

Our timer should work like this:

The various parts of the program will be explained in the order in which they are executed by the program. In your program file the order must be reversed, because a declaration must always precede the use of the declared object.

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

In the eternal loop we first switch the LED off. When the button is pressed we call the procedure pressed which handles all details.

   procedure pressed is
      pin_a0 = low
      var byte count = 1
      delay_100ms( 1 )
      while pin_a1 == low loop
         delay_100ms( 1 )
         count = count + 1
         if count == 50 then
            configure
            blink
            return
         end if
      end loop
      delay_1s( seconds )
   end procedure

In the procedure pressed we first switch the LED on and we declare a byte variable in which we will count how long the button is pressed. We count this time in units of 100 ms. Next we wait the first 100 ms to make sure that the swicth bouncing is over. Next we enter a loop in which we will remain as long as the button is pressed down. When the button is released soon the loop ends, we wait the configured amount of seconds (with the LED still on) and we return to the main loop which will turn the LED off.

Within the loop we wait 100 ms and increment the time counter. When the counter reaches 50 (5 seconds) we call the procedures configure and blink and then we return immediately to the main loop, without the waiting.

   procedure configure is
      seconds = 0
      var byte idle = 0
      while idle < 20 loop
         if pin_a1 != low then
            delay_100ms( 1 )
            idle = idle + 1
         else
            idle = 0
            delay_100ms( 2 )
            seconds = seconds + 1
            while pin_a1 == low loop end loop
         end if
      end loop
   end procedure

In the configure procedure we count the number of times the button is pressed, because this is the number of seconds that the LED must be on. We must also count how long the button is not pressed (idle), because two seconds indicates that the user has finished. When the button has not been pressed for 2 seconds (idle == 20) we leave our procedure.

As long as the user keeps pressing the button in rapid succession we remain in the while loop. When the button is not pressed we wait 100 ms and increase the idle time. When the button is pressed we clear the idle counter, increase the configured number of seconds, wait (for the swith bouncing) and wait until the switch is released again.

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

Finally the procedure blink just loops for the configured number of times. Within the loop the LED is turned on, we wait a little, turn the LED off, and again wait a little.

work switch5.jal
Enter the new program and try it out. Test whether the on time of the LED can indeed be configured at for instance 2 and 10 seconds.

A disadvantage is that the configured time will be lost when the power is removed (try this).

The 16x84 contains a data eeprom which we can use to store a small amount of data (atually 64 bytes - quite large compared to the RAM of a 16x84!). The eeprom can be accessed using the procedure eeprom_put and the function eeprom_get. These calls look like this:

   eeprom_put( 0, ... )   -- save the value ... at eeprom address 0
   ... = eeprom_get( 0 )  -- get the value from eeprom address 0

work switch6.jal
Adapt your program to retrieve the timeout from eeprom at startup and to store it in the eeprom when it is modified. Use eeprom address 0.

Now we will use the same circuit again but for a very different purpose: a code slot. The LED must only be turned on when the button is pressed in a particular rythm. We use an 8 bit code, each bit in the code stands for either a long or a short push on the button.

First we must decide how along a "short push" actually is. The next program will only turn the LED on when the button is pressed shorter than 50 ms.

   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

work switch7.jal
Try the program and modify the delay time until you can reliably activate the LED.

A good code lock is not easy to open by trying out all combinations. This can be achieved by allowing a very large number of codes, but that makes the actual code long and thereby hard to remember. Another way is to make sure that after a code has been tried and found invalid the user must wait some time before a next code can be tried. For the rightfull user this is only a small nuisance.

Another thing to pay attention to is that the program must understand when the entering of the code starts, otherwise it will be virtually impossible to open the lock after a few button presses. Our program assumes that a new code will be started when the button has not been pressed for a few seconds.

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

The main loop is simple. Fisrt we switch the LED off. When the button is pressed we call the procedure read_code which returns with the code that was read. We compare the result to the secret code and when it matches we turn the LED on for 5 seconds. When there is no match we wait 10 seconds before we try to read a new code. The secret code shown here is 0b_0000_1111, which means four times short and then four times long.

      procedure read_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

The read_code procedure consists of a big for loop which is meant to be executed 8 times. In this loop we first wait for the button to be pressed. When this takes too long we resturn with the code 0 (= 0b_0000_0000), which makes this code a bad choice for your secret code!

The second part is executed when the key is pressed. We first wait 200 ms. When the key is now still pressed we shift a 1 into x at the right, otherwise we shift in a 0.

In the last part we wait untill the button is released again, otherwise the loop would immediately assume that the button was pressed again.

work switch8.jal
Enter the code lock program and test it.