title='Introduction' chapter='Lab 2: Switches and torches' file=intro builder=makeself.htm level=2

title='Introduction' source=left.htm dest=l_intro level=2

We are going to look at how we can use the input switches on our PICmicro to control the state of the LEDs.

You probably never thought that a light switch is particularly complicated. After all, it is either on or off. However, as we shall see, there is quite a bit to writing a program which will give us the same behaviour as a switch which controls a light.

Once we have done that we will look at ways in which we can make our light switch more intelligent, and even useful!

title='Introduction' source=right.htm dest=r_intro level=2

Insert picture of light switches here.

title='Hardware Requirements' chapter='Lab 2: Switches and torches' file=hware builder=makeself.htm level=2

title='Hardware Requirements' source=left.htm dest=l_hware level=2

This program can be run using a PIC16F84 processor with a single LED connected to an output pin. We are using PORTA BIT 0, which means that you should use pin 17 of the PICmicro as the output. The LED should be connected between pin 17 and pin 5 of the PICmicro, with a suitable current limiting resistor.

To provide the inputs you will need to connect push switches to PORTB BIT 0 and PORTB BIT 1. The switches should pull the inputs high when pressed down.

If you are using a PICmicro development board you do not have to add any extra hardware.

title='Hardware Requirements' source=right.htm dest=r_hware level=2

Circuit diagram of PICmicro with LED between pins 17 and 5 with current limiter. Also switches on the inputs of pins 6 and 7 which pull high through an appropriate resistor. .

title='Simple Light Switch' chapter='Lab 2: Switches and torches' file=simplsw builder=makeself.htm level=2

title='Simple Light Switch' source=left.htm dest=l_simplesw level=2

Our first light switch will be very simple. If I hold the button down, the light is on. If I release the button the light goes off.

Note this is just the same as if we had a push switch wired in series with the bulb so I am using a small computer to behave as a piece of wire!

The diagram shows the flow of our first program. The boxes contain actions and the diamond denotes a decision which is made as our program executes. The arrows show how the execution moves from one box to the next.

From this we can see that the program is repeatedly checking the state of the port and switching the light on or off depending on the state of it.

We actually construct the program in C by using an if condition to look at the port and place the whole thing in a while loop.

Sometimes it can be useful to draw out the sequence of actions a program should perform, particularly for the first few programs that you write.

title='Simple Light Switch' source=right.htm dest=r_simplesw level=2

title='Reading Data' chapter='Lab 2: Switches and torches' file=readdta builder=makeself.htm level=2

title='Reading Data' source=left.htm dest=l_readdta level=2

Before I can read data from a PICmicro input port I must set it to be an input. This is controlled by the TRISA and TRISB registers. If a given bit is set in either of these registers it means that the port is configured as an input. To use a port as an output we must clear the appropriate bit.

When the PICmicro starts up all the ports are configured as inputs. This is to stop the PICmicro from providing signals when it isn't supposed to.

The CPICmicro will indicate the input/output status of a pin by using an arrowhead underneath each port bit.

If the arrow points down towards the register displays this indicates that the port is configured as an input. When the button is pressed for that port bit a 1 is sent to the appropriate bit in the port. The status indicator will turn red to indicate that 1 is being supplied and the value in the port will change accordingly.

The way that buttons work is not quite the same as the real PICmicro development board. In order to allow you to set more than one input at once (which would be impossible as you only have once mouse!) we have made our buttons toggle switches. Press the button once to set the input, and again to clear it. The indicator will allow you to keep track of what is going on.

If the arrow points up towards the status indicator this indicates that the pin is set as an output. The status indicator will turn red when the program sets the appropriate bit in the port. Note that when a port is configured as an output you are unable to change its state using the button.

If you step through the program you will see that the arrow for PORTA bit 0 will change to pointing up when the bottom bit of TRISA is clear. We clear the bit by loading a value into TRISA which has the bottom bit clear, in this case hex 0x1E (decimal 30).

