Chapter 3
IN THIS CHAPTER
Reading the status of push buttons
Using a potentiometer as input
Generating random numbers
Creating subroutines with the GOSUB command
In this chapter, you learn some additional PBASIC programming techniques that will become invaluable in your BASIC Stamp projects. Specifically, you learn how to handle input data in the form of push buttons, how to generate random numbers that will make your programs more interesting by adding a degree of randomness, how to read the value of a potentiometer, and how to organize your program into subroutines using the GOSUB command.
In Chapter 2 of this minibook, you learn how to connect an LED to a BASIC Stamp I/O pin and turn the LED on or off by using the HIGH and LOW commands in a PBASIC program. Those commands are designed to use BASIC Stamp I/O pins as output pins by setting the status of an I/O pin to HIGH or LOW so that external circuitry (such as an LED) can react to the pin’s status.
But what if you want to use an I/O pin as an input instead of an output? In other words, what if you want the BASIC Stamp to react to the status of an external circuit instead of the other way around? The easiest way to do that is to connect a push button to an I/O pin. Then, you can add commands to your PBASIC program to detect whether the push button is pressed.
There are two ways to connect a push button to a BASIC Stamp I/O pin:
Figure 3-1
shows examples of both active-high and active-low push buttons. In the active-high circuit, the I/O pin is connected to ground through R1 and R2 when the push button isn’t pressed. Thus, the voltage at the I/O pin is 0. When the push button is pressed, the I/O pin is connected to Vdd (
) through R1, causing the I/O pin to see
. As a result, the I/O pin is LOW when the button isn’t pressed and HIGH when the button is pressed.
FIGURE 3-1: Active-high and active-low input circuits.
In the active-low circuit, the I/O pin is connected to Vdd (
) through R1 and R2, causing the I/O pin to go HIGH. But when the button is pressed, the current from Vdd is shorted to ground through R2, causing the voltage at the I/O pin to drop to zero. Thus, the I/O pin is HIGH when the button isn’t pressed and LOW when the button is pressed.
Once you’ve connected a switch to a BASIC Stamp I/O pin, you need to know how to determine whether the switch is open or closed from a PBASIC program. The easiest way to do that is to first assign a name to the pin you want to test using the PIN directive. For example, if an active-high input button is connected to pin 14, you can assign it a name like this:
Button1 PIN 14
Here, the name Button1 is assigned to pin 14.
Then, to determine whether the push button is pressed, you can use an IF statement like this:
IF Button1 = 1 THEN
HIGH Led1
ENDIF
Here, the output pin designated as Led1 is made HIGH when the button is pressed.
If you want Led1 to be HIGH only when Button1 is pressed, use this code:
IF Button1 = 1 THEN
HIGH Led1
ELSE
LOW Led1
ENDIF
Here, Led1 is made HIGH if the button is pressed and LOW if the button isn’t pressed.
You can put the whole thing in a loop to repeatedly test the status of the button and turn the LED on and off accordingly:
DO
IF Button1 = 1 THEN
HIGH Led1
ELSE
LOW Led1
ENDIF
LOOP
Listing 3-1 shows an interesting program that works with a BASIC Stamp that has a push button switch connected to pin 14 and LEDs connected to pins 0 and 2. The program flashes the LED connected to pin 2 on and off at half-second intervals until the push-button switch is depressed. Then, it flashes the LED on pin 0.
LISTING 3-1 The Push Button Program
΄ Push Button Program
΄ Doug Lowe
΄ July 13, 2011
΄ {$STAMP BS2}
΄ {$PBASIC 2.5}
Led1 PIN 0
Led2 PIN 2
BUTTON1 PIN 14
DO
IF BUTTON1 = 1 THEN
LOW Led2
HIGH Led1
PAUSE 100
LOW Led1
PAUSE 100
ELSE
LOW Led1
HIGH Led2
PAUSE 100
LOW Led2
PAUSE 100
ENDIF
PAUSE 100
LOOP
Project 51 shows how to build a simple circuit you can use to test the program in Listing 3-1 , and Figure 3-2 shows the completed circuit.
FIGURE 3-2: A circuit for testing an active-high push-button switch (Project 51).
In this project, you connect two LEDs and one push button to a BASIC Stamp HomeWork board or Board of Education to test the programs presented in this chapter.
Pin (Cathode) |
Breadboard Hole |
P0 |
A17 |
P2 |
A15 |
Resistor |
From |
To |
R1 |
C3 |
P14 |
R2 |
E5 |
F5 |
R3 |
E15 |
F15 |
R4 |
E17 |
F17 |
Use the jumper wires to connect the resistors to ground.
D1 to (
)Vdd
D3 to D5
G5 to (–)Vss
I17 to I15
J15 to (–)Vss
Connect your BASIC Stamp to the computer and identify it in the Stamp Editor.
For more information about how to do this, refer to Chapter 1 of this minibook.
Type the Switch Test program into the Editor program window, and then save the file.
The Switch Test program is shown here:
΄ {$STAMP BS2}
΄ {$PBASIC 2.5}
Led1 PIN 0
Led2 PIN 2
Button1 PIN 14
DO
IF Button1 = 1 THEN
HIGH Led1
LOW Led2
ELSE
LOW Led1
HIGH Led2
ENDIF
LOOP
Choose Run⇒ Run.
If you prefer, you can click the Run button in the toolbar or press F9.
When you choose the Run command, the program is downloaded to the BASIC Stamp. After the program is downloaded, it automatically starts to run on the Stamp.
Press the button.
When you run the program, the LED connected to pin 2 lights up. When you push the button, the LED on pin 2 goes dark, and the LED on pin 0 lights.
Now try the program in Listing 3-1 .
This program is similar to the Switch Test program but adds the complication that the LEDs are flashing on and off at half-second intervals.
Many computer-controlled applications require a degree of randomness to their operation. A classic example is the Indiana Jones ride at Disneyland and Disney World. Every time you go on this ride, the adventure is slightly different. At the start, there are three doors that your vehicle can drive through; the exact door chosen for your ride is determined randomly. Many other details of the ride are randomly varied in an effort to make the adventure slightly different every time you ride it.
You can add a bit of randomness to your own BASIC Stamp programs by using the RANDOM command. This command scrambles the bits of a variable. Although you can use any type of variable with a RANDOM command, you’ll almost always want to use a BYTE or WORD variable. When you use a Byte variable, the RANDOM command generates a random number between 0 and 255. When you use a Word variable, the random value will be between 0 and 65,535.
Here’s an example of a simple way to use the RANDOM command to add a random pause:
Result VAR Word
RANDOM Result
PAUSE Result
This sequence of code creates a Word variable named Result , randomizes the variable, and then pauses for the number of milliseconds indicated by the Result variable.
In most cases, the value returned by the RANDOM command isn’t really the random number you’re looking for. Usually, you want to determine a random number that falls within a range of numbers. For example, if you’re writing a dice program and want to simulate the roll of a single die, you’ll need to generate a random number between 1 and 6. You can easily reduce the value of a full Word variable to a number between 1 and 6 by using a simple mathematical calculation that involves a special type of division operation called modulus division.
You may recall from Chapter 2
of this minibook that when PBASIC does division, it keeps the integer portion of the quotient and discards the remainder. For example,
; the remainder (1) is simply discarded.
Modulus division, which is represented by two slashes (//) instead of one, throws away the quotient and keeps only the remainder. Thus,
because the remainder of
.
You can put modulus division to good use when working with random numbers. For example:
Result VAR Word
Die VAR Byte
RANDOM Result
Die = Result // 6
Well, actually, the preceding calculation isn’t quite right. I include it to point out a common pitfall that happens if you forget that the remainder might happen to be 0. The above calculation actually returns a random number that falls between 0 and 6.
To find a random number that falls between 1 and 6, you should calculate the modulus division by 5, not by 6, and then add 1 to the result. For example:
Result VAR Word
Die VAR Byte
RANDOM Result
Die = Result // 5 + 1
The preceding calculation returns a random number between 1 and 6.
Listing 3-2 shows a sample program that lights LED1 (pin 0) until the push button on pin 14 is pressed. Then, it lights LED2 (pin 2) for a random period of time between 1 and 10 seconds. It uses the following equation to calculate the number of seconds to pause:
Seconds = Result // 9 + 1
It then multiplies the seconds by 1,000 to convert to milliseconds as required by the PAUSE command.
LISTING 3-2 The Random Program
΄ Random Program
΄ Doug Lowe
΄ July 10, 2011
΄
΄ This program turns on the LED at pin 2 for a random
΄ number of seconds between 1 and 10 when the push
΄ button on pin 14 is pressed.
΄ {$STAMP BS2}
΄ {$PBASIC 2.5}
Result VAR Word
Seconds VAR Byte
Button1 PIN 14
Led1 PIN 0
Led2 PIN 2
DO
HIGH Led1
IF Button1 = 1 THEN
RANDOM Result
Seconds = Result // 9 + 1
LOW Led1
HIGH Led2
PAUSE Seconds * 1000
HIGH Led1
LOW Led2
ENDIF
LOOP
Each time you press the push button in this program, LED2 lights up for a different length of time, between 1 and 10 seconds.
For example, if you use a Word variable whose initial value is 0, the RANDOM command will always change the variable’s value to 64992. If you apply the RANDOM command to the same variable again, the result will always be 9,072. The sequence of numbers generated from a given initial value is distributed randomly across the entire range of possible values (for example, 0 to 65,535), but the sequence is the same every time.
Thus, the program in Listing 3-2 always generates the same sequence of random delays. In particular, the sequence for the first ten button presses will always be as follows:
This sequence appears fairly random, but every time you reset the program and start over, the sequence will be identical.
The easiest way around this is to make the initial value of the variable fed to the RANDOM command dependent on some external event, such as the press of a button. You can easily do that by creating a loop that counts while it waits for the user to press the button. Because your program has no way to determine exactly when the user will press the button, the number used to start the random number generator will be truly random.
Listing 3-3 shows an improved version of the Random program that uses this technique to create a truly random delay. It turns out that only one additional line of code is needed to properly randomize the delay:
Result = Result + 1
By adding 1 to the Result variable each pass through the loop, the seed value for the RANDOM command will be unpredictable, so a true random value will be generated.
LISTING 3-3 An Improved Version of the Random Program
΄ Improved Random Program
΄ Doug Lowe
΄ July 10, 2011
΄
΄ This program turns on the LED at pin 2 for a random
΄ number of seconds between 1 and 10 when the push
΄ button on pin 14 is pressed.
΄
΄ This version of the program uses a counter to create a
΄ truly random number.
΄ {$STAMP BS2}
΄ {$PBASIC 2.5}
Result VAR Word
Seconds VAR Byte
Button1 PIN 14
Led1 PIN 0
Led2 PIN 2
DO
HIGH Led1
Result = Result + 1
IF Button1 = 1 THEN
RANDOM Result
Seconds = Result // 9 + 1
LOW Led1
HIGH Led2
PAUSE Seconds * 1000
HIGH Led1
LOW Led2
ENDIF
LOOP
As you know, a potentiometer (often called a pot ) is simply a variable resistor with a knob you can turn to vary the resistance. Pots of various types are often used as input devices for BASIC Stamp projects. For example, you might use a simple pot to control the speed of a pair of flashing LEDs: As you turn the pot’s knob, the rate at which the LEDs flash changes.
Although the most common type of pot has a mechanical knob to vary its resistance, many pots use some other means to vary their resistance. For example, a joystick uses pots that are connected to a moveable stick. One of the pots measures the motion of the stick in the x- axis; the other measures the motion of the stick in the y -axis. You might also connect a pot to the hinge on a door. Then, the resistance of the pot would indicate not only whether the door is opened or closed, but also the door’s angle if it’s partially opened.
The BASIC Stamp doesn’t have the ability to directly read the value of a pot connected to one of its I/O pins. However, by cleverly combining the pot with a small capacitor, you can measure how long the capacitor takes to discharge. With this knowledge, you can calculate the resistance of the POT by using the resistor-capacitor time calculations that are presented way back in Book 2, Chapter 3 . If you want to review how resistor-capacitor (RC) circuits work and how to do the time calculations, please refer to that chapter. For the purposes of this chapter, you don’t need to perform the time calculations yourself. However, it wouldn’t hurt to brush up on how RC circuits work.
Figure 3-3
shows a typical RC circuit connected to a pin on a BASIC Stamp. Here, a
pot is placed in parallel with a
capacitor. In addition, a
resistor is placed in series with the pot. This is done to protect the BASIC Stamp from damage that might be caused by excess current if you turn the pot’s knob so that the resistance of the pot drops to zero.
FIGURE 3-3: Connecting a pot to a BASIC Stamp I/O pin.
The capacitor in this circuit is small enough (
) that the circuit will charge and discharge very fast — within about a millisecond or so, depending on where the pot knob is set. Thus, your program won’t be delayed significantly while it waits for the capacitor to discharge so it can determine the resistance of the pot.
So given the circuit shown in Figure 3-3 , how would you go about measuring the resistance of the pot? The answer requires a clever bit of programming: First, you set pin 13 to HIGH, which charges the capacitor. Then, you set up a loop to monitor the input status of pin 13. Each time you check the status of pin 13, you add one to a counter. When the capacitor has discharged, pin 13 will go LOW. When pin 13 is LOW, the loop ends, and the counter indicates how long it took to discharge the capacitor. Knowing the size of the capacitor and the length of time it took to discharge the capacitor, you can calculate the resistance of the pot.
Fortunately, PBASIC includes a command called RCTIME that does all of this automatically. All you have to do is tell the RCTIME command what pin the RC circuit is on, whether you want to measure how long it takes the RC circuit to charge or discharge, and the name of a variable to store the resulting time calculation in.
Here’s how to use the RCTIME command to determine how long it takes an RC circuit on pin 13 to discharge, storing the answer in a variable named Timer :
RCTIME 13, 1, Timer
This RCTIME command sets the variable named Timer to a value that indicates how long it took the RC circuit to discharge. Immediately before this command, you should set the I/O pin (in this case, pin 13) to HIGH to charge the capacitor. You’ll also need to pause for a short time (usually, 1 ms is enough) to allow the circuit to charge.
Although you can use this technique to calculate the actual resistance of a pot, you don’t usually have to know the exact resistance. Instead, it’s usually sufficient to know that the counter increases when the resistance of the pot increases, and it decreases when the resistance of the pot decreases.
For the circuit shown in Figure 3-3
, the RCTIME
command calculates time values ranging from about 12 when the resistance of the pot is near 0 to about 54 when the resistance of the pot is at its maximum (
).
Listing 3-4 shows a simple program that alternately flashes LEDs connected to pins 0 and 2. The rate at which the LEDs flash is set by a pot in an RC circuit on pin 13. As you can see, the program simply multiples the time value calculated by the RCTIME command by ten to determine how long the program should pause between flashes. As you turn the pot’s knob to decrease the resistance of the pot, the LEDs flash at a faster rate.
LISTING 3-4 An LED Flashing Program That Uses a Pot
΄ Potentiometer LED Flashing Program
΄ Doug Lowe
΄ July 10, 2011
΄
΄ This program flashes LEDs connected to pins 0 and 2 at
΄ a rate determined by an RC circuit on pin 13.
΄ {$STAMP BS2}
΄ {$PBASIC 2.5}
Time VAR Word
Led1 PIN 0
Led2 PIN 2
Pot PIN 13
DO
HIGH Pot
RCTIME Pot, 1, Time
HIGH Led1
LOW Led2
PAUSE Time * 10
LOW Led1
HIGH Led2
PAUSE Time * 10
LOOP
Project 52 shows how to build a circuit that includes a
potentiometer and a capacitor so that you can test the code in Listing 3-4
. Figure 3-4
shows the completed circuit.
FIGURE 3-4: A circuit that uses a potentiometer to control flashing LEDs (Project 52).
In this project, you create a BASIC Stamp circuit that uses a potentiometer to control the rate at which two LEDs alternately flash.
Pin (Cathode) |
Breadboard Hole |
P0 |
A17 |
P2 |
A15 |
Resistor |
From |
To |
R1 |
B4 |
Pin 13 |
R2 |
E15 |
F15 |
R3 |
E17 |
F17 |
Insert the jumper wires.
F1 to C4
H3 to Vss
I17 to I15
J15 to Vss
Insert the capacitor.
One lead goes in G1, the other in Vss.
Insert the potentiometer.
The potentiometer has three leads, which should go in D4, F3, and F5.
Connect your BASIC Stamp to the computer and identify it in the Stamp Editor.
For more information about how to do this, refer to Chapter 1 of this minibook.
Type the Potentiometer LED Flashing program into the Editor program window, and then save the file.
The Potentiometer LED Flashing program is shown in Listing 3-4 .
Choose Run⇒ Run.
If you prefer, you can click the Run button in the toolbar or press F9.
When you choose the Run command, the program is downloaded to the BASIC Stamp. Once the program is downloaded, it automatically starts to run on the Stamp.
Turn the potentiometer knob.
The rate at which the LEDs flash will vary as you increase or decrease the resistance of the potentiometer.
A subroutine is a section of a program that can be called upon from any location in the program. When the subroutine finishes, control of the program jumps back to the location from which the subroutine was called. Subroutines are useful because they let you separate long portions of your program from the program’s main loop, which simplifies the main program loop to make it easier to understand.
Another benefit of subroutines is that they can make your program smaller. Suppose you’re writing a program that needs to perform some complicated calculation several times. If you place the complicated calculation in a subroutine, you can call the subroutine from several places in the program. That way, you have to write the code that performs the complicated calculation only once. Without subroutines, you would have to duplicate the complicated code each time you need to perform the calculation.
To create and use subroutines, you need to use two PBASIC commands. The first is GOSUB , which calls the subroutine. You typically use the GOSUB command within your program’s main loop whenever you want to call the subroutine. The second command is RETURN , which is always the last command in the subroutine. RETURN jumps back to the command that immediately follows the GOSUB command.
To create a subroutine, you start with a label and end with a RETURN command. Between them, you write whatever commands you want to execute when the subroutine is called.
Here’s an example of a subroutine that generates a random number between 1 and 999 and saves it in a variable named Rnd :
GetRandom:
RANDOM Rnd
Rnd = Rnd // 999 + 1
RETURN
To call this subroutine, you would simply use a GOSUB command like this:
GOSUB GetRandom
This GOSUB command transfers control to the GetRandom label. Then, when the GetRandom subroutine reaches its RETURN command, control jumps back to the command immediately following the GOSUB command.
Listing 3-5 shows a complete program that uses a subroutine to get a random number between 1 and 1,000 and uses the random number to cause the LED on pin 0 to blink at random intervals. You can run this program on any Basic Stamp circuit that has an LED on pin 0, including the circuits you built for Projects 51 and 52 in this chapter.
LISTING 3-5 Using a Subroutine to Blink an LED
΄ LED Blinker Program
΄ Doug Lowe
΄ July 10, 2011
΄
΄ This program blinks the LED on pin 0 randomly.
΄ {$STAMP BS2}
΄ {$PBASIC 2.5}
Rnd VAR Word
Led1 PIN 0
DO
GOSUB GetRandom
HIGH Led1
PAUSE Rnd
LOW Led1
PAUSE 100
LOOP
GetRandom:
RANDOM Rnd
Rnd = Rnd // 999 + 1
RETURN
FOR Counter = 1 TO 100
GOSUB GetRandom
HIGH Led1
PAUSE Rnd
LOW Led1
PAUSE 100
NEXT
GetRandom:
RANDOM Rnd
Rnd = Rnd // 999 + 1
RETURN
Do you see why? After the FOR-NEXT loop blinks the LED 100 times, the program will continue with the next command after the FOR-NEXT loop, which is the subroutine!
To prevent that from happening, you can use yet another PBASIC command, END , which simply tells the BASIC Stamp that you have reached the end of your program, so it should stop executing commands. You would place the END command after the NEXT command, like this:
FOR Counter = 1 TO 100
GOSUB GetRandom
HIGH Led1
PAUSE Rnd
LOW Led1
PAUSE 100
NEXT
END
GetRandom:
RANDOM Rnd
Rnd = Rnd // 999 + 1
RETURN
Then, the program will stop after the FOR-NEXT loop finishes.
Note that most BASIC Stamp programs don’t require an END command. That’s because most BASIC Stamp programs are written so that they loop continuously as long as power is applied to the Stamp. Even in the case of programs that loop indefinitely, however, you must be careful to make sure that your subroutines appear after the program’s main loop. That way, your subroutines will be executed only when you explicitly call them with a GOSUB command.