Because we did so much work up front, this will be easy.
The combined list looks like this:
Believe it or not, that's almost the program, right there!
Each comment line starts with a single apostrophe (').
After that, you can put anything.
None of that actually does anything.
It just helps me understand which program this is, why I wrote it,
and how I have changed it over time.
I'll add more comments along the way.
Luckily, you can assign a symbolic name to things, making then easier to understand.
Let's give names to the pins for each gadget.
See those apostrophes to the right of each SYMBOL line?
More comments!
They remind us how to use each gadget.
Notice that I have a line for every I/O pin on the controller,
in numeric order,
with unused pins being a whole-line comment.
This gives me a complete checklist of what is used and what is not.
Someday, if I need to add another output,
I can just take a peek at this list and pick one that's not being used.
We will also define some other symbols that will make our code more readable:
THis will make the program a lot easier for us to read, because we can do things like
"check the MonsterCount", which is more understandable than "check W2".
In order to prepare for input and output, you need to tell the Prop-1 which of the eight pins
are used for input and which are for output.
[This isn't always necessary, as the Prop-1 has certain "default" values that
take effect until you decide to change them.
But when I write a program, I dislike making assumptions,
so I explicitly tell the Prop-1 what I want to do.]
This is done by setting "DIRS" equal to some value.
Another thing that I like to do in the front of the program is to clear all output bits.
You can set all the bits at once by assigning a value to PINS.
We never described what DIRS and PINS are.
These are special names, built in to the Prop-1 programming language.
Assigning a value to DIRS indicates which of the I/O pins are used for input and which are used for output.
Assigning a value to PINS sets the value of the output pins all at once.
The value that is assigned to DIRS and PINS starts with "%".
This means that it is a binary value,
with each digit being a "0" or "1", and corresponding to an I/O pin.
When setting DIRS, "1" means the corresponding pin will be used for output and "0" means input.
The "bit positions" comment lines up with the "1"s and "0"s of DIRS and PINS,
so we can see at a glance how each pin is used.
This program has a main loop that it pretty big.
It starts at the top of list of things to do
and runs through the bottom where we say "start again".
At the bottom of the main loop it says "GOTO Main",
which uses the label that we put up at the top.
When the Prop-1 "runs the program",
it starts at the top, looks at each line and does that thing,
and moves on to the next line.
In this case, the Prop-1 will run through the whole body of the program,
hit "GOTO Main",
and then go back to the "Main" label at the top.
It's like playing Monopoly: "Go directly to Jail. Do not pass GO. Do not collect $200."
The easy part is detecting the kid. "IF KidPresent..." does that.
The rest of the lines make sure we see the kid several times before we start the show.
The line "KidWait = KidWait + 1" deserves special attention,
because you would flunk math if you wrote "1 = 1 + 1".
But remember that this isn't math.
It is an assignment.
We take the thing on the right of "=" and figure out what it means.
That means we read the counter of how many times we saw a kid and add one to it.
Then the result of that is placed in the location on the left of the "=".
So "KidWait = KidWait + 1"
just increases by one the value of KidWait.
Notice that there are two labels, SStartAgain and SLookAgain.
Each time we see the kid and want to peek again, we will go to SLookAgain.
If we peek and the kid is gone, we go to SStartAgain and clear the counter.
When we have seen somebody 20 times, we're happy.
(The design wants us to see a kid for a whole second, but this just looks 20 times.
I didn't bother measuring or calculating this,
and the number of times should probably be much higher.
I just threw in 20 so you could get the idea.)
This code should look mighty familiar.
It works just like
Wait For A Kid,
except this test is waiting until everybody is gone.
Programming
Let's write the program, a little piece at a time...
The Beginning
I like to start programs with some comments.
These are notes to myself, inside the program,
that will remind me what's going on when I come back to the program later.
' © Wolfstone, 2006
' =========================================================================
'
' File....... PopUpMonster.bs1
' Purpose.... Animate a pop-up monster - a learning exercise for PROP-1 programming
' Author..... Dennis Griesser
' E-mail..... wolfstone@pobox.com
'
' {$STAMP BS1}
' {$PBASIC 1.0}
'
' =========================================================================
' -----[ Program Description ]---------------------------------------------
'
' This is a PROP-1 program designed to animate a pop-up monster.
' It is a learning exercise for the Prop-1 controller.
' -----[ Revision History ]------------------------------------------------
'
' PopUpMonster_01.bs1 - 30 May 2006 - First version.
' PopUpMonster_02.bs1 - 1 June 2006 - New improved version.
Symbols And Constants
We could write this program using just plain numbers for the various gadgets we talk to.
For example, we could look for kids (the PIR sensor) by seeing if PIN7 is 1.
Such a program would work just fine.
But it would be hard for us to understand later - when we have to come back and add something.
It might even be hard to understand right now, when we are writing the program
and trying to get it to work for the first time.
All of these definitions look similar.
The first one, "SYMBOL KidPresent = PIN7", tells the Prop-1,
"whenever you see the word KidPresent, look at input PIN7 and use whatever you see there".
' -----[ I/O Definitions ]-------------------------------------------------
'
' unused PIN7 ' unused
SYMBOL KidPresent = PIN6 ' 1 = kid present; 0 = no kids
' unused PIN5 ' unused
' unused PIN4 ' unused
SYMBOL ThunderCrash = PIN3 ' to trigger thunder sound, pulse 0->1->0
SYMBOL MonsterRoar = PIN2 ' to trigger monster sound, pulse 0->1->0
SYMBOL StrobeFlash = PIN1 ' to flash strobe, pulse 0->1->0
SYMBOL MonsterUp = PIN0 ' 1 = show monster; 0 = hide monster
We will soon see how these help out.
' -----[ Constants ]-------------------------------------------------------
'
SYMBOL No = 0
SYMBOL Yes = 1
Variables
During the execution of the program we will have to remember a few things:
Here, we reserve space to hold these things:
The Prop-1 has several storage locations where you can keep a number.
They are numbered W1, W2, W3, etc.
"SYMBOL MonsterCount = W2" tells the Prop-1,
"Wherever the program uses the word MonsterCount, use the number stored in location W2."
' -----[ Variables ]-------------------------------------------------------
'
SYMBOL KidWait = W1
SYMBOL MonsterCount = W2
Initialization
"Initialization" is the beginning of the program where things are set up
for later use.
Normally, the program only executes the initialization part once - when you turn it on.
See that "DIRS = %00111111"?
It is an "assignment" statement.
It tells the Prop-1, "take the value on the right (%00111111) and stuff it into the storage location called DIRS."
' -----[ Initialization ]--------------------------------------------------
' Do this only once, when we first start up.
'
Reset:
' 76543210 ' bit positions
DIRS = %00111111 ' make P7-P6 inputs, P5-P0 outputs
PINS = %00000000 ' all outputs off
MonsterCount = 0 ' we never showed monster before
The Main Loop
Whenever programmers write a piece of program that is executed again and again,
they call it a "loop".
The "Main:" towards the top is a "label".
It is a way to tell the Prop-1 about an important part of the program
that we will refer to later.
' -----[ Program Code ]----------------------------------------------------
' We start each cycle up here.
' We will come here after every show, to get ready for the next show.
Main:
' THE WHOLE "BODY" OF THE PROGRAM GOES HERE!
' LOTS OF STUFF!
' COMING SOON!
' Start again at the top
GOTO Main
Wait For A Kid
We're not supposed to start the show until we detect a kid.
And not just notice one litle flicker from the detector.
We have to be sure he's there.
' Wait for the Trick-or-Treater.
'
' To be sure that the kid is really there, make sure that we see
' the KidPresent several times in a row (ideally a full second)
' before actually starting.
SStartAgain:
KidWait = 0
SLookAgain:
IF KidPresent = No THEN SStartAgain ' if no kid, start at the beginning
KidWait = KidWait + 1
IF KidWait < 20 THEN SLookAgain ' keep looking until we see kid many times
Pop Up The Monster
Now it's time for the show!
' Begin the show!
' Pop up the monster
MonsterUp = Yes
' It takes 1 second for the monster to get all the way up.
PAUSE 1000 ' wait 1.0 second for monster to get up
' After the monster is all the way up, make it roar
PULSOUT MonsterRoar, 1 ' 10 µs pulse; make monster roar
Occasional Lightning
We need to simulate lightning every third time the monster pops up.
' Every third appearance of the monster should be accompanied by a flash of
' lightning.
MonsterCount = MonsterCount + 1 ' count this appearance
IF MonsterCount <> 3 THEN SkipFlash
MonsterCount = 0 ' reset the counter
PULSOUT StrobeFlash, 1 ' 10 µs pulse; make lightning flash
PULSOUT ThunderCrash, 1 ' 10 µs pulse; make thunder crash
SkipFlash:
Hide
Now we need to keep the monster visible for 5 seconds, and then hide him.
' Display the monster for 5 seconds
PAUSE 5000 ' wait 5.0 seconds
' Hide the monster again
MonsterUp = No
' It takes 1 second for the monster to hide all the way.
PAUSE 1000 ' wait 1.0 second for monster to hide
Clear The Room
The show is over.
But before we loop around and start again, we have to make sure all the kids are gone.
' Do not trigger again until the current kid leaves.
'
' To be sure that the kid is really gone, make sure that we see
' no KidPresent several times in a row (ideally a full 5 seconds)
' before actually starting.
FStartAgain:
KidWait = 0
FLookAgain:
IF KidPresent = Yes THEN FStartAgain ' if there is kid, start at the beginning
KidWait = KidWait + 1
IF KidWait < 100 THEN FLookAgain ' keep looking until we see no kid many times