Note that we need to select the appropriate bank when we wish to change the settings of the TRISA and TRISB registers. We do this by setting a bit in the status register. This is covered in more detail in the PICmicro reference section.

title='Reading Data' source=right.htm dest=r_readdta level=2

title='Testing a Bit' chapter='Lab 2: Switches and torches' file=testbit builder=makeself.htm level=2

title='Testing a Bit' source=left.htm dest=l_testbit level=2

The C2C function input_port_bit_b will return a non-zero value (1) if the particular input pin is set high. You tell the function which bit to look at by the number in the function call:

input_port_bit_b (0) ;

- would return true if bit number 0 is set. The C2C compiler will drop the PICmicro assembler instructions to read this port, but as far as we are concerned it looks like a function has been called to do the job. Remember that the parameter to this function (in this case the value 0) is the number of the bit we are working on.

Note that we use this call in a condition and that the state of this bit will change the sequence of instructions which our program runs. A lot of our programs will respond to user input in this way. There is of course a matching input_port_bit_a function to read the other input port.

title='Testing a Bit' source=right.htm dest=r_testbit level=2

title='Setting a Bit' chapter='Lab 2: Switches and torches' file=setbit builder=makeself.htm level=2

title='Setting a Bit' source=left.htm dest=l_setbit level=2

We turn on the output bit (and light our LED) by using another function which is called in exactly the same way as our input function:
output_high_port_bit_a (0) ;

The complementary function output_low_port_bit_a will turn the bit off. Remember that these functions are particular to C2C. Other C compilers for the PICmicro will probably provide the same features but they may not have the same name.

title='Running Program 2.1' chapter='Lab 2: Switches and torches' file=progrun builder=makeself.htm level=2

title='Running Program 2.1' source=left.htm dest=l_progrun level=2

If you run the program in the CPIC you will see that it continually loops around testing the input bit and branching appropriately. If you press the input button connected to bit 0 of PORTB you will see the program "notice" that the bit has changed and it will turn on bit 0 of PORTA.

I am using a very common trick to make sure the program runs for ever. The while condition will continue while its condition is set to a non-zero (i.e. true) value. This means that while(1) will repeat a block of code for ever.

Load program 2.1 into the compiler and execute it.Note that it works just like a real light switch!

title='Running Program 2.1' source=right.htm dest=r_progrun level=2

title='The Need for Speed' chapter='Lab 2: Switches and torches' file=speedneed builder=makeself.htm level=2

title='The Need for Speed' source=left.htm dest=l_speedneed level=2

It takes a small amount of time for the program to pick up the fact that the input has changed, and you can "catch the program out"in the CPIC by changing the state of the input very quickly. This shows a defect in this way of working, which is often called "polling". If we get very small pulses these are not noticed by the program which is polling for input.

If you select the RC clock and set the speed of the PICmicro development board to slow you will notice a delay between the button being pressed and the light changing.

Later in the course we will see how you can use interrupts to detect very short signal pulses. For now the fact that the PICmicro is running around a million instructions a second will ensure that we don't notice this!

title='The Need for Speed' source=right.htm dest=r_speedneed level=2

title='On and Off Switches' chapter='Lab 2: Switches and torches' file=onoff builder=makeself.htm level=2

title='On and Off Switches' source=left.htm dest=l_onoff level=2

The problem with our first light switch is that we have to hold it down to make the light work. Sometimes you might not want this. You may prefer a solution where we have two buttons, one to turn the light on and another to turn it off. (I actually have a torch which works like this).

To do this we will need another button, I am going to use bit 1 on PORTB. If bit 0 goes high I turn the light on, if bit 1 goes high I turn the light off. This means that my program must "remember" the state of the light, so that it can turn it on if it is off, and vice versa.

Fortunately the bits in the ports do remember their state, i.e. if I set the bit high it will stay high until I set it low. I can therefore "read back" the states of the ports, in effect treating them as variables. Note that this only works because the PIC has been designed this way. If you use a different chip as an output port you may find that you cannot read the settings. In this case you need to use a variable to "shadow" the state of the port.

