|
Programming
There
were three main tasks in the programming of the Ultimate Etch-A-Sketch:
1. Moving the stepper motors
2. Monitoring inputs / Setting LEDs
3. Function algorithms
Moving
the stepper motors
-------------------------
Stepper
motors are controlled from the Handyboard through the built-in H-Bridge
(L239D). The H-Bridge has four inputs and four outputs, but by assuming
that the user plugs their stepper motor coils across two outputs,
the two inputs are mapped by the Handyboard to only one logical
output. They are called 0,1,2,3. To control the motors bipolarally,
we used the built-in fd() and bk() functions. (They stopped working
halfway through for some reason, so we actually ended up using motor(x,100)
and motor(x,-100). )
We
made each motor (the horizontal motor and the vertical motor) a
seperate process that would step every third millisecond. Why every
third millisecond? Because we had three processes, each of which
was given one millisecond to execute. This is the HandyBoard's understanding
of "multitasking".
Mapping
to actual speed of the motors was a little bothersome. Because we
had three processes running, the program could only step a motor
once every three milliseconds, giving us a true "speed"
of 1/3ms. This was slow, but we decided that offloading the stepper
motor control to an external stepper motor controller would take
too many of our limited handyboard outputs.
The
speed was variable. The amount of time to wait between steps (1/speed)
was calculated in another process to maximize the speed of these
motor processes. The sleep time (xSleepTime) was calculated from
an integer value (xSpeed) that ranged from zero to an arbitrary
maximum. The higher the number, the lower the sleep time, the faster
the motor.
<code>
void xMotor(void)
{
/*Simple enough- move the x stepper.*/
int xState=0;
while(1)
{
while (xSpeed==0);
if (xSpeed < 0)
{
if (xState!=3) xState++;
else xState=0;
xPos++;
}
else if (xSpeed > 0)
{
if (xState!=0) xState--;
else xState=3;
xPos--;
}
if (xState==0)
{
motor(2,100);
motor(3,100);
}
else if (xState==1)
{
motor(2,-100);
motor(3,100);
}
else if (xState==2)
{
motor(2,-100);
motor(3,-100);
}
else if (xState==3)
{
motor(2,100);
motor(3,-100);
}
sleep (xSleepTime);
}
}
</code>
Monitoring
Inputs / Setting LEDs
--------------------------------
The
third process that was running, beside the two motor processes,
would poll the inputs at every iteration (updateFunction). The polling
function checked the analog inputs for button presses and also did
the math for xSpeed => xSleepTime. If an analog input went true,
the global currentFunction value would change and the polling function
would return a "0" value to signal that whatever called
it should stop whatever it was doing and go back to the beginning
of the process.
It
would also turn on an LED depending on which function was chosen,
via the digital outputs. "Egad! Digital outputs on the Handyboard?!"
you say? On the last page or two of the technical reference there
is a description of how to convert some other-functioning pins to
digital outputs.
Function Algorithms
-------------------
For
the real Brains of the operation, see these functions. There were
four functions:
0:
The drawing function
All
that this function did was to check the position of the joystick,
then pass that position as a speed to the motors. Then return and
wait until called again. There was a funny task in converting the
analog values from the analog inputs to the proper motor speeds:
The joystick position did not map directly to the value at the analog
input. It was some funny exponential relationship. At first, we
tried to model it. But then, after realizing how much computing
time we were wasting doing these silly calculations, we generalized
the function to two linear functions. Sure, we confused some of
the mapping between joystick position and motor speed, but it was
barely noticeable.
1:
Geometric shapes
This
function kept a state variable that would tell the function to alternate
through four different simple shapes: Square, Triangle, Circle,
and Squiggly Line. To make these precise shapes, we used a positioning
function (move). The positioning function would move to a new location,
relative to the location the pointer was at when the function was
called. Because this positioning was relative, we never figured
out how to make a circle. We think that we were really close to
figuring it out, though.
2:
Stick Figure
This
function was a lot like the primitive shapes button, but drew a
stick figure. It took a little while to execute, since it was more
complex. (It also has the ability to write the name "Lynch",
but after the brown-nosing flak we got, we took it out.)
3:
Spiral of DOOM
Here
the Etch-A-Sketch would start at the current position and make an
increasingly large square. The results were much like the attempts
of children to cover the screen completely so that they could see
the internal components of the Etch-A-Sketch. We never let it run
long enough to do the entire screen, though.
The final version of the entire code is attached
(655 lines). There might also be some insightful comments in the
code itself.
|