The Basic Starter Package includes a number of tutorials. The intent is to teach the user how to effectively use the Stamp in the context of various applications.
Some may find this first tutorial rather trivial. However, what is trivial to an experienced user may well be quite useful to a newcomer. The tutorials become increasingly advanced.
Routines were verified and corrected on Dec 2, '97.
Initial Ouput Routines.
At this point you have assembled the power supply, made the necessary connections from the serial port cable to the Stamp and have assembled and tested the logic probe.
Lets forge ahead and write some simple programs to flash a a single LED. In doing so, I hope to get across many of the PBASIC commands in a simple context which is understandable.
Please refer to Figure #1. Note that this consists of the addition of a single LED from P0 (Terminal 5) on the Stamp to ground. Note that the LEDs supplied with the Basic Starter package have an integrated 330 Ohm resistor to limit the LED current to nominally 10 mA.
The idea is to force a logic one on an ouput for a period of time, thus turning the LED on. And then, output a zero for a period of time, causing the LED to turn off.
' LED_1.BS2 ' ' Flashes an LED on Output 0 on and off ' ' Peter H. Anderson, MSU, 25 November, '97 ' TOP: DIR0=1 ' make P0 an output OUT0=1 ' turn on the LED PAUSE 500 ' pause for 500 msecs OUT0=0 ' turn off the LED PAUSE 500 GOTO TOPNote that an "'" indicates that all that follows on the line is a comment. Although not required, good programmers spend a bit of effort briefly describing the overall operation of the program and include annotations within the code to give the reader a clear idea of what the statements are doing.
The word TOP is a label, in this case, a place in the program. Labels can be virtually anything other than specific key words.
Each of the 16 I/O pins on the Basic Stamp may be configured as an input or as an output. The Stamp reserves a 16-bit variable DIRS with the state of each bit being used to confgure that pin as either as an input or an output.
Specific bits, may be selectively configured using the DIRp nomenclature. That is, DIR0 or DIR7 or DIR15. A one configures the bit as an output. Thus;
DIR0=1 ' make pin 0 an output DIR7=0 ' make pin 7 an inputThe Stamp reserves another 16-bit variable termed OUTS and here again, each of the specific pins may be addressed using OUTp; e.g., OUT0.
In this case;
OUT0=1 ' copy a one into bit OUT0 OUT0=0 ' copy a zero into bit OUT0Note that the pin will only assume the designated state if the associated DIR bit is defined to be an output.
The PAUSE command is pretty obvious. Note that the time is in milliseconds. The maximum value is 2^16-1 or 65,535. Thus, using the PAUSE, the largest delay one could achieve is nominally 60.5 seconds.
The GOTO redirects the program flow to the designated label. Thus, in this case, the program simply loops, continually flashing the LED.
' LED_2.BS2 ' ' Flashes an LED on Output 0 on and off. Simply another implementation of ' LED_1.BS2. ' ' Peter H. Anderson, MSU, 25 November, '97 ' OUT CON 1 ' define the word OUT to be the same as 1 DELAY_TIME CON 500 DIR0=OUT TOP: HIGH 0 ' turn on the LED PAUSE DELAY_TIME' pause for 500 msecs LOW 0 ' turn off the LED PAUSE DELAY_TIME GOTO TOP
Note that a number of changes have been made. However, the program does the same thing.
The concept of constants has been introduced using the keyword CON. These are nothing more than constants which are substituted into your code.
In the first program;
DIR0=1forced pin 0 to be an output. However, I always tend to forget whether it is a zero or a one and thus I use a standard set of constants that I cut and paste from one program to another. Thus;
OUT CON 1simply means, to substitute a "1" where the word OUT appears in the program.
Similarly, substitute 500 wherever the the word DELAY_TIME appears.
Note that the DIR0=1 has been pulled out of the loop, and is now executed only one time when the program begins. Once the direction of a bit has been defined, there is no need to continually do so as was done in the first program.
The commands HIGH and LOW followed by the pin number are simply alternative representations of the OUT command;
HIGH 0 ' same as OUT0 = 1 LOW 0 ' same as OUT0 = 0Program LED_3.BS2.
' LED_3.BS2 ' ' Flashes an LED on Output 0 on and off 50 times ' ' Peter H. Anderson, MSU, 25 November, '97 ' TIMES VAR BYTE OUT CON 1 ' define the word OUT to be the same as 1 LED CON 0 ' LED is on pin 0 DELAY_TIME CON 500 DIR0=OUT TOP: FOR TIMES = 1 TO 50 HIGH LED ' turn on the LED PAUSE DELAY_TIME ' pause for 500 msecs LOW LED ' turn off the LED PAUSE DELAY_TIME DEBUG ?TIMES NEXT DEBUG "Done!" DONE: GOTO DONEIn this program, the variable TIMES has been declared as a byte (8 bits). Note that a byte may accommodate the numbers 0 through 255 and in this application where the highest value is 50, a byte is sufficient. If I desired to flash the LED a 1000 times, the variable would have been declared as a "word" which is two bytes. A word can accommodate values from 0 to 65,535.
Note that an additional constant has been defined; LED. Rather than burdening one's mind with the fact that the LED is on pin 0, this simple idea of a constant permits you to make that connection just once and then refer to the pin as simply "LED". As we only are using one lead at this point, the advantage may not be obvious, but it will when we begin to use many of the 16 inputs and outputs.
Program LED_3 introduces the use of the FOR - NEXT command. In this context, the commands between the FOR and the NEXT are executed with TIMES equal to 1, and then with TIMES equal to 2, etc until TIMES is equal to 50. TIMES is then incremented to 51, which is beyond the upper bound (50) and the program thus breaks from the FOR loop.
Note that on each pass through the loop, the LED is flashed and, in addition the value of TIMES is displayed on the terminal using the DEBUG command.
The DEBUG command allows you to print values and text to the terminal. Future programs will treat this in much greater detail. For the moment;
DEBUG "Hello", CR ' prints Hello followed by a new line DEBUG DEC TIMES ' prints the value of TIMES is decimal ' not there is no new line DEBUG "N=", DEC TIMES CR ' prints TIMES=value. For example TIMES=33 DEBUG ?N ' this is a much simpler shorthand for the aboveAfter breaking from the FOR - NEXT loop, the program displays a "Done" message and then continually loops doing nothing.
' LED_4.BS2 ' ' Flashes an LED on Output 0 on and off 50 times. Uses a subroutine ' ' Peter H. Anderson, MSU, 25 November, '97 ' TIMES VAR BYTE OUT CON 1 ' define the word OUT to be the same as 1 LED CON 0 ' LED is on pin 0 DELAY_TIME CON 500 DIR0=OUT TOP: FOR TIMES = 1 TO 50 GOSUB FLASH NEXT DEBUG "Done!" DONE: GOTO DONE FLASH: ' winks LED on and off one time HIGH LED ' turn on the LED PAUSE DELAY_TIME ' pause for 500 msecs TOGGLE LED ' turn off the LED PAUSE DELAY_TIME DEBUG ?TIMES RETURNNote that the FOR loop is now reduced to simply a "call" or GOSUB to subroutine FLASH. In the subroutine, the LED is turned on, there is a pause, and then the LED lead is toggled. That is, as the current state of the LED lead is high, the TOGGLE results in bringing it low. If it were low, TOGGLE would bring the lead high.
The RETURN statement then causes the program to return to the statement after the GOSUB.
In calling (GOSUB) a subroutine, the return address is pushed on to a stack. When the RETURN is executed this return address is pulled from the top of the stack. Fortunately, this is all hidden from the Stamp user, but the general mechanics are important in appreciating the following limitations.
A subroutine must be called (GOSUB). You cannot GOTO a subroutine, as with a GOTO, the return address is not pushed on to the stack. Thus, in executing the RETURN, whatever junk happens to be on the top of the stack will be pulled and the program will, in theory, continue at that junk address and your program will not work properly.
Along the same line, your subroutine must not be in the program flow of other routines. An example is illustrated in LED_4A.
' LED_4A.BS2 (This routine illustrates a common error. It will not work). ' ' Flashes an LED on Output 0 on and off 50 times. Uses a subroutine ' ' Peter H. Anderson, MSU, 25 November, '97 ' TIMES VAR BYTE OUT CON 1 ' define the word OUT to be the same as 1 LED CON 0 ' LED is on pin 0 DELAY_TIME CON 500 DIR0=OUT TOP: FOR TIMES = 1 TO 50 GOSUB FLASH NEXT FLASH: ' winks LED on and off one time HIGH LED ' turn on the LED PAUSE DELAY_TIME ' pause for 500 msecs TOGGLE LED ' turn off the LED PAUSE DELAY_TIME DEBUG ?TIMES RETURN DEBUG "Done!" DONE: GOTO DONENote that this program is wrong. The LED will indeed flash the 50 times. However, on exiting the FOR - NEXT loop, the program will enter the subroutine. Note that there has been no call (GOSUB). The LED will again flash (for the 51st time). However, on executing the RETURN, the top of the stack does not contain a valid return address and your program will go to off to "never never land".
Finally, you must RETURN from a subroutine. Consider the following very incorrect routine.
' LED_4B.BS2 (This routine illustrates another common error. It will not ' work.) ' ' Flashes an LED on Output 0 on and off 50 times. Uses a subroutine ' ' Peter H. Anderson, MSU, 25 November, '97 ' TIMES VAR BYTE OUT CON 1 ' define the word OUT to be the same as 1 LED CON 0 ' LED is on pin 0 DELAY_TIME CON 500 DIR0=OUT TOP: FOR TIMES = 1 TO 50 GOSUB FLASH AGAIN: NEXT DEBUG "Done!" DONE: GOTO DONE FLASH: ' winks LED on and off one time HIGH LED ' turn on the LED PAUSE DELAY_TIME ' pause for 500 msecs TOGGLE LED ' turn off the LED PAUSE DELAY_TIME DEBUG ?TIMES GOTO AGAIN ' !!!!!!!!!! wrong!In ths incorrect routine, the FLASH routine is called with a GOSUB and the return address is pushed on to the top of the stack. However, at the conclusion of the FLASH routine, I have erroneously used a GOTO. Thus, the return address is not pulled off the top of the stack. The routine is again called, but there is no corresponding pull in the subroutine. Thus, the stack is growing and growing and eventually, your program will stop working.
In summary, when using subroutines, be certain that the only way the program enters the subroutine is with a GOSUB. No GOTOs and no "walk-ins" as illustrated in routine LED_4B. Be certain to use a RETURN to go back to the calling program.
Note that in program LED_4, the DEBUG ?TIMES was included in the subroutine to make a point. All variables in PBASIC are global. That is, the variables are known to all subroutines.
Consider rolling a die. This generates a random number in the range of 1 to 6. Roll a second die, generating a second number. Sum them and wink the LED that number of times. This might be used as a rather expensive and inferior replacement for rolling dice as in playing Monopoly, Parchesi and other less redeeming games of chance. I say all of this with tongue in cheek, but the routine helps to make a number of points.
' LED_5.BS2 ' ' Flashes an LED on Output 0 on and off the number of times "rolled" by ' two dice. ' ' Peter H. Anderson, MSU, 25 November, '97 ' DIE_1 VAR BYTE ' first DIE DIE_2 VAR BYTE ' second DIE DICE VAR BYTE ' total R VAR WORD ' random number in range 0-65,535 TIMES VAR BYTE ' flash counter OUT CON 1 ' define the word OUT to be the same as 1 LED CON 0 ' LED is on pin 0 DELAY_TIME CON 500 DIR0=OUT TOP: LOW LED ' be sure the LED is off RANDOM R ' R is now 0 - 65,535 DIE_1 = (R // 6) + 1 ' roll first die RANDOM R DIE_2 = (R // 6) + 1 ' roll the second DICE = DIE_1 + DIE_2 GOSUB WINK ' wink LED DICE times DEBUG "Done!" DONE: GOTO DONE WINK: ' flashes LED DICE times FOR TIMES=1 TO DICE GOSUB FLASH NEXT RETURN FLASH: ' winks LED on and off one time HIGH LED ' turn on the LED PAUSE DELAY_TIME ' pause for 500 msecs TOGGLE LED ' turn off the LED PAUSE DELAY_TIME RETURNThe RANDOM command generates a number in the range of 0 - 65,535 and places the result in variable R. Note that R has been declared as a word to accommodate this.
The "//" is the mod operator. Somewhere back in junior high, you probably had such exercises as 12323 divided by 6 = 2053 with a remainder of 5. The mod operation is simply the remainder. Thus R // 6 generates a number in the range of 0 to 5. Adding one gives a random number in the range of 1 to 6.
This operation is performed twice to provide values for DIE_1 and DIE_2. The results are summed (DICE) and subroutine WINK is called which in turn calls subroutine FLASH, DICE times.
Note that in this example, the FOR loop takes a variable.
There is a flaw in this program in that it will always produce the same "random" numbers. The command RANDOM takes the arguement, in this case variable R and operates on it so as to produce a pseudo random number. In this program, the initial value of R is 0 and thus the RANDOM command will always produce the same random R, and thus DIE_1 will always be the same. This value of R is then used to calculate another value of R, but it will also always be the same and thus DIE_2 will always be the same. None too practical, but I like the program as it shows the use of the RANDOM function, the mod operator, nested subroutines and the use of a variable in a FOR-NEXT loop.
This problem of lack of randomness can be corrected by intializing R to some random value. A common technique is to use time and this will be presented in a future tutorial.
All of this has used only one LED which is none too interesting.
However, I am hopeful you have an appreciation for the direction control DIRS, and controlling the direction of a specific bit (DIR0). You know how to define a constant using CON, how to declare a variable using VAR and the difference between a byte and a word. You should know what a label is. All outputs are referred to as OUTS, but a specific bit may be referenced as OUT0. The commands GOTO, FOR-NEXT, HIGH, LOW, TOGGLE and PAUSE have been treated. The commands GOSUB and RETURN have been discussed and hopefully you appreciate the mechanics of subroutines.