The flowchart shows how we check each button in turn and turn the led on or off. We then go around and repeat the tests.

We need two if constructions, one for each button.

title='On and Off Switches' source=right.htm dest=r_onoff level=2

title='Running Program 2.2' chapter='Lab 2: Switches and torches' file=runpr2 builder=makeself.htm level=2

title='Running Program 2' source=left.htm dest=l_runpr2 level=2

When we run the program it will test each button in turn. When you press down the button on PORTB bit 0 the light is turned on. When you press the button on PORTB bit 1 the light is turned off. This gives us the effect we want.

You can have fun with this program. If you set bit 0 and bit 1 on the input port (the equivalent of holding down both buttons on our torch) you will see something interesting. The light continually flicks on and off as the program notices that first the on button is set (so turn the light on) and then the off button is set (so turn the light off).

The user of a torch implemented in this way would note that if they held both buttons down the light glows dimly. This is because the PIC runs so fast that we would not see the flashing, instead we would see the light at around quarter brightness (for a bonus point you could consider why the light is not at half brightness)

 If the torch used mechanical buttons we would probably break the buttons doing this, so I suppose the PIC based solution is better! We could even change the manual to say "If both buttons are depressed the torch goes into battery save half brightness mode" and make our bug into a feature!

Load program 2.2 into the PIC and watch it run. Hold down both buttons and see that it does indeed give a dim light. If you use the RC clock and set the speed of the PIC to slow you will see the light flicker on and off if both buttons are held down.

title='Running Program 2.2' source=right.htm dest=r_runpr2 level=2

title='The Ultimate Switch?' chapter='Lab 2: Switches and torches' file=ultimate builder=makeself.htm level=2

title='The Ultimate Switch?' source=left.htm dest=l_runpr2 level=2

The ultimate torch switch is probably a "push on - push off" switch. You would press the switch once to turn the light on and again to turn it off again. (I had a torch like this too!). In digital circuit terms I am talking about a "flip flop" which you flip from one state to another by sending a clock pulse.

This is the most complicated flow diagram so far. We can break it down to the following steps:
  1. Set up the hardware
  2. Wait for the button to go down
  3. Flip the state of the LED
  4. Wait for the button to go up
  5. Go back to step 2

I flip the state of the LED by turning if off if it is on and vice versa. You can convince yourself the program works by going through it by hand.

When a program gets larger a complete flow diagram becomes harder to write and make sense of. In that case I often create a list as above which describes the steps my program will go through. Different programmers have their own ideas on what is meaningful. I suggest that you use what works for you!

title='The Ultimate Switch?' source=left.htm dest=l_runpr2 level=2

title='Running the Ultimate Program' chapter='Lab 2: Switches and torches' file=ultrun builder=makeself.htm level=2

title='Running the Ultimate Program' source=left.htm dest=l_ultrun level=2

When you run the program in the CPIC you will find that it works well. The first time you press and release the button the light is lit. The second time the light goes out. In reality the program in the CPIC would not work well, because of bounce in the switches. Because of the way that the CPIC works the switches always change from on to off instantaneously.

In real life a mechanical switch will bounce very slightly when it closes and opens. This means that while it is moving there is an instant when the program will get a signal which changes rapidly from on to off. This is a common problem in systems where a computer must read the state of an input from a switch and it means that our perfect program is no good as it stands. When we press and release our button the noise causes the program to rapidly toggle the state of the light.

Run program 2.3 and note that it doesn't work very well! Turning the light on and off becomes a game of skill!

title='Running the Ultimate Program' source=right.htm dest=r_ultrun level=2

title='Debouncing an Input' chapter='Lab 2: Switches and torches' file=debounce builder=makeself.htm level=2

title='Debouncing an Input' source=left.htm dest=l_debounce level=2

