|
Knight Rider Display Project
This
project started out as a VU meter. I reused parts and pieces to improve the
VU meter and give it additional functionality. It is amazing how much time
it takes to create a small thing like this.
Knight Rider was a movie in
the early 80’s. The main story is about a police officer who is deadly
wounded and considered to be dead. He is found by a Samaritan doctor and
nursed back to health. Under his new identity he is given a fully autonomous
robot built inside a Pontiac Trans-Am named KITT. There was a small ambient
display on the hood of the car that displayed the current condition of the
car.
Example: during normal
travel it would only scan back and fourth, but when KITT “spoke” it turned
into a VU meter to display the intensity of the voice.
Building
the LED display from scratch I used 2 x 16 (32) individual LED’s. The PIC
chip has pins that can be used as inputs or outputs. There is a built in
function that will set up each pin as an input or an output. Register A has
6 inputs/outputs, register B has 8 inputs/outputs, register C has 6
inputs/outputs.
I used four (4) pins from
register B. Normally pin B0 is reserved for bus communication, but my code
does not need to communicate with any other devices through the bus, so I
used port B0. Using pin B0 made my code easier to write.
First I
built a small 8 LED display using only the 8 pins of the B register. That
was fairly straight-forward. Starting from position 0 and will count up to 7
and turn only the pin on that currently corresponds to the counter’s value.
All the rest will be turned off. The following is the code that I wrote:
|
|
global [r = 0]
constants
[[portb 6][portb-ddr $86]]
to rider
write portb-ddr 0
rider_up
end
to rider_up
loop [
write portb r
setr r + 1
if r > 28 [ rider_down ]
wait 1
]
end
to rider_down
loop [
write portb r
setr r - 1
if r < 2 [ rider_up ]
wait 1
]
end |
|
Pictures of the 8 LED’s and
the Knight Rider KITT car:
I
realized that using only 8 LED’s will not look realistic it was way to small
and the Knight Rider effect fell short of my expectations. So I doubled the
number of LED’s but I quickly realized that I need to double the number of
the outputs to achieve a realistic chaser effect. There are almost enough
output pins on the PIC, but using all of them would have been a painful and
uninteresting code to write. There are ways to create outputs from a limited
amount of inputs. In this case there is always one LED on at any given time
so the natural choice was using decoders.
Decoders
are … that take n number of inputs and return 2n number of
outputs. I used HC138 decoders. There are 3 inputs and 8 outputs on each.
There are 3 control/enable inputs two have Positive logic and on Negative
logic. I wired the two Positive logic pins to +5 volts so the decoder would
be only controlled by the Negative logic pin.
With
this method I could have created a maximum of 2^17 outputs versus the very
limited 17 provided by the PIC chip.
Bad code for the PIC
|
|
global [r = 0]
constants
[[portb 6][portb-ddr $86]]
to rider
write portb-ddr 0
loop [
write portb 255
if r < 8 [
go_right
]
if r > 7 [
go_left
]
setr r + 1
if r > 15 [setr 0]
wait 1.5
]
end
to go_left
clearbit 15 - r portb
end
to go_right
clearbit r portb
end |
|
I started programming when I was 12 and my computer
was a 086 Zenith laptop with less than a megabyte of memory. Programming the
PIC chip brought back memories from the days I had to be careful using the
stack space. I wrote a small code that used recursion. A very interesting
thing happened: the program would run 3 times then indicator LED would
flash, the PIC would crash and the code on it was corrupted. After
downloading the code on it again the same thing happened. So naturally I
rebooted the chip and tried again. I get the same result.
I reload the assembler on
the PIC and download my code on it. No change in the result: still I get 3
correct runs and then the code crashes.
I looked up in the manual
an the stack space of the PIC is very small and my code being recursive
pushes values and addresses on the stack but never takes it off and the
stack overflows – overwriting my code.
I remembered that in
Assembly class we learned that recursion is really inefficient and iteration
is always better and faster, while recursion is quicker to write and easier
to understand by others. I rewrote my code to use iteration instead of
recursion.
|
|
global [r]
global [c]
constants
[[portb 6][portb-ddr $86]]
to rider
write portb-ddr 0
setr 0
setc 1
loop [
write portb r
setr r + c
if r > 14 [setc -1 ]
if r < 1 [setc 1]
wait 1
]
end |
|
This works as follows:
There are 4 bits that can be on or off:
|
bit 0 |
bit 1 |
bit 2 |
bit 3 |
|
|
|
0 |
0 |
0 |
0 |
Decoder
1 is enabled
Decoder 2 is disabled |
Pin 15 is
pulled to ground, all other outputs positive |
|
0 |
0 |
0 |
1 |
Pin 14 is
pulled to ground, all other outputs positive |
|
0 |
0 |
1 |
0 |
Pin 13 is
pulled to ground, all other outputs positive |
|
0 |
0 |
1 |
1 |
Pin 12 is
pulled to ground, all other outputs positive |
|
0 |
1 |
0 |
0 |
Pin 11 is
pulled to ground, all other outputs positive |
|
0 |
1 |
0 |
1 |
Pin 10 is
pulled to ground, all other outputs positive |
|
0 |
1 |
1 |
0 |
Pin 9 is
pulled to ground, all other outputs positive |
|
0 |
1 |
1 |
1 |
Pin 7 is
pulled to ground, all other outputs positive |
|
1 |
0 |
0 |
0 |
Decoder
2 is enabled
Decoder 1 is disabled |
Pin 15 is
pulled to ground, all other outputs positive |
|
1 |
0 |
0 |
1 |
Pin 14 is
pulled to ground, all other outputs positive |
|
1 |
0 |
1 |
0 |
Pin 13 is
pulled to ground, all other outputs positive |
|
1 |
0 |
1 |
1 |
Pin 12 is
pulled to ground, all other outputs positive |
|
1 |
1 |
0 |
0 |
Pin 11 is
pulled to ground, all other outputs positive |
|
1 |
1 |
0 |
1 |
Pin 10 is
pulled to ground, all other outputs positive |
|
1 |
1 |
1 |
0 |
Pin 9 is
pulled to ground, all other outputs positive |
|
1 |
1 |
1 |
1 |
Pin 7 is
pulled to ground, all other outputs positive |
Pictures of weak LED's:
Pictures of the bright LED's:
If bit 0 is false the first
decoder is enabled, because I used the negative logic enable pin. The second
decoder is connected through an inverter to bit 0, so when B0 becomes true
that will do two things it will disable the first decoder and it the output
of the inverter will be 0 – which is the enable signal needed for the second
decoder.
I wired the bits 1-3 to the
data inputs of the decoders. There are 8 outputs on each decoder. Each
output corresponds to a decimal value from 0-7. The three inputs on the
decoder correspond to converted decimal value that ranges from 0-7. There is
only one output on at any given time.
The program has an internal
counter that will add a value that is either +1 or -1 depending if it is
counting up or down. This method replaces the recursion which calls a
function fur counting up or counting down.
Conclusion:
During the Umass Lowell
BotFest I watched people looking at the Knight Rider and they were trying to
figure out what it is doing. There was another group of people who did not
know enough about circuits and chips to understand the design complexity and
time that went into creating a small ambient display like the Knight Rider.
Overall I learned a lot
designing and building the VU light and the Knight Rider. There were
theories that I had to read up on, and concepts that I knew about but I
never used it like the stack management. Also soldering 32 LED’s taught me a
few things (I only burned out 2 LEDs), but the experience gained by actually
building the entire display is something that all computer science students
should have as part of their training because just learning about theories
is not enough to understand important design issues.
Videos of the project please
right click and select <Save as>
Knight Rider 1
Knight Rider 2
|