Chapter 3

Discovering More PBASIC Programming Tricks

In This Chapter

arrow Connecting pushbuttons

arrow Generating random numbers

arrow Using a potentiometer as input

arrow Creating subroutines with the GOSUB command

In this chapter, we describe some more PBASIC programming techniques, to add to the fundamental ones we introduce in Chapter 2 of this minibook. These tips are sure to become invaluable in your BASIC Stamp projects. Specifically, you discover handling input data in the form of pushbuttons, generating random numbers (that make your programs more interesting by adding a degree of randomness), reading the value of a potentiometer and using the GOSUB command to organise your program into subroutines.

Pushing Buttons with a BASIC Stamp

In Chapter 2 of this minibook, we discuss connecting a light-emitting diode (LED) to a BASIC Stamp I/O pin and turning 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, 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 pushbutton to an I/O pin. Then, you can add commands to your PBASIC program to detect whether the pushbutton is pressed.

remember.eps

You can connect a pushbutton to a BASIC Stamp I/O pin in two ways:

check.png Active-high: This connection places +5 V on the I/O pin when the pushbutton is pressed. When the button is released, the I/O pin sees 0 V.

check.png Active-low: This connection sees +5 V when the pushbutton isn’t pressed. When you press the pushbutton, the +5 V is removed and the I/O pin sees no voltage.

Figure 3-1 shows examples of active-high and active-low pushbuttons. In the active-high circuit, the I/O pin is connected to ground through R1 and R2 when the pushbutton isn’t pressed. Thus, the voltage at the I/O pin is 0. When the pushbutton is pressed, the I/O pin is connected to Vdd (+5 V) through R1, causing the I/O pin to see +5 V. As a result, the I/O pin is low when the button isn’t pressed and high when the button is pressed.

9781118589717-fg070301.eps

Figure 3-1: Active-high and active-low input circuits.

In the active-low circuit, the I/O pin is connected to Vdd (+5 V) through R1 and R2, causing the I/O pin to go high. But when the button is pressed, the current from Vdd is connected 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.

warning_bomb.eps Note that in both circuits, R1 is connected directly to the I/O pin to prevent excessive current flow when the switch is pressed. Without this resistor, the pin would be connected directly to Vdd (+5 V) or Vss (ground) when the button is pressed, which can damage your BASIC Stamp.

technicalstuff.eps In an active-high circuit, R2 is called a pull-down resistor because it pulls the current from the I/O pin down to zero when the pushbutton isn’t depressed. In an active-low circuit, R2 is called a pull-up resistor because it pulls the voltage at the I/O pin up to Vdd (+5 V) when the pushbutton isn’t depressed.

Checking the Status of a Switch in PBASIC

When 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 so 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 (see the preceding section for an explanation) is connected to pin 14, you can assign it a name such as this:

Button1 PIN 14

Here, the name Button1 is assigned to pin 14.

To determine whether the pushbutton is pressed, you can use an IF statement as follows:

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.

tip.eps In addition, you can put the whole thing in a loop to test repeatedly 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 pushbutton 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 pushbutton switch is depressed. Then, it flashes the LED on pin 0.

Listing 3-1: The Pushbutton Program

' Pushbutton 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 3-1 shows how to build a simple circuit you can use to test the program in Listing 3-1.

9781118589717-un070301a.eps

9781118589717-un070301b.eps