The solution is to provide code to debounce the signal. This means that I wait until I have twenty readings all the same before I decide that the button is in one position or the other. You can write the sequence we want to perform as follows:

  1. Set our counter to 0
  2. Read the state of the inputs
  3. Record it
  4. Read the state of the inputs again
  5. If the state is the same as the previous state, make our counter one bigger
  6. If the state is not the same as the previous state, make the counter 0 and record the state.
  7. If the counter has reached 20 we can exit with the correct state.
  8. Go back to step 4

If you work through this "mini-program" you will see that it needs 20 consecutive readings before it can escape. As soon as it receives an input which is different from the previous one it resets the counter.

The upper limit of the counter determines how sensitive our function is. If we make the counter larger we improve our rejection of noise, but it takes us longer to notice a pulse.

On the other hand, if we make the counter smaller we notice a pulse more quickly, but we are more likely to see noise as a valid reading. I have found that a value of 20 seems to work well.

The CPIC program on the right hand side implements a function called key which you can use to get a debounced value of the key input. If you run it you can toggle the value on the input pin and watch it react. Only when the value has been steady long enough for count to reach 20 does the function return a value.

title='Debouncing an Input' source=right.htm dest=r_debounce level=2

title='Building a Library' chapter='Lab 2: Switches and torches' file=buildlib builder=makeself.htm level=2

title='Building a Library' source=left.htm dest=l_buildlib level=2

Each time we perform an exercise we will create functions which do things for us. In this case I now have a function which can be used to get the state of a user input. It will return (after a short interval) with the "proper" state of the input.

On the right we have the example code for a fully working torch with a debounced switch. The code is similar to our previous example, but we now call the key function to "debounce" the inputs each time. If you run program 2.4 you should find that the torch operation is now quite solid.

You will find the key function appearing in other programs which need to read the state of an input key.

title='Building a Library' source=right.htm dest=r_buildlib level=2

/*  Program 2.4 Debounced Light Switch */
/*  Rob Miles January 2000    */

unsigned char key ( void ) 
{
    unsigned char count = 0 ;
    unsigned char oldv, newv ;
    oldv = input_pin_port_b ( 0 ) ;
    while ( count < 20 ) {
        newv = input_pin_port_b ( 0 ) ;
        if ( oldv == newv ) {
            count++ ;
        }
        else {
            count = 0 ;
            oldv = newv ;
        }
    }
    return oldv ;
}


void main ( void )
{
    /* Select the Register bank 1 */
    set_bit(STATUS,RP0);
    
    /* set all of PORTB for input */
    TRISB=0xff;
    
    /* set bit 0 of PORTA for output */
    TRISA=0x1e;
    
    /* now use Register bank 0    */
    clear_bit(STATUS,RP0);

    /* repeat forever */
    while (1){

        /* wait for the button to go down */
       while (key () == 0);

        /* if the light is on turn it off  */
        if (input_pin_port_a(0)) {
            output_low_port_a(0);
        }
        else {
            /* if the light is off turn it on  */
            output_high_port_a(0);
        }

        /* wait for the button to go up again */
        while (key() == 1);
    }
}

title='Torch with Time Out' chapter='Lab 2: Switches and torches' file=timeout builder=makeself.htm level=2

title='Torch with Time Out' source=left.htm dest=l_timeout level=2

You have decided that a problem with your torch is that if someone leaves it switched on the battery goes flat. What you need to do is change your light switching program so that if the light is on too long it turns off automatically.

To do this you will use a counter which is set to 0 when the torch is turned off. When the torch is turned on we increment the counter each time round our loop. When the counter hits a particular value we turn the torch off.

You may have a problem with the speed of the PIC. It can count very quickly, so it will hit the end of one counter before your light has been on for a decent length of time. To solve this you must put one counter inside another. When the inner counter reaches a limit you increase the outer counter by one and reset the inner to 0. You then continue until the outer value reaches a limit.

You should check the counter when you are waiting for the pin to change state. This means that you now have something to do in the while loops.

I found that setting both the inner and the outer counters at 100 gives around 8 or 9 seconds of light.

If you run program I have invented functions which we may use later when we want to time things. The function do_time sorts out what we need to do while we wait for key presses. The function start_clock clears the counters. We call start_clock when we turn the light on.

Note that do_time checks to see if the light is on before it updates the counters. This stops us wasting time if the light is not on.

You can learn a lot from studying this code. You might even decide that you can think of a better way of doing the job. If you do decide this, it means that you are learning. I do not pretend that this is the best way of doing the job, but it does work and I haven't found any bugs in it.

title='Torch with Time Out' source=right.htm dest=r_timeout level=2

/*  EX 2.5 Torch with timeout        */
/*  Rob Miles January 2000           */

unsigned char key ( void ) 
{
    unsigned char count = 0 ;
    unsigned char oldv, newv ;
    oldv = input_pin_port_b ( 0 ) ;
    while ( count < 20 ) {
        newv = input_pin_port_b ( 0 ) ;
        if ( oldv == newv ) {
            count++ ;
        }
        else {
            count = 0 ;
            oldv = newv ;
        }
    }
    return oldv ;
}    


#define C1LIMIT 1000
#define C2LIMIT 1000

int c1, c2 ;

void start_clock (void)
{
    c1 = 0 ;
    c2 = 0 ;
}

void do_time (void) 
{
    /*  if the light is off do nothing  */
    
    if (input_pin_port_a(0) == 0) {
        return ;
    }
    
    /* increase our first counter  */
    
    c1++ ;
    
    if ( c1 > C1LIMIT ) {
		/*  clear the first counter            */
        c1 = 0 ;
        
        /*  need to update the second counter  */
        c2++ ;

		/*  .. and check its value             */        
        if ( c2 > C2LIMIT ) {
            /* turn the light off here  */
            output_low_port_a(0) ;
        }
    }
}
                        

void main ( void )
{
    int c1 = 0, c2 = 0 ;
    
    /* Select the Register bank 1 */
    set_bit(STATUS,RP0);
    
    /* set all of PORTB for input */
    TRISB=0xff;
    
    /* set bit 0 of PORTA for output */
    TRISA=0x1e;
    
    /* now use Register bank 0    */
    clear_bit(STATUS,RP0);

    /* repeat forever */
    while (1){

        /* wait for the button to go down */
        while (key () == 0) {
            do_time () ;
        }

        /* if the light is on turn it off  */
        if (input_pin_port_a(0)) {
            output_low_port_a(0);
        }
        else {
            /* if the light is off turn it on  */
            output_high_port_a(0);
            /* start the timer  */
            start_clock () ;
        }

        /* wait for the button to go up again */
        while (key() == 1) {
            do_time () ;
        }
    }
}

title='Exercises' chapter='Lab 2: Switches and torches' file=ex builder=makeself.htm level=2

title='Exercises' source=left.htm dest=l_ex level=2

  1. Make a copy of program 2.1 and modify it so that the light goes off when the button is not pressed. Your new program should be exactly the same size as the original. [2.6]
  2. Make a copy of program 2.2 and fix it so that if both buttons are held down the light goes out rather than flickers. I suggest that you look at performing a third test which uses a logical && operation to see if both buttons are down and then turns the light off if they are. Remember that if you do this you don't want to look to see if either of the buttons are down as well, otherwise they will still flash! You can ensure that statements are not performed by using the else addition to an if construction or you may find a use for the continue. [2.7]
  3. Make a copy of program 2.5 and change it so that the light does not go off if the button is held down. You should be able to get this effect by making the program smaller! (you might find it useful to make the time out period smaller when you are testing the program) [2.8]
  4. Create a new program which implements a "coin tosser". The program will flash a LED very quickly while a button is held down. When the button is released the flashing will stop and the led will be lit or not lit with a 50/50 probability. Do you think you need to worry about debouncing the signal in this case? [2.9]

title='Exercises' source=right.htm dest=r_ex level=2

Put something here. Maybe use it to display the exercise source code.