WARRANTY
Parallax Inc. warrants its products against defects in materials and workmanship for a period of 90 days from receipt
of product. If you discover a defect, Parallax Inc. will, at its option, repair or replace the merchandise, or refund the
purchase price. Before returning the product to Parallax, call for a Return Merchandise Authorization (RMA)
number. Write the RMA number on the outside of the box used to return the merchandise to Parallax. Please enclose
the following along with the returned merchandise: your name, telephone number, shipping address, and a description
of the problem. Parallax will return your product or its replacement using the same shipping method used to ship the
product to Parallax.
14-DAY MONEY BACK GUARANTEE
If, within 14 days of having received your product, you find that it does not suit your needs, you may return it for a
full refund. Parallax Inc. will refund the purchase price of the product, excluding shipping/handling costs. This
guarantee is void if the product has been altered or damaged. See the Warranty section above for instructions on
returning a product to Parallax.
COPYRIGHTS AND TRADEMARKS
This documentation is copyright 2005 by Parallax Inc. By downloading or obtaining a printed copy of this
documentation or software you agree that it is to be used exclusively with Parallax products. Any other uses are not
permitted and may represent a violation of Parallax copyrights, legally punishable according to Federal copyright or
intellectual property laws. Any duplication of this documentation for commercial uses is expressly prohibited by
Parallax Inc. Duplication for educational use is permitted, subject to the following Conditions of Duplication:
Parallax Inc. grants the user a conditional right to download, duplicate, and distribute this text without Parallax's
permission. This right is based on the following conditions: the text, or any portion thereof, may not be duplicated for
commercial use; it may be duplicated only for educational purposes when used solely in conjunction with Parallax
products, and the user may recover from the student only the cost of duplication.
This text is available in printed format from Parallax Inc. Because we print the text in volume, the consumer price is
often less than typical retail duplication charges.
BASIC Stamp, Stamps in Class, Boe-Bot SumoBot, SX-Key and Toddler are registered trademarks of Parallax, Inc.
If you decide to use registered trademarks of Parallax Inc. on your web page or in printed material, you must state
that "(registered trademark) is a registered trademark of Parallax Inc.” upon the first appearance of the trademark
name in each printed document or web page. HomeWork Board, Parallax, and the Parallax logo are trademarks of
Parallax Inc. If you decide to use trademarks of Parallax Inc. on your web page or in printed material, you must state
that "(trademark) is a trademark of Parallax Inc.”, “upon the first appearance of the trademark name in each printed
document or web page. Other brand and product names are trademarks or registered trademarks of their respective
holders.
ISBN 1-928982-35-2
DISCLAIMER OF LIABILITY
Parallax Inc. is not responsible for special, incidental, or consequential damages resulting from any breach of
warranty, or under any legal theory, including lost profits, downtime, goodwill, damage to or replacement of
equipment or property, or any costs of recovering, reprogramming, or reproducing any data stored in or used with
Parallax products. Parallax Inc. is also not responsible for any personal damage, including that to life and health,
resulting from use of any of our products. You take full responsibility for your BASIC Stamp application, no matter
how life-threatening it may be.
INTERNET DISCUSSION LISTS
We maintain active web-based discussion forums for people interested in Parallax products. These lists are accessible
from www.parallax.com.
•
•
•
•
•
•
•
Propeller Chip – This list is specifically for our customers using Propeller chips and products.
BASIC Stamp – This list is widely utilized by engineers, hobbyists and students who share their
BASIC Stamp projects and ask questions.
Stamps in Class® – Created for educators and students, subscribers discuss the use of the Stamps in
Class curriculum in their courses. The list provides an opportunity for both students and educators to
ask questions and get answers.
Parallax Educators – A private forum exclusively for educators and those who contribute to the
development of Stamps in Class. Parallax created this group to obtain feedback on our curricula and
to provide a place for educators to develop and obtain Teacher’s Guides.
Robotics – Designed for Parallax robots, this forum is intended to be an open dialogue for robotics
enthusiasts. Topics include assembly, source code, expansion, and manual updates. The Boe-Bot®,
Toddler®, SumoBot®, HexCrawler and QuadCrawler robots are discussed here.
SX Microcontrollers and SX-Key – Discussion of programming the SX microcontroller with
Parallax assembly language SX – Key® tools and 3rd party BASIC and C compilers.
Javelin Stamp – Discussion of application and design using the Javelin Stamp, a Parallax module
that is programmed using a subset of Sun Microsystems’ Java® programming language.
ERRATA
While great effort is made to assure the accuracy of our texts, errors may still exist. If you find an error, please let us
know by sending an email to editor@parallax.com. We continually strive to improve all of our educational materials
and documentation, and frequently revise our texts. Occasionally, an errata sheet with a list of known errors and
corrections for a given text will be posted to our web site, www.parallax.com. Please check the individual product
page’s free downloads for an errata file.
ACKNOWLEGEMENTS
Many thanks to fellow Parallaxians Jen Jacobs for cover and title page art and Chris Savage for technical review of
this edition.
Table of Contents · Page i
Table of Contents
Preface......................................................................................................... iii
Author’s Note ................................................................................................................. iii
Getting the Most from StampWorks................................................................................v
Steps to Success ............................................................................................................v
Preparing the StampWorks Lab ................................................................... 1
StampWorks Kit Contents...............................................................................................1
Setting Up the Hardware and Software ..........................................................................2
Notes on Using Integrated Circuits in StampWorks Experiments...................................9
Programming Essentials............................................................................. 11
Contents of a Working Program ...................................................................................11
Branching – Redirecting Program Flow ........................................................................12
Looping – Running Code Again and Again...................................................................14
Subroutines – Reusable Code that Saves Program Space ..........................................16
The Elements of PBASIC Style.................................................................... 19
Time to Experiment .................................................................................... 25
Learn the Programming Concepts ................................................................................25
Building the Projects .....................................................................................................25
What to do Between Projects .......................................................................................25
Experiment #1: Flash an LED .......................................................................................26
Experiment #2: Flash an LED (Advanced) ...................................................................29
Experiment #3: Display a Counter with LEDs ...............................................................33
Experiment #4: Science Fiction LED Display ...............................................................36
Experiment #5: LED Graph (Dot or Bar) .......................................................................40
Experiment #6: A Simple Game ...................................................................................46
Experiment #7: A Lighting Controller ............................................................................51
Building Circuits on Your Own.................................................................... 57
Using 7-Segment LED Displays .................................................................. 59
Experiment #8: A Single-Digit Counter .........................................................................60
Experiment #9: A Digital Die .........................................................................................63
Experiment #10: A Digital Clock ...................................................................................67
Using Character LCDs ................................................................................. 73
Experiment #11: Basic LCD Demonstration .................................................................75
Experiment #12: Creating Custom LCD Characters .....................................................82
Experiment #13: Reading the LCD RAM ......................................................................88
Page ii ·StampWorks
Moving Forward ......................................................................................... 93
Experiment #14: Scanning and Debouncing Multiple Inputs ........................................94
Experiment #15: Counting Events ................................................................................98
Experiment #16: Frequency Measurement ................................................................101
Experiment #17: Advanced Frequency Measurement ...............................................106
Experiment #18: A Light Controlled Theremin............................................................109
Experiment #19: Sound Effects (SFX)........................................................................112
Experiment #20: Infrared Object Detection ................................................................119
Experiment #21: Analog Input with PULSIN ...............................................................123
Experiment #22: Analog Output with PWM ................................................................126
Experiment #23: Expanded Digital Outputs with Shift Registers ................................130
Experiment #24: Expanded Digital Inputs with Shift Registers...................................137
Experiment #25: Mixed IO with Shift Registers ..........................................................143
Experiment #26: Hobby Servo Control .......................................................................146
Experiment #27: Stepper Motor Control .....................................................................150
Experiment #28: Voltage Measurement .....................................................................156
Experiment #29: Temperature Measurement .............................................................161
Experiment #30: High Resolution Temperature Measurement ..................................168
Experiment #31: Advanced 7-Segment Multiplexing ..................................................173
Experiment #32: I2C Communications .......................................................................179
Experiment #33: Using a Real-Time Clock.................................................................188
Experiment #34: Serial Communications with a PC ...................................................197
Experiment #35: (BONUS) BS2px ADC .....................................................................206
Power PBASIC .......................................................................................... 211
Striking Out on Your Own ........................................................................ 219
Preface · Page iii
Preface
AUTHOR’S NOTE
Dear friends,
It seems like ages ago that Ken Gracey handed me a new prototyping and
development board and asked, “What do you think we could do with this?” That
board, of course, was the original NX-1000 and what we went on to create together
was the first edition of the book you’re now reading: StampWorks.
A lot of things have changed since then, and yet many things remain comfortably
constant: there are still many ways to learn microcontroller programming and one of
the best – in our opinion – is to do so using the BASIC Stamp® microcontroller. Our
philosophy has always been rooted in the belief that learning by doing provides the
fastest, deepest, most satisfying results. We teach theory by putting it into practice.
That’s what StampWorks is all about.
Most of you that find your way to StampWorks will have had some applicable
experience; perhaps you’ve worked your way through our excellent Stamps in Class
student guides and are looking to build on that experience. Perhaps you have an
electronics and/or programming background and are looking to apply those skills
with the BASIC Stamp microcontroller. Either way, this book will teach you to apply
the skills that you have and develop new ones along the way so that you can
confidently translate your ideas into working projects. Microcontrollers are a part of
our daily lives – whether we see them or not – so learning to design with and
program them is a very valuable skill.
Like earlier editions, this book assumes that you’re ready to work – ready to read
component documentation, willing to open the BASIC Stamp IDE help file for details
on a PBASIC command, that you’re unafraid to do a web search if necessary to
obtain data that will be required for a challenge; in short, whatever it takes to
succeed. We’ll push a bit harder this time, but we’ll do it together. My goal is that
even if this isn’t your first exposure to StampWorks, it will be a worthwhile and
pleasurable experience.
Page iv ·StampWorks
Among the changes that affect this edition of StampWorks is an updated PBASIC
language: PBASIC 2.5. For those that come from a PC programming background,
PBASIC 2.5 will make the transition to embedded programming a bit easier to deal
with. And what I’m especially excited about is a new development platform: the
Parallax Professional Development Board. My colleague, John Barrowman, with
feedback from customers and Parallax staff alike, put about all of the features we
would ever want into one beautiful product. For those of you have an NX-1000 (any
of the variants), don’t worry; most of the experiments will run on it without major
modification.
Finally, as far as the text goes, many of the project updates are a direct result of
those that have come before you, and you, my friend, have the opportunity to affect
future updates. Please, if you ever have a question, comment, or suggestion, feel
free to e-mail them to Editor@parallax.com.
Preface · Page v
GETTING THE MOST FROM STAMPWORKS
Before you get started, you’ll want to have a copy of the BASIC Stamp Syntax and
Reference Manual (version 2.1 or higher) handy – either printed or in PDF (available
as a free download from www.parallax.com). Through the course of this book I will
ask you to review specific sections of the BASIC Stamp Manual in preparation for an
experiment. At other times I may ask you to go to the Internet to download a
datasheet; by doing this we can focus on the details of the experiment and not have
to print a lot of redundant information.
STEPS TO SUCCESS
Read (or review if you have previous BASIC Stamp programming experience)
sections 1 – 4 of the BASIC Stamp Syntax and Reference Manual. This will introduce
you to the BASIC Stamp microcontroller, its programming IDE, and its memory
organization. And if you’ve never worked with microcontrollers or programming of
any kind, I strongly suggest that you download and work your way through our
What’s A Microcontroller? student guide. This outstanding resource is used in
schools all over the world and is considered the best introduction to microcontroller
principals and programming available anywhere.
The focus of StampWorks is on embedded programming and circuit integration.
That said, this is not a text on electronics principles. If you are new to the world of
electronics, a great beginning text is Getting Started in Electronics by renowned
electronics author, Forrest M. Mims. You can find this at your favorite bookseller.
Read “Preparing the StampWorks Lab” in the next section. This will introduce you to
the Parallax Professional Development Board (PDB) and get it ready for the
experiments that follow.
Finally, work your way through the experiments, referring to the BASIC Stamp
Syntax and Reference Manual (or online Help file) as needed. This is the fun part –
and the part that is the most work. Don’t allow yourself to be satisfied with simply
loading and running the code – dig in and work with it, modify it, make it your own.
By the time you’ve completed the experiments in this book I believe you will be
ready and will have the confidence to take on your own BASIC Stamp microcontroller
projects; from projects that may be very simple to those that are moderately
complex. The real key is to make sure you truly understand an experiment before
Page vi ·StampWorks
moving on to another. Oftentimes we will rely on what we’ve previously worked
through as support for a new experiment. Taken one at a time, the experiments are
not difficult and if you work through them methodically, you’ll find your confidence
and abilities increasing at a very rapid pace.
Preparing the StampWorks Lab · Page 1
Preparing the StampWorks Lab
STAMPWORKS KIT CONTENTS
Before getting to the experiments, let’s start by taking inventory of the kit and then
preparing the PDB for use in the experiments that follow. Once this is done, you’ll be
able to move through the experiments smoothly, and when you’ve completed
StampWorks you’ll be ready for just about any project you can imagine.
StampWorks Lab Kit Contents #27297
Stock Code #
27218
27220
23138
BS2-IC
750-00007
800-00003
805-00006
700-00050
700-00051
700-00052
200-01030
200-01040
150-02210
150-04710
150-01020
150-04720
150-01030
350-00009
350-00003
350-90000
350-90001
350-00014
603-00006
604-00009
602-00015
602-00009
602-00010
ADC0831
604-00002
603-00014
604-00020
900-00005
27964
(parts and quantities subject to change without notice)
Description
Marking
BASIC Stamp Syntax and Reference Manual
StampWorks Manual v2.1
Professional Development Board
BASIC Stamp 2 module
Power supply, 12 vdc, 1 amp
Serial cable
USB cable, Mini-A to Mini-B
22-gauge wire, solid, red
22-gauge wire, solid, white
22-gauge wire, solid, black
0.01 µF capacitor
0.1 µF capacitor
220 ohm resistor
470 ohm resistor
1 k-ohm resistor
4.7 k-ohm resistor
10 k-ohm resistor
CdS photoresistor
IR LED
LED stand-off (for IR LED)
LED shield (for IR LED)
IR receiver
Parallel LCD module
LM555 timer
LM358 dual op-amp
74HC595, serial-in-parallel-out shift register
74HC165, parallel-in-serial-out shift register
ADC0831, 8-bit A/D converter
DS1620, digital thermometer
MC14489 LED multiplexer
24LC32 EEPROM
Servo, Parallax Standard
Stepper motor, 12 vdc, unipolar
103
104
Red-Red-Brn
Yel-Vio-Brn
Brn-Blk-Red
Yel-Vio-Red
Brn-Blk-Org
Qty
1
1
1
1
1
1
1
1
1
1
2
2
3
3
3
3
3
2
1
1
1
1
1
1
1
2
2
1
1
1
1
1
1
Page 2 · StampWorks
SETTING UP THE HARDWARE AND SOFTWARE
To set up the StampWorks lab for experiments, you’ll need the following items:
•
•
•
•
•
•
Professional Development Board
BASIC Stamp 2 module
12-volt wall pack (2.1 mm, center-positive plug)
Programming cable (serial or USB)
Red and black hook-up wire (22-gauge, solid)
Wire cutters/strippers (not included in the StampWorks Kit)
Installing the BASIC Stamp Module
Start by removing the BASIC Stamp 2 module from its protective foam and carefully
inserting it into the 40-pin DIP socket on the PDB (upper-left, near the DB-9
programming connector). You’ll notice that the BASIC Stamp 2 module and the PDB
socket are marked with semi-circle alignment guides. The BASIC Stamp 2 module
should be inserted into the socket so that the alignment guides match. Ensure that
the BASIC Stamp 2 module is fully left-aligned in the socket as shown in the
illustration below.
Make the Programming Connection
Use a programming cable (either serial or USB, but not both at the same time) to
connect the PDB to your PC. It is best to select a serial (COM) port that is not already
in use. If, however, you’re forced to unplug another device, for example, a PDA or
electronic organizer from your computer, make sure that you also disable its
communication software before attempting to program your BASIC Stamp
microcontroller.
Preparing the StampWorks Lab · Page 3
Note: For USB programming, make sure that you have the latest FDTI VCP (Virtual Com
Port) driver. Step-by-step installation instructions of the VCP driver may be obtained via
the StampWorks Product Page http at www.parallax.com.
Computer System Requirements
You will need either a desktop or laptop PC to run the BASIC Stamp Editor software.
For the best experience with the StampWorks experiments, check that you computer
system meets the following requirements:
•
•
•
Microsoft Windows® 2000/XP or newer operating system
An available serial or USB port (with VCP driver installed)
World Wide Web access
Note: While third-party developers have made BASIC Stamp editors available for
operating systems other than Windows, these editors are not supported by Parallax. This
text assumes that you’re running the official Parallax BASIC Stamp Editor on a Windows
computer. If you’re using another operating system and editor, you may need to make
adjustments in editor-specific instructions.
Installing the BASIC Stamp Editor
Download the latest version of the BASIC Stamp Editor for Windows (version 2.1 or
later) from www.parallax.com. Run the program installer, following the on-screen
prompts.
Download the StampWorks Program Files
The sample programs listed in this book, with the exception of Experiment 35, were
written for the BASIC Stamp 2. These programs and some additional bonus programs
are available for free download from www.parallax.com. Many of them contain
additional code to support conditional compliation with different BASIC Stamp
models.
Page 4 · StampWorks
Preparing the Breadboard
In the center of the PDB is a solderless breadboard where we will build circuits that
are not integral to the PDB lab board itself (a variety of components are included in
the StampWorks kit). It’s important to understand how this breadboard works. With
a little bit of preparation, it will be even easier to use with the experiments that
follow.
The innermost portion of the breadboard is where we will connect the components.
This section of the breadboard consists of several columns of sockets (there are
numbers printed along the top for reference). For each column there are two sets of
rows. The rows are labeled A through E and F through J, respectively. For any
column, sockets A through E are electrically connected. The same holds true for
rows F through J.
Above and below the main section of breadboard are two horizontal rows of sockets,
each divided in the center. These horizontal rows (often called “rails” or “buses”) will
be used to carry +5 volts (Vdd) and Ground (Vss). The preparation of the
breadboard involves connecting the rails so that they run from end-to-end,
connecting the top and bottom rails together and, finally, connecting the rails to the
Vdd and Vss connections of the PDB power supply. Here’s what the breadboard
looks like on the outside:
Preparing the StampWorks Lab · Page 5
If the breadboard was X-Rayed, we would see the internal connections and the
breaks in the Vdd and Vss rails that need to be connected. Here’s a view of the
breadboard’s internal connections:
Start by setting your wire stripper for 22 gauge (0.34 mm2). Take the spool of black
wire and strip a ¼-inch (6 mm) length of insulation from the end of the wire. With
your needle-nose pliers, carefully bend the bare wire 90 degrees so that it looks like
this:
Now push the bare wire into the topmost (ground) rail, into the socket that is just
above breadboard column 29 (this socket is just left of the middle of the breadboard,
near the top). Hold the wire so that it extends to the right. Mark the insulation by
lightly pinching it with the wire cutters at the socket above column 32. Be careful
not to cut the wire.
Remove the wire from the breadboard and cut it about ¼-inch (6 mm) beyond the
mark you just made. With your wire strippers, remove the insulation at the mark.
Now bend the second bare end 90 degrees so that the wire forms a squared “U”
shape with the insulation in the middle.
Page 6 · StampWorks
If you’ve measured and cut carefully, this “U” shaped wire will plug comfortably into
the ground rail at sockets 29 and 32. This will create a single ground rail. Repeat
this process with black wire for the bottom-most rail. Then, connect the two rails
together using the same process at column 60 (right-most sockets on each rail).
With the red wire, connect the top and bottom inside rail halves together. These rails
will carry +5 volts, or Vdd. Connect the Vdd rails together at column 59.
Now take a 1½-inch (4 cm) section of black wire and a 1½-inch (4 cm) section of
red wire and strip ¼-inch (6 mm) insulation from the ends of both. Bend each wire
into a rounded “U” shape. These wires are not designed to lie flat like the other
connections, making them easy to remove from the StampWorks lab board if
necessary.
Carefully plug one end of the red wire into any of the terminal sockets of the VDD
block (near pin 1 of the BASIC Stamp socket) and the other end into the Vdd (+5)
rail at column 5. Then, plug one end of the black wire into any of the sockets of the
VSS block and other end into the ground rail at column 1. Be very careful with these
last two connections. If the Vdd and Vss rails get connected together damage may
occur when power is applied to the PDB. When completed, the PDB breadboard will
look like this:
Preparing the StampWorks Lab · Page 7
Final Checkout
With the BASIC Stamp module installed and the breadboard prepared it is time for a
final checkout before proceeding to the experiments. If you haven’t done so already,
connect a programming cable (serial or USB) between your PC and the PDB.
Connect a 12-volt DC power supply to the PDB power connector. Move the PDB
power switch to ON; a blue LED next to the power switch should illuminate. If it
doesn’t, move the power switch to OFF and recheck all connections, as well as the
power supply.
Start the BASIC Stamp Editor and enter the following short program:
' {$STAMP BS2}
Main:
DEBUG "Ready for StampWorks 2.1!"
END
Page 8 · StampWorks
Now run the program. If all went well the program will be downloaded to the BASIC
Stamp module and a Debug Terminal window will appear.
If an error occurs, check the following items:
•
•
•
•
•
•
Is the BASIC Stamp module plugged into the PDB correctly?
Is the PDB power switch set to ON? Is the blue ON LED lit?
Is the programming cable connected between the PC and the PDB?
Have you (manually) selected the wrong PC com port?
Is the PC com port being used by another program?
If using USB, have you installed the FTDI VCP driver?
When the Debug Terminal window appears and tells you that the StampWorks lab is
ready, it’s time to talk about BASIC Stamp programming.
Preparing the StampWorks Lab · Page 9
NOTES ON USING INTEGRATED CIRCUITS IN STAMPWORKS
EXPERIMENTS
There are two ways to draw integrated circuits (ICs) in a schematic: One way is
considered “chip-centric” in which I/O pins appear in the schematic according to their
physical location on the device. StampWorks uses schematics drawn for efficiency,
meaning that I/O pins are placed to make the schematic legible. I/O pins on all
chips are counted according to their indicator, starting with Pin 1 and counting in a
counter-clockwise direction as shown below:
Page 10 · StampWorks
Programming Essentials · Page 11
Programming Essentials
CONTENTS OF A WORKING PROGRAM
In Sections 1 - 4 of the BASIC Stamp Syntax and Reference Manual you were
introduced to the BASIC Stamp, its architecture, and the concepts of variables and
constants. In this section, we’ll introduce the various elements of a program: linear
code, branching, loops, and subroutines.
The examples in this discussion use pseudo-code to demonstrate and describe
program structure. Italics are used to indicate the sections of pseudo-code that
require replacement with valid programming statements in order to allow the
example to compile and run correctly. You need not enter any of the examples here
as all of these concepts will be used in the experiments that follow.
People often think of computers and microcontrollers as “smart” devices and yet,
they will do nothing without a specific set of instructions. This set of instructions is
called a program, and it is our job to write it. Programs for the BASIC Stamp are
written in a language called PBASIC, a Parallax-specific version of the BASIC
(Beginner’s All-purpose Symbolic Instruction Code) programming language. BASIC is
very popular because of its simplicity and English-like syntax. Since its creation at
Dartmouth College in the mid 1960’s it has become one of the dominant
programming languages available for platforms as small as the BASIC Stamp
microcontroller, and as large as mainframe computer systems.
A working program can be as simple as a list of statements. Like this:
statement 1
statement 2
statement 3
END
This is a very simple, yet valid program structure. What you’ll find, however, is that
most programs do not run in a straight, linear fashion like the listing above. Program
flow is often redirected with branching, looping, and subroutines, with short linear
sections in between. The requirements for program flow are determined by the goal
of the program and the conditions under which the program is running.
Page 12 · StampWorks
BRANCHING – REDIRECTING PROGRAM FLOW
A branching instruction is one that causes the flow of the program to change from its
linear path. In other words, when the program encounters a branching instruction, it
will, in almost all cases, not be running the next [linear] line of code. The program
will usually go somewhere else, often creating a program loop. There are two
categories of branching instructions: unconditional and conditional. PBASIC has two
instructions, GOTO and GOSUB that cause unconditional branching.
Here’s an example of an unconditional branch using GOTO:
Label:
statement 1
statement 2
statement 3
GOTO Label
We call this an unconditional branch because it always happens. GOTO redirects the
program to another location. The location is specified as part of the GOTO instruction
and is called an address. Remember that addresses start a line of code and are
followed by a colon (:). You’ll frequently see GOTO at the end of the main body of
code, forcing the program statements to run again.
Conditional branching will cause the program flow to change under a specific set of
circumstances. The simplest conditional branching is done with an IF-THEN
construct. PBASIC includes two distinct versions of IF-THEN; the first is used
specifically to redirect program flow to another point based on a tested condition.
Take a look at this listing:
Start:
statement 1
statement 2
statement 3
IF (condition) THEN Start
In this example, statements 1- 3 will run at least once and then continue to run as
long as the condition evaluates as True. When required, the condition can be tested
prior to the code statements:
Programming Essentials · Page 13
Start:
IF (condition) THEN
statement 1
statement 2
statement 3
ENDIF
Note that the code statements are nested in an IF-THEN-ENDIF structure which
does not require a branch label. If the condition evaluates as False, the program will
continue at the line that follows ENDIF. Another use of this conditional structure is
to add the ELSE clause:
Start:
IF (condition) THEN
statement 1
statement 2
statement 3
ELSE
statement 4
statement 5
statement 6
ENDIF
If the condition evaluates as True then statements 1 – 3 will run, otherwise
statements 4 – 6 will run.
As your requirements become more sophisticated, you’ll find that you’ll want your
program to branch to any number of locations based on the value of a control
variable. One approach is to use multiple IF-THEN constructs.
IF (index = 0) THEN Label_0
IF (index = 1) THEN Label_1
IF (index = 2) THEN Label_2
This approach is valid and does get used. Thankfully, PBASIC has a special command
called BRANCH that allows a program to jump to any number of addresses based on
the value of an index variable. BRANCH is a little more complicated in its setup, but
very powerful in that it can replace multiple IF-THEN statements. BRANCH requires
a control (index) variable and a list of addresses
The previous listing can be replaced with one line of code:
BRANCH index, [Label_0, Label_1, Label_2]
Page 14 · StampWorks
When index is zero, the program will branch to Label_0, when index is one the
program will branch to Label_1 and so on.
Related to BRANCH is ON-GOTO, in fact, it can serve as direct replacement:
ON index GOTO Label_0, Label_1, Label_2
Programmers coming from a PC background are probably more familiar with ONGOTO, hence its inclusion in PBASIC 2.5.
LOOPING – RUNNING CODE AGAIN AND AGAIN
As demonstrated in the previous section, program loops can be created with
conditional and unconditional branching instructions. Modern variants of BASIC,
including PBASIC 2.5, simplify looping with the DO-LOOP structure. With DO-LOOP
the branching label is no longer required. Here's how DO-LOOP is used to force
unconditional looping of number of code statements:
DO
statement 1
statement 2
statement 3
LOOP
As in the previous example, statements 1 - 3 will run in order, continuously.
The DO-LOOP construct can be made conditional by testing before or after the loop
statements:
DO WHILE (condition)
statement 1
statement 2
statement 3
LOOP
In this example the loop statements will only run if and while the condition evaluates
as True.
DO
statement 1
statement 2
statement 3
LOOP WHILE (condition)
Programming Essentials · Page 15
In the second example, the loop statements will run at least once, even if the
condition evaluates as False. As you can see, the strength of DO-LOOP is that it
simplifies how and where the condition testing occurs.
DO-LOOP adds another type of testing with UNTIL.
DO
statement 1
statement 2
statement 3
LOOP UNTIL (condition)
DO UNTIL (condition)
statement 1
statement 2
statement 3
LOOP
By using UNTIL, the loop statements will run while the condition evaluates as False.
And, as demonstrated earlier, placing the test at the end of the loop will cause the
loop statements to run at least one time.
Another example of looping is the programmed loop using FOR-NEXT.
FOR controlVar = startVal TO endVal STEP stepSize
statement 1
statement 2
statement 3
NEXT
The FOR-NEXT construct is used to run a section of code a specific number of times.
FOR-NEXT uses a control variable to determine the number of loop iterations. The
size of the variable will determine the upper limit of loop iterations. For example, the
upper limit when using a byte-sized control variable would be 255. In the example
below, controlVar could be defined as a Nib (4-bit) variable as the end value is
less than 16:
FOR controlVar = 1 TO 10
statement 1
statement 2
statement 3
NEXT
Page 16 · StampWorks
The STEP option of FOR-NEXT is used when the loop needs to count in increments
other than one. If, for example, the loop needed to count even numbers, the code
would look something like this:
FOR counter
statement
statement
statement
NEXT
= 2 TO 20 STEP 2
1
2
3
SUBROUTINES – REUSABLE CODE THAT SAVES PROGRAM SPACE
The final programming concept we’ll discuss is the subroutine. A subroutine is a
section of code that can be called from anywhere in the program. GOSUB is used to
redirect the program to the subroutine code. The subroutine is terminated with the
RETURN instruction. RETURN causes the program to jump back to the line of code
that follows the calling GOSUB.
Start:
DO
GOSUB My_Sub
PAUSE 1000
LOOP
My_Sub:
statement 1
statement 2
statement 3
RETURN
In this example, the code in the My_Sub subroutine is executed and then the
program jumps back to the line PAUSE 1000.
Advanced programmers will take advantage of subroutines and the ON-GOSUB
instruction. ON-GOSUB works like ON-GOTO, except that the program returns to the
line that follows ON-GOSUB. This technique is very useful for creating task manager
program structures as shown next:
Main:
DO
GOSUB Critical_Task
ON task GOSUB Task_1, Task_2, Task_3
task = task + 1 // 3
LOOP
Programming Essentials · Page 17
Critical_Task:
statement(s)
RETURN
Task_1:
statement(s)
RETURN
Task_2:
statement(s)
RETURN
Task_3:
statement(s)
RETURN
With this type of program the code section at Critical_Task is interleaved
between the other tasks. And by placing all task code into discrete subroutines, they
can be called from any point in the program. This allows one task to test for a
condition and call another subroutine if required, or to set the next task by modifying
the task pointer.
Page 18 · StampWorks
The Elements of PBASIC Style · Page 19
The Elements of PBASIC Style
Like most versions of the BASIC programming language, PBASIC is very forgiving
and the compiler enforces no particular formatting style. So long as the source code
is syntactically correct, it will compile and download to the BASIC Stamp without
trouble.
Why, then, would one suggest a specific style for PBASIC? With millions of BASIC
Stamp microcontrollers sold, and tens of thousands of active users world-wide, it is
very likely that you'll be sharing your PBASIC code with someone, if not codeveloping a BASIC Stamp-based project. Writing code in an organized, predictable
manner will save you – and your potential colleagues – time; in analysis, in
troubleshooting, and especially when you return to a project after a long break.
The style guidelines presented here are just that: guidelines. They have been
developed from style guidelines used by professional programmers using other highlevel languages such as Java®, C/C++ and Visual Basic®. Use these guidelines as is,
or modify them to suit your needs. The key is selecting a style that works well for
you or your organization and sticking to it.
1. Do It Right the First Time
Many programmers, especially new ones, fall into the "I'll knock it out now and fix it
later." trap. Invariably, the "fix it later" part never happens and sloppy code makes
its way into production projects. If you don't have time to do it right, when will you
find time to do it again?
Start clean and you'll be less likely to introduce errors in your code. And if errors do
pop up, clean and organized formatting will make them easier to find and fix.
2. Be Organized and Consistent
Using a blank program template will help you organize your programs and establish a
consistent presentation. The BASIC Stamp Editor allows you to specify a file
template for the File | New function (see Edit | Preferences | Files & Directories...).
Page 20 · StampWorks
3. Use Meaningful Names
Be verbose when naming constants, variables, and program labels. The compiler will
allow names up to 32 characters long. Using meaningful names will reduce the
number of comments and make your programs easier to read, debug and maintain.
4. Naming I/O Pins
BASIC Stamp I/O pins are a special case as various elements of the PBASIC language
require a pin to be a constant value, an input variable or an output variable. Begin
I/O pin names with an uppercase letter and use mixed case, using uppercase letters
at the beginning of new words within the name. When using the BS2, the PIN
definition is used. This will cause the compiler to use the correct variant (constant
value, input bit, or output bit) for the pin.
HeaterCtrl
PIN
15
Since connections don't change during the program run, I/O pins are named like
constants (#5) using mixed case, beginning with an uppercase letter.
5. Naming Constants
Begin constant names with an uppercase letter and use mixed case, using uppercase
letters at the beginning of new words within the name.
AlarmCode
CON
25
6. Naming Variables
Begin variable names with a lowercase letter and use mixed case, using uppercase
letters at the beginning of new words within the name. Avoid the use of internal
variable names (such as B0 or W1) in your programs. Allow the compiler to
automatically assign RAM space by declaring a variable of specific type.
waterLevel
VAR
Word
The Elements of PBASIC Style · Page 21
7. Variable Type Definitions
Conserve BASIC Stamp user RAM by declaring the variable type required to hold the
expected values of the variable.
bitValue
nibValue
byteValue
wordValue
VAR
VAR
VAR
VAR
Bit
Nib
Byte
Word
'
'
'
'
0
0
0
0
-
1
15
255
65535
8. Program Labels
Begin program labels with an uppercase letter, use mixed case, separate words
within the label with an underscore character and begin new words with a number or
uppercase letter. Labels should be preceded by at least one blank line, begin in
column 1 and must be terminated with a colon (except after GOTO and THEN where
they appear at the end of the line and without a colon).
Print_ZString:
DO
READ eeAddr, char
eeAddr = eeAddr + 1
IF (char = 0) THEN EXIT
DEBUG char
LOOP
RETURN
9. PBASIC Keywords
All PBASIC language
serial/debugging format
should be uppercase.
keywords automatically,
your personal tastes.
keywords, including SYMBOL, CON, VAR, PIN and
modifiers (DEC, HEX, BIN) and control characters (CR, LF)
The BASIC Stamp editor will correctly format PBASIC
and allow you to set color highlighting by category to suit
Main:
DEBUG "BASIC Stamp", CR
END
Page 22 · StampWorks
10. Indent Nested Code
Nesting blocks of code improves readability and helps reduce the introduction of
errors. Indenting each level with two spaces is recommended to make the code
readable without taking up too much space.
Main:
..DO
....FOR testLoop = 1 TO 10
......IF (checkLevel < threshold) THEN
........lowLevel = lowLevel + 1
........LedOkay = IsOff
......ELSE
........LedOkay = IsOn
......ENDIF
......PAUSE 100
....NEXT
..LOOP WHILE (testMode = Yes)
Note: The dots are used to illustrate the level of nesting and are not a part of the code.
11. Condition Statements
Enclose condition statements in parenthesis for clarity.
Check_Temp:
IF (indoorTemp >= setPoint) THEN
AcCtrl = IsOn
ELSE
lowLevel = lowLevel + 1
ENDIF
Fill_Water_Tank:
DO WHILE (waterLevel = IsLow)
TankFill = IsOn
PAUSE 250
LOOP
Get_Delay:
DO
DEBUG HOME, "Enter time (5 – 30)... ", CLREOL
DEBUGIN DEC2 tmDelay
LOOP UNTIL ((tmDelay >= 5) AND (tmDelay =< 30))
The Elements of PBASIC Style · Page 23
12. Be Generous With White Space
White space (spaces and blank lines) has no effect on compiler or BASIC Stamp
performance, so be generous with it to make listings easier to read. As suggested in
#8 above, allow at least one blank line before program labels (two blanks lines
before a subroutine label is recommended). Separate items in a parameter list with
a space.
Main:
DO
ON task GOSUB Update_Motors, Scan_IR, Close_Gripper
LOOP
Update_Motors:
PULSOUT leftMotor, leftSpeed
PULSOUT rightMotor, rightSpeed
PAUSE 20
task = (task + 1) // NumTasks
RETURN
An exception to this guideline is with the Bits parameter used with SHIFTIN and
SHIFTOUT, the REP modifier for DEBUG and SEROUT, and the byte count and
terminating byte value for SERIN. In these cases, format without additional white
space.
SHIFTIN A2Ddata, A2Dclock, MSBPOST, [result\9]
DEBUG REP "*"\25, CR
SERIN IRbSIO, IRbBaud, [buffer\8\255]
13. Use Conditional Compilation for Compatibility
Some commands such as SERIN and SEROUT use different parameters based on the
target BASIC Stamp. Use conditional compilation for maximum compatibility of your
programs.
#SELECT $STAMP
#CASE BS2, BS2E, BS2PE
T1200
CON
813
T2400
CON
396
T9600
CON
84
#CASE BS2SX, BS2P
T1200
CON
2063
T2400
CON
1021
Page 24 · StampWorks
T9600
#CASE BS2PX
T1200
T2400
T9600
#ENDSELECT
CON
240
CON
CON
CON
3313
1646
396
The StampWorks files (available for download from www.parallax.com) include a
blank programming template (Template.BS2) that will help you get started writing
organized code. It's up to you to follow the rest of the guidelines above – or develop
and use guidelines of your own.
Time to Experiment · Page 25
Time to Experiment
LEARN THE PROGRAMMING CONCEPTS
What follows is a series of programming experiments that you can build and run with
your StampWorks lab. The purpose of these experiments is to teach programming
concepts and the use of external components with the BASIC Stamp. The
experiments are focused and designed so that as you gain experience, you can
combine the individual concepts to produce sophisticated programs.
BUILDING THE PROJECTS
This section of the manual is simple but important because you will learn important
programming lessons and construction techniques using your StampWorks lab. As
you move through the rest of the manual, construction details will not be included
(you’ll be experienced by then and can make your own choices) and the discussion
of the program will be less verbose, focusing specifically on special techniques or
external devices connected to the BASIC Stamp.
WHAT TO DO BETWEEN PROJECTS
The circuit from one project may not be electrically compatible with another and
could, in some cases, cause damage to the BASIC Stamp if the old program is run
with the new circuit. For this reason, a blank program should be downloaded to the
BASIC Stamp before connecting the new circuit. This will protect the BASIC Stamp by
resetting the I/O lines to inputs. Here’s a simple program that will clear and reset the
BASIC Stamp.
' {$STAMP BS2}
Main:
DEBUG "BASIC Stamp clear."
END
For convenience, save this program to a file called CLEAR.BS2.
Page 26 · StampWorks
EXPERIMENT #1: FLASH AN LED
LEDs are everywhere; virtually every piece of electronic equipment that provides
some indication to a user can or does use LEDs. The purpose of this simple
experiment is to flash an LED with the BASIC Stamp, as flashing LEDs are frequently
used as alarm and status indicators.
Look It Up: PBASIC Elements to Know
•
•
•
•
•
•
•
•
$STAMP (compiler directive)
$PBASIC (compiler directive)
PIN
CON
HIGH
LOW
PAUSE
GOTO
Building the Circuit
All StampWorks experiments use a dashed line to indicate components that are
installed on the PDB. The LED is available on the “LEDS” section of the PDB, just to
the right of the BASIC Stamp socket.
The PDB has 16 discrete LEDs built in; connect just one to the BASIC Stamp module.
1. Start with a three-inch (8 cm) segment of white hook-up wire. Strip ¼-inch
(6 mm) of insulation from each end.
2. Plug one end into BASIC Stamp connection for P0.
3. Plug the other end into LED 0.
Time to Experiment · Page 27
Program: SW21-EX01-Flash_LED.BS2:
' {$STAMP BS2}
' {$PBASIC 2.5}
' -----[ Program Description ]--------------------------------------------'
' Flashes an LED connected to P0. This program will work, unmodified, on
' any BS2-family module.
' -----[ I/O Definitions ]------------------------------------------------AlarmLed
PIN
0
' LED on P0
' -----[ Constants ]------------------------------------------------------FlashTm
CON
500
' delay 500 milliseconds
' -----[ Program Code ]---------------------------------------------------Main:
HIGH AlarmLed
PAUSE FlashTm
LOW AlarmLed
PAUSE FlashTm
GOTO Main
' turn the LED on
' turn the LED off
Behind the Scenes
Each of the BASIC Stamp’s I/O pins has three bits associated with its control. A bit in
the DIRS register determines whether the pin is an input (bit = 0) or an output (bit
= 1). If the pin is configured as an output, the current state of that pin is stored in
the associated bit in the OUTS register. If the pin is configured as an input, the
current pin value is taken from the associated bit in the INS register.
HIGH and LOW actually perform two functions with one command: the selected pin
is configured as an output (1 in the DIRS register) and the state bit is modified in
the OUTS register (1 for HIGH, 0 for LOW).
Page 28 · StampWorks
For example, this:
HIGH 0
… actually performs the same function as:
DIR0 = 1
OUT0 = 1
' make P0 an output
' set P0 high
but does it with just one line of code. Conservation of program space is an
important aspect of microcontroller programming, and when we can save code space
we should – we’ll probably want or need that space later.
A very common beginner’s error is this:
OUTPUT 0
HIGH 0
There is no need to manually configure the pin as an output as this function is part of the
HIGH command. While doing this won’t harm the program, it does consume valuable code
space. There are very few occasions when INPUT and OUTPUT are required for proper
program operation, as most PBASIC commands handle setting the pin’s I/O state.
Write Code like a Pro
Note that even in this very simple program, we are following the style guidelines
detailed in “The Elements of PBASIC Style”. By using this professional style, the
program becomes somewhat self-documenting, requiring fewer comments, and it
allows the program to be modified far more easily. If, for example, we wanted to
change the LED pin assignment or the flash rate, we would only have to make small
changes to the declarations sections and not have to edit the entire listing. When
our programs grow to several hundred lines, using cleverly-named pin definitions and
constant values will save us a lot of time and frustration.
Time to Experiment · Page 29
EXPERIMENT #2: FLASH AN LED (ADVANCED)
Now that we’ve got things moving, let’s step up a bit and explore an advanced
approach to flashing an LED. The method revealed in this experiment provides the
best in program readability and ease-of-maintenance.
Look It Up: PBASIC Elements to Know
•
•
•
•
•
OUTPUT
DO-LOOP
VAR
Nib (variable type)
BIT0..BIT15 (variable modifier)
Building the Circuit
This experiment uses the same circuit as Experiment #1.
Program: SW21-EX02-Flash_LED-Adv.BS2:
' {$STAMP BS2}
' {$PBASIC 2.5}
' -----[ Program Description ]--------------------------------------------'
' Flashes an LED connected to P0. This program will work, unmodified, on
' any BS2-family module.
' -----[ I/O Definitions ]------------------------------------------------Strobe
PIN
0
' LED on P0
' -----[ Constants ]------------------------------------------------------IsOn
IsOff
CON
CON
1
0
' on for active-high LED
' off for active-high LED
FlashOn
FlashOff
CON
CON
50
950
' on for 50 ms
' off for 950 ms
' -----[ Initialization ]--------------------------------------------------
Page 30 · StampWorks
Reset:
Strobe = IsOff
OUTPUT Strobe
' enable pin to drive LED
' -----[ Program Code ]---------------------------------------------------Main:
DO
Strobe = IsOn
PAUSE FlashOn
Strobe = IsOff
PAUSE FlashOff
LOOP
Behind the Scenes
The version of the LED blinker gets to the core of the hardware and works at a lower
level – a little more setup work, yes, but the result is a program with greater
readability, as well as flexibility for modification. And there is no mistaking the
meaning of:
Strobe = IsOn
On reset, the LED control pin, called Strobe, is set to its off state by writing the
IsOff constant to it, and then the pin is made an output so that it can drive the
LED. This is one of those rare cases where the OUTPUT keyword is used; the reason
is that after this point, LED control will be by writing to a bit in the OUTS register.
This initialization section demonstrates the context-sensitivity of the PIN declaration.
In actual fact, these lines of code:
Strobe = IsOff
OUTPUT Strobe
… are translated by the compiler to:
OUT0 = 0
OUTPUT 0
Note how the compiler intelligently substitutes OUT0 in the first line of code, and the
number 0 in the second. Of course, we could have written the code as the compiler
ultimately translates it. The difference is that Strobe is more meaningful (to us
Time to Experiment · Page 31
humans) in terms of program functionality, and any design change would have been
more difficult to deal with.
The main program loop is handled with the DO-LOOP construct, and separate onand off-times are provided for flashing the LED. As with the pin configuration, we
can easily change the flash behavior by making a simple edit in the declarations
section. Since the LED has two states, having independent timing values for each
state gives us the greatest flexibility.
When does one make the choice between DO-LOOP and GOTO Label? While both styles
are functionally equivalent, DO-LOOP provides the convenience of not having to define a
program label for the GOTO. The downside of DO-LOOP is that it can be difficult to follow
when very long sections of code are embedded within it – especially when indentation
guidelines are ignored.
While there is no hard and fast rule, a reasonable guideline is that about ten lines of code
or fewer are fine for DO-LOOP; longer sections are best used with GOTO Label.
Taking it Further
Another advantage to direct use of output bits is that we can create code segments
like this:
DO
Strobe = cntr.BIT0
PAUSE 500
cntr = cntr + 1
LOOP
Can you tell what’s happening here? Since Strobe is actually a bit variable (OUT0 in
this program), we can write any bit value to it – even a bit that’s part of another
variable. In the example above, BIT0 (the LSB) of cntr will be written to the LED
control pin through each iteration of the program loop. Using our active-high
configuration, this will cause the LED to light when the value of cntr is odd because
BIT0, which has a value of one, will be on when cntr is odd.
Q: Without changing the PAUSE 500 line, how could we make the LED flash at half
the current rate?
A: Write the value of cntr.BIT1 to the LED. Do you understand why this is?
Page 32 · StampWorks
Write Code like a Pro
This version of the LED blinker is how a professional programmer would approach
the task. Why? What if you were asked to write a program that required several
LEDs and you assumed that they were active-high, yet after hours of work on the
program you were handed a schematic with LED connections that looked like this:
The LED in the schematic above is active-low; you must take the control pin low to
light the LED. Now you would be forced to change the HIGH commands that control
LEDs to LOW, and then original LOW commands to HIGH which would be a lot of work
and possibly lead to the introduction of program errors.
The professional programmer builds flexibility into the program so that an electrical
design change can be accommodated with ease. By using the strategy employed in
this experiment, we only have to change the following declarations:
IsOn
IsOff
CON
CON
0
1
' on for active-low LED
' off for active-low LED
The rest of the program remains unchanged and is ready to run.
Time to Experiment · Page 33
EXPERIMENT #3: DISPLAY A COUNTER WITH LEDS
Most applications will require more than one LED, and from a programming standpoint it is convenient to update all LEDs at the same time if possible. This
experiment demonstrates updating multiple LEDs by displaying a running 4-bit
counter.
Look It Up: PBASIC Elements to Know
•
•
•
OUTS, OUTL, OUTH, OUTA - OUTD
DIRS, DIRL, DIRH, DIRA - DIRD
FOR-NEXT
Building the Circuit
For this experiment we will add three more LEDs to the circuit used in Experiments
#1 and #2.
1. Start with four three-inch (8 cm) segments of white hook-up wire. Strip ¼inch (6 mm) of insulation from each end.
2. Plug one end of a wire into BASIC Stamp connection for P0.
3. Plug the other end into LED 0.
4. Repeat steps 2 and 3 for P1 – P3 connecting to LEDs 1 – 3, respectively.
Page 34 · StampWorks
Program: SW21-EX03-Counter_LEDs.BS2:
' {$STAMP BS2}
' {$PBASIC 2.5}
' -----[ Program Description ]--------------------------------------------'
' Displays a 4-bit binary counter on LEDs connected to P0 - P3. This
' program will work, unmodified, on any BS2-family module.
' -----[ I/O Definitions ]------------------------------------------------LEDs
LEDsDirs
VAR
VAR
OUTA
DIRA
' LEDs on P0 - P3
' DIRS control for LEDs
' -----[ Constants ]------------------------------------------------------MinCount
MaxCount
CON
CON
0
15
' counter start value
' counter end value
DelayTm
CON
100
' delay time for LEDs
' -----[ Variables ]------------------------------------------------------cntr
VAR
Nib
' 4-bit counter variable
' -----[ Initialization ]-------------------------------------------------Reset:
LEDsDirs = %1111
' make LEDs outputs
' -----[ Program Code ]---------------------------------------------------Main:
DO
FOR cntr = MinCount TO MaxCount
LEDs = cntr
PAUSE DelayTm
NEXT
LOOP
' loop through all values
' move count to LEDs
' hold a bit
' repeat forever
Time to Experiment · Page 35
Behind the Scenes
As explained in Experiment #1, the state of the BASIC Stamp output bits is stored in
a RAM register called OUTS. The variable OUTA is the lower 4-bits of OUTS,
corresponding to I/O pins P0 – P3. Since OUTA is part of the BASIC Stamp’s general
purpose (RAM) memory, values can be written to and read from it like any other
variable.
In this program we simply transfer (copy) the contents of 4-bit variable cntr to
OUTA (alias for the LEDs). Since P0 – P3 have been made outputs, this causes the
value of cntr to be displayed on the LEDs in binary format.
Challenge yourself: Modify the program to count backwards.
Q: Can we get the same results without using the cntr variable?
A: Yes – simply use LEDs as the control variable for the FOR-NEXT loop.
Write Code like a Pro
Since we’re dealing with multiple LEDs as a group and we cannot take advantage of
the PIN type declaration, we’re forced to use a standard variable (OUTA in this case)
to update the LEDs simultaneously. When possible, it’s best to group outputs to
match the natural boundaries of the BASIC Stamp I/O and memory structure. Our
programs will not always be as neat and tidy as this experiment, but when we do
indeed end up with groupings of four or eight pins, it’s best to use the BASIC
Stamp’s natural boundaries.
And note that while the LEDsDirs variable does not actually control the state of the
I/O pins, it does set pin directions and this is required for making these pins outputs
with a single line of code. For this reason, it is defined near the LEDs declaration in
the I/O definitions block. If we needed to make a design change that moved the
LEDs to OUTD, for example, the required changes would take place in the same area
of the program.
LEDs
LEDsDirs
VAR
VAR
OUTD
DIRD
' LEDs on P12 – P15
' DIRS control for LEDs
Page 36 · StampWorks
EXPERIMENT #4: SCIENCE FICTION LED DISPLAY
We’ve seen how LEDs can be used to display a binary value (Experiment #3), and
now we’ll take it just one more step and do something a bit artistic. In this
experiment we’ll “ping-pong” one lit LED across a bank of eight to create a sciencefiction (think evil robot warrior) type display. Circuits like this are frequently used in
film and television props.
Look It Up: PBASIC Elements to Know
•
•
•
•
•
WHILE (related to DO-LOOP)
UNTIL (related to DO-LOOP)
< (less than operator)
> (shift right operator)
Building the Circuit
For this experiment we will add four more LEDs to the circuit used in Experiment #3.
1. Start with eight three-inch (8 cm) segments of white hook-up wire. Strip ¼inch (6 mm) of insulation from each end.
2. Plug one end of a wire into BASIC Stamp connection for P0.
Time to Experiment · Page 37
3. Plug the other end into LED 0.
4. Repeat steps 2 and 3 for P1 – P7 connecting to LEDs 1 – 7, respectively.
Program: SW21-EX04-SciFi_LEDs.BS2:
' {$STAMP BS2}
' {$PBASIC 2.5}
' -----[ Program Description ]--------------------------------------------'
' "Ping-Pongs" a single LED back-and-forth across a bank of eight. This
' program will work, unmodified, on any BS2-family module.
' -----[ I/O Definitions ]------------------------------------------------LEDs
LEDsDirs
VAR
VAR
OUTL
DIRL
' LEDs on P0 - P7
' DIRS control for LEDs
' -----[ Constants ]------------------------------------------------------DelayTm
CON
100
' delay time for lit LED
' -----[ Initialization ]-------------------------------------------------Reset:
LEDS = %00000001
LEDsDirs = %11111111
' start with right LED on
' make LEDs outputs
' -----[ Program Code ]---------------------------------------------------Main:
DO WHILE (LEDs < %10000000)
PAUSE DelayTm
LEDs = LEDs 1)
seconds
formatted time
current display digit
' -----[ EEPROM Data ]----------------------------------------------------'
'
Digit0
Digit1
Digit2
Digit3
Digit4
Digit5
Digit6
Digit7
DATA
DATA
DATA
DATA
DATA
DATA
DATA
DATA
.GFEDCBA
-------%00111111
%00000110
%01011011
%01001111
%01100110
%01101101
%01111101
%00000111
' digit patterns
Using 7-Segment LED Displays · Page 69
Digit8
Digit9
DATA
DATA
%01111111
%01100111
DigSel
DATA
DATA
DATA
DATA
%1110
%1101
%1011
%0111
'
'
'
'
digit
digit
digit
digit
0
1
2
3
active
active
active
active
' -----[ Initialization ]-------------------------------------------------Reset:
Digs = %1111
DIRS = $0FFF
' all off
' make segs & digs outputs
' -----[ Program Code ]---------------------------------------------------Main:
DO WHILE (Tic = IsHigh)
GOSUB Show_Clock
LOOP
DO WHILE (Tic = IsLow)
GOSUB Show_Clock
LOOP
secs = secs + 1 // 3600
GOTO Main
' wait during high cycle
' wait during low cycle
' update current time
' -----[ Subroutines ]----------------------------------------------------Show_Clock:
time = (secs / 60) * 100
time = time + (secs // 60)
Segs = Blank
READ (DigSel + theDig), Digs
READ (Digit0 + (time DIG theDig)), Segs
IF (theDig = 2) THEN
Segs = Segs | DecPnt
ENDIF
theDig = theDig + 1 // 4
RETURN
'
'
'
'
'
get mins, move to 100s
add seconds in 1s/10s
clear display
select digit
move digit pattern to segs
' add decimal point
' update digit pointer
Behind the Scenes
The first two projects with 7-segment displays used only one digit. This project uses
four. A new problem arises; since the segment (anode) lines of the displays are tied
together, we can only activate one at a time. This is accomplished by putting the
Page 70 · StampWorks
segment pattern on the anodes and then enabling the desired digit (by making its
cathode low).
It would be nice, though, if we could see all four digits at the same time. Well, we
can’t, but if we switch between them fast enough we can fool our eyes into thinking
that they are.
The human eye has a property known as Persistence of Vision (POV), which causes it
to hold an image briefly. The brighter the image, the longer it holds in our eyes. POV
is what causes us to see a bright spot in our vision after a friend snaps a flash photo.
We can use POV to our advantage by rapidly cycling through each of the four digits,
displaying the proper segments for that digit for a short period. If the cycle is fast
enough, the POV of our eyes will cause the all four digits to appear to be lit at the
same time. This process is called multiplexing.
Multiplexing is the process of sharing data lines; in this case, the segment lines to
the 7-segment displays. If we didn’t multiplex, 28 output lines would be required to
control four 7-segment displays. That’s 12 more lines than are available on the
BASIC Stamp module. To be honest, multiplexing in PBASIC is not terribly practical,
but it does allow us to gain an understanding of the process so that when we turn to
multiplexers for assistance (see Experiment #31), we are able to get the results we
desire.
The main loop of the program proceeds in three stages:
•
•
•
Display the current time while the signal generator input is high
Display the current time while the signal generator input is low
Update the seconds counter
Note again how the modulus operator (//) is used to keep seconds in the range of 0
to 3599 (the number of seconds in one hour).
The real work in this experiment happens in the subroutine called Show_Clock. Its
purpose is to reformat the raw seconds into a time format (MMSS) and then update
the current digit. Since the routine can only show one digit at a time, it must be
called very frequently, otherwise display strobing will occur. As we saw earlier, the
main loop of the program does nothing but call this subroutine while waiting for the
Signal Generator input to change.
Using 7-Segment LED Displays · Page 71
The clock display is created by moving the minutes value (secs / 60) into the
thousands and hundreds columns of the variable time. The remaining seconds
(secs // 60) are added to time, placing them in the tens and ones columns.
Here’s how the conversion math works:
Example: 754 seconds
754 / 60 = 12
12 x 100 = 1200
754 // 60 = 34
1200 + 34 = 1234
(time = 1200)
(time = 1234; 12 minutes and 34 seconds)
Now that the time display value is ready, the segments are cleared for the next
update. Clearing the current segments value keeps the display sharp. If this isn’t
done, the old segments value will cause “ghosting” in the display. Once the display
is clear the current digit is selected and the segments get updated.
Pay special attention to the DIG operator; it is quite handy. DIG returns the single
digit value from the specified position of a number. For example:
725 DIG 1 = 2
Remember, the right-most digit is digit 0. By updating the variable, theDig, we use
it as a column pointer for both the cathode control as well as pulling the digit offset
from time for use in reading the segments.
The PDB display does not have the colon (:) normally found on a digital clock, so
we’ll enable the decimal point behind digit 2 (ones digit of hours). When theDig is
not pointing to this digit the decimal point illumination is skipped. The final step is to
update theDig for the next calling of the subroutine.
Take it Further
Update the program to use a 10 Hz input from the Signal Generator and blink the
decimal point on every other transition (see SW21-EX10-Clock-DP_Blink.BS2 for full
listing).
Page 72 · StampWorks
Main:
DO WHILE (Tic = IsHigh)
GOSUB Show_Clock
LOOP
DO WHILE (Tic = IsLow)
GOSUB Show_Clock
LOOP
tenths = tenths + 1 // 36000
GOTO Main
' wait during high cycle
' wait during low cycle
' update time @ 10 Hz
' -----[ Subroutines ]------------------------------------------------Show_Clock:
time = (tenths / 600) * 100
time = time + (tenths // 600 / 10)
Segs = Blank
READ (DigSel + theDig), Digs
READ (Digit0 + (time DIG theDig)), Segs
IF (theDig = 2) THEN
Segs.BIT7 = tenths.BIT0
ENDIF
theDig = theDig + 1 // 4
RETURN
'
'
'
'
'
get mins, move to 100s
add seconds in 1s/10s
clear display
select digit
move digit pattern to segs
' blink decimal point
' update digit pointer
Using Character LCDs · Page 73
Using Character LCDs
While LEDs and 7-segment displays make great output devices, there will be projects
that require providing more complex information to the user. Of course, nothing
beats the PC video display, but these are large, expensive, and almost always
impractical for microcontroller projects. Character LCD modules, on the other hand,
fit the bill well. These inexpensive modules allow both text and numeric output, use
very few I/O lines, and require little effort from the BASIC Stamp. And since the
introduction of the BS2p, character LCD instructions have become part of the PBASIC
2.0 and later 2.5 languages. That said, we can still use the stock BS2 to drive these
versatile displays and the experiments that follow will demonstrate how.
Character LCD modules are available in a wide variety of configurations: one-line,
two-line, and four-line are very common. The number of columns (characters) per
line is also variable, with 16- and 20- character displays being the most common and
popular.
The datasheet for the parallel LCD (2 lines x 16 characters) included in the
StampWorks Kit is available for download from www.parallax.com.
The LCD module connects to the PDB by a 14-pin IDC header (X1). The header is
keyed, preventing the connector from being inserted upside-down.
Initialization
The character LCD must be initialized before displaying characters on it. The projects
that follow initialize the LCD in accordance with the specification for the Hitachi
HD44780 controller. The Hitachi controller is the most popular available and many
Page 74 · StampWorks
controllers are compatible with it. When in doubt, be sure to download and examine
the driver documentation for an LCD that does not work properly with these
programs.
Modes of Operation
There are two essential modes of operation with character LCDs: writing a character
on the LCD, and sending a command to the LCD (to clear the screen, for example).
When sending a character, the RS line is high and the data sent is interpreted as a
character to be displayed at the current cursor position. The code sent is usually the
ASCII code for the character to be displayed. Several non-ASCII characters also are
available in the LCD ROM, as well as up to eight user-programmable custom
characters (stored in an area called CGRAM).
Commands are sent to the LCD by taking the RS line low before sending the data.
Several standard commands are available to manage and manipulate the LCD
display.
Clear
Home
Cursor Left
Cursor Right
Display Left
Display Right
$01
$02
$10
$14
$18
$1C
Clears the LCD and moves cursor to first position of first line
Moves cursor to first position of first line
Moves cursor to the left
Moves cursor to the right
Shifts entire display to the left
Shifts entire display to the right
Connecting the LCD
The standard parallel LCD has a 14-pin IDC connector at the end of its cable. The
connector is “keyed” so that it is always inserted correctly into the PDB. Simply align
the connector key (small bump) with the slot in X1 and press the connector into the
socket until it is firmly seated.
Using Character LCDs · Page 75
EXPERIMENT #11: BASIC LCD DEMONSTRATION
This experiment demonstrates character LCD interfacing and control fundamentals by
putting the LCD module through its paces.
Look It Up: PBASIC Elements to Know
•
•
•
•
PULSOUT
HIGHNIB, LOWNIB
^ (Exclusive Or operator)
#ERROR
Building the Circuit
Note on connections: On the PDB, X2 splits the LCD data buss between the left and right sides of the
lower portion of the connector.
Page 76 · StampWorks
Be sure to insert the wires for DB4-DB7 into the right side of the connector as shown
below:
Program: SW21-EX11-LCD_Demo.BS2
' {$STAMP BS2}
' {$PBASIC 2.5}
'
'
'
'
'
'
'
-----[ Program Description ]--------------------------------------------This program demonstrates essential character LCD control.
The connections for this program conform to the BS2p-family LCDCMD,
LCDIN, and LCDOUT instructions. Use this program for the BS2, BS2e,
or BS2sx. There is a separate program for the BS2p, BS2pe, and BS2px.
' -----[ I/O Definitions ]------------------------------------------------E
RW
RS
LcdBus
PIN
PIN
CON
VAR
1
2
3
OUTB
'
'
'
'
Enable pin
Read/Write
Register Select
4-bit LCD data bus
' -----[ Constants ]------------------------------------------------------LcdCls
LcdHome
LcdCrsrL
LcdCrsrR
LcdDispL
LcdDispR
CON
CON
CON
CON
CON
CON
$01
$02
$10
$14
$18
$1C
'
'
'
'
'
'
clear the LCD
move cursor home
move cursor left
move cursor right
shift chars left
shift chars right
LcdDDRam
CON
$80
' Display Data RAM control
Using Character LCDs · Page 77
LcdCGRam
LcdLine1
LcdLine2
CON
CON
CON
$40
$80
$C0
' Character Generator RAM
' DDRAM address of line 1
' DDRAM address of line 2
#DEFINE LcdReady = ($STAMP >= BS2P)
' -----[ Variables ]------------------------------------------------------char
idx
VAR
VAR
Byte
Byte
' character sent to LCD
' loop counter
' -----[ EEPROM Data ]----------------------------------------------------Msg
DATA
"The BASIC STAMP!", 0
' store message
' -----[ Initialization ]-------------------------------------------------Reset:
#IF ($STAMP >= BS2P) #THEN
#ERROR "Please use BS2p version: SW21-EX11-LCD_Demo.BSP"
#ENDIF
DIRL = %11111110
PAUSE 100
Lcd_Setup:
LcdBus = %0011
PULSOUT E, 3
PAUSE 5
PULSOUT E, 3
PULSOUT E, 3
LcdBus = %0010
PULSOUT E, 1
char = %00001100
GOSUB LCD_Cmd
char = %00000110
GOSUB LCD_Cmd
' setup pins for LCD
' let the LCD settle
' 8-bit mode
' 4-bit mode
' disp on, no crsr or blink
' inc crsr, no disp shift
' -----[ Program Code ]---------------------------------------------------Main:
char = LcdCls
GOSUB LCD_Cmd
PAUSE 500
idx = Msg
' clear the LCD
' get EE address of message
Page 78 · StampWorks
Write_Message:
DO
READ idx, char
IF (char = 0) THEN EXIT
GOSUB LCD_Out
idx = idx + 1
LOOP
PAUSE 2000
Cursor_Demo:
char = LcdHome
GOSUB LCD_Cmd
char = %00001110
GOSUB LCD_Cmd
PAUSE 500
char = LcdCrsrR
FOR idx = 1 TO 15
GOSUB LCD_Cmd
PAUSE 150
NEXT
'
'
'
'
get character from EE
if 0, message is complete
write the character
point to next character
' wait 2 seconds
' move the cursor home
' turn the cursor on
' move cursor l-to-r
FOR idx = 14 TO 0
char = LcdDDRam + idx
GOSUB LCD_Cmd
PAUSE 150
NEXT
' move cursor r-to-l by
' moving to a specific
'
column
char = %00001101
GOSUB LCD_Cmd
PAUSE 2000
' cursor off, blink on
char = %00001100
GOSUB LCD_Cmd
' blink off
Flash_Demo:
FOR idx = 1 TO 10
char = char ^ %00000100
GOSUB LCD_Cmd
PAUSE 250
NEXT
PAUSE 1000
Shift_Demo:
FOR idx = 1 TO 16
char = LcdDispR
GOSUB LCD_Cmd
PAUSE 100
NEXT
PAUSE 1000
' flash display
' toggle display bit
' shift display
Using Character LCDs · Page 79
FOR idx = 1 TO 16
char = LcdDispL
GOSUB LCD_Cmd
PAUSE 100
NEXT
PAUSE 1000
' shift display back
GOTO Main
' do it all over
' -----[ Subroutines ]-----------------------------------------------------
LCD_Cmd:
LOW RS
' enter command mode
LCD_Out:
LcdBus = char.HIGHNIB
PULSOUT E, 3
LcdBus = char.LOWNIB
PULSOUT E, 3
HIGH RS
RETURN
' output high nibble
' strobe the Enable line
' output low nibble
' return to character mode
Behind the Scenes
This is a very simple program which demonstrates the essential functions of a
character LCD. The LCD is initialized using four-bit mode in accordance with the
Hitachi HD44780 controller specifications. This mode is used to minimize the number
of BASIC Stamp I/O lines needed to control the LCD. While it is possible to connect
to and control the LCD with eight data lines, this will not cause an appreciable
improvement in program performance and will use four more I/O lines; for most
projects it is better to conserve I/O.
The basics of the initialization are appropriate for most applications:
•
•
•
•
•
The
The
The
The
The
display is on
underline cursor is off
blinking cursor is off
cursor is automatically incremented after each write
display does not shift
Page 80 · StampWorks
Note that this program initializes the LCD for just one line, even though two lines are
physically available on the LCD. See the following experiment for initializing the LCD
for multi-line mode.
With the use of four data bits on the LCD bus, two write cycles are necessary to send
a byte to the LCD. The BASIC Stamp’s HIGHNIB and LOWNIB variable modifiers
make this process exceedingly easy. Each nibble is latched into the LCD by pulsing
the E (enable) line high with PULSOUT.
The main portion of the program starts by clearing the LCD and displaying a
message that has been stored in a DATA statement. This technique of storing
messages in EEPROM is very useful and makes programs easier to update. In this
program, characters are written until a zero is encountered. This method lets us
change the length of the string without worrying about loop control settings. With
the message displayed, the cursor position is returned home (first position of first
line) and turned on (an underline cursor appears).
The cursor is sent back and forth across the LCD using two distinct techniques. The
first uses the cursor-right command. Moving the cursor left is accomplished by
manually positioning the cursor to a specific column position. Manual cursor
positioning is required by many LCD programs for tidy formatting of the information
in the display.
With the cursor back home, it is turned off and the blink attribute is enabled. Blink
causes the current cursor position to alternate between the character and a solid
black box. This can be useful as an attention getter. Another attention-getting
technique is to flash the entire display. This is accomplished by toggling the display
enable bit. The Exclusive OR operator (^) simplifies bit toggling, as any bit XORed
with a 1 will invert:
1 ^ 1 = 0
0 ^ 1 = 1
Using the display shift commands, the entire display is shifted off-screen to the right,
then back. What this demonstrates is that the visible display is actually a window
into the LCD’s display memory (called the DDRAM). One method of using the
additional memory is to write messages off-screen and shift the visible display to
them.
Using Character LCDs · Page 81
Write Code like a Pro
Where possible, take advantage of built-in PBASIC instructions instead of manually
coding them. The BS2p-family, for example, has instructions for handling parallel
LCD modules so the code presented in the standard BS2-version of this project would
use program space unnecessarily.
By using conditional compilation we are
frequently able to write a program that will run identically on any BS2-type
microcontroller.
Using the following definition from the LCD program:
#DEFINE _LcdReady = ($STAMP >= BS2P)
… we are able to write code that uses the LCD instructions available in the BS2pfamily. Here’s how the LCD_Cmd and LCD_Out subroutines could be updated to
reduce program memory requirements when a BS2p-family module is installed:
LCD_Cmd:
#IF _LcdReady #THEN
LCDCMD E, char
RETURN
#ELSE
LOW RS
#ENDIF
LCD_Out:
#IF _LcdReady #THEN
LCDOUT E, 0, [char]
#ELSE
LcdBus = char.HIGHNIB
PULSOUT E, 3
LcdBus = char.LOWNIB
PULSOUT E, 3
HIGH RS
#ENDIF
RETURN
' send command to LCD
' return to program
' enter command mode
' output high nibble
' strobe the Enable line
' output low nibble
' return to character mode
Note the use of the underscore in the labels LCD_Cmd and LCD_Out – this prevents
conflict with internal reserved words LCDCMD and LCDOUT while making very clear
the intent of the subroutine.
See SW21-EX11-LCD_Demo-All.BS2 for the complete listing.
Page 82 · StampWorks
EXPERIMENT #12: CREATING CUSTOM LCD CHARACTERS
This program demonstrates the creation of custom LCD characters, animation with
the custom characters, and initializing the LCD for multi-line mode.
Building the Circuit
Use the same circuit as in Experiment #11.
Program: SW21-EX11-LCD_Demo.BS2
' {$STAMP BS2}
' {$PBASIC 2.5}
'
'
'
'
'
'
'
'
-----[ Program Description ]--------------------------------------------This program demonstrates custom character creation and animation on a
character LCD.
The connections for this program conform to the BS2p-family LCDCMD,
LCDIN, and LCDOUT instructions. Use this program for the BS2, BS2e,
or BS2sx. There is a separate program for the BS2p, BS2pe, and BS2px.
' -----[ I/O Definitions ]------------------------------------------------E
RW
RS
LcdBus
PIN
PIN
CON
VAR
1
2
3
OUTB
'
'
'
'
Enable pin
Read/Write
Register Select
4-bit LCD data bus
' -----[ Constants ]------------------------------------------------------LcdCls
LcdHome
LcdCrsrL
LcdCrsrR
LcdDispL
LcdDispR
LcdDDRam
LcdCGRam
LcdLine1
LcdLine2
CON
CON
CON
CON
CON
CON
CON
CON
CON
CON
$01
$02
$10
$14
$18
$1C
$80
$40
$80
$C0
#DEFINE _LcdReady = ($STAMP >= BS2P)
'
'
'
'
'
'
'
'
'
'
clear the LCD
move cursor home
move cursor left
move cursor right
shift chars left
shift chars right
Display Data RAM control
Character Generator RAM
DDRAM address of line 1
DDRAM address of line 2
Using Character LCDs · Page 83
' -----[ Variables ]------------------------------------------------------char
newChar
idx1
idx2
VAR
VAR
VAR
VAR
Byte
Byte
Byte
Nib
' character sent to LCD
' loop counters
' -----[ EEPROM Data ]----------------------------------------------------Msg1
Msg2
DATA
DATA
"THE BASIC STAMP "
" IS VERY COOL! ", 3
' preload EE with messages
CC0
DATA
DATA
DATA
DATA
DATA
DATA
DATA
DATA
%01110
%11111
%11100
%11000
%11100
%11111
%01110
%00000
' mouth 0
CC1
DATA
DATA
DATA
DATA
DATA
DATA
DATA
DATA
%01110
%11111
%11111
%11000
%11111
%11111
%01110
%00000
' mouth 1
CC2
DATA
DATA
DATA
DATA
DATA
DATA
DATA
DATA
%01110
%11111
%11111
%11111
%11111
%11111
%01110
%00000
' mouth 2
Smiley
DATA
DATA
DATA
DATA
DATA
DATA
DATA
DATA
%00000
%01010
%01010
%00000
%10001
%01110
%00110
%00000
' smiley face
Page 84 · StampWorks
' -----[ Initialization ]-------------------------------------------------Reset:
#IF _LcdReady #THEN
#ERROR "Please use BS2p version: SW21-EX12-LCD_Chars.BSP"
#ENDIF
DIRL = %11111110
PAUSE 100
Lcd_Setup:
LcdBus = %0011
PULSOUT E, 3
PAUSE 5
PULSOUT E, 3
PULSOUT E, 3
LcdBus = %0010
PULSOUT E, 1
char = %00101000
GOSUB LCD_Cmd
char = %00001100
GOSUB LCD_Cmd
char = %00000110
GOSUB LCD_Cmd
Download_Chars:
char = LcdCGRam
GOSUB LCD_Cmd
FOR idx1 = CC0 TO (Smiley + 7)
READ idx1, char
GOSUB LCD_Out
NEXT
' setup pins for LCD
' let the LCD settle
' 8-bit mode
' 4-bit mode
' multi-line mode
' disp on, no crsr or blink
' inc crsr, no disp shift
'
'
'
'
'
'
download custom chars
point to CG RAM
prepare to write CG data
build 4 custom chars
get byte from EEPROM
put into LCD CG RAM
' -----[ Program Code ]---------------------------------------------------Main:
char = LcdCls
GOSUB LCD_Cmd
PAUSE 250
FOR idx1 = 0 TO 15
READ (Msg1 + idx1), char
GOSUB LCD_Out
NEXT
PAUSE 1000
Animation:
FOR idx1 = 0 TO 15
READ (Msg2 + idx1), newChar
' clear the LCD
' get message from EEPROM
' read a character
' write it
' wait 2 seconds
' cover 16 characters
' get new char from Msg2
Using Character LCDs · Page 85
FOR idx2 = 0 TO 4
char = LcdLine2 + idx1
GOSUB LCD_Cmd
LOOKUP idx2, [0, 1, 2, 1, newChar], char
GOSUB LCD_Out
PAUSE 100
NEXT
NEXT
PAUSE 2000
'
'
'
'
'
'
5 characters in cycle
set new DDRAM address
move cursor position
get animation "frame"
write "frame"
animation delay
GOTO Main
' do it all over
' -----[ Subroutines ]-----------------------------------------------------
LCD_Cmd:
LOW RS
LCD_Out:
LcdBus = char.HIGHNIB
PULSOUT E, 3
LcdBus = char.LOWNIB
PULSOUT E, 3
HIGH RS
RETURN
' enter command mode
' output high nibble
' strobe the Enable line
' output low nibble
' return to character mode
Behind the Scenes
In this program, the LCD is initialized for multi-line mode (note the additional lines
after entering 4-bit mode). This will allow both lines of the LCD module to display
information.
With the display initialized, custom character definitions are
downloaded to the LCD.
The LCD has room for eight, user-definable customer characters. The data is stored
for these characters in an area called CGRAM and must be downloaded to the LCD
after power-up and initialization (CGRAM is volatile, so custom character definitions
are lost when power is removed from the LCD). Each custom character requires eight
bytes, the first byte being the top line of the character, the last byte being the
bottom line of the character. The eighth byte is usually $00 as this is where the
cursor is positioned when under the character.
Page 86 · StampWorks
The standard LCD font is five bits wide by seven bits tall. You can create custom
characters that are eight bits tall, but as explained before the eighth line is generally
reserved for the underline cursor. Here’s an example of a custom character
definition:
The shape of the character is determined by the ones and zeros in the data bytes. A
1 in a given bit position will light a pixel; zero will extinguish it.
The bit patterns for custom characters are stored in the BASIC Stamp’s EEPROM with
DATA statements. To move the patterns into the LCD the cursor is moved to the
CGRAM then each data byte is written. Since the LCD has been initialized for autoincrementing, there is no need to address each data byte individually. Before the
characters can be used, the display must be returned to “normal” mode by moving
the cursor back to the DDRAM area. The usual method is to clear the display or
home the cursor.
Interestingly, the LCD retrieves the bit patterns from memory while refreshing the
display. In advanced applications, the CGRAM memory can be updated while the
program is running to create unusual display effects.
The heart of this program is the animation loop. This code grabs a character from
the second message, then, for each character in that message, displays the
animation sequence at the desired character location on the second line of the LCD.
A LOOKUP table is used to cycle the custom characters for the animation sequence.
At the end of the sequence, the new character is revealed.
Using Character LCDs · Page 87
Write Code like a Pro
Note the use of binary formatted numbers in the DATA statements for this program.
While the beginning programmer may consider this technique overly verbose, the
professional knows that the small amount of up-front work to use this format saves a
lot of time later when editing or redefining characters. The purpose of the various
numeric formats supported by the BASIC Stamp IDE is to assist the programmer –
once downloaded to the BASIC Stamp the values are all stored in a binary format.
Take it Further
Create your own custom character sequence. Update the initialization and animation
code to accommodate your custom character set.
Page 88 · StampWorks
EXPERIMENT #13: READING THE LCD RAM
This program demonstrates the use of the LCD’s CGRAM space as external memory.
Look It Up: PBASIC Elements to Know
•
INS, INL, INH, INA - IND
Building the Circuit
Use the same circuit as in Experiment #11.
Program: SW21-EX13-LCD_Read.BSP
' {$STAMP BS2}
' {$PBASIC 2.5}
'
'
'
'
'
'
'
'
-----[ Program Description ]--------------------------------------------This program demonstrates how to read data from the LCD's display RAM
(DDRAM) or character RAM (CGRAM).
The connections for this program conform to the BS2p-family LCDCMD,
LCDIN, and LCDOUT instructions. Use this program for the BS2, BS2e,
or BS2sx. There is a separate program for the BS2p, BS2pe, and BS2px.
' -----[ I/O Definitions ]------------------------------------------------E
RW
RS
LcdDirs
LcdBusOut
LcdBusIn
PIN
PIN
CON
VAR
VAR
VAR
1
2
3
DIRB
OUTB
INB
'
'
'
'
'
Enable pin
Read/Write
Register Select
bus DDR
4-bit LCD data bus
' -----[ Constants ]------------------------------------------------------LcdCls
LcdHome
LcdCrsrL
LcdCrsrR
LcdDispL
LcdDispR
CON
CON
CON
CON
CON
CON
$01
$02
$10
$14
$18
$1C
'
'
'
'
'
'
clear the LCD
move cursor home
move cursor left
move cursor right
shift chars left
shift chars right
Using Character LCDs · Page 89
LcdDDRam
LcdCGRam
LcdLine1
LcdLine2
CON
CON
CON
CON
$80
$40
$80
$C0
'
'
'
'
Display Data RAM control
Character Generator RAM
DDRAM address of line 1
DDRAM address of line 2
#DEFINE _LcdReady = ($STAMP >= BS2P)
' -----[ Variables ]------------------------------------------------------char
idx
rndVal
addr
tOut
tIn
temp
width
VAR
VAR
VAR
VAR
VAR
VAR
VAR
VAR
Byte
Byte
Word
Byte
Byte
Byte
Word
Nib
'
'
'
'
'
'
'
'
character sent to LCD
loop counter
random value
address to write/read
test value - out to LCD
test value - in from LCD
use for formatting
width of value to display
' -----[ Initialization ]-------------------------------------------------Reset:
#IF _LcdReady #THEN
#ERROR "Please use BS2p version: SW21-EX13-LCD_Read.BSP"
#ENDIF
DIRL = %11111110
PAUSE 100
Lcd_Setup:
LcdBusOut = %0011
PULSOUT E, 3
PAUSE 5
PULSOUT E, 3
PULSOUT E, 3
LcdBusOut = %0010
PULSOUT E, 1
char = %00101000
GOSUB LCD_Cmd
char = %00001100
GOSUB LCD_Cmd
char = %00000110
GOSUB LCD_Cmd
Display:
char = LcdHome
GOSUB LCD_Cmd
PAUSE 2
FOR idx = 0 TO 15
' setup pins for LCD
' let the LCD settle
' 8-bit mode
' 4-bit mode
' multi-line mode
' disp on, no crsr or blink
' inc crsr, no disp shift
Page 90 · StampWorks
LOOKUP idx, ["ADDR=??
GOSUB LCD_Out
NEXT
char = LcdLine2
GOSUB LCD_Cmd
PAUSE 2
FOR idx = 0 TO 15
LOOKUP idx, ["
GOSUB LCD_Out
NEXT
OUT:???"], char
IN:???"], char
' -----[ Program Code ]---------------------------------------------------Main:
RANDOM rndVal
addr = rndVal.LOWBYTE & $3F
tOut = rndVal.HIGHBYTE
' generate random number
' create address (0 to 63)
' create test value
char = LcdCGRam + addr
GOSUB LCD_Cmd
char = tOut
GOSUB LCD_Out
PAUSE 100
' set CGRAM pointer
char = LcdCGRam + addr
GOSUB LCD_Cmd
GOSUB LCD_In
tIn = char
' reset CGRAM pointer
' move the value to CGRAM
' read value from LCD
' display results
char = LcdLine1 + 5
GOSUB LCD_Cmd
temp = addr
width = 2
GOSUB Put_Val
' show address @ L1/C5
char = LcdLine1 + 13
GOSUB LCD_cmd
temp = tOut
width = 3
GOSUB Put_Val
' show output @ L1/C13
char = LcdLine2 + 13
GOSUB LCD_Cmd
temp = tIn
width = 3
GOSUB Put_Val
PAUSE 1000
' show output @ L2/C13
Using Character LCDs · Page 91
GOTO Main
' do it again
' -----[ Subroutines ]-----------------------------------------------------
LCD_Cmd:
LOW RS
LCD_Out:
LcdBusOut = char.HIGHNIB
PULSOUT E, 3
LcdBusOut = char.LOWNIB
PULSOUT E, 3
HIGH RS
RETURN
LCD_In:
HIGH RS
HIGH RW
LcdDirs = %0000
HIGH E
char.HIGHNIB = LcdBusIn
LOW E
HIGH E
char.LOWNIB = LcdBusIn
LOW E
LcdDirs = %1111
LOW RW
RETURN
Put_Val:
FOR idx = (width - 1) TO 0
char = (temp DIG idx) + "0"
GOSUB LCD_Out
NEXT
RETURN
' enter command mode
' output high nibble
' strobe the Enable line
' output low nibble
' return to character mode
' data command
' read
' make data lines inputs
' get high nibble
' get low nibble
' make buss lines outputs
' return to write mode
' display digits l-to-r
' convert digit to ASCII
' write to LCD
Behind the Scenes
This program demonstrates the versatility of the BASIC Stamp’s I/O lines and their
ability to be reconfigured mid-program. Writing to the LCD was covered in the last
two experiments. To read data back, the BASIC Stamp’s I/O lines that serve as the
LCD bus must be reconfigured as inputs. This is no problem for the BASIC Stamp.
Page 92 · StampWorks
Aside from the I/O reconfiguration, reading from the LCD requires the use of an
additional control line: RW. In most programs this line can be held low to allow
writing to the LCD. For reading from the LCD RAM the RW line is made high.
Using the RANDOM function this program generates an address that fits within the
CGRAM, as well a data byte to write to the LCD. The address is kept in the range of
0 to 63 by masking out the highest bits of the LOWBYTE; the HIGHBYTE is used as
the data to be written to the LCD.
The LCD’s CGRAM is normally used for custom character maps. For programs that
do not require custom characters, this area (up to 64 bytes) can be used as a
storage space by the BASIC Stamp. In programs that require fewer than eight
custom characters the remaining bytes of CGRAM can be used as off-board memory
(subtract eight bytes from the CGRAM for each custom character definition).
Reading data from the LCD is identical to writing: the address is set and the data is
retrieved. For this to take place, the LCD data lines must be reconfigured as inputs.
Pulsing the E (enable) line makes the data (one nibble at a time) available for the
BASIC Stamp. Once again, HIGHNIB and LOWNIB are used, this time to build a
single byte from the two nibbles returned during the read operation.
When the retrieved data is ready, the address, output data and input data are
written to the LCD for examination. A short subroutine, Put_Val, handles writing
numerical values to the LCD. To use this routine, move the cursor to the desired
location, put the value to be displayed in temp, the number of characters to display
in width, and then call Put_Val. The subroutine uses the DIG operator to extract a
digit from temp and adds 48 (the ASCII code for “0”) to convert the digit value to a
character so that it can be displayed on the LCD.
Moving Forward · Page 93
Moving Forward
The first sections of this book dealt specifically with output devices, because the
choice of output is often critical to the success of a project. By now, you should be
very comfortable with LEDs, 7-Segment displays, and even character LCD modules.
From this point forward we will work through a variety of experiments; some are
simple, others are somewhat complex, all of them will round your education as a
BASIC Stamp programmer and help build the confidence you need to develop your
own BASIC Stamp-controlled applications.
Remember, the key to success here is to complete each experiment and to take on
any challenge that is presented. Then, go further by challenging yourself. Each time
you modify a program you will learn something. It’s okay if your experiments don’t
work as expected the first time you run them, because you will still be learning. Be
patient and push yourself to learn a little more each day. Very soon you will find
yourself being considered an expert BASIC Stamp programmer.
Page 94 · StampWorks
EXPERIMENT #14: SCANNING AND DEBOUNCING MULTIPLE
INPUTS
This experiment will teach you how to debounce multiple BASIC Stamp inputs. With
modification, any number of inputs, from two to 16, can be debounced using this
method.
Look It Up: PBASIC Elements to Know
•
•
•
•
•
~ (Invert operator)
DEBUG
HOME (used with DEBUG)
IBIN (used with DEBUG)
LOWBIT() (variable modifier)
Building the Circuit
Moving Forward · Page 95
Program: SW21-EX14-Debounce.BS2
' {$STAMP BS2}
' {$PBASIC 2.5}
' -----[ Program Description ]--------------------------------------------'
' This program demonstrates the simultaneous debouncing of multiple inputs.
' The input subroutine is easily adjusted to handle any number of inputs.
' -----[ I/O Definitions ]------------------------------------------------BtnBus
VAR
INA
' four inputs, pins 0 - 3
' -----[ Variables ]------------------------------------------------------btns
idx
VAR
VAR
Nib
Nib
' debounced inputs
' loop counter
' -----[ Program Code ]---------------------------------------------------Main:
DO
GOSUB Get_Buttons
DEBUG HOME,
"Inputs = ", IBIN4 btns
PAUSE 50
LOOP
' get debounced inputs
' display in binary mode
' -----[ Subroutines ]----------------------------------------------------Get_Buttons:
btns = %1111
FOR idx = 1 TO 5
btns = btns & ~BtnBus
PAUSE 5
NEXT
RETURN
' enable all four inputs
' test inputs
' delay between tests
Behind the Scenes
When debouncing only one input, the BASIC Stamp’s BUTTON instruction works
perfectly well and even adds a couple of useful features (like auto-repeat). To
debounce two or more inputs, however, we need to create a bit of code. The
Page 96 · StampWorks
workhorse of this experiment is the subroutine Get_Buttons. As presented, it will
accommodate four normally-open, active-low inputs but it can easily be modified for
any number of inputs from two to 16.
The purpose of Get_Buttons is to ensure that the inputs stay pressed for at least
25 milliseconds with no contact “bouncing.” Debounced inputs will be returned in the
variable, btns, with a valid input represented by a “1” in the respective button
position.
The Get_Buttons routine starts by assuming that all button inputs will be valid, so
all the bits of btns variable are set to one. Then, using a FOR-NEXT loop, the inputs
are scanned and compared to the previous state. Since the inputs are active-low
(zero when pressed), the Invert operator (~) flips them. The And operator (&) is
used to update the current state. For a button to be valid, it must remain pressed
through the entire FOR-NEXT loop.
Here’s how the debouncing technique works: When a button is pressed, the input to
the BASIC Stamp will be zero. The Invert operator will flip zero to one. One “Anded”
with one is still one, so that button remains valid. If the button is not pressed, the
raw input to the BASIC Stamp will be one (because of the 10K pull-up to Vdd). One
is inverted to zero. Zero “Anded” with any number is zero and will cause the button
to remain invalid through the entire debounce loop.
The debounced button inputs are displayed in a DEBUG window with the IBIN4
modifier so that the value (state, pressed = “1”) of each button is clearly displayed.
Write Code like a Pro
Many programs will require the ability to “single shot” a button input, that is, to
activate some event or process only after the change-of-state of a button. By
keeping track of the last scan value we can report to the program which buttons
changed between the current scan and the last.
Moving Forward · Page 97
Here’s the modified subroutine:
Get_Buttons:
nBtns = %1111
FOR idx = 1 TO 5
nBtns = nBtns & ~BtnBus
PAUSE 5
NEXT
xBtns = nBtns ^ oBtns & nBtns
oBtns = nBtns
RETURN
' enable all four inputs
' test new inputs
' delay between tests
' look for 0 -> 1 changes
' save this scan
The real work is done by this line of code:
xBtns = nBtns ^ oBtns & nBtns
' look for 0 -> 1 changes
The current button state (nBtns) is compared with the previous scan value (oBtns)
using the Exclusive OR (^) operator. This will cause a bit to be ‘1’ when there is a
difference between the previous scan and the current. This [comparison] value is
then ANDed with nBtns which holds ‘1’ for each pressed button. The result is that
xBtns will have a ‘1’ for each button that was ‘0’ on the last scan and is ‘1’ on this
scan.
Note that if the button remains pressed and Get_Buttons is called again, the
respective bit of xBtns will change from ‘1’ to ‘0’ since there was no change of
button state.
See listing SW21-EX14-Debounce-Adv.BS2 for a full demonstration.
Take it Further
Modify the program to scan, debounce, and display eight buttons (Hint: Use INL or
INH).
Page 98 · StampWorks
EXPERIMENT #15: COUNTING EVENTS
This experiment demonstrates an events-based program delay.
Look It Up: PBASIC Elements to Know
•
CLS, CR, CRSRXY (used with DEBUG)
Building the Circuit
Program: SW21-EX15-Event_Count.BS2
' {$STAMP BS2}
' {$PBASIC 2.5}
' -----[ Program Description ]--------------------------------------------'
' Counts extenal events by wait for a low-to-high transition on the event
' input pin.
' -----[ I/O Definitions ]------------------------------------------------EventIn
PIN
15
' event input pin
' -----[ Variables ]------------------------------------------------------nScan
oScan
xScan
VAR
VAR
VAR
Bit
Bit
Bit
' new scan (changed)
' old scan of input
' scan change
eCount
target
VAR
VAR
Word
Word
' event count
' target count value
' -----[ Initialization ]-------------------------------------------------Reset:
Moving Forward · Page 99
DEBUG CLS,
"Started...", CR
' -----[ Program Code ]---------------------------------------------------Main:
target = 25
GOSUB Wait_For_Count
DEBUG "Count complete."
' set target value
' wait for 25 pulses
END
' -----[ Subroutines ]----------------------------------------------------Wait_For_Count:
DO
nScan = EventIn
xScan = nScan ^ oScan & nScan
oScan = nScan
IF (xScan = 1) THEN
eCount = eCount + 1
DEBUG CRSRXY, 0, 1,
"Count = ", DEC eCount, CR
ENDIF
LOOP UNTIL (eCount = target)
RETURN
' capture input
' look for 0 -> 1 change
' save this scan
' add new event
Behind the Scenes
The purpose of the Wait_For_Count subroutine is to cause the program to wait
for a specified number of events. In an industrial setting, for example a packaging
system, we might need to run a conveyor belt until 100 boxes pass a sensor.
As you can see we’ve built upon the “pro” technique explored in the previous
chapter. At the top of the loop the input state is captured in nScan, and then
compared to the previous state (oScan) to detect a change (saved in xScan).
When the input has changed from ‘0’ to ‘1’ between scans the event count is
updated and displayed. The reason for capturing the input before the comparison is
to prevent the possibility of being affected by an input state change while processing
the comparison line.
Note that displaying the current event count in the middle of the Wait_For_Count
subroutine does put a limit on the rate of change the subroutine can accommodate.
Page 100 · StampWorks
This is due to DEBUG requiring several milliseconds to send its output to the Debug
Terminal window.
Removing the DEBUG output (simple using conditional
compilation) will increase the events input rate that can be detected.
Note, too, that the subroutine expects a clean input. A noisy input could cause
spurious counts, leading to early termination of the subroutine. This is easily fixed by
adapting the Get_Buttons subroutine from the last experiment.
Scan_Input:
nScan = 1
FOR idx = 1 TO 5
nScan = nScan & EventIn
PAUSE 5
NEXT
xScan = nScan ^ oScan & nScan
oScan = nScan
RETURN
' use with "noisy" inputs
' look for 0 -> 1 change
' save this scan
Moving Forward · Page 101
EXPERIMENT #16: FREQUENCY MEASUREMENT
This experiment demonstrates how the BASIC Stamp can measure the frequency of
an input signal by using the COUNT function.
Look It Up: PBASIC Elements to Know
•
•
COUNT
#SELECT-#CASE-#ENDSELECT
Building the Circuit
Note: The 1 kΩ resistor is marked: brown-black-red.
Page 102 · StampWorks
Program: SW21-EX16-Freq_Measure.BS2
' {$STAMP BS2}
' {$PBASIC 2.5}
'
'
'
'
'
-----[ Program Description ]--------------------------------------------This program counts the number of events in one second and calculates
frequency from it. Since frequency in Hertz is cycles per second, the
number of cycles counted is the input frequency.
' -----[ I/O Definitions ]------------------------------------------------FreqIn
PIN
15
' frequency input pin
' -----[ Constants ]------------------------------------------------------OneSec
CON
1000
' capture window = 1 sec
' -----[ Variables ]------------------------------------------------------cycles
VAR
Word
' counted cycles
' -----[ Program Code ]---------------------------------------------------Main:
DO
COUNT FreqIn, OneSec, cycles
DEBUG HOME,
"Frequency: ", DEC cycles, " Hz"
LOOP
' count for 1 second
' display
Behind the Scenes
In the previous experiment, several lines of code were used to count pulses on an
input pin. That method works when counting to a specific number. Other programs
will want to count the number of pulses that arrive during a specified time period.
The BASIC Stamp’s COUNT function is designed for this purpose.
The frequency of an oscillating signal is defined as the number of cycles per second
and is expressed in Hertz. The BASIC Stamp’s COUNT function monitors the specified
Moving Forward · Page 103
pin for a given amount of time (the Duration parameter). To create a simple
frequency meter, the specified time window is set to 1000 milliseconds (one second).
Note the comparison between the BASIC Stamp output and the input frequency
measured with a Parallax USB Oscilloscope on the next page:
Page 104 · StampWorks
When using the COUNT function with a Duration of one second, the frequency
measurement is very accurate up to the specified input of the BASIC Stamp module
(input frequency varies from module-to-module).
Write Code like a Pro
COUNT is one of several BASIC Stamp functions that behave differently based on the
module being used. The BS2, for example, expresses the Duration parameter in
units of one millisecond, while the BS2p expressed this parameter in units of 0.287
milliseconds.
Moving Forward · Page 105
This is another situation where conditional compilation directives are particularly
useful. To accommodate COUNT using any BASIC Stamp 2 module, we can add this
block to our program:
#SELECT $STAMP
#CASE BS2, BS2E
DurAdj
CON
#CASE BS2SX
DurAdj
CON
#CASE BS2P, BS2PX
DurAdj
CON
#CASE BS2PE
DurAdj
CON
#ENDSELECT
$100
' Duration / 1
$280
' Duration / 0.400
$37B
' Duration / 0.287
$163
' Duration / 0.720
Now that we have a multiplier for the Duration parameter, the COUNT code is
modified like this:
COUNT FreqIn, OneSec */ DurAdj, cycles
' count for for 1 second
… and the program will behave in the same manner using an BS2-family module.
Take it Further
Improve the responsiveness (make it update more frequently) of this program by
changing the COUNT period. What other adjustment has to be made? How does this
change affect the ability to measure very low frequency signals?
Page 106 · StampWorks
EXPERIMENT #17: ADVANCED FREQUENCY MEASUREMENT
This experiment demonstrates how the BASIC Stamp can measure the frequency of
an input signal by using the PULSIN function.
Look It Up: PBASIC Elements to Know
•
•
•
PULSIN
DEC (used with DEBUG)
CLREOL (used with DEBUG)
Building the Circuit
Use the same circuit as Experiment #16
Program: SW21-EX17-Freq_Measure-Adv.BS2
' {$STAMP BS2}
' {$PBASIC 2.5}
'
'
'
'
'
-----[ Program Description ]--------------------------------------------This program monitors and displays the frequency of a signal on 15. The
period of the input cycle is measured in two halves: low, then high.
Frequency is calculated using the formula F = 1 / Period.
' -----[ I/O Definitions ]------------------------------------------------FreqIn
PIN
15
' frequency input pin
' -----[ Constants ]------------------------------------------------------Scale
CON
$200
' 2.0 us per unit
' -----[ Variables ]------------------------------------------------------pHigh
pLow
period
freq
VAR
VAR
VAR
VAR
Word
Word
Word
Word
'
'
'
'
high pulse timing
low pulse timing
cycle time (high + low)
frequency
Moving Forward · Page 107
' -----[ Initialization ]-------------------------------------------------Reset:
DEBUG CLS,
"Period.(uS)... ", CR,
"Freq (Hz)..... "
' setup report output
' -----[ Program Code ]---------------------------------------------------Main:
DO
PULSIN
PULSIN
period
freq =
FreqIn,
FreqIn,
= (pLow
62500 /
0, pLow
1, pHigh
+ pHigh) */ Scale
period * 16
DEBUG CRSRXY, 15, 0, DEC period, CLREOL,
CRSRXY, 15, 1, DEC freq, CLREOL
LOOP
'
'
'
'
get high side of input
get low side of input
scale to uSecs
calculate frequency
' display values
Behind the Scenes
In the last experiment, we learned that the frequency of a signal is defined as the
number of cycles per second. We created a simple frequency meter by counting the
number of pulses (cycles) in one second. This method works well, especially for lowfrequency signals. There will be times, however, when project requirements will
dictate a quicker response time for frequency measurement.
The frequency of a signal can be calculated from its period, or the time for one
complete cycle as shown in the illustration below:
By measuring the period of an incoming signal, its frequency can be calculated with
the equation (where the period is expressed in seconds):
Frequency = 1 / Period
Page 108 · StampWorks
The BASIC Stamp’s PULSIN function is designed to measure the width of an
incoming pulse. By using PULSIN to measure the high and low portions of an
incoming signal, its period and frequency can be calculated. The result of PULSIN
(on the BS2) is expressed in units of two microseconds. First the PULSIN values
are converted to µs by the following formula:
period = (pLow + pHigh) */ Scale
Scale refers to the units of the PULSIN command. Thus, the formula for calculating
frequency becomes:
Frequency = 1,000,000 / period (µs)
This creates a problem for BASIC Stamp math though, as values are limited to 16
bits (maximum value is 65,535). To fix the formula, we can divide 1,000,000 by 16
(62,500) and rewrite the formula like this:
Frequency = 62,500 / period (µs) * 16
This formula works with any BS2 module – after the raw measurements from
PULSIN have been converted to microseconds. This is the purpose of the Scale
constant in the program: it converts the raw input from PULSIN to microseconds for
the generalized frequency calculations.
Run the program and adjust the 10 kΩ potentiometer. Notice that the Debug
Terminal window is updated without delay and that there is no waiting as when
using COUNT to determine frequency. This method of measuring a frequency works
better at higher frequencies (above 100 Hz).
Moving Forward · Page 109
EXPERIMENT #18: A LIGHT CONTROLLED THEREMIN
This experiment demonstrates FREQOUT by creating a light-controlled Theremin (the
first electronic musical instrument ever produced). While the output from our BASIC
Stamp-based Theremin is not quite as haunting as the real thing, it is a fun project
and demonstrates the ability to use a non-standard input (light level) for program
control.
Look It Up: PBASIC Elements to Know
•
FREQOUT
Building the Circuit
Note: The 220 Ω resistor is marked: red-red-brown.
Program: SW21-EX18-Theremin.BS2
' {$STAMP BS2}
' {$PBASIC 2.5}
' -----[ Program Description ]--------------------------------------------'
' This program uses RCTIME with a photocell to create a light-controlled
' Theremin.
' -----[ I/O Definitions ]------------------------------------------------Speaker
PitchCtrl
CON
CON
0
1
' speaker output
' pitch control input
Page 110 · StampWorks
' -----[ Constants ]------------------------------------------------------TAdj
FAdj
CON
CON
$100
$100
' time adjust factor
' frequency adjust factor
Threshold
NoteTm
CON
CON
200
40
' cutoff frequency to play
' note timing
' -----[ Variables ]------------------------------------------------------tone
VAR
Word
' frequency output
' -----[ Program Code ]---------------------------------------------------Main:
DO
HIGH PitchCtrl
PAUSE 1
RCTIME PitchCtrl, 1, tone
tone = tone */ FAdj
IF (tone > Threshold) THEN
FREQOUT Speaker, NoteTm */ TAdj, tone
ENDIF
LOOP
'
'
'
'
'
discharge cap
for 1 ms
read the light sensor
scale input
play?
Behind the Scenes
A Theremin is an interesting musical device used to create those weird, haunting
sounds often heard in old horror movies. This version uses the light falling onto a
photocell to create the output tone.
Since the photocell is a resistive device, RCTIME can be used to read its value.
FREQOUT is used to play the note. The constant, Threshold, is used to control the
cutoff point of the Theremin. When the photocell reading falls below this value, no
sound is played. This value should be adjusted to the point where the Theremin
stops playing when the photocell is not covered in ambient light.
Behind the Scenes…Going Deeper
You may wonder how the BASIC Stamp is able to create a musical note using a pure
digital output. The truth is that it gets a little help from the outside world. At the
Moving Forward · Page 111
front end of the PDB’s audio amplifier is a low-pass filter circuit that takes the pure
digital output (a special type of PWM output) from FREQOUT and smoothes it into a
nice sine wave that produces a clean musical note.
To see this in action, build the following circuit:
Using an oscilloscope, monitor the points marked “A” and “B” in the circuit while
running the following short program:
Main:
FREQOUT Speaker, 1000, 440
GOTO Main
On a stock BS2 this will generate a 440 Hz tone for one second. Note the digital
output at point “A” and the sine-wave produced after the filter circuit at point “B”
(the 10 kΩ resistor represents the audio amplifier input).
Page 112 · StampWorks
EXPERIMENT #19: SOUND EFFECTS (SFX)
This experiment uses DTMFOUT and FREQOUT to mimic telephone system sounds,
create sound effects, and even play a simple song.
Look It Up: PBASIC Elements to Know
•
•
DTMFOUT
INPUT
Building the Circuit
Program: SW21-EX19-Sound_FX.BS2
' {$STAMP BS2}
' {$PBASIC 2.5}
' -----[ Program Description ]--------------------------------------------'
' Demonstrates sound FX and simple music using FREQOUT and DTMFOUT.
' -----[ I/O Definitions ]------------------------------------------------Speaker
PIN
0
' speaker on pin 0
' -----[ Constants ]------------------------------------------------------R
C
Cs
D
Ds
E
F
Fs
CON
CON
CON
CON
CON
CON
CON
CON
0
33
35
37
39
41
44
46
'
'
'
'
'
'
'
'
rest
ideal
ideal
ideal
ideal
ideal
ideal
ideal
is
is
is
is
is
is
is
32.703
34.648
36.708
38.891
41.203
43.654
46.249
Moving Forward · Page 113
G
Gs
A
As
B
CON
CON
CON
CON
CON
49
52
55
58
62
'
'
'
'
'
ideal
ideal
ideal
ideal
ideal
is
is
is
is
is
48.999
51.913
55.000
58.270
61.735
N1
N2
N3
N4
N8
CON
CON
CON
CON
CON
500
N1/2
N1/3
N1/4
N1/8
'
'
'
'
'
whole note duration
half note
third note
quarter note
eighth note
TAdj
FAdj
CON
CON
$100
$100
' x 1.0 (time adjust)
' x 1.0 (freq adjust)
' -----[ Variables ]------------------------------------------------------idx
note1
note2
onTime
offTime
oct1
oct2
eePntr
digit
clickDly
VAR
VAR
VAR
VAR
VAR
VAR
VAR
VAR
VAR
VAR
Word
Word
Word
Word
Word
Nib
Nib
Byte
Byte
Word
'
'
'
'
loop counter
first tone for FREQOUT
second tone for FREQOUT
duration for FREQOUT
'
'
'
'
'
octave for freq1 (1 - 8)
octave for freq2 (1 - 8)
EEPROM pointer
DTMF digit
delay betweens "clicks"
' -----[ EEPROM Data ]----------------------------------------------------Phone1
Phone2
DATA
DATA
"123-555-1212", 0
"916-624-8333", 0
' stored telephone numbers
' -----[ Program Code ]---------------------------------------------------Main:
DEBUG CLS,
"BASIC Stamp Sound FX Demo", CR, CR
Dial_Tone:
DEBUG "Dial tone", CR
onTime = 35 */ TAdj
note1 = 35 */ FAdj
FREQOUT Speaker, onTime, note1
PAUSE 100
onTime = 2000 */ TAdj
note1 = 350 */ FAdj
' "click"
Page 114 · StampWorks
note2 = 440 */ FAdj
FREQOUT Speaker, onTime, note1, note2
Dial_Phone1:
DEBUG "Dialing number: "
eePntr = Phone1
GOSUB Dial_Phone
Phone_Busy:
PAUSE 1000
DEBUG CR, " - busy...", CR
onTime = 400 */ TAdj
note1 = 480 */ FAdj
note2 = 620 */ FAdj
FOR idx = 1 TO 8
FREQOUT Speaker, onTime, note1, note2
PAUSE 620
NEXT
onTime = 35 */ TAdj
note1 = 35 */ FAdj
FREQOUT Speaker, onTime, note1
' combine 350 Hz & 440 Hz
' dial phone from EE
' initialize eePntr pointer
' combine 480 Hz and 620 Hz
' "click"
Dial_Phone2:
DEBUG "Calling Parallax: "
eePntr = Phone2
GOSUB Dial_Phone
Phone_Rings:
PAUSE 1000
DEBUG CR, " - ringing"
onTime = 2000 */ TAdj
note1 = 440 */ FAdj
note2 = 480 */ FAdj
FREQOUT Speaker, onTime, note1, note2
PAUSE 4000
FREQOUT Speaker, onTime, note1, note2
PAUSE 2000
' combine 440 Hz and 480 Hz
' combine 440 Hz and 480 Hz
Camptown_Song:
DEBUG CR, "Play a Camptown song", CR
FOR idx = 0 TO 13
LOOKUP idx, [ G, G, E, G, A, G, E,
R, E, D, R, E, D, R], note1
LOOKUP idx, [ 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4], oct1
LOOKUP idx, [N2, N2, N2, N2, N2, N2, N2,
N2, N2, N1, N2, N2, N1, N8], onTime
GOSUB Play_1_Note
NEXT
Howler:
Moving Forward · Page 115
DEBUG "Howler -- watch out!!!", CR
FOR idx = 1 TO 4
onTime = 1000 */ TAdj
note1 = 1400 */ FAdj
note2 = 2060 */ FAdj
FREQOUT Speaker, onTime, note1, note2
onTime = 1000 */ TAdj
note1 = 2450 */ FAdj
note2 = 2600 */ FAdj
FREQOUT Speaker, onTime, note1, note2
NEXT
Roulette_Wheel:
DEBUG "Roulette Wheel", CR
onTime = 5 */ TAdj
note1 = 35 */ FAdj
clickDly = 250
FOR idx = 1 TO 8
FREQOUT Speaker, onTime, note1
PAUSE clickDly
clickDly = clickDly */ $00BF
NEXT
FOR idx = 1 TO 10
FREQOUT Speaker, onTime, note1
PAUSE clickDly
NEXT
FOR idx = 1 TO 20
FREQOUT Speaker, onTime, note1
PAUSE clickDly
clickDly = clickDly */ $010C
NEXT
FOR idx = 1 TO 30
FREQOUT Speaker, onTime, note1
PAUSE clickDly
clickDly = clickDly */ $0119
NEXT
Computer_Beeps:
LEDs
DEBUG "1950's Sci-Fi Computer", CR
FOR idx = 1 TO 50
onTime = 50 */ TAdj
RANDOM note1
note1 = (note1 // 2500) */ FAdj
FREQOUT Speaker, onTime, note1
PAUSE 100
NEXT
Space_Transporter:
DEBUG "Space Transporter", CR
onTime = 10 */ TAdj
' combine 1400 Hz and 2060 Hz
' combine 2450 Hz and 2600 Hz
'
'
'
'
'
onTime for "click"
frequency for "click"
delay between clicks
spin up wheel
click
' accelerate (speed * 0.75)
' spin stable
' slow down
' decelerate (speed * 1.05)
' slow down and stop
' decelerate (speed * 1.10)
' looks great with randmom
' run about 5 seconds
'
'
'
'
create random note
don't let note go to high
play it
short pause between notes
Page 116 · StampWorks
FOR idx =
note1 =
FREQOUT
NEXT
FOR idx =
note1 =
FREQOUT
NEXT
5 TO 5000 STEP 5
' frequency sweep up
idx */ FAdj
Speaker, onTime, note1, note1 */ 323
5000 TO 5 STEP 50
' frequency sweep down
idx */ FAdj
Speaker, onTime, note1, note1 */ 323
DEBUG CR, "Sound demo complete."
INPUT Speaker
END
' -----[ Subroutines ]----------------------------------------------------Dial_Phone:
DO
READ eePntr, digit
' read a digit
IF (digit = 0) THEN EXIT
' when 0, number is done
DEBUG digit
' display digit
IF (digit >= "0" AND digit 1
LOOP UNTIL (pattern = %00000001)
GOTO Main
' shift pattern right
' -----[ Subroutines ]----------------------------------------------------Out_595x2:
SHIFTOUT SerData, Clock, MSBFIRST, [counter]
SHIFTOUT SerData, Clock, MSBFIRST, [pattern]
PULSOUT Latch, 5
RETURN
' send counter to 595-2
' send pattern to 595-1
' latch outputs
Behind the Scenes
The 74HC595 has a serial output pin (9) that allows the cascading of multiple devices
for more outputs – the serial output from one 595 feeds the serial input of the next
device in line. This works by moving the data in QH to the QH’ output (9) on a new
clock pulse. When connecting cascaded 595s, the Clock and Latch pins should be
tied together to keep all devices synchronized.
In our program we must be concerned with the order of shifted values when working
with cascaded devices. Subsequent SHIFTOUT sequences will "push" the data
through each register until the data is loaded into the correct device. In the
illustration below the value intended for 595-2 is output first and will be shifted
through 595-1.
After the data has been output to all shift registers in the chain, the Latch pulse is
applied to transfer the new data to the 74HC595 output pins.
Moving Forward · Page 137
EXPERIMENT #24: EXPANDED DIGITAL INPUTS WITH SHIFT
REGISTERS
This experiment demonstrates the expansion of BASIC Stamp inputs with a simple
shift register – the 74HC165 which is a complementary device to the 74HC595 used
in Experiment #23.
Look It Up: PBASIC Elements to Know
•
•
SHIFTIN
MSBPRE (used with SHIFTIN)
Building the Circuit
Page 138 · StampWorks
Program: SW21-EX24-74HC165-1.BS2
' {$STAMP BS2}
' {$PBASIC 2.5}
' -----[ Program Description ]--------------------------------------------'
' This program demonstrates a simple method of turning three BASIC Stamp
' I/O pins into eight digital inputs with a 74HC165 shift register.
' -----[ I/O Definitions ]------------------------------------------------Clock
SerData
Load
PIN
PIN
PIN
0
1
2
' shift clock (74HC165.2)
' serial data (74HC165.7)
' output latch (74HC165.1)
' -----[ Constants ]------------------------------------------------------DelayTime
CON
100
' -----[ Variables ]------------------------------------------------------switches
VAR
Byte
' switch data
' -----[ Initialization ]-------------------------------------------------Reset:
HIGH Load
DEBUG CLS,
"Switches
"-------"Status
' make output and high
76543210", CR,
--------", CR,
........"
' -----[ Program Code ]---------------------------------------------------Main:
DO
GOSUB Get_165
DEBUG CRSRXY, 10, 2, BIN8 switches
PAUSE DelayTime
LOOP
' get switch inputs
' display current status
' pad the loop a bit
' -----[ Subroutines ]----------------------------------------------------Get_165:
PULSOUT Load, 5
SHIFTIN SerData, Clock, MSBPRE, [switches]
RETURN
' load switch inputs
' shift them in
Moving Forward · Page 139
Behind the Scenes
The experiment demonstrates SHIFTIN, the complementary function to SHIFTOUT.
In this case, three BASIC Stamp I/O pins are used to read the state of eight DIP
switches. To read the data from the 74HC165, the parallel inputs are latched by
briefly pulsing the Load line (high-low-high), then using SHIFTIN to move the data
into the BASIC Stamp.
Note that the DIP-switches are pulled-up to Vdd, so setting them to the closed
position puts a logic low (0) on the shift register inputs. By using the Q\ (inverted
Data Out) pin from the 74HC165, the switch data arrives at the BASIC Stamp with
"1" bit indicating that a switch is closed.
Taking it Further
As with the 74HC595, we can cascade the 74HC165 to create more inputs with the
same three I/O pins. Connect your choice of inputs to the circuit below:
Page 140 · StampWorks
Moving Forward · Page 141
Program: SW21-EX24-74HC165-2.BS2
' {$STAMP BS2}
' {$PBASIC 2.5}
'
'
'
'
'
-----[ Program Description ]--------------------------------------------This program demonstrates a simple method of turning three BASIC Stamp
I/O pins into sixteen digital inputs with two 74HC165 shift registers
that have been cascaded.
' -----[ I/O Definitions ]------------------------------------------------Clock
SerData
Load
PIN
PIN
PIN
0
1
2
' shift clock (74HC165.2)
' serial data (74HC165.7)
' output latch (74HC165.1)
' -----[ Constants ]------------------------------------------------------DelayTime
CON
100
' -----[ Variables ]------------------------------------------------------xInputs
VAR
Word
' external inputs
' -----[ Initialization ]-------------------------------------------------Reset:
HIGH Load
DEBUG CLS,
"XInputs
"------"Status
' make output and high
FEDCBA9876543210", CR,
----------------", CR,
................"
' -----[ Program Code ]---------------------------------------------------Main:
DO
GOSUB Get_165x2
DEBUG CRSRXY, 10, 2, BIN16 xInputs
PAUSE DelayTime
LOOP
' get inputs
' display current status
' pad the loop a bit
' -----[ Subroutines ]----------------------------------------------------Get_165x2:
PULSOUT Load, 5
SHIFTIN SerData, Clock, MSBPRE, [xInputs\16]
RETURN
' load inputs
' shift them in
Page 142 · StampWorks
Behind the Scenes
This program is very similar to 74HC595 cascading in that the serial output from one
shift register is fed into the serial input of the next device up the chain. It is
important to note that cascaded stages are connected using the non-inverted output;
only the stage connected directly to the BASIC Stamp uses the inverted output (all
data passing through will be inverted here).
In the program the Get_165x2 subroutine has been updated to accommodate the
second 74HC165. Since a Word variable was defined for the external inputs, the bit
modifier is used with SHIFTIN; this allows all sixteen bits to be collected at one
time. The bit modifier is only required when the number of bits differs from eight
(default bit count).
We could also define separate Byte variables for each device. The code fragment
below shows how we could handle this situation:
Get_165x2:
PULSOUT Load, 5
' load inputs
SHIFTIN SerData, Clock, MSBPRE, [switches, buttons]
RETURN
In this example, the variable called switches would be loaded with the data from
the first shift register in the chain (i.e., the device connected to the BASIC Stamp).
Moving Forward · Page 143
EXPERIMENT #25: MIXED IO WITH SHIFT REGISTERS
This experiment demonstrates the ability to mix the 74HC595 and 74HC165 and use
the fewest number of BASIC Stamp I/O pins.
Building the Circuit
Note: The 4.7 kΩ resistor is marked: yellow-violet-red.
Page 144 · StampWorks
Program: SW21-EX25-Mixed_IO.BS2
' {$STAMP BS2}
' {$PBASIC 2.5}
'
'
'
'
'
'
'
-----[ Program Description ]--------------------------------------------This program demonstrates the ability to use the 74HC595 and 74HC165
together with the fewest number of BASIC Stamp IO pins. This is
accomplished by placing a 4.7K resistor between the data out (pin 7) of
the 74HC165 and the data in (pin 14) of the 74HC595. The serial data
pin from the BASIC Stamp connects to the 74HC595.
' -----[ I/O Definitions ]------------------------------------------------Clock
SerData
Latch
Load
PIN
PIN
PIN
PIN
0
1
2
3
'
'
'
'
shift clock
serial data (74HC595.14)
output latch (74HC595.12)
input load (74HC165.1)
' -----[ Constants ]------------------------------------------------------DelayTime
CON
100
' -----[ Variables ]------------------------------------------------------xInputs
VAR
Byte
' external inputs
' -----[ Initialization ]-------------------------------------------------Reset:
LOW Latch
HIGH Load
DEBUG CLS,
"XInputs
"------"Status
76543210", CR,
--------", CR,
........"
' -----[ Program Code ]---------------------------------------------------Main:
DO
GOSUB
GOSUB
DEBUG
PAUSE
LOOP
Get_165
Put_595
CRSRXY, 10, 2, BIN8 xInputs
DelayTime
'
'
'
'
get inputs
move to extended outputs
display current status
pad the loop a bit
Moving Forward · Page 145
' -----[ Subroutines ]----------------------------------------------------Get_165:
PULSOUT Load, 5
SHIFTIN SerData, Clock, MSBPRE, [xInputs]
RETURN
' load inputs
' shift them in
Put_595:
SHIFTOUT SerData, Clock, MSBFIRST, [xInputs]
PULSOUT Latch, 5
INPUT SerData
RETURN
' send inputs to 595
' latch 595 outputs
' float data I/O line
Behind the Scenes
This program is a fairly simple combination of the previous experiments – with one
critical detail: the placement of a 4.7 kΩ resistor between the 74HC165 data output
pin and the 74HC595 data input pin. The reason that this is required is the 74HC165
data output pin is just that, an output, and if that pin were connect directly to the
BASIC Stamp a data collision could occur (when the BASIC Stamp puts the serial
data pin in output mode for SHIFTOUT) that would cause a short circuit. The
resistor provides a load that safely limits the current between the BASIC Stamp had
the 74HC165.
The resistor also gives the BASIC Stamp a load to drive its output across, so no
matter what the state of the 74HC165 output pin, the data input of the 74HC595 will
always be correct. Do not leave the 4.7 kΩ resistor out of the circuit; otherwise your
BASIC Stamp module could be damaged. Notice that the serial data line is made an
input (floating) at the end of the Put_595 subroutine. This stops current flow
between the BASIC Stamp and the 74HC165 when the pins are in opposite states.
Page 146 · StampWorks
EXPERIMENT #26: HOBBY SERVO CONTROL
This experiment demonstrates the control of a standard hobby servo. Hobby servos
frequently are used with microcontrollers in amateur robotics and animatronics.
Look It Up: PBASIC Elements to Know
•
•
MAX (maximum operator)
SDEC, SDEC1 – SDEC16 (used with DEBUG)
Building the Circuit
Moving Forward · Page 147
Program: SW21-EX26-Servo_Control.BS2
' {$STAMP BS2}
' {$PBASIC 2.5}
'
'
'
'
'
-----[ Program Description ]--------------------------------------------This program shows how to control a standard servo with the BASIC Stamp.
Servo position is controlled by reading position of a potentiometer that
is part of opposing RCTIME networks.
' -----[ I/O Definitions ]------------------------------------------------PotCW
PotCCW
Servo
PIN
PIN
PIN
0
1
2
' clockwise pot input
' counter-cw pot input
' servo control pin
' -----[ Constants ]------------------------------------------------------Scale
Center
PwAdj
CON
CON
CON
$00C6
1500
$0080
' to scale RCTIME values
' servo center position
' pulse width adjust (0.5)
' -----[ Variables ]------------------------------------------------------rcRt
rcLf
diff
sPos
pWidth
VAR
VAR
VAR
VAR
VAR
Word
Word
Word
Word
Word
'
'
'
'
'
rc reading - right
rc reading - left
difference
servo position
pulse width for servo
' -----[ Initialization ]-------------------------------------------------Reset:
LOW Servo
' initialize for PULSOUT
' -----[ Program Code ]---------------------------------------------------Main:
HIGH PotCW
PAUSE 1
RCTIME PotCW, 1, rcRt
HIGH PotCCW
PAUSE 1
' read clockwise position
' read ccw position
Page 148 · StampWorks
RCTIME PotCCW, 1, rcLf
rcRt =
rcLf =
sPos =
pWidth
(rcRt */ Scale) MAX 500
(rcLf */ Scale) MAX 500
rcLf - rcRt
= (Center + sPos)
PULSOUT Servo, (pWidth */ PwAdj)
PAUSE 20
' scale RCTIME to 0-500
' position (-500 to 500)
' finalize pulse width
' move the servo
' servo refresh delay
GOTO Main
Behind the Scenes
Hobby servos are specialized electromechanical devices used most frequently to
position the control surfaces of model aircraft. The position of the servo output shaft
is determined by the width of an incoming control pulse. The control pulse is typically
between one and two milliseconds wide. The servo will center when the control
signal is 1.5 milliseconds. In order to maintain its position, the servo must be
periodically updated. The typical update frequency is about 50 times per second, or
every 20 milliseconds as shown in the illustration below
The BASIC Stamp’s PULSOUT command is ideal command for controlling hobby
servos. In this experiment, two RCTIME circuits are constructed around a single 10K
potentiometer. This configuration allows the code to split the potentiometer (at the
wiper), measuring each side independently. By doing this we are able to determine
the relative position of the potentiometer. The readings from each side are scaled to
between 0 and 500 with the */ and MAX operators. By subtracting one side from the
other, a servo position value between –500 and +500 is returned.
Moving Forward · Page 149
The value for the constant Scale is determined empirically. After constructing the
circuit, insert appropriate DEBUG statements to display the raw potentiometer
readings from both sides (they may not match exactly due to component
differences). Take the lower of the two values and divide that into 500 (desired
output). Convert this fractional value to the */ operand by multiplying by 256.
Example:
Raw RCTIME value:
645
250 / 645 = 0.775
0.775 x 256 = 198 (this is the value called Scale)
The difference between the two scaled RCTIME values is added to the centering
position of 1500 (microseconds). Remember that on the BASIC Stamp 2 module,
PULSOUT works in two-microsecond units. What this means is that the pulse width
value needs to be divided by two in order to create the correct pulse output for the
servo. This is done by using the */ with the PwAdj constant set to $0080 (0.5).
This program demonstrates that the BASIC Stamp does indeed work with negative
numbers. You can see the value of sPos by inserting this line after the calculation:
DEBUG Home, "Position: ", SDEC sPos, "
"
Negative numbers are stored in two’s complement format. The SDEC (signed
decimal) modifier prints standard decimal with the appropriate sign.
Challenge
Replace the potentiometer with two photocells and update the code to cause the
servo to point toward at the brightest light source.
Can you think of a method that uses two potentiometers and two servos to create a
sun tracker?
Page 150 · StampWorks
EXPERIMENT #27: STEPPER MOTOR CONTROL
This experiment demonstrates the control of a small 12-volt stepper motor. Stepper
motors convert a pattern of inputs and the rate-of-change of those inputs into
precise rotational motion. The rotational angle and direction for each change (step)
is determined by the construction of the motor as well as the step pattern input.
Stepper motors are used as precision positioning devices in robotics and industrial
control applications.
Look It Up: PBASIC Elements to Know
•
•
ABS
MIN (minimum operator)
Building the Circuit
Remove the servo from Experiment #26 and add a stepper motor as shown below.
Moving Forward · Page 151
Program: SW21-EX27-Stepper_Control.BS2
' {$STAMP BS2}
' {$PBASIC 2.5}
'
'
'
'
'
-----[ Program Description ]--------------------------------------------This program demonstrates simple stepper motor control. A potentiometer
allows for speed and direction control. Using the L293D driver, this
program will work with unipolar and bipolar stepper motors.
' -----[ I/O Definitions ]------------------------------------------------PotCW
PotCCW
Coils
PIN
PIN
VAR
0
1
OUTB
' clockwise pot input
' counter-cw pot input
' output to stepper coils
' -----[ Constants ]------------------------------------------------------Scale
CON
$100
' to scale RCTIME
Mitsumi
Howard
CON
CON
48
100
' steps/rev by type
RevSteps
NumSteps
LastStep
CON
CON
CON
Mitsumi
4
NumSteps - 1
' steps per revolution
' use 4-step sequence
' last step in sequence
#DEFINE Testing = 0
' 1 for POT testing
' -----[ Variables ]------------------------------------------------------idx
stpIdx
stpDelay
VAR
VAR
VAR
Byte
Nib
Byte
' loop counter
' step pointer
' delay for speed control
rcRt
rcLf
diff
VAR
VAR
VAR
Word
Word
Word
' rc reading - right
' rc reading - left
' difference in readings
' -----[ EEPROM Data ]----------------------------------------------------'
'
'
Step1
Step2
DATA
DATA
__
ABAB
----%1100
%0110
Page 152 · StampWorks
Step3
Step4
DATA
DATA
%0011
%1001
' -----[ Initialization ]-------------------------------------------------Setup:
DIRB = %1111
stpDelay = 5
' make P4..P7 outputs
' set step delay
' -----[ Program Code ]---------------------------------------------------Demo:
FOR idx = 1 TO RevSteps
GOSUB Step_Fwd
NEXT
PAUSE 200
FOR idx = 1 TO RevSteps
GOSUB Step_Rev
NEXT
PAUSE 200
Main:
HIGH PotCW
PAUSE 1
RCTIME PotCW, 1, rcRt
' 1 rev forward
' 1 rev back
' read clockwise position
HIGH PotCCW
PAUSE 1
RCTIME PotCCW, 1, rcLf
' read ccw position
rcRt = (rcRt */ Scale) MAX 600
rcLf = (rcLf */ Scale) MAX 600
diff = ABS (rcRt - rcLf)
stpDelay = 100 - (diff / 6) MIN 2
' set speed limits
IF (diff < 25) THEN
GOTO Main
ELSE
IF (rcLf < rcRt) THEN
GOSUB Step_Fwd
ELSE
GOSUB Step_Rev
ENDIF
ENDIF
' allow for dead band
GOTO Main
' repeat
' get difference
' calculate step delay
' do a step
Moving Forward · Page 153
' -----[ Subroutines ]-----------------------------------------------------
' Turn stepper clockwise one full step
Step_Fwd:
stpIdx = stpIdx + 1 // NumSteps
GOTO Do_Step
' point to next step
' Turn stepper counter-clockwise one full step
Step_Rev:
stpIdx = stpIdx + LastStep // NumSteps
GOTO Do_Step
' point to previous step
' Read new step data and output to pins
Do_Step:
READ (Step1 + stpIdx), Coils
PAUSE stpDelay
RETURN
' output new coil data
' pause between steps
Behind the Scenes
Stepper motors differ from standard DC motors in that they do not spin freely when
power is applied. For a stepper motor to rotate, the power source must be
continuously pulsed in specific patterns. The step sequence (pattern) determines the
direction of the stepper’s rotation. The time between sequence steps determines the
rotational speed. Each step causes the stepper motor to rotate a fixed angular
increment. The stepper motor supplied with the current StampWorks kit rotates 7.5
degrees per step. This means that one full rotation (360 degrees) of the stepper
requires 48 steps. Use the table below as a guide to the motor connections.
Manufacturer
Degrees per Step
Steps per Revolution
Phase 1
Phase 2
Phase 3
Phase 4
Common
Mitsumi
7.5
48
Brown
Orange
Black
Yellow
Red
Howard Industries
3.6
100
White
Red
Green
Brown
Black
Page 154 · StampWorks
The step sequences for the motor are stored in DATA statements. The Step_Fwd
subroutine will read the next sequence from the table to be applied to the coils. The
StepRev subroutine is identical except that it will read the previous step. Note the
trick with the modulus (//) operator used in StepRev. By adding the maximum
value of the sequence to the current value and then applying the modulus operator,
the sequence goes in reverse. As a review, here’s the modulus math for full steps
(four steps per cycle):
0
3
2
1
+
+
+
+
3
3
3
3
//
//
//
//
4
4
4
4
=
=
=
=
3
2
1
0
This experiment reads both sides of the 10K potentiometer to determine its relative
position. The differential value between the two readings is kept positive by using
the ABS function. The position is used to determine the rotational direction and the
strength of the position is used to determine the rotational speed. Remember, the
shorter the delay between steps, the faster the stepper will rotate. A dead-band
check is used to cause the motor to stop rotating when the RCTIME readings are
nearly equal.
Taking It Further
Surplus stepper motors are very easy to come by, and the experimenter is often
faced with two challenges: 1) How to control a bipolar (4-wire) stepper motor and,
2) How to determine the coil sequence of an unknown motor.
By using the L293D the first challenge is nullified; the L293D is a push-pull driver
(versus the ULN2x03 that only sinks current) and will work – without any
modifications to the code – with unipolar and bipolar stepper motors.
The second challenge can be overcome with a multimeter. Create a table with the
wire colors as column and row headings, jotting down the resistance measured
between the wires. For example:
Moving Forward · Page 155
Yel
Blk
Org
Brn
Red
Yel
x
225
225
225
112
Blk
Org
Brn
Red
x
225
225
112
x
225
112
x
112
x
Note how that when the Red wire is part of a pair the resistance is half the other
readings; this is the common wire. Some unipolar motors have six wires. In this
case, two of the wires will be common.
To determine the wiring sequence, follow these steps:
1. Connect the coil wires in any order. Run the program; if it moves smoothly,
you’re done.
2. If Step 1 doesn’t work, swap the #1 and #4 wire connections. Retest.
3. If Step 2 doesn’t work, swap the #2 and #3 wire connections. The motor
should now run.
If the motor is spinning in the direction opposite of what is expected, swap the #1
and #4 leads, and the #2 and #3 leads. The motor should now be spinning
smoothly and in the desired direction.
Challenge
Rewrite the program to run the motor in half steps. Keep in mind that while half
steps provide greater position accuracy, the motor torque is reduced and may not be
able to move devices connected to it. Here’s the step sequence:
Step1
Step2
Step3
Step4
Step5
Step6
Step7
Step8
=
=
=
=
=
=
=
=
%1000
%1100
%0100
%0110
%0010
%0011
%0001
%1001
Page 156 · StampWorks
EXPERIMENT #28: VOLTAGE MEASUREMENT
This experiment demonstrates the use of the popular ADC0831 analog-to-digital
converter IC to read a variable voltage input
Look It Up: PBASIC Elements to Know
•
MSBPOST (used with SHIFTIN)
Building the Circuit
Program: SW21-EX28-ADC0831-Simple.BS2
' {$STAMP BS2}
' {$PBASIC 2.5}
'
'
'
'
'
-----[ Program Description ]--------------------------------------------This program demonstrates reading a variable voltage with an ADC0831
analog-to-digital converter chip. This program uses a Vref input of
5.000 volts (Vdd) for a bit resolution of 19.6 millivolts.
' -----[ I/O Definitions ]------------------------------------------------CS
Clock
PIN
PIN
0
1
' chip select (ADC0831.1)
' clock (ADC0831.7)
Moving Forward · Page 157
DataIn
PIN
2
' data (ADC0831.6)
' -----[ Constants ]------------------------------------------------------Cnts2Mv
CON
$139C
' x 19.6 (to millivolts)
' -----[ Variables ]------------------------------------------------------result
mVolts
VAR
VAR
Byte
Word
' result of conversion
' millivolts
' -----[ Initialization ]-------------------------------------------------Reset:
DEBUG CLS,
"ADC.... ", CR,
"volts... "
' create report screen
' -----[ Program Code ]---------------------------------------------------Main:
DO
GOSUB Read_0831
mVolts = result */ Cnts2Mv
DEBUG HOME,
CRSRXY, 9, 0, DEC result, CLREOL,
CRSRXY, 9, 1, DEC mVolts DIG 3,
".", DEC3 mVolts
' read the ADC
' convert to millivolts
' report
PAUSE 100
LOOP
' -----[ Subroutines ]----------------------------------------------------Read_0831:
LOW CS
SHIFTIN DataIn, Clock, MSBPOST, [result\9]
HIGH CS
RETURN
' enable ADC
' read ADC
' disable ADC
Page 158 · StampWorks
Behind the Scenes
Previous projects have used RCTIME to read resistive components. This is a form of
analog input, but isn’t voltage measurement. For that, the BASIC Stamp needs help
from an external device. The simplest way to measure a variable voltage is with an
analog-to-digital converter.
In this experiment, the National Semiconductor ADC0831 is used to convert a voltage
(0 – 5) to a synchronous serial signal that can be read by the BASIC Stamp with
SHIFTIN. One thing of note about the Read_0831 subroutine is that we specify
nine bits in SHIFTIN, even though the result is only eight bits? Why?
The ADC0831 requires one pulse on the clock line after being activated to do the
voltage conversion. The next eight clock pulses move the data out of the device as
shown in the illustration below:
The first clock pulse (gray) after the CS line goes low causes the ADC0831 to do the
voltage conversion. The MSBPOST mode is used with SHIFTIN as the data bits are
presented MSB first, and after the clock line falls. The POST modes sample the data
line after each clock pulse.
The voltage measurement – which is actually the positive difference between the
Vin+ (pin 2) and Vin- (pin 3) pins – will be a value between 0 and 255 (Vref). In our
first application we have connected Vin- to ground and Vref to Vdd; this gives us a
voltage span of 5.00 volts. Dividing five (volts) by 255, we find that each bit in the
result is equal to 19.6 millivolts. For display purposes, the result is converted to
millivolts by multiplying by 19.6 (result */ $139C).
Moving Forward · Page 159
A neat trick with DEBUG is used to display the variable, mVolts. The DIG 3
operation prints the whole volts and the DEC3 modifier prints the fractional volts
(rightmost three digits).
Reconnect the circuit as shown below and rerun the program.
Now use a multimeter to measure the voltage between pins 2 and 3 of the ADC0831.
Note that when the voltage on pin 3 is higher than pin 2, the output will be zero.
Taking It Further
As stated earlier, the voltage-per-bit for the ADC output is determined by the voltage
applied to Vref. Reconnect the circuit as shown below, and set the voltage on the
Vref pin to 2.55 volts (confirm with a multimeter).
Page 160 · StampWorks
By reducing the Vref voltage the resolution per output bit is increased. With a Vref
of 2.55 volts, the voltage per bit is 0.01 volts, nearly twice as when 5.00 volts was
used for Vref, and the conversion to millivolts is simplified. This configuration is
useful for sensors like the GP2D12 distance sensor that has a voltage output of 0 to
2.4 volts.
Before running the program modify the Cnts2Mv constant to reflect the Vref
change. With each bit equal to 0.01 volts (1/100) we can multiply by 10 to convert
to millivolts (1/1000).
Cnts2Mv
CON
$0A00
' x 10 (to millivolts)
Note that as the ADC0831 cannot measure below zero volts (floor value is 0), it
cannot measure above Vref. If the differential voltage between pins 2 and 3 is
greater than Vref, the output will be limited to 255. Keep this limitation in mind for
designs where the voltage input could move above Vref.
Moving Forward · Page 161
EXPERIMENT #29: TEMPERATURE MEASUREMENT
This experiment demonstrates the use of a popular digital temperature sensor IC:
the DS1620. Accurate temperature measurement is a necessary component of
environmental control applications (heating and air conditioning).
Look It Up: PBASIC Elements to Know
•
•
•
LSBFIRST (used with SHIFTOUT)
LSBPRE (used with SHIFTIN)
BYTE0, BYTE1 (variable modifier)
Building the Circuit
Program: SW21-EX29-DS1620-Simple.BS2
' {$STAMP BS2}
' {$PBASIC 2.5}
' -----[ Program Description ]--------------------------------------------'
' This program measures temperature using the Dallas Semiconductor DS1620
' temperature sensor. Resolution is 0.5 degrees Celsius.
' -----[ I/O Definitions ]-------------------------------------------------
Page 162 · StampWorks
DQ
Clock
Reset
CON
CON
CON
0
1
2
' DS1620.1 (data I/O)
' DS1620.2
' DS1620.3
' -----[ Constants ]------------------------------------------------------RdTmp
WrHi
WrLo
RdHi
RdLo
RdCntr
RdSlope
StartC
StopC
WrCfg
RdCfg
CON
CON
CON
CON
CON
CON
CON
CON
CON
CON
CON
$AA
$01
$02
$A1
$A2
$A0
$A9
$EE
$22
$0C
$AC
'
'
'
'
'
'
'
'
'
'
'
read temperature
write TH (high temp)
write TL (low temp)
read TH
read TL
read counter
read slope
start conversion
stop conversion
write config register
read config register
DegSym
CON
186
' degrees symbol
' -----[ Variables ]------------------------------------------------------tempIn
sign
VAR
VAR
Word
tempIn.BIT8
' raw temperature
' 1 = negative temperature
tC
tF
VAR
VAR
Word
Word
' Celsius
' Fahrenheit
' -----[ Initialization ]-------------------------------------------------Setup:
HIGH Reset
SHIFTOUT DQ, Clock, LSBFIRST, [WrCfg, %10]
LOW Reset
PAUSE 10
HIGH Reset
SHIFTOUT DQ, Clock, LSBFIRST, [StartC]
LOW Reset
' alert the DS1620
' use with CPU; free-run
' start conversions
DEBUG CLS,
"DS1620 ", CR,
"---------"
' -----[ Program Code ]---------------------------------------------------Main:
DO
Moving Forward · Page 163
GOSUB Read_DS1620
' get the temperature
Display_C:
DEBUG CRSRXY, 0, 2,
(tC.BIT15 * 13 + " "),
DEC (ABS tC / 10), ".", DEC1 (ABS tC),
DegSym, " C", CLREOL
Display_F:
DEBUG CRSRXY, 0, 3,
(tF.BIT15 * 13 + " "),
DEC (ABS tF / 10), ".", DEC1 (ABS tF),
DegSym, " F", CLREOL
PAUSE 1000
LOOP
' delay between readings
' -----[ Subroutines ]----------------------------------------------------Read_DS1620:
HIGH Reset
SHIFTOUT DQ, Clock, LSBFIRST, [RdTmp]
SHIFTIN DQ, Clock, LSBPRE, [tempIn\9]
LOW Reset
'
'
'
'
alert the DS1620
give command to read temp
read it in
release the DS1620
tempIn.BYTE1 = -sign
tC = tempIn * 5
' extend sign bit
' convert to tenths
IF (tC.BIT15 = 0) THEN
tF = tC */ $01CC + 320
ELSE
tF = 320 - ((ABS tC) */ $01CC)
ENDIF
RETURN
'
'
'
'
temp C is positive
convert to F
temp C is negative
convert to F
Behind the Scenes
The largest organ of the human body is the skin and it is most readily affected by
temperature. Little wonder then that so much effort is put into environmental control
systems (heating and air conditioning).
This
experiment
uses
the
Dallas
Semiconductor
DS1620
digital
thermometer/thermostat chip. This chip measures temperature and makes it
available to the BASIC Stamp through a synchronous serial interface. The DS1620 is
Page 164 · StampWorks
an intelligent device and, once programmed, is capable of stand-alone operation
using the THi, TLo, and TCom control outputs.
The connections to the DS1620 are similar to other synchronous serial devices, with
the exception of the 1K resistor in the DQ line. Do not leave this out; the DQ pin of
the DS1620 is bi-directional so it could – under the right conditions – be an output
and in the opposite state of the BASIC Stamp pin that it connects to. This condition
could lead to damage to one device or the other. The 1K resistor limits the current
between the BASIC Stamp and the DS1620 to a safe level should a programming
error occur.
The DS1620 requires initialization before use. In active applications like this, the
DS1620 is configured for free running with a CPU. After the configuration data is sent
to the DS1620, a delay of 10 milliseconds is required so that the configuration can be
written to the DS1620’s internal EEPROM (this delay is required after any write to the
EEPROM). After the delay, the DS1620 is instructed to start continuous conversions.
This will ensure a current temperature reading when the BASIC Stamp requests it.
The DS1620 requires about one second to complete a temperature conversion, so
access to new temperature should be no more frequent than every second.
To retrieve the current temperature, the Read Temperature ($AA) command byte is
sent to the DS1620. Then the latest conversion value is read back. The data returned
is nine bits wide, and holds the temperature in half-degrees Celsius units. Bit8
indicates the sign of the temperature. If negative (sign bit is 1), the other eight bits
hold the two’s-complement value of the temperature.
The sign bit is extended to the upper byte of tempIn to allow positive or negative
values in the equations that follow. This is required because the BASIC Stamp stores
negative values in 16-bit two’s complement format, but only nine bits are returned
from the DS1620. You see how the sign gets properly extended with the following
test program:
DEBUG BIN8 -0, CR,
BIN8 -1
' %00000000 (positive)
' %11111111 (negative)
With a full (signed) 16-bit value in tempIn, the Celsius temperature is calculated by
multiplying tempIn by five. If the current temperature was 22.5 degrees C, tC
would now hold 225.
Moving Forward · Page 165
To convert from Celsius (in tenths) to Fahrenheit (also in tenths) a modification of
the standard temperature equation is used:
Ftenths = (Ctenths * 1.8) + 320
Note that 32 degrees from the standard equation has also been converted to tenths.
For the conversion of negative temperatures the order of elements in the equation is
reversed. The reason for this is that negative numbers cannot be divided in PBASIC.
The ABS operator is used to convert the intermediate result to a positive value.
When subtracted from 320 the result will be properly aligned (and signed); some
negative values in the Celsius range are still positive in Fahrenheit.
The display routine uses a little trick that looks at Bit15 of the value; if Bit15 is one
then the temperature is negative and a “-“ will precede the temperature reading,
otherwise a space will be printed.
Page 166 · StampWorks
Taking It Further
The DS1620 has thermostat outputs that can be used to control other devices.
These outputs are typically used in stand-alone mode, but will also work
autonomously when the DS1620 is connected to the BASIC Stamp or another host.
Connect two LEDs to the DS1620 THi and TLo outputs as shown below:
With the LEDs connected, add the following code after the DS1620 initialization:
Set_Alarms:
HIGH Reset
tC = (THi - 32 */ $008E) * 2
SHIFTOUT DQ, Clock, LSBFIRST, [WrHi, tC\9]
LOW Reset
PAUSE 10
HIGH Reset
tC = (TLo - 32 * 5 / 9) * 2
SHIFTOUT DQ, Clock, LSBFIRST, [WrLo, tC\9]
LOW Reset
PAUSE 10
' convert to 0.5 C
' write high temp
' write low temp
Behind the Scenes
The THi output will go high when the current temperature is at or above the value
stored in the high-temperature register. The TLo output will go high when the
current temperature is at or below the low-temperature register.
Moving Forward · Page 167
In the program the constants THi and TLo are used to set the high and low
temperature thresholds. These values are expressed in whole degrees Fahrenheit,
and are converted to half-degrees Celsius before being written to the appropriate
register.
Chalf = (F - 32) x 5 / 9 x 2
Finally, note that as in the setup of the configuration register, a 10 millisecond
PAUSE is required after every EEPROM write. Once the thresholds are written to the
thermostat registers the THi and TLo outputs will operate independently and without
further program interface. The BASIC Stamp can read the configuration register to
get the status of the DS1620 THi and TLo outputs. See Experiment #30.
Page 168 · StampWorks
EXPERIMENT #30: HIGH RESOLUTION TEMPERATURE
MEASUREMENT
This experiment demonstrates advanced use of the DS1620 temperature sensor,
allowing for high resolution (0.05 degrees C) measurements.
Building the Circuit
Use the circuit from Experiment #29.
Program: SW21-EX30-DS1620-HiRes.BS2
' {$STAMP BS2}
' {$PBASIC 2.5}
'
'
'
'
'
'
'
-----[ Program Description ]--------------------------------------------This program measures temperature using the Dallas Semiconductor DS1620
temperature sensor. Resolution is = THi
1 when temp 0)
money = money + (nickel * 5)
money = money + (dime * 10)
money = money + (quarter * 25)
money = money + (dollar * 100)
GOSUB Show_The_Money
PAUSE 250
IF (money < FullValue) THEN Main
' wait for coins
' add coins
' update the display
' scan until full
DO
PAUSE 500
config.BIT0 = ~config.BIT0
GOSUB Update_Cfg
LOOP
' toggle display
' -----[ Subroutines ]----------------------------------------------------Get_Coins:
deposit = %1111
FOR idx = 1 TO 10
deposit = deposit & ~Coins
PAUSE 5
NEXT
RETURN
' enable all coin inputs
' test inputs
' delay between tests
' Display money value until that value meets or
' exceeds the bank limit.
Show_The_Money:
IF (money < FullValue) THEN
dpCtrl = %1011
segs5 = Blank
segs4 = Blank
' show money count
' display bright, show DP
Page 176 · StampWorks
segs3 = money DIG 2
segs2 = money DIG 1
segs1 = money DIG 0
GOSUB Update_Segs
ELSE
config = Blank
GOSUB Update_Cfg
config = %11101111
dpCtrl = %1000
segs5 = Blank
segs4 = Ltr_F
segs3 = Ltr_U
segs2 = Ltr_L
segs1 = Ltr_L
GOSUB Update_Segs
GOSUB Update_Cfg
ENDIF
RETURN
' dollar digit
' tens digit
' ones digit
' show "FULL"
' setup for "FULL"
' display bright, no DPs
'
'
'
'
'
'
F
U (Special Decode)
L (Special Decode)
L (Special Decode)
show message
display on
' Update MC14489 configuration register
Update_Cfg:
LOW Enable
SHIFTOUT SerData, Clock, MSBFIRST, [config]
HIGH Enable
RETURN
' enable MC14489
' send config register
' disable MC14489
' Update MC14489 decimal point control and segments registers
Update_Segs:
LOW Enable
SHIFTOUT SerData, Clock, MSBFIRST, [dpCtrl\4,
segs5\4, segs4\4, segs3\4, segs2\4, segs1\4]
HIGH Enable
RETURN
Behind the Scenes
As demonstrated in Experiment #10, 7-segment display multiplexing requires a lot of
effort that consumes most of the computational resources of the BASIC Stamp.
Enter the Motorola MC14489 display multiplexer. By using just three BASIC Stamp
I/O pins it will effectively control up to five 7-segment displays. The interface is
simple, allowing the display of numbers (all hex values), a few letters (those that can
be displayed on a 7-segment LED), and a few special characters (e.g., dash, degrees
Moving Forward · Page 177
symbol, etc). The MC14489 can also be configured to control up to 25 discrete LEDs
(using No Decode mode).
The MC14489 connects to the LED displays in a straightforward way; pins A through
H connect to segments A through G and the decimal point of all of the commoncathode displays. Pins BANK 1 through BANK 5 connect to the individual cathodes of
each of the displays (Digit 0 – Digit 4). If you use fewer than five digits, omit the
highest digit number(s). For example, this experiment uses four digits, numbered 0
through 3, so Digit 4 need not be connected.
When the MC14449 is used with seven-segment displays, it can be configured to
automatically convert binary-coded decimal (BCD) values into appropriate patterns of
segments – this is called Hex Decode mode. This makes the display of decimal and
hexadecimal numbers quite simple. The MC14489 also has a Special Decode mode
that displays a few letters and symbols. Finally, there is a No Decode mode wherein
the bits used for a digit register are output directly (but only to segments A-D;
segments E-G are turned off in No Decode mode).
The key to getting information into a display controlled by the MC14489 is
understanding the configuration register and how the bits interact to control the
display decoding. The table below is a review of the configuration register bits and
how they affect the display:
Bit0
Bit1
Bit2
Bit3
Bit4
Bit5
Bit6
Bit7
0
0
0
0
0
0
0
0
=
=
=
=
=
=
=
=
display blank; 1 = display on
Hex Decode for Bank 1; 1 = Depends on Bit6
Hex Decode for Bank 2; 1 = Depends on Bit6
Hex Decode for Bank 3; 1 = Depends on Bit6
Hex Decode for Bank 4; 1 = Depends on Bit7
Hex Decode for Bank 5; 1 = Depends on Bit7
No Decode; 1 = Special Decode for Bank1 – Bank 3
No Decode; 1 = Special Decode for Bank4 – Bank 5
Sending data to the MC14489 happens one of two ways: 1) the eight bit
configuration register is sent, or 2) 24 bits (six nibbles) that hold display information
are transmitted. There are no addresses for the data as with other synchronous
serial devices; the MC14489 properly routes information sent to it based on the size
of the packet.
Page 178 · StampWorks
For the counter program we initially want to use Hex (numeric) decoding for digits
0 – 2, blank digits 3 and 4, and set the decimal point to be on digit 2. The proper
configuration register value for this requirement is %00110001 (review the
configuration bit table above). The Update_Cfg subroutine handles sending the
configuration register to the MC14489.
The decimal point is controlled by one of the six nibble-sized registers passed to the
MC14489 for display. The position of the decimal point(s) – if used – is transmitted
using the Update_Segs subroutine along with the control values for each of the
display digits.
Most of the work takes place in the subroutine called Show_The_Money. When the
money count is less than 500, the value will be displayed on the 7-segment LEDs.
The routine scans through each digit of money and sends the digit position and value
(from the DIG operator) to the MC14489. Since the display shows dollars and cents,
the decimal point on the third digit is enabled.
When the value of money reaches or passes 500, the display will change to “FULL.”
This is accomplished by setting Banks 1 – 3 (digits 0 – 2) to Special Decode so that
the letters “U” and “L” can be displayed. The letter “F” is part of the hexadecimal
number set so Bank 4 (digit 3) is left in Hex Decode mode.
The main loop of the program is simple: it scans the switch inputs with Get_Coins
and updates the money count for each switch pressed. When the “bank” is full, the
program enters an infinite loop that toggles the display bit of the configuration
register; this is a simple way to flash the display without modifying display contents.
Challenge
Modify the code in Experiment #28 to display the input voltage on the sevensegment displays.
Moving Forward · Page 179
EXPERIMENT #32: I2C COMMUNICATIONS
This experiment demonstrates the BASIC Stamp’s ability to communicate with other
devices through the use of the popular Philips I2C protocol. The experiment uses
this protocol to write and read data to a serial EEPROM using high- and low-level I2C
routines which can be used to communicate with any I2C device.
Building the Circuit
Program: SW21-EX32-24LC32.BS2
' {$STAMP BS2}
' {$PBASIC 2.5}
'
'
'
'
'
-----[ Program Description ]--------------------------------------------This program demonstrates essential I2C interfacing by connecting to
a 24LC32 EEPROM. The connections in the program conform to the BS2p
I2CIN and I2COUT instructions.
' -----[ I/O Definitions ]------------------------------------------------SDA
SCL
PIN
PIN
8
9
' I2C serial data line
' I2C serial clock line
' -----[ Constants ]-------------------------------------------------------
Page 180 · StampWorks
Ack
Nak
CON
CON
0
1
' acknowledge bit
' no ack bit
EE24LC32
CON
%1010 = BS2P) #THEN
#ERROR "Please use BS2p version: SW21-EX32-24LC32.BSP"
#ENDIF
Setup:
devNum = %000
slvAddr = EE24LC32 | (devNum 0) THEN
i2cWork = slvAddr & %11111110
GOSUB I2C_TX_Byte
IF (i2cAck = Nak) THEN Read_Byte
IF (addrLen = 2) THEN
i2cWork = wrdAddr.BYTE1
GOSUB I2C_TX_Byte
ENDIF
i2cWork = wrdAddr.BYTE0
GOSUB I2C_TX_Byte
GOSUB I2C_Start
ENDIF
i2cWork = slvAddr | %00000001
GOSUB I2C_TX_Byte
GOSUB I2C_RX_Byte_Nak
GOSUB I2C_Stop
i2cData = i2cWork
RETURN
' send Start
' send slave ID (write)
' wait until not busy
' send word address (1)
' send word address (0)
' send slave ID (read)
' -----[ Low Level I2C Subroutines ]--------------------------------------' *** Start Sequence ***
I2C_Start:
INPUT SDA
INPUT SCL
LOW SDA
Clock_Hold:
DO : LOOP UNTIL (SCL = 1)
RETURN
' I2C start bit sequence
' wait for clock release
' *** Transmit Byte ***
I2C_TX_Byte:
SHIFTOUT SDA, SCL, MSBFIRST, [i2cWork\8]
SHIFTIN SDA, SCL, MSBPRE, [i2cAck\1]
RETURN
' send byte to device
' get acknowledge bit
Moving Forward · Page 183
I2C_RX_Byte_Nak:
i2cAck = Nak
GOTO I2C_RX
I2C_RX_Byte:
i2cAck = Ack
I2C_RX:
SHIFTIN SDA, SCL, MSBPRE, [i2cWork\8]
SHIFTOUT SDA, SCL, LSBFIRST, [i2cAck\1]
RETURN
' no Ack = high
' Ack = low
' get byte from device
' send ack or nak
' *** Stop Sequence ***
I2C_Stop:
LOW SDA
INPUT SCL
INPUT SDA
RETURN
' I2C stop bit sequence
Behind the Scenes
The I2C-bus is a two-wire, synchronous bus that uses a Master-Slave relationship
between components. The Master initiates communication with the Slave and is
responsible for generating the clock signal. If requested to do so, the Slave can send
data back to the Master. This means the data pin (SDA) is bi-directional and the
clock pin (SCL) is [usually] controlled exclusively by the Master.
The transfer of data between the Master and Slave works like this:
Master sending data
• Master initiates transfer
• Master addresses Slave
• Master sends data to Slave
• Master terminates transfer
Master receiving data
• Master initiates transfer
• Master addresses Slave
• Master receives data from Slave
• Master terminates transfer
Page 184 · StampWorks
The I2C specification actually allows for multiple Masters to exist on a common bus
and provides a method for arbitrating between them. That's a bit beyond the scope
of what we need to do so we're going to keep things simple. In our setup, the BS2
(or BS2e or BS2sx) will be the Master and anything connected to it will be a Slave.
You'll notice in I2C schematics that the SDA (serial data) and SCL (serial clock) lines
are pulled up to Vdd (usually through 4.7 kΩ). The specification calls for device bus
pins to be open drain. To put a high on either line, the associated bus pin is made
an input (floats) and the pull-up takes the line to Vdd. To make a line low, the bus
pin pulls it to Vss (ground).
This scheme is designed to protect devices on the bus from a short to ground. Since
neither line is driven high, there is no danger. We're going to cheat a bit. Instead of
writing code to pull a line low or release it (certainly possible – I did it), we're going
to use SHIFTOUT and SHIFTIN to move data back and forth. Using SHIFTOUT and
SHIFTIN is faster and saves precious code space. If you're concerned about a bus
short damaging the BASIC Stamp's SDA or SCL pins during SHIFTOUT and
SHIFTIN, you can protect each of them with a 220 ohm resistor. If you’re careful
with your wiring and code this won’t be necessary.
Low Level I2C Code
At its lowest level, the I2C Master needs to do four things:
•
•
•
•
Generate a Start condition
Transmit 8-bit data to the Slave
Receive 8-bit data from Slave – with or without Acknowledge
Generate Stop condition
A Start condition is defined as a high-to-low transition on the SDA line while the SCL
line is high. All transmissions begin with a Start condition. A Stop condition is
defined as a low-to-high transition of the SDA line while the clock line is high. A Stop
condition terminates a transfer and can be used to abort it as well.
Moving Forward · Page 185
There is a brief period when the Slave device can take control of the SCL line. If a
Slave is not ready to transmit or receive data, it can hold the SCL line low after the
Start condition. The Master can monitor this to wait for the Slave to be ready. At
the speed of the BS2, monitoring the clock line usually isn't necessary but the
capability to monitor “clock hold” is built into the I2C_Start subroutine just to be
safe.
For our experiments we'll be using 7-bit addressing (see figure below) where the
upper seven bits of the slave address byte contain the device type and address, and
bit zero holds the data direction: "0" indicating a device write; "1" indicating a device
read. What follows the slave address will vary, depending on the device and the
type of request. Most I2C devices have one or two address bytes which will be
followed by the data byte(s) to write to or read from the device
Data is transferred eight bits at a time, sending the MSB first. After each byte, the
I2C specification calls for the receiving device to acknowledge the transmission by
bringing the bus low for the ninth clock. The exception to this is when the Master is
the receiver and is receiving the final byte from the Slave. In this case, there is no
Acknowledge bit sent from Master to Slave.
Sending and receiving data from a specific slave always requires a Start condition,
sending the Slave address and finally, the Stop condition. What happens between
Page 186 · StampWorks
the Slave address and the Stop are dependent on the device and the application
process.
What you'll need to do is get the data sheet for the I2C device you want to connect
to. You will find that most data sheets for I2C-compatible parts have very clear
protocol definitions – usually in graphic form – that make implementing the low-level
I2C routines very simple.
The experiment uses the low-level I2C routines to implement the Write_Byte and
Read_Byte routines. These routines are generalized to work with any I2C device,
allowing the slave address, number of address bytes, and the address to read or
write (if required). Note that each routine begins with an I2C Start condition and is
terminated with the Stop condition. The code in between sends the device
command/type code, the address to deal with and then actually deals with (writes or
reads) the data. While this takes a few lines of code, it is actually very
straightforward.
The core of the demo program loops through the available addresses of the 24LC32
EEPROM, writing and reading back four distinct bit patterns. If the value read back
does not match the value written, a variable called fails is incremented. The Debug
Terminal window gives current status of the program as shown below. Note that
with 4096 addresses and four writes and reads at each address, this program takes a
bit of time to run through to completion.
Moving Forward · Page 187
Page 188 · StampWorks
EXPERIMENT #33: USING A REAL-TIME CLOCK
This experiment uses the I2C framework developed in Experiment #32 to
communicate with a DS1307 Real-Time Clock chip. RTC time capability and
management is important for time-of-day oriented applications, and applications that
require the measurement of elapsed time.
Look It Up: PBASIC Elements to Know
•
HEX, HEX1 – HEX4 (used with DEBUG)
Building the Circuit
Connect four pushbuttons to P4-P7 (see Experiment #14) and connect the DS1307
as shown below:
Moving Forward · Page 189
Program: SW21-EX33-DS1307.BS2
' {$STAMP BS2}
' {$PBASIC 2.5}
' -----[ Program Description ]--------------------------------------------'
' This program demonstrates the access and control of an external real' time-clock chip, the DS1307.
' -----[ I/O Definitions ]------------------------------------------------SDA
SCL
PIN
PIN
0
1
' I2C serial data line
' I2C serial clock line
BtnBus
VAR
INB
' four inputs, pins 4 - 7
' -----[ Constants ]------------------------------------------------------Ack
Nak
CON
CON
0
1
DS1307
CON
%1101 = BS2P) #THEN
#ERROR "Please use BS2p version: SW21-EX33-DS1307.BSP"
#ENDIF
Setup:
slvAddr = DS1307
addrLen = 1
' 1 byte in word address
DEBUG CLS,
"DS1307 Demo", CR,
"-----------"
Reset_Clock:
GOSUB Get_Buttons
idx = btns & %0011
IF (idx = %11) THEN
secs = $00
mins = $00
hrs = $06
day = $07
date = $01
month = $01
year = $05
control = 0
GOSUB Set_Clock
ENDIF
' scan buttons
' isolate hrs & mins
' if both pressed, reset
'
'
'
'
'
'
'
6:00 AM
Saturday
1st
January
2005
disable SQW output
block write clock regs
' -----[ Program Code ]---------------------------------------------------Main:
GOSUB Get_Clock
' read DS1307
hrs = hrs & $3F
DEBUG CRSRXY, 0, 2,
HEX2 hrs, ":", HEX2 mins, ":", HEX2 secs, CR
GOSUB Print_Day
PAUSE 100
GOSUB Get_Buttons
IF (btns > %0000) THEN
IF (btns %1000) THEN
hrs = hrs.NIB1 * 10 + hrs.NIB0
' button pressed?
' ignore back only
' BCD to decimal
Moving Forward · Page 191
mins = mins.NIB1 * 10 + mins.NIB0
IF (btnBack =
day = ((day
hrs = hrs +
mins = mins
ELSE
day = ((day
hrs = hrs +
mins = mins
ENDIF
0) THEN
- 1) + btnDay // 7) + 1
btnHr // 24
+ btnMn // 60
'
'
'
'
increment values
keep 1 - 7
keep 0 - 23
keep 0 - 59
- 1) + (btnDay * 6) // 7) + 1
(btnHr * 23) // 24
+ (btnMn * 59) // 60
hrs = (hrs / 10