Randomising Your Programs

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, in which the adventure is slightly different every time. At the start, your vehicle can enter through one of three doors; 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 climb board.

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 (check out this minibook's Chapter 2 for a definition).

tip.eps Although you can use any type of variable with a RANDOM command, you almost always want to use a Byte or Word variable:

check.png Byte: The RANDOM command generates a random number between 0 and 255.

check.png Word: The random value is 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, randomises the variable and then pauses for the number of milliseconds (ms) 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 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.

As we explain in Chapter 2 of this minibook, when PBASIC does division, it keeps the integer portion of the quotient and discards the remainder. For example, 10 / 3 = 3; 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, 10 // 3 = 1, because the remainder of 10 / 3 = 1.

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

remember.eps In fact, the preceding calculation isn’t quite right. We include it to point out a common pitfall that happens if you forget that the remainder may 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 need to 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 pushbutton 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

The program 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 pushbutton 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 pushbutton in this program, Led2 lights up for a different length of time, between 1 and 10 seconds.

technicalstuff.eps In fact, the RANDOM command isn't really all that random. The starting value of the variable used by the RANDOM command is called the seed value. The RANDOM command applies a complex mathematical calculation to the seed value to determine a result that appears to be random. However, the result isn't random: given a particular seed value, the RANDOM command always returns the same result.

For example, if you use a Word variable whose initial value is 0, the RANDOM command always changes the variable's value to 64,992. If you apply the RANDOM command to the same variable again, the result is always 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 is always as follows:

check.png First press: 4 seconds

check.png Second press: 1 second

check.png Third press: 4 seconds

check.png Fourth press: 1 second

check.png Fifth press: 3 seconds

check.png Sixth press: 5 seconds

check.png Seventh press: 4 seconds

check.png Eighth press: 1 second

check.png Ninth press: 1 second

check.png Tenth press: 4 seconds

This sequence appears fairly random, but every time you reset the program and start over, the sequence is identical.

The easiest way around this problem 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 is going to press the button, the number used to start the random number generator is truly random.

Listing 3-3 shows an improved version of the random program that uses this technique to create a truly random delay. Only one additional line of code is needed to randomise the delay properly:

Result = Result + 1

By adding 1 to the Result variable each pass through the loop, the seed value for the RANDOM command is unpredictable, and so a true random value is 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 pushbutton 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

Reading a Value from a Potentiometer

A potentiometer (often called a pot) is simply a variable resistor with a knob you can turn to vary the resistance (check out Book II, Chapter 2 for more about potentiometers). Pots of various types are often used as input devices for BASIC Stamp projects. For example, you can 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 can also connect a pot to the hinge on a door, in which case the resistance of the pot indicates not only whether the door is opened or closed, but also the door’s angle if it’s partly opened.

The BASIC Stamp doesn’t have the ability to read directly 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 (RC) time calculations that we present in Book II, Chapter 3 (flip to that chapter too to find out how RC circuits work and how to do the time calculations). For the purposes of this chapter, you don’t need to perform the time calculations yourself, but brushing up on how RC circuits work isn’t going to hurt.

Figure 3-2 shows a typical RC circuit connected to a pin on a BASIC Stamp. Here, a 10 kΩ pot is placed in parallel with a 0.1 μF capacitor. In addition, a 220 Ω resistor is placed in series with the pot to protect the BASIC Stamp from damage that may be caused by excess current if you turn the pot’s knob so that the resistance of the pot drops to zero.

The capacitor in this circuit is small enough (0.1 μF) that the circuit charges and discharges very quickly – within about a millisecond or so, depending on where the pot knob is set. Thus, your program isn’t delayed significantly while it waits for the capacitor to discharge so that it can determine the resistance of the pot.

9781118589717-fg070302.eps

Figure 3-2: Connecting a pot to a BASIC Stamp I/O pin.

Given the circuit in Figure 3-2, how do you go about measuring the resistance of the pot? The answer requires a clever bit of programming. You set pin 13 to high, which charges the capacitor. Then, you set up a loop to monitor the input status of pin 13, which stops driving P13 high and allows the capacitor to discharge through the R2. Each time you check the status of pin 13, you add 1 to a counter. When the capacitor has discharged, pin 13 goes low. When pin 13 is low, the loop ends and the counter indicates how long discharging the capacitor took. Knowing the size of the capacitor and the length of time required to discharge the capacitor allows you to calculate the resistance of the pot.

Fortunately, PBASIC includes a command called RCTIME that does this process 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 the RC circuit takes to charge or discharge, and the name of a variable in which to store the resulting time calculation.

Here's how to use the RCTIME command to determine how long an RC circuit on pin 13 takes 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 need to set the I/O pin (in this case, pin 13) to high to charge the capacitor. You also need to pause for a short time (usually, 1 ms is enough) to allow the circuit to charge.

tip.eps 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, knowing that the counter increases when the resistance of the pot increases and decreases when the resistance of the pot decreases, is usually sufficient.

For the circuit shown in Figure 3-2, 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 (10 kΩ).

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 10 to determine how long the program is to 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 3-2 shows you how to build a circuit that includes a 10 kΩ potentiometer and a capacitor so that you can test the code in Listing 3-4. Figure 3-3 shows the completed circuit.

9781118589717-fg070303.tif

Figure 3-3: A circuit that uses a potentio­meter to control flashing LEDs (Project 3-2).

9781118589717-un070302a.eps

9781118589717-un070302b.eps

Using Subroutines and the GOSUB Command

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 shorter. Suppose that 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’d have to duplicate the complicated code each time you need to perform the calculation, which would be a real drag.

To create and use subroutines, you make use of two PBASIC commands:

check.png remember.eps GOSUB: Calls the subroutine. You typically use the GOSUB command within your program's main loop whenever you want to call the ­subroutine.

check.png RETURN: 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 simply use a GOSUB command as follows:

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 build for Projects 3-1 and 3-2 earlier 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

warning_bomb.eps When you use a subroutine, you have to prevent your program from accidentally 'falling into' your subroutine and executing it when you don't intend it to. For example, suppose that the program in Listing 3-5 uses a FOR-NEXT loop instead of a DO loop, because you want to blink the LED only 100 times. Here's an example of how not to write that program:

FOR Counter = 1 TO 100

GOSUB GetRandom

HIGH Led1

PAUSE Rnd

LOW Led1

PAUSE 100

NEXT

 

GetRandom:

RANDOM Rnd

Rnd = Rnd // 999 + 1

RETURN

We hope you can see the problem: after the FOR-NEXT loop blinks the LED 100 times, the program continues with the next command after the FOR-NEXT loop, which is the subroutine!

To prevent that from happening, you can use another PBASIC command, END, which simply tells the BASIC Stamp that you've reached the end of your program and it should stop executing commands. You place the END command after the NEXT command, as follows:

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

The program stops after the FOR-NEXT loop finishes.

remember.eps Most BASIC Stamp programs don't require an END command, because they're 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 have to be careful to make sure that your subroutines appear after the program's main loop. That way, your subroutines are executed only when you explicitly call them with a GOSUB command.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset