Further Input

The next project is to develop the 4 bit counter to accept input. In this section, we will introduce a number of important topics. Let's start with a program specification:

A program that outputs a number between 0 and 15. The number can be changed by two switches designated "UP" and "DOWN". The counter should "wrap around" - in other words it should go from 15 to 0 when "UP" is pressed at a count of 15, and it should go from 0 to 15 when "DOWN" is pressed at a count of 0.

Seems simple enough, and we've already covered most of the elements before. We know how to set up our variables, and how to output them. We know how to check our Counter variable to see if it's reached the highest number allowed, and we could probably work out how to check for zero. We've seen how to test for a switch press, so surely we're almost there...

We can take our basic 4 bit counter and modify it for this purpose. The first step is to remove the incf Count,f line, and also the time delay. This means that our program will sit in a loop, outputing the value of Count. Unfortunately, there is nothing in the program to actually modify Count, so it will do nothing...

Flow diagram (6kB)

So we need to check for switch presses and modify Count accordingly. Here is the flow-diagram for this section:

Flow diagram (8kB)

As we saw on the previous page, coding this is straightforward:

      btfss    PORTA, 0       ;Is "UP" switch pressed?
      incf     Count,f        ;  Yes, increment Count
                              ;  No
      btfss    PORTA, 1       ;Is "DOWN" switch pressed?
      decf     Count,f        ;  Yes, decrement Count
                              ;  No

So we just insert these lines in the loop, and every few microseconds, the switches will be checked and Count will be modified accordingly.

After modifying Count, we must ensure that Count stays within the designated limits, and "wrap" Count as specified above.

Flow diagram (8kB)

For the first check, we've already done something similar for the original 4-bit counter, and we can recycle that code here:

      movlw    d'16'          ;
      xorwf    Count,w        ;W = Zero if Count = 16
      btfsc    STATUS,Z       ;Is Z-flag set?
      clrf     Count          ;Yes, so reset Count
                              ;  No, OK

Count is compared with MAX+1, and if equal, the Z-flag is set. The clrf Count line is skipped except when Count reaches 16. This is neat and easy because the reset only requires one line.

This deals with Count > MAX, but how are we going to check for Count < 0? This is more tricky to understand, but ultimately straightforward. We can also introduce a new "trick"...

Assume Count is currently 0, and the "DOWN" button is pressed. What happens to Count? In a normal number system, the answer would be -1, but what is this in our 8-bit binary world? The answer is "1111,1111", or 255.

So here we compare Count to 255. But to "wrap around" (make Count = 15) takes 2 lines. There are a number of ways to achieve this - this example demonstrates using goto to skip lines.

      movlw    d'255'         ;
      xorwf    Count,w        ;W = Zero if Count = 255 ("-1")
      btfss    STATUS,Z       ;Is Z-flag set?
      goto     ok             ;  No, Ok...
      movlw    d'15'          ;  Yes, so count had reached "-1"
      movwf    Count          ;  So make Count = 15
ok                            ;rest of code...

This would work perfectly well, but here's a neater way:

      movlw    d'255'         ;
      xorwf    Count,w        ;W = Zero if Count = 255 ("-1")
      movlw    d'15'          ;Setup W - Note this doesn't affect Z!
      btfsc    STATUS,Z       ;Is Z-flag set?
      movwf    Count          ;  Yes, so count had reached -1; make it 15
                              ;  No, so do nothing

We still XOR Count with 255, but note that we load W with 15 before doing the bit-test instruction. This works because movlw doesn't affect the Z flag. Having loaded W, we decide whether we need to move this into Count. This subtle change is neat - it avoids using a goto, saves a word in programme memory and saves a few clock cycles. I've been surprised by how often I use this trick...

To save you having to type this in, you can download input1.ASM - I recommend you study it, and then program this into a PIC. Use the same circuit as before, but add some more LEDs. We'll use the same circuit for the rest of this page, so it's definitely worth taking the time to try this.

Input test schematic

If you haven't spotted the problem from studying the source code, you will discover it when you press the buttons. The LEDs all light up continuously, or at least appear to. In actual fact, the PIC counts rapidly while a switch is held. If you have access to an oscilloscope, you comfirm this - also check the frequency of each of the LEDs and determine the relationship between them...

This circuit does have a practical use - because the counter runs so very fast, it is impossible for you to predict the result you'll get when you release the button - therefore, it generates a random number between 0 and 15. We'll return to this topic soon...

Stopping the count...

It's clear that we need to do something that involves waiting for the switch to be released. And this is easy to achieve:

Flow diagram (8kB)

This flow diagram shows the steps necessary to deal with one switch - this needs to be repeated for each switch. As there are more steps for each switch, it worth considering using subroutines for each one:

      btfss    PORTA,0        ;Is "UP" switch pressed?
      call     Up             ;  Yes, so count up
      btfss    PORTA,1        ;Is "UP" switch pressed?
      call     Down           ;  Yes, so count down

In the above program, we did the limit-checking and wrapping after all the switches had been checked, but before we outputted the result. This meant that we didn't output any spurious values.

October 2023: Finishing this page is on my to-do list! I have been greatly encouraged by all the positive feedback this guide has received and I honestly intend to greatly expand this section over the next few years. Please understand that producing the text and flow diagrams is very, very time-consuming, and there are so many other pressures on my time at the moment. Please accept my apologies for the lack of updates here over the last few years.