The Propeller microprocessor is a computer developed
and produced by Parallax Inc. It is a 32 bit computer
running at 80 MHz. Its most important feature is that
it has eight individual processors, called cogs,
which operate in parallell. When all these processors
are working, the total capacity of the machine is thus
640 MHz.
More importantly, however, the eight parallell processors
allow you to work easily and nicely with real time
systems, controlling and interacting with "real things".
You can avoid the interrupt concept, with all its
pedagogical difficulties and pitfalls. Instead,
you assign a dedicated processor to monitor events from,
say, a sensor. When it detects the event it can grab
some external signals and then invoke computations in the
other processors in a very versatile and transparent
way. For the monitoring there are instructions that
monitor an event in a single instrucion, so that the
computer stays within the instruction until the event
happens.
The processors communicate with each other over a global
memory. This memory is connected to the processors in a
round robin fashion, thus avoiding the risk that two
processors try to access the memory simultaneously.
To allow constistent transfers of data chunks with more
than one word, there is a semaphore concept.
The processors communicate with the external world over
32 I/O pins which are programmable to be either input or
output pins. There is a simple scheme for resolving
conflicts between several processors accessing the same
output pins. All input pins are simultaneousy available
to all processors.
The processor also has a real time counter, running through
all the values of a 32 bit word in a few minutes (after which
it overflows and restarts), and
wait instructions that halt the processor (cog) until
the time counter has reached a specified value. In this
way it is possible to contruct very accurate real time
functions.
The Assembler language of the processer is available to the user through the Propeller Programming Environment (even though in some sense it has to be encapsulated in a Spin program). Here is a sequnce of four instructions which can illuminate some special features of this assembler language:
The Propeller computer is supplied with another
language at a higher level, the Spin language.
It is an interpretive language. This means that the
programmer is not running his own code. He runs an
interpreter, resident in the propeller, which interprets
the Spin code (somehow encoded into 1:s and 0:s).
This interpretive principle facilitates the design of
a nice high level language. But the whole method is slow.
Given a high level Spin language and a fast Assembler language,
the natural approach would be to write time critical
functions in assembler, and connect them through a Spin
program. To my understanding, this is not the way Spin
and assembler code work together. The Spin "call" to
Assembler code amounts to that a cog is started to run
from the machine adress of the indicated assembler code.
But there is no return instruction to return to the Spin
code.
An interest I had in stack programming several years ago,
led me to try to design a stack based higher level
language, that could be compiled into assembler. I was
also inspired to this by someone, who had made a FORTH
programming language system for the Propeller. FORTH
is a stack based programming language, and so is my
language, called MYRA. A documentation of this
language is found here.
But here are some introductory comments.
Stack based programming means that one has a stack
to which one piles up data,and from which one fetches data.
These data are all 32 bit words, and there is no typing of
data. There are producers which pile (or push, as the term is)
data on the top of the stack, and there are consumers who take
(or pop, as the term is) data from the stack.
But they may only take data from the top of the stack.
Thus, when a consumer has consumed one item from
the stack, the item under it is reachable for the next
consumer. Most things in a computer program are functions,
and functions are both consumers and procucers. They
consume the argument for the function, compute the
result and produce the result on the stack. This
interface works for both standard operations like + and
for functions written by yourself. The latter can be
functions written in MYRA, and functions written in
assembler. The functions written in MYRA can be written
inside the main program code, or they can be localized
to external, so called, object files. Assembler routines
are written into an assembler resource file, and at
compilation these functions are put into the compiled
code as needed. This "universal interface" to functions
seems to me to be most important advantage of stack
based programming.
The intended meaning of object files, is that they
should contain functions useful to interface with some
object, like a display, a sensor etc.. If you have
an object file for a display with functions for handling
the display, you can effectively hide the details of
display handling in the main program. To make this
transparent, you should also name functions in object
files with object "dot" notation, disp.write.
A function that most objects would contain would be
an initialization function obj.init. When you call it,
the call can look as follow:
Here is the software you have to download in order to use the Myra language:
I developed the MPS language for the Propeller recently,
in order to get faster code. Historically, however, MPS
is several years older than Myra. In the early 1970:s
I wanted to build my own computer. At that time, one could
buy a chip from Intel called 4004. It was a 4 bit machine.
To add two 16 bit numbers required a full page of programming
code. So I decided to build the computer myself from
loose integrated circuits (counters, adders, shift registers
etc.) The computer got the name Computer 108, as the
computer in the Swedish fighter aircraft Viggen (JA37) was called
computer 107. For it I developed the MPS language, which
was short for My Programming Language in Swedish.
Now, some 40 years later, I have adapted almost the same
language for the Propeller computer. Myra worked on a
computer model with a stack, but the Propeller doesn't have
a stack, so the stack has to be simulated, which is costly
for execution speed and program size. Computer 108 just
has a single register, accu, in which the result is
accumulated. Then most instructions refer to
The general structure of MPS is the same as for Myra.
There is a system line, declarations of global variables,
an exec lines, which describes how processes are
executed (in parallell and in series), and then a
number of processes, starting with the "process" keyword,
and ending with a "\". In the process, functions can
be called. Functions can be allocated in object files,
now with the filename ending with ".mpo".
A difference is that the MPS system doesn't have any
assembler resource file. As MPS code comes very close
to assembler code in effectivitiy, there is virtually
no assembler code in MPS, but everything is written in MPS.
The functionality of the assembler resource file, is put
in the object file std.mpo.This object file
is used as any other object file.
There is a possibility to write "in line assembler code"
in MPS, but this is used very sparesly, and is incapsulated
in functions in object files.
With this mentioned, the differences between MPS and Myra
code, appear in executable parts of processes and
functions (what comes after the 'begin' keyword).
In the executable parts of the program, instructions are separated with blank spaces and with line feeds if one wishes to structure the code on several lines.
Unary instructions act solely on accu. Hence they don't mention any other operand. The unary operands are:
The propeller computer allows every instruction to
be executed conditionally. In Myra I used this fully.
Whether the execution is done or not is based on
two status registers in the computer. This could be
dangerous, if some instruction in a conditioned chain
of instructions happens to change the status registers.
Subroutine jumps are particularly suspect here, because
they look innocent, but you never know what a subroutine
does to the status register, unless you look at it in
detail.
To avoid this, MPS was made in the opposite spirit. No
instructions are executed conditionally, except jumps.
If we separate the condition from the instruction, we
are talking about only one type of jump here. Subroutine
jumps are not conditioned. The format of this conditional
jump is:condition:target.
Execution goes to target if the condtion is satisfied.
The target is expressed as a label, which
we shall return to in a minute. The conditions are the
following:
The Propeller has a DJNZ instruction, that combines decrementing an index with a conditional jump. As it does pretty much in a single instruction cycle, it is worthwhile to encapsulate it in an MPS instruction. It is in a sense a binary instruction, as it has two arguments, but none of them is accu. This is how you write it:
Incrementing a number x by one can be done withe the code:
The nicest use of the stack in a stack based language like Myra, is to transfer parameters to functions (and back). In MPS we have to manage anyway. If we transfer only one argument, we can use accu. For transferring more arguments, we have reserved some more variables, called arg0 to arg5. We load these variables, and jump to the function. Then the function can find its arguments there. The syntax for this is as follows:
Arrays are handled with a so called index register.
The index register is a variable. like accu,
called index. This register is set by an
MPS instruction with a pair of brackets. If there
is a variable name between the brackets, the value
of that variable is loaded into the index register.
An expression, i.e. a sequence of MPS instructions
is not allowed between the brackets. However the
content between the brackets may be empty. In that
case, the value of accu is loaded into
index. In this way the value of index
can be computed as an expression. There is, however,
a weakness with this, that we will return to.
The instruction, that imediately follows the
bracketed instruction, now modifies its adress
by adding the value of index. Hence, one can
point oneself into an array.
For getting the i:th entry in the array arr, we
could write
'L0' ("Local zero") is an aliasname for the first memory cell in the cog memory of the current cog. Hence L0 is also the name of an array that fills out the entire cog memory. With this, here are two ways of getting the i:th entry of the array arr:
The propeller computer doesn't have any analog inputs.
Its cousin, the Basic Stamp, handles analog inputs by
means of RC circuits, which convert analog voltages to
times. Times can be measured accurately with both
Basic Stamps an Propellers. So I guess, one can use
a similar design for the Propeller. However, one needs
one pin for each analog input, and probably one more
input to discharge, or precharge the RC-circuits.
So my alternative has been to use separate A/D converters.
My favorite is Microchip's 3208. It converts 8 channels
to 12 bit digital data. It has a serial interface. You
need to handle a chip select pin, a clock pin, one data-in-pin
and one data-out-pin. So for 8 analog channels, you need
4 pins. If 8 channels aren't enough, you can get 16
channels for 5 pins etc..
You send a conversion order to the converter's data-in-pin.
This order announces which channel should be converted. Then
you can clock your digital result from the converter's
data-out-pin. This is best written in assembler. The
interface is like this:
D/A converters are available, but they waste a number
of output pins for a single analog output channel. The
normal way of creating analog outputs is therefore by
pulse width modulation (PWM). There is a pulse repetition
frequency (prf) for the pulses. Then the pulse is on
for some percentage of the total period, and off for the
the rest. Afterwards, you can remove the pulses with
a RC-circuit, and just keep the average, which will
be an analog signal. But if the output goes to, for instance,
a motor, the motor can filter out the pulses with its
mechanical inertia. In that case, you may need to
raise the prf to well over 10 kHz. Otherwise you will
hear the prf from the motor. The time constant of the
RC circuit or the motor will limit the bandwidth of
the output. You need a margin between the desired bandwidth
and the prf, but for mechanical systems, this is usually
not a problem.
Here's an example. We want to control two
motors, and we want to drive them in both directions.
Assume that we connect motor 1 between pins 0 and 1,
and motor 2 between pins 2 and 3. We can drive the motors
with codes like this:
We now want to connect power drivers to the Propeller output pins, and connect the motors between the outputs of the power drivers. This structure is sometimes called an H-bridge.
Here's a solution based on an integrated circuit
called TDA2050:
TDA2050 is marketed as an audio amplifier, but it
is actually a universal operational amplifier, with
capability to deliver high power. It amplifies the
difference between the + and --inputs with a gain
of one million or so. Then we can reduce the gain
with a feedback through the resistors R2
and R1. We would like to create a gain
of about four, to increase the 3.3v span of the Propeller
to an output span of 12v. But with the TDA2050 you can't
reduce the gain that much without getting oscillations.
A gain of about 40 is minimum. So we live with that, and
reduce the input signal first with RI and
RT.
RC and CC make up
a capacitive load, also to enhance stability. The
capacitor CV shall eliminate spurious feedback
between the different steps of the amplifier over the
12v supply rail. It should be placed physically close to
the circuit.
The capacitor CT together with (essentially)
RT helps remove the pulses from the PWM-system,
and just keep an average.
We have time contant of appoximately 500μs, which should
filter out the pulses with a period of 100μs pretty well.
At the same time, such a time constant shouldn't cause
any stability problems for the mechanical system.
The question is still, if the capacitor CT should
be there. With the
capacitor in place we avoid hearing a tone from the motor.
At least cats may appreciate that. Perhaps we save the
motor from some wear. And we avoid disturbing the electrical
environment around our device. The penalty for all this
is heat dissipation.
Without the capacitor, we drive the TDA2050 in full
saturation all the time. The output transistors are either
fully on or fully off, and hence consume no power.
However, such operational amplifiers are not so fast,
so they spend some time in the transition phase, so
some power is still dissipated.
With reasonably small motors, both solutions should work,
but you need a heatsink for your amplifier, and free
air flow round the heatsink. TDA2050 and many other
modern circuits have the advantage that the cooling
tab is connected to ground, so you can without risk
screw all the circuits firmly to the same heatsink.
An alternative for even higher power is to use
a IR2103 circuit, which is a FET-transistor driver from
International Rectifier. Here's a quick sketch of how
it is connected.
To drive the upper FET to open, you need a gate voltage
a few volts over the drain. So to bring the output all
the way up to the E volt rail, you need more voltage than
E somewhere. In fact, the upper FET is
driven by a little circuit, that sits between the
terminals O and X. X should be 12 volts higher than O.
The question is, how we get that voltage there.
A hint is to think about a so called charge pump, as
in this figure:
C1 and D1 will clamp the voltage
at P never to be under the rail voltage. (12 volts).
Consequently
the voltage at P will vary between 12 volts and 17 volts
(as 5 volts is the amplitude of the oscillator).
D2 and and C2 will rectify and smoothen this
signal to give a voltage 5 volts over the 12 volt rail.
This works fine, but it probably doesn't work
unmodified, if we are going to pump a voltage over
a rail, that moves all the time. That is the situation
we have with our IR2103 circuit.
The conventional way to provide the X voltage
is to use the systems output signal as charge pump
oscillator. Here's the way it is done.
When the output O is all the way down to zero, the
capacitor C will be charged through the diode D.
Then, when O flies up, C will fly up with it. It
will gradually discharge, though, so O will have to
go down to zero every now and then. This is a constraint
for the programming of the device. With the PWM, the
pulse width must never be 100% of the full period.
This is OK, but it may be easy to forget, when you
make your programs.
Hence it would be nice if we could design a system
with an independent oscillator for the charge pumping.
It's open for invention. With a little luck, we could
maybe use one oscillator for all IR2103-circuits around.
IR2103 is a purely digital system. The input is truly
digital, so there is no way, that you could smooth
away the pulses with capacitors. The circuit is fast,
so the time in transition between the two saturated
states is short, and the switching is also synchronized,
so that both FET:s are on simultaneously only for very
short periods.
The most dramatic property of this circuit is, however,
the bound on the voltage E. E can be as high as 600 volts!
With 600 volts supply, these 600 volts penetrate right into
the IR2103. It's a tiny 8 pin dip-circuit, or an even
smaller surface-mount version, but it is said, it can
take it. Beware however! Your circuit board design might
not tolerate 600 volts. Maybe you dare to work with
200 volts. You easily find FETs that can handle 7 A (with
some cooling). Then you can control 1.4 kW power from
a 3.3v output from a Propeller pin. That's pretty awesome.
RS232 is a very old protocol for communication. It has
changed name now to EIA232, but very few people seem to
have noticed that. It is a standard for serial communication
over only one line (per direction), where the bits
are sent after one another in time. It was fairly specific
about voltage levels, and it had several options for flow
control, so that a receiver could inhibit data sending,
when it was too busy. Surprisingly for a one line concept,
the protocol was standardized for a 25 pin D-sub connector.
The standard was also pretty open, when it came to
different parameters like number of bits sent, parity,
sign on start bit, communications speed etc..
Luckily a certain set of parameter values have become
de facto standard, so now we communicate with
a large number of devices and modules, with almost the
same communication parameters. At the same time, the
voltage level issue has become less important. Most
devices use 0 volts as 0, and 5 or 3.3 volts as 1. But
if you buy a true standard RS232 device, and connect it
to a Propeller computer, the Propeller will be destroyed,
as the voltages are too high.
If I can, I always avoid flow control, as it complicates
things (more wires, more Propeller pins used etc.) Most
devices can be configured so that flow control is not used.
The standard allows parity control, but the most common
choice is "no parity", which simplifies your software.
You don't need to set parity bits, and you don't have
to check them (you never have to check them, even if
they are there).
The most common communications speed nowadays is perhaps
115200 bits per second, which is about what today's processors
can manage. But the default setting is often 9600.
Conventionally these bit rates are called baud rate,
and the unit is baud, and not bits per second. So we
have 115200 baud etc. (The reason for this is that the
term bits/second is reserved for the effective speed
of a line with disturbances. If there are disturbances,
you have to check and resend, and you loose speed on
that.)
Some devices can tell you which baud rate they are set to,
but if you don't know the baud rate, you can neither put the
question, nor interpret the answer. Most devices have
some sort of reset button, which restore default parameters,
like 9600 baud. For the eventuality that you
have abandoned the default parameters, and happen to
press that button, you have to have a little reconfiguration
program up your sleeve. This is a maintenance problem
for your system.
Then, the normal standard is that the line is 1 when it
is idle. A new transmission is announced by the line
falling to 0. This is the start pulse. Then you wait
one and a half of TS, where TS is
1/(baud rate).
Then you can clock in yor data every TS, least
significant bit first. The standard is that one such package
of bits is 8 bits long. Then follows an optional parity bit
and one or more stop bits. When it's all over the line
goes up to 1 again. It is not terribly complicated to
build and read such a pulse train, if you have computer.
To do it with conventional hardware was probably quite
complicated.
On the Assembler Resource File there are two programs
called send and receive.
They are called lik this:
I2C is a bus standard invented by Philips. I2C means
Inter Integrated Circuits, and is thus a bus for communication
between individual circuits in an electronic system. It is
a master/slave concept, where a master controls the
trafic on the bus. In the general case, there can be more
than one master on the bus, which calls for an arbitration
policy to divide the bus between the masters. However, in
most cases, there is only one master.
The bus has two wires, one data line and one clock line.
The slaves and the masters are connected with open collectors.
Consequently, when a device tries to send a '1', the collector
is open, and thus, the impedance is infinite. The voltages
are pulled up with a set of pull up resistors, which belong
to the bus.
The role of master in our application, will most likely be
played by a Propeller computer. The Propeller doesn't have open
collector outputs, but we can mimic this by using the
direction register. We permanently set the output to the
pin to zero. When we direct the pin to be an output pin,
zero with low impedance will be visible on the pin. When
we direct the pin to be an input pin, a high impedance is
visible, and the pull up resistor of the bus will pull
the line high. When we expect inputs from the line, we
should set the pin to 1, i.e. high impedance. When we send
data to the device, we should expect an acknowledge signal from
the receiver. At that moment the master should have high
impedance, but there is no harm if we forget that (except
that we mislead ourselves to beleive that the acknowledge
signal has come).
To send data to a slave, the master will send a start
pattern, followed by an address to the receiving device,
and a write bit. After this the master will send the data
on the bus. Data (and address) are clocked, in the sense,
that the slave should trust what's on the data line, when
the clock line is high. The slave will finally respond
with an acknowledge signal, and to receive this, the
slave has to turn its datapin into an input pin.
The protocol for receiving data to the slave is slightly
more complicated.
We will return to more details about the I2C bus in
the radio application. There you
will use assembler routines on the assembler resource file
to handle the I2C bus. Formally, when you use
the I2C bus for comercial use, you should pay
a license fee to Philips.
USB, Universal Serial Bus, is an almost indispensible
protocol, as it is almost the only way to talk
to a PC nowadays (specially a laptop). It is a complicated
protocol though. It is a master slave concept, where the
master is called host, and the slave is called device.
Writing a host software for USB is certainly not easy,
but we get it for free, when we by a PC. But writing
device software is not easy either.
Fortunately, there is a Scottish company, called FTDI,
that has made the jobs for us, in the form of integrated
circuits, and hybrid modules. Most of them wrap an
RS232 interface into a USB interface.
In the other end,
in the PC, the RS232 reappears again as something called
Virtual Com Ports. They behave as good old RS232 ports,
but there is an arbitration of port numbers, that can cause
the programmer some troubles, specially when the port number
exceeds 10. As a Java programmer, I regret that there
is no USB object available (it has been anounced), so I
have to use Java Native Interface and C code.
An alternative to Virtual Com Ports, which is said to be
faster, is called D2XX, which is a series of DLL:s
to access the data.
FTDI also produces a module, where 8 bits a time are
loaded in parallell. This allows a rate of at least
1 MByte/s, where the limit seems to be on the PC side,
at least if one uses the Virtual Com Port concept.
The propstick and propclips, which are used for programming
the Propeller, also contain FTDI circuits. They are
probably RS232 circuits, with an extra ability to handle
a Propeller reset signal.
The standard IEEE 802.15.4 describes a wireless
communication protocol for the 2.4 GHz band, with
checks, resending and acknowledgement, so that data
are safely brought from one point to another. It more
or less acts as an RS232 connection, where the cable
is replaced with wireless communication.
On top of this standard, there has been built a higher
level standard for communication in networks, called
Zigbee. That's why I call IEEE 802.15.4 "proto-Zigbee".
Zigbee is an advanced standard for building networks, where
units beyond range can talk to one another by relaying
over intermediate units. Important is also means for
letting units go down to sleep mode, from which they
can be wakened up on command. This is intended for battery
driven data logging equipment and the like.
All these advanced possibilities are certainly usefull,
but I have found much of it difficult to understand in
detail, and handle. That's why I have stayed with
"proto-Zigbee". Parallax have gone through different
communication concepts and have chosen "proto-Zigbee" to
be very easy to work with. Units are manufactured by
Digi inc. previously Maxstream. But buying from them
can be a little confusing, because it's hard to tell
if you are buying Zigbee or "proto-Zigbee". Proto Zigbee
is mostly called "IEEE 802.15.4 OEM-modules".
Proto Zigbee units are gathered in networks call PAN:s,
Personal Area Networks, and each such PAN has its one
PAN-ID. In the same place, there can be another PAN
with another PAN-ID, and in that case, they are isolated
from each other. Furthermore you can set which 2.4GHz channel
you work on. You can set the role of the unit in the network.
The simplest role is as an end-point. If your units are
end-points, then they can communicate point to point with
one another. You can also set the baud rate for
communication between the unit and a computer. This is
about what you need to configure in the first place.
When you buy units, they are configured to the same
default PAN-ID, the same default 2.4GHz channel, and
they are all set to be end-points. Then you can just
connect your units to power and to two Propeller pins,
and then you are ready to communicate between computers.
What you send from one computer to one unit, arrives
to the other computer via its unit. That's what we
call cable replacement.
What you might like to reconfigure is the baud rate, and
maybe move from default 9600 baud to 115200.
Once I bought three units, and later two more. But in
the meanwhile, for some reason, that I have forgotten,
I had changed the PAN-ID for my old units. So I had to
spend some time reconfiguring. It took some time, but
when I finally succeded, I had done it exactly as I had
thought it would be done. So now all my units can talk
to another. Among the object files there will be a simple
program that may be helpful for reconfiguring units.
Say that you want to command a robot wirelessly. At the
same time you want to upload sensor data to a PC, (which
can also talk to Zigbee, it is RS232, but nowadays, you
have to take the way over USB). There may be many ways
to solve that. The simplest way, but maybe not the cheapest,
is to have two PAN:s for the two tasks, command
and data uploading. A unit can not belong to two PAN:s
so you must have two units onboard the robot. This is
a simple example of how you can use the simpler concepts
of "proto-Zigbee".
Beside the simplicity ("point to point communication direct
out of the box") proto-Zigbee has another important
advantage: The units are cheap. You get them for something
like 30 or 40 dollars.
Bluetooth is a complicated standard with a stack of
different profiles, for transmission of images, music
etc. One such profile is SPP, Serial Port Profile, which
is "RS232" in both ends. Like Zigbee, Bluetooth has checks,
acknowledges and resending to guarantee safe arrival of
data. Parallax marketed devices called Embedded Blue,
which you accessed through a RS232 interface. I found
them easy to work with, but they are now out of production,
and their replacers seem more complicated to interface.
The main drawback of bluetooth is however that the
modules are expensive, about four times as expensive
as the proto Zigbee modules.
Parallax markets some simple transmitters and receivers. They are truly cable replacements. When you set the imput to the transmitter high, the output of the receiver goes high. So you build your own RS232 link with that. The RF modules don't contribute with any security checking. I found that the transmission failed now and then. But I never found out how to pair transmitters and recievers in this system. There are new modules now which combine transmitter and receiver, and this could make it easier.
These are Myra Objects files, named "Name.myo", which contain functions, that are useful to handle things like sensors, displays, special number types and the like. These functions are used by a main program named "Name.myr".
This object file, "Num.myo", contains some numerical functions, They are far fewer than what is available in Spin. I have simply made what I have needed. The file contains the following functions:
This object uses Parallax' Intelligent Alphanumerical Display. It is somewhat expensive, but relative to a standard alphanumerical LCD display with Hitachi's interface HD44780, it does a good job, and it let's you interface the display with only one pin. The object has a field, which contains the number of that one pin. Here are the functions:
The code in the file "Graf.myo" uses a graphical LCD display,
which is controlled in a somewhat standard way. Such a
control consumes pretty many Propeller pins, so i chose
to give the display its own Propeller processor, hereafter
called the display computer. Another
computer, hereafter called the application computer,
interfaces with this with a serial link,
that sends a pixel pattern to the display computer.
The display is monochrome (white on blue) and has a size
of 64 by 128 pixels. We put the pixel pattern in 32 bit
words (long words),
so we need 2 for each columm, or 256 long words in total.
These 256 words are sent one word at time (32 bits) with
a RS232 like protocol with a bit rate of 1 MHz. There
are assembler routines for sending and receiveing according
to this protocol.The pixel pattern has to be declared
in both computers under the name gr. The
declaration is written as
This is a very simple object, that handles a three color RGB-LED. The object file has only one function:
This is a relatively simple object for handling a standard
ps2 keyboard. The object normally only receives data from
the keyboard, but there is a historical circumstance, that
forces us also to send to the keyboard. The creators of
ps2 decided that the PC and the keyboard should synchronize
the following way. The keyboard would repeatedly send a
character, but with the wrong parity. Synchronization would
be established when the PC complained about this. So we
have to send such a complain message.
The user has to call the function key.init. He
also has to declare two global variables key and
keyp. Keys are scanned with key.scan. This
program has to be alert at all times, as one never knows
when the operator hits a key. Thus, it has to have its own
process. The keycode for the key pressed the latest,
will be deposited in the variable key. Keycodes are seemingly
arbitrary numbers assigned at the dawn of computing.
The A-key has its number, as well as the shift key. The
latter would enable us to distinguish 'a' from 'A', but
we don't care about that here. When a key is released,
key will assume a value called 'break'.
Now, we can call the function key.ascii. It will
translate the key code to the corresponding ascii code
and push that on the stack.
As long as we keep a key pressed, we will thus find its
ascii code on the stack. If the key is released, the
keycode will be 'break' and then key.ascii will
return 0, which is the ascii code for the null character.
Download key.myo
In higher mathematics, a complex number is defined as a pair, (x,y), of real numbers x and y together with the following addition and multiplication rules:
(x1,y1)+(x2,y2) = (x1+x2,y1+y2)Then, there is the style, to write (x,y) as x+i·y. We think of x + i·0 as just the real number x. We call 0 + i·y just i·y, and 0 + i·1 just i. Now, if we take (0,1)*(0.1) alias i·i alias i2 and use the above multiplication rule, we find that i2 = -1.
(x1,y1)*(x2,y2) = (x1*x2 - y1*y2, ,x1*y2 + y1*x2)
This is really an untested group of functions,
so right now, I don't give any closer details.
Floating point representation means that a number is
written as m·2exp, where exp is chosen
so that m, called the mantissa, is a number between
a half and one. IEEE has standardized floating point
numbers, so that the mantissa and the exponent are packed
into one 32 bit number. That leaves 23 bits for the mantissa,
which is somewhat little, so now double precision
floating point numbers are popular. To keep precsion
and make things simpler, we store exponent and mantissa
in separate words. They are put on the stack with the
exponent highest. With this double pushing and poping,
floating point operations can be made as normal fix point
or integer operations.
Download float.myo (untested)
This object handles an accelerometer, which delivers
its results as pulse widths.
Accelerometers never measure
acceleration only. They measure the sum of acceleration
and gravity, i.e. they measure one component of this
vector. Hence, if the accelerometer isn't moved very
vividly, it can be used to measure tilt. When the
accelerometer is tilted, a no longer horizontal accelerometer
will measure a component of the vertical gravity vector.
I used this to make a simple joystick. It has a vertical
plastic tube with a horizontal accelerometer mounted between
some foam rubber pads. At he bottom of the tube, there
is a heavy plane brass foot. If you leave the joystick
on the table, it will show nothing (except some bias), but
when you grab and tilt it, it will show the tilt angles.
The object contains a censor function
which eliminates sudden spikes in the signals. Biases
can be eliminated by calling the acc.cal function
in a situation, when the accelerometer shouldn't show
any acceration (the joystick resting on the table).
The signal is scaled as a number between 0 and 4096,
with 2048 representing zero signal.
Odometry comes from a Greek word for "way" or "road",
and it is the art of determining travelled distance.
To do it we have a striped strip and two optical sensors.
We can put that strip around a wheel of a robot, to measure
its travelled distance, or we can glue the strip on the
road itself, that we travel along. Here's how it works:
We have two pictures of a strip and two optical sensors
(the circles) in both pictures. The strips move as
indicated. In both these cases, the right hand detector
switches from white to gray, which indicates a motion. But
which motion is it, the upper or lower case? We can tell
by looking at the left hand sensor. In the upper case
it is white, in the lower it is grey. So now we can
detect a motion "with sign", and we can accumulate the
incremental motions to a distance travelled.
The Odo object odo.myo handles two strips and two sets
of optical sensors, to determine movements in the plane.
These objects control IEEE 802.15.4 modules. Read about them here. The basic object is zig.myo, which contains the following functions.
This objects controls a little chip, AR1010, which contains a complete FM radio in a chip, 4 x 4 mm in size. The chip is controlled from a computer, and the object contains functions for that. We will however describe all the details about this in the FM radio application.
The linear camera is a camera that produces an image, 1 pixel high and 512 pixels wide. We will describe the functions of this object, while we describe the Linear Camera Application.
Once I made a box containing a Propeller Computer,
A/D converters,
a bluetooth modem, an alphanumeric display, an SD card
reader and three power amplifiers. Unfortunately the
box became full of wires, and it wasn't easy to keep all
these wires intact.
So here's a more modest project. It is a smaller box
which contains a Propeller computer together with the
normal EEPROM and the normal 5 MHz crystal, an A/D
converter, an RGB led for debugging etc., and circuits
for voltage supply. Here's what the circuit board looks
like:
Here's how the circuit board is connected to external
connectors. I have always liked to use 9 pin D-sub
connectors.
On this side there is a reset button, and a switch which
disconnects the reset input from the PC connection. This
is necessary if you want to send data from the PC, without
resetting the computer every time. In the middle there
is a programming connector. I only use 4 of the connection
points there, and they go directly to the pins of a
Prop Stick device, that I have built into a D-sub connector
with housing. The right hand connector goes to 7 of
the 8 analog inputs of the A/D converter. The remaining
two pins of the D-sub connector go to ground and 5 volts
supply. To the right on the top of the box, you can
see the end of a plastic screw, that acts as a ray guide
from the RGB-led.
On this side there are connections to 14 of the universal
digital I/O pins of the Propeller. The left connector
also has a ground connection and a connection to 3.3 volts.
The right connector instead provides ground and 5 volts.
Further right there is a small handmade connector for
connection of the alphanumerical or graphical displays, which
are housed in separate boxes. Rightmost is the supply input
which receives an unstabilized voltage of 7.2 to 12 volts.
Bluetooth has been replaced with Zigbee, but the Zigbee
modem is now placed on a D-sub connector. The power amplifiers
are built into a separate box of the same shape as the
computer. As mentioned, the two displays, the alphanumeric
and the graphic, are placed in separate boxes.
Everybody who has a Propeller, or any other microprocessor,
wants to make his own robot. Here is mine:
It has two relatively strong 12 volt motors with gear
boxes (1:21), two strong rubber wheels and a caster wheel
in front, and a led battery, that I charge with a CTEK
led battery charger.
It has three power amplifiers. They
are built with discrete components, but they don't work
at very high PWM frequencies, as they get hot. It's OK
for this application to work with low frequencies, but I
don't recommend these amplifiers. The alternatives mentioned
previously are better. Anyway, you can see the coolers for
the transistors as three aluminum bars on the circuit
board.
The computer is a Spin Stamp, which is a surface mounted
Propeller mounted on a hybrid circuit, which also contains
a crystal (10MHz unfortunately, perhaps), voltage stabilizers,
and an EEPROM. On the card, there is also a 3208 A/D converter,
and two voltage stabilizers. One of them reduces the 12 volt
battery power, not to exhaust the stabilizers on the
Spin Stamp. The other provides 3.3 volts for some sensors.
Unfortunately, the Spin Stamp doesn't make its own
3.3v voltage available externally.
In the front, there is a Lego block, on which I can put
sensors and other stuff, mounted on small Lego blocks.
In the picture you can see a compass, mounted on a high
Lego tower, to keep it away from the magnets of the
motors, an infrared distance sensor, and a "proto Zigbee"
unit. The whole robot is controlled over proto
Zigbee from a remote control. I also have an infrared receiver,
adapted to an infrared LED modulated to 38 kHz with a
Propeller computer. So, there is an infrared control for
the robot, but it requires that you direct the LED
pretty accurately towards the receiver.
One thing I don't have is odometry sensors on the
wheels. I once had that. You can read more about
that in the description of the
milling machine.
I am a specialist in autonomous control of unmanned
vehicles, but as this is a hobby, I have made a robot
completely free from all autonomy. It is a remotely controlled
vehicle, basta!. But I did some experiments with a
software, that would drive the robot to the nearest object.
But that is not ready yet.
Otherwise the robot is pretty robust, fast and strong,
though it can't compete with modern radio controlled model
cars with brushless motors.
Here's the software
RobotZ.myr
The first thing you notice is a "10 MHz" tag. That's
because the Spin Stamp has a 10 MHz crystal, while the
standard is rather 5 MHz. The Spin code, that is generated
from the Myra code has to know that. So the Myra code
uses the "10MHz" tag to modify the Spin code.
The process input receives the input from the Zigbee
unit. It's a single number comm whose bits
signal which buttons on the remote control are pressed.
The process motor's main program decodes comm
and sets two motor masks and a velocity. The motor masks
are each of two bits, and determine whether the motor
will run forward (01), backward (10) or be stopped (00).
The velocity is set lower if we turn the robot left
or right, and higher when we drive forward and backward.
This facilitates the driving. In the main program, there
is also a call to a heading control function, heading,
which drives the robot to
a given heading, as measured by the compass.
The motor is finally controlled by the function
drive. It sets the PWM duty cycle according to
v (16 is full speed, 100% duty cycle). The motor masks
merged together with shifts and an or instruction are
sent to the amplifiers during the on period, and 0 is
sent during the off period. The total cycle time of
the PWM signal is 80000000/100 ticks, which gives a
repetition frequency of 100 Hz.Fortunately we don't
hear any 100 Hz tone from the motors, probably
because of the inertia of the wheels.
The compass process catches the signal from the
compass, which amounts to measuring the length of a
pulse, that the compass electronics sends regularly.
The scaling i 100μs/degree.
Finally a word about the heading control function
heading. The control error is basically
psi - psiC where psiC lies on
the stack, and the measured heading is a global
variable. But just subtracting angles is a little
dangerous. 1 degree and 359 degrees are only 2 degrees
away from each other, but the subtraction would
give 358 degrees. So we have to "normalize" the result to
within ±180 degrees, by adding or subracting an
appropriate multiple of 360 degrees. Then we control
the motor mask with the sign of the control error,
and we control the velocity with a ramp function of
the absolute error. (We use the || assembler function).
The ramp function is difficult to trim, due to the
friction of the motors. The motors don't run at all
if the signal to them is too low. That is also why
I haven't succeded yet with my "drive to the nearest
object function".
The remote control is a box with buttons, a Propeller
computer with its crystal and EEPROM and a proto Zigbee
unit. It is driven with a 9 volt battery. At the back
side there is an on/off switch and a programming connector.
There are 7 buttons, which is less than 8, so the whole
button pattern can be sent in one byte. For that, each
button is connected to its own Propeller input pin.
Download remote.myr.
Download zig.myo.
This picture shows the wheel steamer
I built it together with my son, when we were in the country
with limited technical resources. You can see, perhaps,
that we are not exactly any expert naval architects.
The ship is built in fur wood, and not plastic foam, so
it is rather heavy. To keep it afloat, it is not a katamaran,
but a pentamaran.
We found a small DC-motor as a main motor. To keep it away
from the water, you can have a propeller with a long shaft,
or a belt drive. But a wheel steamer was also a way to
bring the motor up. We have only one drive wheel, placed
in the middle of the ship, and built around a square hub.
Control is made with a rudder, which is driven with a little
gearbox kit (for those who want to learn what a gear box is).
It gives a slow enough movement to make the rudder
controllable.
We also found a waterproof lunch box, in which we mounted
the electronics, which consists of a proto Zigbee module,
a Spin Stamp module, and a TDA2050 based power driver.
I don't publish any software here, as it is essentially the
same as the software for the robot, although we have one
drive wheel and a rudder here, while the robot has two
drive wheels and a passive caster wheel.
I am the happy owner of the milling and drilling machine
you can see here:
It has a coordinate table with two cranks to move the
workpiece in two orthogonal directions. I have mounted
striped tape on the two cranks, and then I have mounted
a pair of reflex detectors nearby. The reflexdetectors
are from Polulu and intended for odometry. The following
pictures show the arrangement more in detail:
Then i have coupled the reflexdetector to a box containing
a Propeller computer and an alphanumeric display.
The software consist of odometry computations based on
the odometry object as described above. With the stripes
I have made, the resolution in table movement becomes
1/14 mm, but I have recalculated this, so that I get
resolution of 0.1 mm. On the two first lines of the
display, I show the coordinate table positions in
tenths of milimeter. All coordinates refer to a reference
point, that you set by going there, and then pressing
the right hand red button.
The third line shows the distance to the referenc position.
This is computed with Pythagoras' theorem and the
num.itsqrt function for computing the square root.
This function is of course usefull when you want to mill
out a big circular hole. If the hole should have the radius
R, and your milling tool has the radius r, you should move
out to the distance R-r everywhere, but no longer.
Another thing that you maybe would like to mill, is an
oblique straight line. You could define such a line by
moving to two points on the line, and press the two
red buttons. Then the display could show the distance
to the line. This is a function I haven't made, but it
would be possible.
The equipment is good for milling, but also for drilling where
you could place your holes with good precision. In all,
this is a very nice little equipment, that raises the value
of the machine considerably. Of course, from this point one
can go on and add motors, and make a numerically controlled
machine.
The following picture gives a little instruction for how
to make a hole for a D-sub connector.
Download Odo.myr
Download Odo.myo
Download Num.myo
FTDI manufactures some USB modules, where data to be
transfered are loaded in parallell, one byte at a time,
rather than serially. On the PC side, data appear in
a VCP port, which makes data available one byte at a
time, or over D2XX. With this type of module, data can
be transferred faster than over RS232 emulators.
The module can handle the situation, that the USB bus
is busy, by stacking up non sent data on a stack
(a FIFO stack, First In First Out).
I have used this module to build a data logging system,
with data display on the PC side, a so called USB
oscilloscope. It works in two modes, a digital mode
where one or more digital signals (being either 1 or 0)
are transferred, and an analog mode where one or more
analog signals are transferred. The advantage of the
digital mode, is that it can sample data faster.
In digital mode, I can transfer 300000 bytes/second.
The byte can contain up to 8 digital signals, which
are read directly from the computer input pins. I have
preferred, though, to use the 8:th bit for time marking.
The last bit flips every 1ms with high precision. This
allows me to create a very exact time scale on the PC
side. Furthermore, I have chosen to pull out only
two digital signal wires from the computer, but that
is easy to change.
In analog mode, I have two operational amplifiers,
to scale the inputs, and give them a symmetric range
around zero volts, and then I use two channels of
an 8 channel 12 bit A/D-converter (Microchip 3208).
I make the A/D conversion, and the packing of data into
bytes in one processor. A second processor handles the
USB device. An analog data takes up 12 bits, and I pack
that into two bytes. That leaves 4 bits free. I use
the last bit for time marking, as in the digital mode.
The remaining 3 bits contain the fix code of 101. The
PC uses this, to recognize the least significant byte,
so that it doesn't loose phase, and mixes the two bytes
up. This describes how I handle 1 analog signal and
two bytes, and that is what I do right now. With two
analog signals, I have to reduce the sampling rate to
one half. In analog mode, I transfer data at a rate
of 66.666 kHz, but not all these data are new analog
samples.
For both modes, there is a red trig button. Its signal
is also pulled out, to admit electronic triggering.
When the trig signal comes, 50000 data are transferred
to the PC. Then the signals can be analyzed with
an interactive plot program. There are different programs
in digital and analog mode. In the digital mode, one
sample covers 0.168 seconds of real time, in analog
0.75 seconds.
The fundamental principle here is one time triggering.
You get one set of 50000 samples, to analyze afterwards,
unlike a conventional oscilloscope, where you retrigger
all the time, which gives you problems, when you have
non periodic signals. Here, the problem is to trigger
at the right moment.
The signals enter the oscilloscope through a 9 pin
D-sub connector. At these frequencies there is no nead
for high quality coaxial connectors. There are two
probes, one for digital mode and one for analog mode.
The software distinguishes the modes through a jumper
wire in the digital probe. Then you have to select
the corresponding software on the PC side. On the
Propeller side, there is only one program, but if you
change probe, you have to restart the device.
Here is a drawing of the electronics:
You can see the standard set up of a Propeller with
crystal, EEPROM and prop plug. In the upper left
corner you see the connection of the 3208 A/D converter.
As it is connected to other pins than the normal
you have to use the parametrized initialization
init_analogp. The lower left part of the drawing,
is the analog part of the system. We come back to
that in a while.
In the upper right corner, there is the connection
of the USB module, with the USB connector depicted as
a gray square. The module is such, that you have to
connect ground and power to a number of places externally.
With the way it is done here, the whole apparatus will be
fed from the USB output of the PC. There is also
a 3.3 volt input, to make sure that the module outputs
are constrained to 3.3 volts, so that the Propeller
inputs are not destroyed. The 8 data pins are
connected directly to the Propeller outputs 0 to 7.
Then there are four control pins, which go to the Propeller,
The Propeller software will handle these pins
apropriately.
Download fifo.myr.
This is a simple program, that, depending on mode, uses
the two assembler functions fifolog or fifoalog.
These refer over the stack to the variable tstamp,
which is the time marking signal, or x which is
the word to transfer. It would seem natural that the
references here would be through adresses #tstamp and
#x, but these variables are variables in the common
variables, and such variables are always transferred via
adresses, that are used in rdlong instruction. It
would be neater, if we could write #tstamp and #x, but it
would be difficult to use that in the assembler functions.
Otherwise, fifolog and fifoalog are simple 50000 times
loops.
Here are some hints about the analog circuitry at the
lower left of the diagram. To create a symmetric range
of the analog input, we need a negative reference voltage.
We use a chargepump, MAXIM 660 for that. The operational
amplifiers are of type OP90. They can be driven with
a single 5 volts supply, and they produce output "rail to rail"
i.e. from 0 to 5 volts. The trim potentiometer should be
trimmed, with the input disconnected or grounded, so that
the output i exactly 2.5 volts. This is the middle of the
A/D converter's range. Hence the ouput will be 2048. This
number is then subtracted on the PC side, so we get zero
output for zero voltage. Now, nominally, the trim pot value
should be 390/2 = 195 kΩ. The all over impedance at the
input is then 390/3 = 130kΩ. So if you let R be 130 kΩ,
you get a gain of 1, and a range of ±2.5 volts. You probably
prefer a range of ±5 volts, so you chose 260 kΩ. This
is also the input impedance of the device. It is not so
impressive, but probably good enough for most purposes.
If you trust the precision of the 390kΩ resistor and R,
you trim the bias and the gain at the same time with
the trim potentionmeter.
Information and download of the PC java programs is
found here.
The data logger makes the same job as the USB oscilloscope,
but without a PC. Instead data are shown on a graphical
display. The device is controlled from a standard
PS/2 keyboard.
Data are stored in a, 4096 words wide, data area in the
common memory, by means of an assembler routine logg.
Data are displayed with a program in the graf object (graf.myo)
called gr.loggshow. It makes a pixelmap in the standard
way for the graphical display in an area in common memory
called gr, 256 words wide. This is then transferred
to the graphical display for final drawing. gr.loggshow
is controlled by 4 variables. The scaling in the horizontal
and vertical direction is made as shifting an integer number
of steps according to the variables tshift and
fshift. The curves are also moved in both directions,
so that the left hand edge corresponds to a time tstart,
and that the low edge corresponds to a value flow.
These parameters are controlled from the keyboard.
The keys used, have the following meanings:
When you look at a star with a telescope, the telescope
will rotate with the rotation of the earth. So to get a
stable picture, you have to rotate the telescope backwards,
relative to the earth. Amateur telescopes usually have
something called an equatorial gimbal system, which is
a mechanical arrangement, such that one of the degrees
of freedom falls along the rotation axis of the earth.
There is a knob there, and by rotating that slowly, you
can counteract the rotation of the earth. Equatorial
gimbal systems are a bit clumsy, though. They require a
big counterweight, which makes the whole telescope heavy,
and the telescope tube sometimes makes a pretty awkward
journey, when you direct it against different parts of
the sky.
So the whole project here is about making a virtual
equatorial gimbal system. The starting point then,
is what I would call a "fork gimbal system". The telescope
tube is hung up between the arms of a fork, and can
be moved up and down there. This is called the elevation
movement. Then the fork can be rotated around a vertical
axis. This is called the azimuth movement. The problem
now is to distribute the counterrotation against the rotation
of the earth to the two axes, azimuth and elevation.
Here is a picture of the fork gimbal arrangement:
First of all, the rotation of the earth is directed towards
north, and it is tilted up from the horisontal plane with
the latitude of the place. So on the equator, the rotation
axis is lying down, and at the North Pole it stands right up,
so the direction towards north doesn't matter, which is lucky,
because there is no notion of north at the North Pole. Then,
there is a more tricky mathematical problem. The azimuth angle
and the elevation angle are so called Euler angles. There is
a third Euler angle around the axis of the tube. So what we
want to control is time derivatives of the Euler angles,
and what we have is the earth rotation vector. But the
relation between rotation vectors and Euler angle derivatives is
a little bit involved, and it is easy to make mistakes there.
But I provide you with a Java program that solves the problem.
The result shall be a function with three inputs:
1/24·1/2/100 radians = 2.08333·10-4This value appears in the Java program.
This radio is based on a chip from Airoha called AR1010.
In a volume of 4x4x1 mm it contains a complete FM radio
with stereo decoder, sound outputs and two different
computer interfaces. I don't know if this is a so called
Software Defined Radio, i.e. a completely computerized
radio, or if there are any special circuits. Nevertheless,
you don't see any traditional things like coils or capacitors.
My work is based on a small breakout board, which includes
a crystal. It is sold by Spark Fun Electronics. Apparently
Telefunken has made circuits of this type, and there is
another chip called SI4375, which also has a short wave
band, but it seems to be sold by nobody. This will
change perhaps.
The problem with Airoha AR1010 is that the documentation
to it is confidential!. All that is availble is a
datasheet that describes the electrical connections, but
it says nothing about the protocols, with which you speak
to your radio chip. A software specification is avaiable on
the internet, however,
though it is pretty hard to find. The name of the document
is ar1000F_progguide-0.81.pdf, which you can try on Google.
One adress you find is www.docin.com/p-34190202.html which
is a chinese site where you can see and read the document,
but it is unclear to me how to download it. I have downloaded
the document, of course, but I don't dare to make it
available here. Some people have complained about the
quality of this document, but I think it is quite good.
As you can see from the name, it is actually about the
circuit ar1000, which is a more advanced sister to ar1010,
which has RDS, but it works for ar1010 also.
The breakout board is maybe intended for surface mounting
on a circuit board, but it is easy to connect to a normal
hole mount board with some small wires. The breakout board
hides some details of the original cicuit, so all you
have to think about is this:
The linear camera is a little device from Hamamatsu
labeled S9227. It procuduces an image 512 x 1 pixel, so
it produces a one line picture. Another name is line sensor
or line scanner. I didn't know what I would use it for
when I bought it, and I still don't know. A use as a
robot sensor would be natural, but the vertical field of
view is quite small, so to use it for recognizing beacons,
you need either beacons of some height or a spread lens
in front of the sensor. In fact, the vertical pixel size
is 20 times bigger than the horizontal pixel size, but
the vertical pixel size is still quite small.
On the Parallax forum, there is someone who has taken
a sensor like this and mounted on a tilt table. Then he
can scan in a complete 2 dimensional image. He has even
put movable color filters in front of the camera, and thus
made a color picture. The exposure time is long, and the
colors are perhaps not perfect, but it's nice. You can
do it with this sensor and this software, if you just
make a tilt table. You have to accumulate the image in
a PC, because there is hardly enough memory onboard the
Propeller.
Nevertheless, I think this was a nice project. It's a
nice mixture of digital and analog technique, because
you clock in a video signal, that is analog in nature.
And with a single line, you can store the whole picture
in the Propeller, so you can build a stand alone solution
in a Propeller. With normal cameras you can hardly hope
to do that.
The sensor is just a sensor, so I made it a camera by
mounting it behind a magnifier. The focus of the magnifier
is such that I should mount the camera slightly behind
the backplane of the magnifier, so I used a sheet of
macrolone for that (plexiglass would do too). Then I
have an experiment circuit board and a connector and
cable, and that is all. Here it is, mounted on a
gorilla pod:
Here's a close up of the sensor:
The software is a main program Lcam.myr, which captures
the image (process capt), displays it as a plot on the
graphical display (process show), and also sends image
data to a PC (process topc). The PC has to request the
data by sending an arbitrary byte, and then receives
512 bytes of image data.
For capturing, you could consult the spec. I don't include
it here, for I assume, that if you can get hold of the
sensor, you can get hold of the spec. You communicate with
the device with two pins, a startpin, and a clockpin.
When the startpin is high, you do the exposure. I preferred
to do that immediately after the previous picture was sent.
I use now an exposure time of 64μs. During transfer of a
picture, I keep the start pin low. You can transfer and
expose simultaneousy, but I chose not to use that.
For transferring, you should first send 15 clock pulses
to initialize the device. Then you run the clock signal up and
down, and then the next pixel is available, so you start
A/D conversion and fill your image array im. You
do this 512 times to get 512 pixels. Then you turn
the startpin on and continue to send clockpulses to expose
the next picture. (It is not clear to me, why you have to
send clockpulses to expose, but I tried without, and it
didn't work).
Here's the "picture" of a small lamp hanging in my window.
It is a little dark outside, so you only see the lamp.
The ambient light outdoors is undersaturated.
The Mandelbrot set is a set of complex numbers. Now,
as we have learnt here, a complex
number has two coordinates (the real and the imaginary), and
a point in the plane also has two coordinates, and thus
we can depict a set of complex numbers as a set of points
in the plane. So the set becomes a "figure" in the plane.
The Mandelbrot set was discovered by Bénoit Mandelbrot,
while he worked for IBM, but the Mandelbrot set has a
kind of dual relative, called the Julia set, discovered
by Gaston Julia and Pierre Fatou much earlier. But
Mandelbrot had the opportunity to make his set into
computer graphics, which quickly made it gain world
reputation.
The Mandelbrot set is generated from a very simple
equation (or rather function), but once it is there, and
you can show it with your computer graphics, and you can
zoom into its details, it reveals itself as an extremely
complicated structure.
Here is the function:
f(z) = z2 + cz and c are complex numbers. What you do now, is that you take what comes out of the function (f(z)) and plug that back into z. And then you keep doing that. What happens then? Well one of two things can happen, z can stay limited, and maybe converge to something, or oscillate in a limited world. Or, z can go to infinity. It depends on what we start with, but we always start with z = (0,0) = 0 + i·0 = 0. And it depends on the constant c.
An EEPROM is an Electrically Erasable Programmable
Read Only Memory. A PROM is a memory with a content, that
you can give to it once and for all. An erasable Prom is
usually erased with UV lamp. After that, you can program it
again. An EEPROM is erased with an electrical signal, which
is handier than using a UV lamp. For the EEPROM:s I use
here, you don't really see a specific erase phase. They
behave as normal writeable memory (RAM or RWM), but they
keep the content after power down. However, the EEPROM:s
have a limited number of write cycles. After that, the
component is worn out. Depending on quality class, the
limit for the number of write cycles varies between
10 000 and 1 000 000. This is pretty much, but it makes
these components less suited for data logging, but well
suited for storing of programs.
Different types of memory cards, SD-cards, Memory sticks
etc. are popular components of this type, but it's hard
to find good documentation for them, and they have a
somewhat complicated structure (page structure). Ideally
you would like to implement a standard file system, so
that you can read and write data from a PC. I have a
device that does all this, based on a serial command
interface from a computer like a Propeller. But I didn't
always manage to erase files properly, it was somewhat
slow, and it is not sold anymore. (In Parallax' object
repository, there are Spin objects for handling SD-cards,
including building a file system for them).
EEPROM:s that look like ordinary electrical components
are also available. The upper limit in size is 1 MB,
but more will come, I suppose. The component type I have
chosen, is the type, that is used together with the
Propeller computer. The type is called 24LC256, which
has a size of 32 kB which is 256 kbits, hence the name.
With the same interface, a type called 24LC512 is
available. The size, consequently, is 64 kB. They can
be stacked together up to eight at a time, so then the
limit is 512 kB. If we store Propeller code, we are
interested in the number of long (32 bit) words, so then
the biggest chip contains 16 kLongwords. That's enough
to fill 32 Propeller cogs with code.
These devices are controlled through an I2C
interface. This is described here.
Since the eeprom application of I2C is somewhat
demanding, I have updated the assembler iic-routines,
so they are now in the state as described in
the I2C description.
24LCXXX memories have a memory page structure, but it
doesn't disturb too much. There is a page write
mode, but I haven't used it. My data to write are coming
from a PC over RS232, and if I were to write that data
one page at a time, I would have to store all that data
first in the Propeller RAM. It is possible, and faster
than writing individual bytes, but it is unnecessarily
complicated. You have the same situation if you want to
log real time data into the EEPROM.
For reading, you can read a single cell, but once you
have adressed that single cell, you can keep reading from
consecutive cells, and the limits between pages is
immaterial in this case.
For talking to the memory, your Propeller, as a master,
first has to wake up the device (the memory chip). It
does so by first sending a start pattern, and then a byte
with an I2C adress. The I2C adress
for these devices is always a hexadecimal A. After that
there are three bits, that allow you to adress up to
8 individual chips. The chips have three adress pins, and
you connect them to 1s and 0s in an individual pattern.
The chip that wakes up, is the one, whose wire pattern
matches the three bits sent. Finally, there is a receive/send
bit (0 for sending). To begin with, it is always 0,
because now you have to send a memory adress.This adress
takes up two bytes.
If you write data, you just keep sending the data after
the adress. When you are satisfied, you send a stop pattern.
In all this trafic, you have to leave the bus open between
the bytes, to allow the device to send an acknowledge bit.
When you want to read data, after the adress has been sent,
you send a start pattern and a new device adress, this time
with a 1 as receive/send bit. After an acknowledge bit,
you can clock in your data as long as you want, but you
have to acknowledge each byte received. Acknowledgment
amounts to sending a 0 on the bus.
I have a routine that reads a given number of bytes from
a specific EEPROM adress, to a given adress in an array
in Propeller RAM. I also have a routine that reads 4 bytes,
and then packs them into a longword. There is a consideration
there, about how the bytes in a word are supposed to be
packed into consecutive cells in a byte oriented memory.
The two principles here are called Big Endian and Little Endian.
It can be said that each of these principles is more logical
than the other (in some sense). Consequently Unix and
Windows have made different choices. I am not quite
sure what the alternative chosen in the Propeller should
be called, but I found the way the EEPROM content is
displayed in the Propeller Environment a little bit confusing.
Anyway, the code that you can download here works fine.
This is now my project for using the EEPROM.
When you use your Propeller computer "in the field", but
want to run different applications on it, you find, that
you have to bring a PC with you, to switch application.
It would be nice to have a small memory box, that could
contain a bundle of programs, that you could choose between,
load into
the Propeller and run.
Technically, we build a card with
one or more EEPROM chips, and connect its two control signals
(called clk and sda) to two Propeller pins. We also need
some means to control which program to run. As I have
analog input channel free, I have chosen to use a
potentiometer for choice of program. The current choice is
displayed as a color on my RGB-led. So you need a little
table to pair the applications with a color. I also need a button
to confirm a choice, and it is connected to a Propeller
I/O pin.
The rest is software. I'll come back to the software for
writing the EEPROM in a while.
For running the program, the key mechanism is a Propeller
instruction called coginit. It appears both as a
Spin instruction and an assembler instruction, but I use
the assembler instruction. It has one argument, which is
a reference to a variable, called cogreg by me, which
contains three different arguments packed into different bits.
The first of them is the number of the cog to be
started. The second is an adress to a cell in the
Propeller RAM, where the cog program is stored. The third
argument is an in principle arbitrary reference, that the
cog is provided with, so that it can find its way to global
variables.
Before we can use this, we have to load the program code
from the EEPROM into an appropriate place in Propeller RAM.
In fact, we load all the cog programs first into RAM before
we call coginit for all the cogs, in order to make all the
cogs start with reasonalbe simultaneity. We call the
whole program a Bootloader. The Propeller already has a
bootloader, but its implemented in ROM, so we can't modify
it for our purposes. Hence we have to let the built in
bootloader load our bootloader. It has to do so in a new
cog (number 1), so we waste one cog on this. From this,
our bootloader loads the remaining cogs from EEPROM via
RAM.
A cog has an adressable space of 512 longwords, but the
the sixteen last of these are special registers. Some
of these may be proper memory cells, but some are certainly
more general registers, which are mapped to the memory
adress space. The outa, dira and ina
registers are here. The first of them, which has some
importance here, is the paramater register par.
This register is loaded with content by the coginit
instruction, and the content is usually used as a way
to reach global variables in RAM. But the par register
is interesting for another reason. The bootloader loads
the program code into an array called ee. We should
give the adress of this array to coginit. But what we need,
is the absolute adress of ee. How do we find it?
Well, when we start our bootloader, from the standard
bootloader, we can write (in Spin now)
coginit(1,@boot,@ee). This should load the absolute
RAM adress of ee into the par register. So when
we call coginit to load our programs, we can compute
where the code is by adding the content of par and
some relative adress of the code within ee.
When we run our programs, once we have loaded them, we
would like to use the space taken up by ee for global
variables.
So the third part of the cogreg variable is also
based on par.
There are two more details in this to consider. Firstly,
in the bootloader, we need a few global variables except
ee, and it is easiest to put them first. Hence, the
par register doesn't contain exactly the adress
of ee, but it can be computed. Secondly, the
par register is 16 bits long, so that it can adress
every byte in RAM. But in cogreg, there is a lack of space,
so the 2 LSB's or the adress are shifted out. In this
way, we can still adress every longword in RAM. But we have
to do this shifting, when we load cogreg from
par.
Programs for the individual cogs are loaded both in the
EEPROM and RAM with a spacing of 512 longwords. These programs
are maximum 496 longwords, and minimally much smaller, so
there is some waste, but it is not too bad.
You will find the program Boot.myr
and the objectfile Eeprom.myo below.
The code you load into the program bundle in the EEPROM
is machine code, so I first had to make a machine code
generator. With the structure of Propeller Assembler, it
isn't awfully difficult. It produces the machine code
disguised as assembler code, so that all instructions are
presented as variable declarations, with data initalized
in binary form (following a %-sign). Hexadecimal code is
also presented as a comment on each line. Then there are
four initial lines, which makes the code runnable from
the Propeller environment.
The machine code generation is an integrated part of the
Myra system. In Myra.java there is boolean
machineCode that you can set to false if you want
normal assembler language code. In either case you can
procede through the Propeller Environment as usual, if
you want to run the program directly.
If you get mystified by the machine code, there is a
disassembler, that converts the code back to assembler.
The next step, if you want to make an eeprom bundle, is
to make a packlist, called packlist.txt. It has
a little code for writing a header in the eeprom. On
the packlist there is:
These are files to download. For the files from PromWriteH.java and down, you can find more information here.
Probably, the EEPROM memories we have just mentioned,
are flash memories. But they are really pretty small.
On the other end of the scale, we have the memory cards,
like SD cards and so on. They have a huge capacity, but
I find them difficult to program. In between there are
flash memories, which look like ordinary integrated circuits.
SST25VF032B is such a memory with a capacity of
4 MByte. It is delivered in a 8 pin SOIC surfacemount
package. The interface is serial, according to a standard
called SPI (Serial Peripheral Interface).
You can adress
individual bytes, and read or write them. But
you have to erase the byte before you can write it. But
you can't erase single bytes. You can only erase whole
blocks of memory (the blocks are called sectors by
the manufacturer). This needs to be considered, when
you write the memory. Erasing amounts to that all the
bits in the byte become 1 (so the bit pattern is
11111111 or FF in hexadecimal notation). Writing amounts
to lowering some bits to 0. So if you write more than
once to a byte, the result will effectively be a logical
and-ing of the words you try to write.
These memories have a very elaborated write protection,
system, so if you don't work hard, you have actually bought
yourself a read only memory, that you can't even program.
It is not easy to override this write protection, and you
wouldn't believe that you had been successfull, unless
you had actually tested it. Here are the protection
mechanisms:
On top of the flash.myo system, I've built an
object called file.myo, which represents a file
in a file system. You can create up to 5 instances of
the file object at the same time. They are distinguished
by a unit number, so any call to any of the
file functions should be preceded by the right unit number,
so that you work on the desired file. This allows you
to have 5 files open at a time, and move data between
them.
These files are the standard type files (not so called
random access files), where you stand at a pointer, and
can read or write data there, and then move the pointer
one step ahead.
Here's the main consideration for anyone who tries to
make a file system: You must imagine that you may
want to delete files.. If you have done that, there
is space left for new files. But if your new file is
bigger than the deleted one, you can't squeeze it in.
And if it is smaller, you get an even smaller piece of
space left. After a short time, the memory medium will
be like a Swiss cheese.
The remedy to this is fragmentation. You divide
your file into fragments of equal size. Now, when a
file i removed, you get a set of standard fragment holes
left over. Your new file's fragment fit precicely into
these holes. If the file is two big, there won't be enough
holes, so the new file will have to allocate its last
fragments elsewhere. It the new file is too small, some
holes will be left over for fragments of future files.
To make it possible to read through the file, each
fragments ends with a link to the next fragment of the
file. One says, that the whole file is represented
as a linked list.
In the PC world there is something called
defragmentation. It is not the opposite of
fragmentation, but a procedure to move the fragments
of each file as close as possible to one another,
physically. This is only meaningful for media like
magnetic discs, where one can avoid delays, when the
magnetic head has to move from fragment to fragment
over the disc.
The best choice for my memory, is to let a fragment
be as big as a memory block, i.e. 1024 longwords. It
is pretty much for a fragment, but it makes it easy
to erase files. As we delete a file fragment, we have
to erase the memory there, to make the area usable for
new files.
To find the start block of each file, we need a
file catalog to point out where each file begins.
Each entry in the filecatalog contains a file name,
and a starting block. I have chosen to represent
the block information with a single byte. Thus I can
only adress the first 255 blocks, but the remaining
blocks can contain fragments further down the files.
I have chosen to let the catalog entry for each file
be a single longword. That leaves only three bytes
for the file name. But it is good enough for my
purposes. I have also chosen to let my system contain
up to 64 files. That gives me a file catalog, which
is an array cat[] of 64 longwords.
I also need an account for which blocks are free in the
memory, and which are occupied. There are 1024 blocks.
which i 32x32 so we can store that information in
an array occ[] of 32 longwords.
cat[] and occ[] are placed in the first memory block.
However, they are not easy to work with there, as
you can't write to them without erasing the whole
block first. Hence, these two arrays have to be
mirrored into the Propeller RAM. At power up, you
read data to the mirrors from the flash memory. Then, basically,
you work with the mirror variables. But you should
see to that they get back to the flash memory every
now and then, so that no information is destroyed
at power down. You have a similar problem with a PC,
where you risk to loose information, if you just shut
power down. The system here writes the mirrored variables
back to the flash memory, after creation of any new file,
and after any delete operation.
Here's now a little of what you have to do:
The upside of an interpretive version of Myra is that
one avoids the limit of 496 longwords in Cog RAM. The
program resides in the global RAM of the propeller, where
there is much more space. From there, instructions are
loaded down to a cog and executed there.
The downside is that the execution is slower, firstly
because all the instructions have to be moved from the
global RAM to the cog, which takes a little longer time.
Secondly, the interpreter has to work a little with the
instructions, before they are executed. In practice, I
have observed a reduction in speed of 2 to 3 times, which
for many applications doesn't matter too much. (This is
the relation between compiled Myra, and interpreted Myra;
what you can do with hand written assembler code is
another matter).
Here's then how it works. You load each cog with an
interpreter, which is called Tolk.asm, from 'tolk', which
is Swedish for 'interpreter'. This interpreter, together
with the Propeller Cog, behaves in a new way relative to
the Propeller itself, so we can call it a virtual machine
Hence we can program this virtual machine in a new
language, which comes both as a somewhat readable
Assembler language, and as machine code. We call this
language iMyr.
The iMyr code is generated
from the Myra code, with a special compiler.
Hence, both compiled execution and interpretive execution
are compatible with the original Myra language. However,
interpretive execution has some advantages, and if we
use them, we may create a Myra program, that can't be
executed in compile mode. There are four major such
differences:
An iMyr instruction is parsed into three parts:
Code | Instruction | Description |
---|---|---|
0 | pushim | push the adresspart to the stack top |
1 | push | push the value of the adressed local variable |
2 | pushg | push the value of the adressed global variable |
3 | pop | pop the stack top value to the adressed local variable |
4 | popg | pop the stack top value to the adressed global variable |
5 | pushel | For the following 4 instructions, the stack top value is an index pointing to an array element. The array base adress is the adress part of the instruction. For this instruction, the array resides in local memory. The value of the array element is pushed to the top of the stack. |
6 | pushelg | Like the previous, but the array resides in global memory. |
7 | popel | The next highest element on the stack (i.e. under the index) is pushed to the adressed array element in local memory. |
8 | popelg | Like the above, but the array resides in global memory. |
9 | pushat | The two top elements on the stack are summed, and the value at that adress in local memory is pushed. |
10 | popat | The two top elements on the stack are summed, and the third element on th stack is popped to that adress |
11 | raise | raises the stack pointer, so that a value popped off at a recent pop operation, becomes visible again. |
12 | popoff | removes the top value of the stack, whithout sending it anywhere |
13 | flip | exchanges the two top elements on the stack with one another |
14 | dup | duplicates the top value on the stack over the current stack top |
15 | jmp | A jump. The adress part is the target of the jump |
16 | call | A subroutine jump. The adress part is the target of the jump. This target adress is pushed on the subroutine call stack. |
17 | return | This is a pop of the subroutine stack. The value that is popped up to the top of the stack is incremented with one. |
18 | asm | calls an assembler routine. The adress part selects which of the assembler routines is called. |
19 | status | pushes the values of the Propeller status registers (z and c-register) on the status stack |
20 | rstatus | pops the status stack, so that the previously sampled value of the Propeller status is recovered. |
21 | l21op | Operation stack to stack |
22 | l11op | -"- |
23 | l01op | -"- |
24 | l10op | -"- |
25 | l20op | -"- |
l21op | - |
---|---|
0 | + (addition) |
2 | - (sutraction) |
4 | or (logical or) |
6 | & (logical and) |
8 | xor (exclusive or) |
10 | >>n (non algebraic right shift) |
12 | >> (algebraic right shift) |
14 | << (left shift) |
16 | * (multiplication) |
25 | receive |
l11op | - |
0 | || (abs) |
2 | compl (bitwise complement) |
5 | neg (negate) |
7 | G (1 if stack top greater than zero) |
11 | Z (1 if stack top is exactly zero) |
15 | inpin |
l01op | - |
0 | inall |
2 | now |
l10op | - |
0 | wait |
4 | setdir |
l20op | - |
0 | outpins |
Here's a simple sketch of the virtual machine.
In the middle of the figure, there is a register called
Instr Adress. Through the global RAM (gRAM),
the instruction is brought to the instruction register, where
it is parsed into type, condition and adress.
The type goes a little everywhere. The adress may go to the
memories (the global and the local) In case of a jump,
it goes to the Instr Adress register. This register is
connected to th subroutine call stack. In case of a
normal jump (jmp), nothing happens with this stack. In case
of a subroutine call, the previous value in the Instr Adress
register i pushed down into the call stack. When the stack is
popped, the value, that goes up to the Instr Adress register
is incremented with one. This means that execution
continues at the instruction after the subroutine call.
To the left is the normal stack, which communicates via
its top element with local or global memory. Some of the
memory cells in local memory (cog RAM) are mapped to
input and output registers, so this is how the computer
communicates with the outer world. Operations take data
from the highest layers of the stack, and push the results
to the top of the stack.
The status stack is depicted as a horizontal bar, as it
is actually a single memory cell, where status data
are shifted in. The least significan bits are updated from
the Propeller status registers, upon execution of a
"?" instruction (STATUS instruction). Subroutines that
use the "?" instruction, should restore the status
by popping the status stack with a "!" instruction.
Based on the condition and the value of the status stack
the condition checker decides whether the instruction shall
be executed or skipped.
As with Spin, there are two ways to run iMyr code;
directly from the PC or via the EEPROM.
For direct running,
we first have to bring a bootloader into the Propeller,
using the standard Propeller system. That bootloader is
called BootI, where "I" stands for this mode of running
iMyr. BootI will load the program into RAM, and then load
it further into the Cog memories with coginit as described
above. The file iMyrLoad.bat is a suggested
batch file, to make this as easy as normal execution of
Spin code. As easy - but a little slower, as the speed
is limited by the RS232-link with a speed of 115200 baud.
Also, the operator has to operate a "reset off" button,
to disconnect the reset of the Propeller chip from the
Prop stick device. Otherwise, the Propeller may reset, as
data come from the PC.
For running via EEPROM, we first need more EEPROM space,
than in the normal system. Replacing a 24LC256 chip with
a 24LC512 chip will do, and this causes no further work.
Alternately we can add an extra 24LC256, and connect
its adress pins to something other than (0,0,0). Then
we will load the loader EEPROG into the Propeller.
To control where the program goes into the EEPROM, we
should have an eeprom directive somewhere in
the source code file. It typically reads:eeprom:0,17.
0 is the EEPROM chip number. If we replace the 24LC256
chip with a 24LC512 chip, nr 0 is appropriate. If we have
more chips, and we want to use them, we direct the code
to them with some other number than 0. The figure 17
points out a 512 longwords big block in memory. For
24LC512, there are 32 such blocks. For chip 0, 16 blocks
are reserved for the ordinary Propeller system.
Compiling the Myra source code with the MyrI compiler
gives an .eep-file, which is loaded into the
EEPROM.
Running the code requires another program, BootJ,
where J stands for this EEPROM-mode. BootJ can be loaded
in the Propellers standard EEPROM, so that everything
starts at power up. BootJ has to be programmed so that
it loads the program from the correct block in EEPROM.
In the example above, a variable iblock should be
set to 17. You can use hardware jumpers, switches,
or potentiometers and A/D-converters, to allow different
applications to be ran at start up.