Drive
While You Surf:
The car
you drive over the Internet
CSE 477, Group G
Duy Le
Ngochan Nguyen
Valdis Riekstins
June 8, 2000
University of Washington
Department
of Computer Science and Engineering
Summary
Information
Project Status
Project
Description
Project
Specification
Project Implementation
Micro-Controller
XBus Interface
Camera Interface
Servo Interface
User Interface
Retrospective s
Project Presentation Slides
Presentation 1
Presentation 2
Final Presentation
Appendices
Appendix A: Xilinx Schematics
Appendix B: Microcontroller C Code
Appendix C: User Interface VB Code
Appendix D: XBus Documentation
Appendix E: EEPROM RC-1 Configuration
Documentation
Appendix F: Operating Instructions
Appendix G: Testing Procedures
The Internet has invaded practically
every aspect of human life. You can
chat with other people, order groceries, and participate in auctions. You can find information on almost every
topic imaginable, apply for jobs, the list goes on. However, what about controlling robots? Programming languages like Visual Basic have made it very easy to
provide custom user interfaces on the Internet for tasks like this! Imagine bomb squads disarming terrorist
devices from their desktops taking away the chance for loss of human life, or
specialist doctors operating from across the globe saving lives that wouldn’t
have had any other chance. The
possibilities are endless, and all the necessary technology exists today.
This is the premise for our group’s
project. We set out to convert a
ten-year-old radio controlled car into a vehicle that is controlled over the
Internet. By using a digital camera and
programmable hardware devices, we can control the steering and velocity of the
car and give the user a picture of the road ahead. Obviously, providing a clear and intuitive user interface is
vital for this type of project, and is also probably the most challenging
aspect of the design.
Our project status is quite good. We are only two steps away from our original
goals! We were not able to integrate
our new oscillator chip that would have given our serial link a speed boost by
a factor of 12, so we’re still stuck at 9600bps. We were also unable to get a high-speed wireless development kit
for our serial link, so our vehicle is tied to our web server by a serial
cable. If those two pieces were in
place as well, our project would meet our original specifications.
The rest of our project is finished and
working well. One problem we are
experiencing is some noise and pixel shifting in our camera images. This is the only camera that we can get
configured, and the stereo-imaging group who had used this same camera earlier
reported the same kind of behavior with it.
Also, we have a bug where if the user enters a lot of throttle
information, the next picture to display will have large black areas and the
picture will be unrecognizable because of all the interference. We aren’t quite sure what is causing it, but
we do know that we are out of time trying to fix this. It is also still a real pain to get the
camera configured correctly and sending good data.
Our goal for this project was to have
a vehicle that was fully controlled over the Internet. We wanted to have a camera that would
provide visual information to the user through a web-page-based user
interface. That interface would also
take steering and throttle input from the user and send that along to the
vehicle. Ideally, we had hoped to have
a wireless serial link from a web server to the car instead of the cable that
we use currently, but cost, time, and bandwidth constraints made this
impossible.
When we started this project, our
vehicle was just a normal radio-controlled car (Team Associated RC10-T). We had to first understand how the current
controls to the car worked, and then figure out how to control the car
ourselves using the tools and hardware available to us.
Luckily, most of the other hardware
aspects of this project were completed in labs or provided to us. The camera interface is just a modification
of the provided VGACAM project, the serial interface was largely complete after
lab 4, and the XBus design that we all defined together in class handled the communication
between the micro-controller, FPGA, and memory. Our work in this aspect of the project was to modify each of
these parts to suit our specific needs, and then combine all these separate
projects and make them all work together as one project (MUCH harder than it
sounds).
The bulk of original work in this
project was in our user interface.
There were so many issues to work through to get our UI complete and
working! Firstly, we needed to figure
out a programming language to use that would integrate easily with web pages
and provide all the other functions we needed, such as controlling the serial
port of the web server (or the ability to link to another program that
controlled the port) and displaying our camera image. We also wanted a UI that was visually appealing, easy to use, and
intuitive for the user.
We also had to figure out how to connect our user
interface to the serial port of the web server. Could the UI handle this directly? Would we need another program that handled the serial port and
communicated with the UI?
The micro-controller has an important role on our project which is central of the data flow. It includes the serial and XBus interface which together reading camera image from memory, send them to web server and receive car control signal from web server and send it to the FPGA to be processed. The micro controller code written in C and include the following function.
·
Main() function starts reading image from address 0 to 128x120 = 15360. It only sends the address to XBus when the
resendAddress variable is true. The
resendAddress is set every time the SendSignal function is called and when we
start reading a new image. At the start
of the image, we send zero to web server to indicate that the new image is
coming. It requires that we change any
pixel = 0 to 1. Note: same as when we call the XWrite
function. We need to disable the
interrupt when we call XRead and enable the interrupt when the function is
done.
·
XWrite(address, value), Xinit(), and XRead(address) are
given to us. I have added XReadCont()
which tell XBus to read the next address.
XBus will automatically increment the last address by one so we can save
time by not sending the address every time.
XBus Interface
The XBus is given to
us. We just need to test it and fixed
and a couple of bugs. One of them is
make the read continuously work correctly.
The XBus documentation is included in this document as Appendix D.
The camera interface project is given, but it was designed for a RoboCam RC-2 with 256 KB memory. We have modified the given project to work with our RC-1 camera and 32 KB memory. Our modified camera interface includes the CAMERAIN, MEM2PORT, and BANKSELECT modules. On this part of the documentation, I am going to give a brief description for the RoboCam RC-1 and 32 KB SRAM memory, and then I will go into more detail on the CAMERAIN, MEM2PORT, and BANKSELECT modules.
SRAM is
the dual port memory with 32k bytes. We
divided the memory into two banks, 16k bytes each which is enough to store a
128x120 pixel ~ 16k byte image. The
memory will store the image written by the CAMERAIN in one bank while the XBus
reads the image in the other bank.
BANKSELECT will decide which bank the camera can write into and which
bank the XBus can read.
RoboCam RC-1 is a 164x124 pixel Black and White camera which uses a Vision VV5300
CMOS image sensor. The module has 20
I/O pins as described in Table 1. Note
that pin 14 is a trigger for the EEPROM config function described in Appendix
E. Using the EEPROM configuration gave
us a couple more free pins in our Xilinx project—a big help when you start
running out of pins!
Table 1: RoboCam RC-1 I/O pin description.
We
only use 11 of the 20 pins.
Data
[7:4]: Send data pixel (8 bits/pixel), 4
bits at a time.
QCK: The
camera clock, telling us when to sample the pixels. 4 high level bits are sent at positive QCK edge and 4 low level
bits are sent at negative QCK edge.
FST: Frame start.
AUTOL: EEPROM auto-load trigger
SDA
and SCL: Data line use to configure the
camera with a config.exe program which is given.
VDD
and GND: Power (9-12 VDC) and ground
CAMERAIN:
Figure 1:
CAMERAIN module
CAMERAIN
includes the data path used to get the pixels coming from the camera through
Data [7:4], a 128 bytes FIFO, and the CAMERA_TEST module.
Pixels come 4 bits at a
time. 4 high level bits are sent at the
positive QCK edge and 4 low level bits are sent at the negative QCK edge.
Figure 2: FIFO_128 module
FIFO is a place to store
pixels when they come from the camera.
CAMDATAIN [7:0] will be put into the FIFO when the WRITEFIFO signal is
asserted. When the READFIFO signal is
asserted, the pixel will come out and be written to memory. The EMPTY signal will be asserted when the
FIFO is empty. There are chances that
memory is busy most of the time, making the FIFO full, and we will lose
pixels. However, that never happened on
this project since XBus was running very slow.
It will read the memory every 150 cycles so the camera have plenty of
time to write pixels to memory.
Figure 3: CAMERA_TEST module
CAMERA_TEST includes the
two counters (HCNT, VNCT), which are the braisn of the module. They determine which pixels will be written
to memory and which ones will be dropped, since we only want 128x120 pixels and
the camera sends us 160x120 visible pixels.
The two counters are also used to generate address; HCNT is output as
COLADDR [6:0] and VNCT is output as ROWADDR [6:0].
Anytime we see a start
frame (FST asserted), we set the two counters to zero. At every clock cycle, if the busy signal is
not asserted and the FIFO is not empty, we assert READFIFO and increase HNCT by
one. When HNCT reaches 128, we set it back to zero and increase VNCT by one (go
to the next line.) When VNCT reaches
120, we set it to zero and assert the ENDFRAME signal, indicating that the
frame has ended.
We assert the WRITE signal
when the incoming pixels are inside 128x120.
If the BUSY signal is asserted, we do not increment HCNT and VCNT and
also don’t assert READFIFO, so on the next clock it will write the same pixel
to the same location in memory.
MEM2PORT:
Figure 3: MEM2PORT module
MEM2PORT
is the memory interface, it sends the signal (read or write) and address to the
32k byte SRAM. For camera writes to
memory and XBus reads from memory we give reads higher priority than writes. This is because we want to send images to
the web as fast as possible. When the
read and write signals are asserted at the same time, MEM2PORT sends the read
to memory, and asserts the BUSY signal which tells CAMERAIN that memory is
busy, so try again later. Since one
image is 128x120 pixel which is about 16k bytes, we divide memory into two
parts (16k bytes each) so the camera writes to one part of the memory while
XBus reads from the other part.
SELECTBANK:
Figure 5: SELECTBANK module
This
module is written in verilog. It will
decide which frame the camera can write into, which frame XBus can read, and
when the order is switched. The module
is a finite state machine that takes the ENDFRAME signal from CAMERAIN and
DONEIMAGE as inputs. Below is the state
diagram that shows exactly what SELECTBANK does.
Figure 6: Select bank state diagram.
Because of the bottleneck
from the serial port, which can only transfer 1 frame every 12.8 seconds (using
9600 baud rate), we have to drop frames.
We let the camera repeatedly write into one half of memory until
DONEIMAGE is asserted, and then wait until the camera itself finishes its frame
before switching banks.
Out of the box, the RC10-T is pretty
much like any other 1/10th scale electric radio-controlled car. The car is powered by a rechargeable battery
pack, and gets information from the user via a hand-held radio controller. There are two signals that have to be sent
to the car to control it: steering and throttle (just like normal cars). A small receiver module connected to an
antenna on the car takes the data sent by the controller, and then relays this
data to two servos on the car. These
servos look (and behave) very different from one another, but they both are
controlled in exactly the same manner.
The servo interface is actually quite
simple. There are three wires that run
to each servo: a +5-volt power rail, a ground rail, and then a control wire
that implements pulse-width encoding similar to the accelerometer used in lab
(see diagram below). While the control
wire is low, the servos do nothing—they just sit and wait for a pulse. Once a servo sees a pulse on the control
wire, it measures the width of this pulse and then updates its output
accordingly. For the mechanical
steering servo this output is represented by the mechanical arm that it moves
from side to side, taking the direction of the front wheels with it.
For the throttle servo (Electronic Speed Controller,
or ESC for short), this output is just the amount of juice that it relays from
the battery to the motor. Valid
Servo control wire format
pulse
widths are anywhere between 0.5 to 2.5 milliseconds. Using the steering servo as an example, a very short pulse (~1
ms) will cause it to steer all the way in one direction, and a long pulse (~2
ms) will make it steer hard the other direction. Since the servos only update their values when they see a pulse,
we have to be sure to send pulses regularly and fairly frequently: 50Hz (or a
pulse every 20 ms) was a recommended figure for this.
Creating hardware to create the
control signals for the steering servo and ESC was not difficult. One Verilog block in our Xilinx project (see
the SERVO_CONTROL module and its Verilog source in Appendix A of this document)
was enough to do the trick. This
SERVO_CONTROL module takes in two 8-bit unsigned integers (sent from the user
interface representing the steering and throttle levels), and outputs the
control signal for each servo. The
Verilog in this module implements a counter that measures out 20 milliseconds,
and based on the input values for steering and throttle sets the output signals
high for the correct amount of time.
More specifically, with our FPGA running at a clock speed of 24MHz, 20
milliseconds works out to 480,000 cycles, and this is what the counter is set
to count to. A two-millisecond pulse
needs to stay high for the first 48,000 counts, a one-millisecond pulse needs
to stay high for 24,000 counts, etc.
This is how we worked out the minimum and maximum counts for each
signal, and then the actual pulses are scaled between the minimum and maximum
values on a linear gradient by the input values to the block. The graph below demonstrates this for the
steering servo:
The Server Application
The server application is what links the hardware
setup to the Internet so that people on the web can drive our vehicle. To create this application, we decided to
use Visual Basic. Visual Basic contains
numerous controls and components to let us do this quickly and efficiently.
The server contains an ActiveX Winsock control to
connect the user via a TCP/IP connection.
An MSComm control is used to link up the server to our hardware using
the serial port at 9600 baud.
First the server administrator needs to click the
“Start Server” button to actually run the application. Now the server can begin taking in the image
data from the XS40 board. The server
begins by grabbing 1 character at a time from the serial port to look for the
frame start header (a 0-byte).
Initially, the MSComm control is set up to trigger an event every time 1
character arrives. Once the frame start
header is found, the control is told to trigger the event when 128 or more
characters have arrived. The server
will begin grabbing 128 characters (one video line) at a time, storing each set
into an element of a 240-element string array.
Once all 120 lines of a frame have been received, the server will append
them to a pre-existing file containing the header data for a 128-by-120 pixel
grayscale bitmap file (for more information about this, read the bitmap file
information appearing later in this section).
While this file is being processed, the server can continue grabbing the
next frame, storing this new data in the remaining 120 elements of the string
array. The server will loop through the
two halves of the array and two bitmap files as it receives more frames.
When a user is connected, the server will send each
frame to them through the TCP/IP connection.
Steering and throttle control values received by the server are
immediately sent to the serial port with a header byte of 255.
On the next page is a picture of the Visual Basic
form for our server application. All of
the components and controls are marked with their types and also with the names
we used for them. See Appendix C for
the actual server code.
TextBox txt_steer_val
Frame frm_steer Frame frm_throt TextBox txt_throt_val CommandButton btn_start Frame frm_video Image img_video Winsock wsk_tcp MSComm MSComm1
A bitmap file header contains data about the size of
the bitmap file, the image dimensions, the image palette, and a bit of other
data. The palette itself is basically
an array, with each element containing a color or grayscale value. Following the bitmap file header are the
image pixels themselves. The pixel
value actually corresponds to an element in the palette. So if a pixel value were 199, its color
would be the 200th element from the palette array.
In our project, we dumped the incoming grayscale
pixels into a bitmap file, one video line at a time. A bitmap file actually stores the image lines from bottom to
top. Since our camera outputs the lines
from top to bottom, we had to store them into the bitmap file in reverse order
that the server received them.
The bitmap header data used in our project was
actually extracted from a 128-by-120 pixel 8-bit grayscale image we created
using Microsoft Photo Editor. The
palette from this header contains 256 grayscale values from blackest (0) to whitest
(255), which is how the camera outputs pixel values. Some sample code to do the extraction can be found in Appendix C.
The
Client User Interface
The client user interface was also created in Visual
Basic. Here, Visual Basic allowed us to
create a very “finished” looking application, which is easily integrated into a
web page.
To begin driving the vehicle, the user need only
click “Start the Engine”, which connects the client to the server.
Once connected, the user will be able to see where
the car is going by the frames of video it receives. Since the client receives bitmap files, we can easily stretch the
incoming images using Visual Basic. The
256-by-240 frames make it a lot easier to see what is ahead of the car.
The user interface is responsible for sending only
the steering and throttle control values to the server. Each of these values is a single byte that
will drive the servos on the car. Also,
these values have adjustable maximum, minimum, and center (or rest) values.
On our interface, each of the two control signals
have slider values, data fields for center, minimum, and maximum values, and a
checkbox for reversing the signal.
However, merely looking at these controls is enough to convince a user
that driving a car by them will be difficult.
Therefore, we came up with our Drive By Mouse™ system, which is located
at the bottom of our interface.
Drive By Mouse™ is simply a label in Visual Basic
that contains a line object. This line
is invisible until a mouse button is clicked while the pointer is in the Drive
By Mouse™ pad. Then, while a button is
held down, the mouse can be moved around inside the pad and the line will be
drawn between the initial point where the button was pressed and the current
cursor location. This line (and more
specifically the delta-x and delta-y values of the endpoints of the line) is
used to set the values for steering and throttle.
Also, the steering and throttle values decay towards
the specified center value. Just like
in a real car, when the driver isn’t actively steering or pressing any pedals,
the car will straighten itself and neither accelerate or brake. This is implemented using a Timer, which
updates both values every tenth of a second.
Also on every update, the server and throttle values are sent together
in a 2-element byte array to the server.
Since all data sent to the server from the client interface follows this
format, there is no need for any kind of header outside of the one handled by
TCP/IP internally.
On the next page is a picture of our client
control. Most of the major components
and controls are labeled with their types and also with the names we used for
them. See Appendix C for the actual client
user interface code.
Image img_video Frame fra_video Timer HScrollBar hsb_throt CheckBox chk_throt_rev HScrollBar hsb_steer CheckBox chk_steer_rev Winsock wsk_tcp CommandButton btn_start Label lbl_mousepad Line lin_mouse_dir
Timer1
Retrospectives
What follows is a collection of
subjective retrospectives on the project written by each member of our
group. These are personal accounts of
our project—how we each contributed, what we thought of the project, etc.
For this project, I worked on setting
up the server computer, creating the
server
application, adding the Internet and bitmap frame processing code
to
the user interface, getting the camera to work with our applications,
and
debugging the hardware in general. Most
of my time was spent trying to get the camera to display an actual image. More than half the time, the camera would
display indistinguishable images. And I
also had a lot of trouble just configuring the camera too. So no, I did not like the RC-1 very much. I did enjoy working on the Visual Basic
server and client user interface applications though. And I thought it was neat being able to create bitmap files with
the pixel data that the camera would output.
My favorite part was seeing the car
steering and throttle work with our hardware.
I’m surprised we didn’t blow anything out when we hooked the servos up
to the XS40 board.
My part of the project is the camera. However, at the end, it is more than that. I first modified the given camera interface project to work with our RoboCam RC-1 and 32kB memory. It included modifying the CAMERA_TEST, MEM2PORT module, and redesigning the SELECTBANK module to make it work. I made the second presentation slides, did the presentation, and because I successfully finished lab 4, the micro-controller is also my part of the project. After testing the XBUS with professor Carl Ebeling, I merged the Camera interface, XBUS interface, and the servo controller module written by Valdis into one project, our final project. This is the most difficult part of all (as I think when I do that) since I have to find enough pins for our project and have to understand how the whole thing works together. Now the rest of the project is testing and debugging until it works and it turns out this is the really difficult part. I have worked with Duy to test the camera again since it wouldn’t get configured anymore until the night before the demo. I also test the XBUS again and fixed it so that it write correct signals to the STEER and THROTTLE registers, and also make it automatically increment the address correctly so we can read continuous pixels without sending address every time. And the last part is document the part that I did for the project.
This project is difficult to me since I have no
background on hardware. There are a lot
of things the professor said in class which I have no idea what are they until
doing a lot of research, reading, asking friend, TA and teacher. However, all my effort and hard work this
quarter has paid off. We got our
project working even if it is not as good as we wanted it to be. I enjoyed doing the project even though
there were times I just wanted to give up, go home and sleep.
I had a pretty good time working on
this project. Not to say that there
weren’t times that I wanted to throw it off the roof (the patented “Valdis
Riekstins Roof Test”), just that these feelings were usually overpowered by
more constructive energies.
I was largely responsible for the car
and everything that dealt directly with it.
This included mechanical aspects of the car (getting a 10 year old model
car dusted off and working again, and re-gearing it for lower top-speed) and
interfacing with the servos. I also
researched wireless widgets for use in our project, although all of that work
was a waste because currently there just aren’t any in the price-range of the
department. According to Larry Heep
from RF Monolith headquarters, a wireless development kit will be available at
the end of this Summer, but he was not able to provide a prototype or anything
else for us to use in time. Other work
I did for this project includes starting the user interface after teaching
myself Visual Basic, working on the configuration EEPROM for the camera,
working on getting the XBus working, and doing miscellaneous other work and
testing. I also did a lot of the
documentation and presentation preparation for the group, as well as kicking
off the group web page.
Parts of this project that I didn’t
like include the RC1 camera for starters.
This camera just seems to be a pain to configure and use, and is just
very fragile for the kind of environment it is being used in. The groups using the RC2 seemed to have far
fewer problems—I really wish we had used one of those from the beginning. Also, the lack of high-speed wireless
development kits really hurt our project.
Included in this section of the report are all our Xilinx
project schematics and Verilog code.
Also, here is the .UCF file for our project:
#Clock Pin
NET PAD_CLK LOC=P68;
# Parallel Port
Configuration Pins for Camera
NET PC_D0 LOC=P44;
#NET PC_D1 LOC=P45;
#NET PAR0 LOC=P70;
#NET PAR1 LOC=P77;
#NET PAR0 LOC=P19; #it
was 70, change to give pin for m
controller
#NET PAR1 LOC=P67; #it
was 77, change to give pin for m
controller
# DATA BITS FROM THE PC
PARALLEL PORT
#NET PP0 LOC=P44; #
Used above for camera
#NET PP1 LOC=P45; #
Used above for camera
#NET PP2 LOC=P46;
#NET PP3 LOC=P47;
#NET PP4 LOC=P48;
#NET PP5 LOC=P49;
#NET PP6 LOC=P32; # MD0 - USE SPECIAL-PURPOSE PAD IN SCHEMATIC
#NET PP7 LOC=P34; # MD2 - USE SPECIAL-PURPOSE PAD IN SCHEMATIC
# I^2C Bus - connected to
camera
#NET SCL LOC=P36;
#NET SDA LOC=P37;
#NET SCL LOC=P46;
#NET SDA LOC=P66;
#
#Camera Pins
#NET PAD_CAM3 LOC=P9;
#NET PAD_CAM2 LOC=P8;
#NET PAD_CAM1 LOC=P7;
#NET PAD_CAM0 LOC=P6;
NET PAD_CAM3 LOC=P23;
NET PAD_CAM2 LOC=P18;
NET PAD_CAM1 LOC=P24;
NET PAD_CAM0 LOC=P20;
#NET PAD_QCK LOC=P27;
NET PAD_QCK LOC=P14;
NET PAD_FST LOC=P28;
NET PAD_DACCLK LOC=P29;
#Memory Address Pins
#NET PADADDR16 LOC=P72;
#NET PADADDR15 LOC=P30;
MD1 - special pad on schematic
NET PADADDR14 LOC=P60;
NET PADADDR13 LOC=P58;
NET PADADDR12 LOC=P50;
NET PADADDR11 LOC=P56;
NET PADADDR10 LOC=P51;
NET PADADDR9 LOC=P57;
NET PADADDR8 LOC=P59;
NET PADADDR7 LOC=P84;
NET PADADDR6 LOC=P83;
NET PADADDR5 LOC=P82;
NET PADADDR4 LOC=P79;
NET PADADDR3 LOC=P78;
NET PADADDR2 LOC=P5;
NET PADADDR1 LOC=P4;
NET PADADDR0 LOC=P3;
#Memory Data Pins
NET PADDATA7 LOC=P10;
NET PADDATA6 LOC=P80;
NET PADDATA5 LOC=P81;
NET PADDATA4 LOC=P35;
NET PADDATA3 LOC=P38;
NET PADDATA2 LOC=P39;
NET PADDATA1 LOC=P40;
NET PADDATA0 LOC=P41;
#Memory Control Pins
NET PADCS1 LOC=P65; #
CS for top RAM chip
#NET PADCS0 LOC=P65; #
CS for bottom RAM chip
NET PADWR LOC=P62;
NET PADOE LOC=P61;
#VGA Pins
#NET PADRGB7 LOC=P69;
#NET PADRGB6 LOC=P66;
#NET PADRGB5 LOC=P23;
#NET PADRGB4 LOC=P18;
#NET PADRGB3 LOC=P24;
#NET PADRGB2 LOC=P20;
#NET PADRGB1 LOC=P25;
#NET PADRGB0 LOC=P26;
#NET PADHSYNC LOC=P19;
#NET PADVSYNC LOC=P67;
# XBUS interface with
microcontroller
NET PADXTAL1 LOC=P37; #
INPUT CLOCK
NET PADRST LOC=P36; #
ACTIVE-HIGH RESET
NET PADSTROBE LOC=P27; #
Port 3.7
NET PADCOM1 LOC=P70; #
Port 1.5
NET PADCOM0 LOC=P77; #
Port 1.4
NET PADXDATA3 LOC=P6; #
Port 1.3
NET PADXDATA2 LOC=P9; #
Port 1.2
NET PADXDATA1 LOC=P8; #
Port 1.1
NET PADXDATA0 LOC=P7; #
Port 1.0
# XBUS and microcontroller
reset
#NET PAD_RESET LOC=P19; # replace for the reset using parallel
port
# Interrupt signal to
microcontroller when a frame is ready to read
#NET PAD_INT LOC=P67;
#Test
#NET PAD_WRITE LOC=P66;
# Servos signal
NET PAD_STEER LOC=P25;
NET PAD_THROT LOC=P26;
Xilinx
project .ZIP file download
#include
"serial.h"
#define IMAGE_PIXELS 15360
#define SIGNAL_ADDR0 65534 //(0xfffe)
#define SIGNAL_ADDR1 65535 //(0xffff)
char control_buffer[10];
long control_head,
control_tail, image_head, image_tail, addr;
char image_buffer[64];
char sendbusy;
char control_sign;
char resendAddress;
//setBuffers is an
interrupt routine which
//Send the image pixels to
RS232 when there are data in image_buffer
//Receive the control
signal from RS232 and send them to the XESS board
//as soon as possible.
void setBuffers (void)
interrupt 4 using 1
{
if(RI == 1){
RI = 0;
//but the new signal to the control_buffer if it
is not full and
//the value is not zero since the getChar doesn't
like this value.
if(control_head != ((control_tail + 1) % 10)
&& SBUF != 0) {
control_buffer[control_tail] = SBUF;
control_tail++;
if(control_tail == 10)
control_tail = 0;
//increment the number of control
control_sign++;
//send the control sign when it come
}
//there are 3 bytes on the control_buffer which
expected to be header, steer, throttle
//so call sendSignal to send the signal to FPGA.
if(control_sign >= 3){
sendbusy = 0;
sendSignal();
}
}
if(TI == 1) {
TI = 0;
sendbusy = 0;
if(image_head != image_tail){
SBUF = image_buffer[image_head];
sendbusy = 1; //this signal indicate that there are pixel being
send in SBUF
//do
not put another one to it (avoid override the sendding pixel)
image_head++;
if(image_head == 64)
image_head = 0;
}
}
}
void init (void)
{
SCON |= 0x50;// Set the mode to 1 and enable serial reception
PCON |= 0x80;// SMOD
bit=1
TMOD = 0x20; //
IE = 0x90; //10010000 enable interrupt 4
TH1 = 243;
TR1 = 1;
//initialize variables
control_head = 0;
control_tail = 0;
image_head = 0;
image_tail = 0;
sendbusy = 0;
addr = 0;
control_sign = 0;
resendAddress = 0;
XInit();
}
char getChar()
{
char c;
if(control_head != control_tail){
c = control_buffer[control_head];
control_head++;
if(control_head == 10)
control_head = 0;
return c;
}
else
return 0; //control_buffer
is empty
}
char putChar(char c)
{
if(image_head != ((image_tail + 1) % 64)){
image_buffer[image_tail] = c;
image_tail++;
if(image_tail == 64)
image_tail = 0;
if(sendbusy == 0)
TI = 1;
return 1;
} else {
if(sendbusy == 0)
TI = 1;
return 0; //image_buffer
is full
}
}
//Now we have enough data
to send. First check if the first
//signal == 255 (the
header). If it is send the other two as
//Steer and throttle. Otherwise, don't do anything.
void sendSignal()
{
unsigned char value;
//disable interrupt until we done sending the whole
//packet of signal.
IE = 0x00;
//read the header
while((value = getChar()) == 0){}
control_sign--;
if(value == 255) {
//read steer signal
while((value = getChar()) == 0){}
control_sign--;
XWrite(0xfffe, value);
//read throttle signal
while((value = getChar()) == 0){}
control_sign--;
XWrite(0xffff, value);
//Have to resend the address in this case.
resendAddress = 1;
}
IE = 0x90; //enable
interrupt
}
//will figure out how to
send the control signal
void main()
{
init();
while(1){
unsigned char value;
addr = 0;
//resend the address since we start to read the
new image
resendAddress = 1;
//send the header to indication the start of frame
while(putChar(0) == 0){}
while (addr < IMAGE_PIXELS){
if(resendAddress == 1){
IE = 0x00;
value = XRead(addr);
resendAddress = 0;
IE = 0x90;
//avoid send the pixel=0 to
serial
if(value == 0)
value = 1;
addr++;
while (putChar(value) == 0){}
}
else {
IE = 0x00;
value = XReadCont();
IE = 0x90;
//avoid send the pixel=0 to
serial
if(value == 0)
value
= 1;
addr++;
while (putChar(value) == 0){}
}
}
}
}
The XBus header file
unsigned char
XRead(unsigned int address);
void XWrite(unsigned int
address, unsigned char value);
void XInit();
unsigned char XReadCont();
#include <reg51.h>
// #define RESETBUS
/* 0xc0 = NOP 0xd0 = Addr
0xe0 = Write 0xf0 = Read */
/* outputs */
sbit STROBE = P3^7; /* clock pin is P3.7 */
sbit COM1 = P1^5;
/* command1 pin is P1.5 */
sbit COM0 = P1^4;
/* command0 pin is P1.4 */
sbit DATA3 = P1^3; /* data3 pin is P1.3 */
sbit DATA2 = P1^2; /* data2 pin is P1.2 */
sbit DATA1 = P1^1; /* data1 pin is P1.1 */
sbit DATA0 = P1^0; /* data0 pin is P1.0 */
#define NOPCOM 0x00
#define ADDRCOM 0x10
#define READCOM 0x30
#define WRITECOM 0x20
/* macro definitions */
#define AddrNibl0(addr)\
P1 = (0xC0 | ADDRCOM | (addr & 0xf)); \
STROBE = 1; STROBE = 0; /* clock it */
#define AddrNibl1(addr)\
P1 = (0xC0 | ADDRCOM | ((addr>>4) & 0xf));\
STROBE = 1; STROBE = 0; /* clock it */
#define AddrNibl2(addr)\
P1 = (0xC0 | ADDRCOM | ((addr>>8) & 0xf));\
STROBE = 1; STROBE = 0; /* clock it */
#define AddrNibl3(addr)\
P1 = (0xC0 | ADDRCOM | ((addr>>12) & 0xf));\
STROBE = 1; STROBE = 0; /* clock it */
#define WriteNibl0(value)\
P1 = (0xC0 | WRITECOM | (value & 0xf));\
STROBE = 1; STROBE = 0; /* clock it */
// The FPGA pulls down on
STROBE to cause us to wait
#define WriteNibl1(value) \
P1 =(0xC0 | WRITECOM | ((value>>4) & 0xf)); \
STROBE = 1; \
while (STROBE==0) {}; /* Wait for FPGA to release STROBE */
\
STROBE = 0;
// We have to tristate the
data lines with the 0xf
// The FPGA pulls down on
STROBE until the read data is ready
#define ReadNibl0(value) \
P1 = (0xC0 | READCOM | 0xf); /* Remember to tristate data
lines! */ \
STROBE = 1; /* clock it */ \
while ( STROBE==0 ) {} /* Wait until FPGA is ready */ \
value = (P1 & 0x0f); \
STROBE = 0;
#define ReadNibl1(value)\
P1 = (0xC0 | READCOM | 0xf); /* Remember to tristate data
lines */ \
STROBE = 1; /* clock it */\
value = ((P1 << 4) | value); \
STROBE = 0;
unsigned char
XRead(unsigned int address)
{
unsigned char value;
/* 16 bits of address */
AddrNibl0(address);
AddrNibl1(address);
AddrNibl2(address);
AddrNibl3(address);
/* read the data bits back */
ReadNibl0(value);
ReadNibl1(value);
return value;
}
void XWrite(unsigned int
address, unsigned char value)
{
/* 16 bits of address */
AddrNibl0(address);
AddrNibl1(address);
AddrNibl2(address);
AddrNibl3(address);
/* Send the data */
WriteNibl0(value);
WriteNibl1(value);
}
// Initialize the Xbus by
issuing a bunch of NOPs to get
// the FPGA state machine
into the idle mode
void XInit()
{
P1 = 0xff; //
Tristate port 1
STROBE = 0; //
Start with strobe low
P1 = (0xC0 | NOPCOM );
STROBE = 1; STROBE = 0; /* clock it */
P1 = (0xC0 | NOPCOM );
STROBE = 1; STROBE = 0; /* clock it */
P1 = (0xC0 | NOPCOM );
STROBE = 1; STROBE = 0; /* clock it */
P1 = (0xC0 | NOPCOM );
STROBE = 1; STROBE = 0; /* clock it */
P1 = (0xC0 | NOPCOM );
STROBE = 1; STROBE = 0; /* clock it */
P1 = (0xC0 | NOPCOM );
STROBE = 1; STROBE = 0; /* clock it */
P1 = (0xC0 | NOPCOM );
STROBE = 1; STROBE = 0; /* clock it */
P1 = (0xC0 | NOPCOM );
STROBE = 1; STROBE = 0; /* clock it */
}
//I add this method to read
the next pixel without send address.
unsigned char XReadCont()
{
unsigned char value;
ReadNibl0(value);
ReadNibl1(value);
return value;
}
'file system settings
Global Const ForReading =
1, ForWriting = 2, ForAppending = 8
Global Const
TristateUseDefault = -2, TristateTrue = -1, TristateFalse = 0
'header sent before
steering and throttle values
Global Const
SERIAL_SEND_HEADER = 255
Global fs, f, ts 'file
system variables
Global pixels(239) As
String * 128 '240-element array of 128 character strings
Global mem_row As Integer
'current row of the image we are processing
Global frame_start As
Boolean 'true when frame start signal arrives, and remains true until image is
complete
Global send_img_data(16437)
As Byte 'bmp file data sent to client
Global send_controls(2) As
Byte
Global serial_port_output
As Variant
Global char_in As String
'used to find the 0 start header byte
The server form code
Private Sub Form_Load()
wsk_tcp.LocalPort = 1001 'Port to listen on
MSComm1.CommPort = 3 'Use COM3
MSComm1.Settings = "9600,N,8,1" '9600 baud, no
parity, 8 data, and 1 stop bit
send_controls(0) = SERIAL_SEND_HEADER
Set fs = CreateObject("Scripting.FileSystemObject")
End Sub
Private Sub
btn_start_Click()
If (btn_start.Caption = "Start Server") Then
'only get one character at a time from the serial port
receive buffer
'to look for the frame start header
MSComm1.InputLen = 1
'set off the serial port event every time a character comes
in
MSComm1.RThreshold = 1
frame_start = False
mem_row = 0
wsk_tcp.Listen 'start listening for a TCP connection
request
MSComm1.PortOpen =
True 'open serial port for data transfer
btn_start.Caption = "Stop Server"
Else
wsk_tcp.Close
MSComm1.PortOpen = False
btn_start.Caption = "Start Server"
End If
End Sub
Private Function img_processing(image
As Integer)
Dim filename As String
filename = "image" & CStr(image) &
".bmp"
Set f = fs.GetFile("header.bmp")
f.Copy filename 'copy the bmp header into the image file to
process
Set f = fs.GetFile(filename)
Set ts = f.OpenAsTextStream(ForAppending, TristateUseDefault)
Dim mem_row_min As Integer
Dim mem_row_max As Integer
'For image1.bmp, we use elements 0 to 119 of our pixel array,
'and for image2.bmp, we use elements 120 to 239
mem_row_min = 0 + (image - 1) * 120
mem_row_max = 119 + (image - 1) * 120
For bmp_row = mem_row_min To mem_row_max
ts.Write pixels(bmp_row) 'Write all the pixels out to the
bmp file
Next bmp_row
ts.Close
Set img_video.Picture = LoadPicture(filename)
If (wsk_tcp.State = sckConnected) Then
Call send_image(image)
End If
End Function
Private Sub
MSComm1_OnComm()
If (frame_start = False) Then
char_in = MSComm1.Input
'found the frame start header
If (Asc(char_in) = 0) Then
frame_start = True
'grab 128 characters at a time (a whole video line)
from the serial port buffer
MSComm1.InputLen = 128
'set off the serial port event every time 128 or more
characters are in the buffer
MSComm1.RThreshold = 128
End If
ElseIf (frame_start = True) Then
'Since the camera outputs rows from top to bottom,
'but a bmp file stores them from bottom to top,
'we need to store the rows in reverse order
'in our array of video lines
If (mem_row <= 119) Then
pixels(119 - mem_row) = MSComm1.Input
Else
pixels(359 - mem_row) = MSComm1.Input
End If
mem_row = (mem_row + 1) Mod 240
If (mem_row = 0 Or mem_row = 120) Then
frame_start = False
MSComm1.InputLen = 1
MSComm1.RThreshold = 1
End If
If (mem_row = 120) Then
Call img_processing(1)
ElseIf (mem_row = 0) Then
Call img_processing(2)
End If
End If
End Sub
Private Sub
wsk_tcp_ConnectionRequest(ByVal requestID As Long)
If wsk_tcp.State <> sckClosed Then
wsk_tcp.Close
End If
wsk_tcp.Accept requestID
End Sub
Private Sub
wsk_tcp_DataArrival(ByVal bytesTotal As Long)
Dim receive_controls() As Byte
ReDim receive_controls(1)
wsk_tcp.GetData receive_controls
send_controls(1) = receive_controls(0)
send_controls(2) = receive_controls(1)
txt_steer_val.Text = send_controls(1)
txt_throt_val.Text = send_controls(2)
'Store the header, and two control values into a variant, so
'we can send them to the serial port all at once
serial_port_output = send_controls
MSComm1.Output = serial_port_output
End Sub
Private Sub
send_image(image As Integer)
Open CurDir & "\image" & CStr(image) &
".bmp" For Binary As #1
'store bmp file into a byte array for transmission
For x = 0 To 16437
Get #1, , send_img_data(x)
Next x
Close #1
wsk_tcp.SendData send_img_data
End Sub
The Visual Basic Client
User Interface Code
'the bitmap file header
will always be 1078 bytes and our pixel data
'is always 15360 bytes
total
Global Const BMP_FILESIZE =
16438
'never use 0 for min and
255 for max,
'since 0 is used to check
the status of the microcontroller getChar() and putChar() functions,
'and 255 is used as a
header before steering and throttle values are sent to the board
Global Const STEER_MIN = 1
Global Const STEER_MAX =
227 'since 114 seems to be the center, we adjusted the max value to make 114
the true center
Global Const STEER_CENT =
114
'we decreased the range of
the throttle to slow down the car to a crawl
Global Const THROT_MIN = 1
Global Const THROT_MAX =
179 'can be set to 254 max, but we set it at 179 to get the car as slow as
possible
Global Const THROT_CENT =
150
Global img_number As
Integer 'image being received, either 1 or 2
Global byte_count As
Integer 'number of bytes already received for the current bitmap file
Global bmp_data(1 To 2,
16437) As Byte 'stores the data for the current image as it arrives
Global send_controls(1) As
Byte
The client user
interface control code
Private Sub
UserControl_Initialize()
'initialize values in the text boxes
txt_steer_min.Text = STEER_MIN
txt_steer_max.Text = STEER_MAX
txt_steer_cent.Text = STEER_CENT
txt_throt_min.Text =
THROT_MIN
txt_throt_max.Text = THROT_MAX
txt_throt_cent.Text = THROT_CENT
'initialize slider bar ranges and values
hsb_steer.Min = STEER_MIN
hsb_steer.Max = STEER_MAX
hsb_steer.Value = STEER_CENT
hsb_throt.Min = THROT_MIN
hsb_throt.Max = THROT_MAX
hsb_throt.Value = THROT_CENT
'initialize the steering and throttle send values to center
send_controls(0) = STEER_CENT
send_controls(1) = THROT_CENT
img_number = 1 'current image we are waiting to receive
byte_count = 0 'bytes received from the current image
wsk_tcp.RemoteHost = "128.95.8.89" 'the server
(thacker.cs) IP address
wsk_tcp.RemotePort = 1001
End Sub
Private Sub
btn_start_Click()
If (wsk_tcp.State = sckClosed) Then
wsk_tcp.Connect
btn_start.Caption = "Stop the Engine"
Else
wsk_tcp.Close
btn_start.Caption = "Start the Engine"
End If
End Sub
Private Sub
wsk_tcp_DataArrival(ByVal bytesTotal As Long)
Dim data_rec() As Byte
ReDim data_rec(bytesTotal - 1)
wsk_tcp.GetData data_rec
For X = 0 To bytesTotal - 1
bmp_data(img_number, byte_count) = data_rec(X) 'store
incoming bytes into a buffer
byte_count = byte_count + 1
'done receiving the current image
If (byte_count = BMP_FILESIZE) Then
byte_count = 0
Open CurDir & "\video" &
CStr(img_number) & ".bmp" For Binary As #1
'write out the bytes into a bmp file
For Y = 0 To 16437
Put #1, ,
bmp_data(img_number, Y)
Next Y
Close #1
Set img_video.Picture = LoadPicture(CurDir &
"\video" & CStr(img_number) & ".bmp")
img_number = 3 - img_number 'switch from image 1 to 2,
or image 2 to 1
End If
Next X
End Sub
Private Sub
hsb_steer_Change()
If hsb_steer.Value > CInt(txt_steer_max.Text) Then
hsb_steer.Value = CInt(txt_steer_max.Text)
ElseIf hsb_steer.Value < CInt(txt_steer_min.Text) Then
hsb_steer.Value = CInt(txt_steer_min.Text)
End If
txt_steer_cur.Text = CStr(hsb_steer.Value)
End Sub
Private Sub
hsb_throt_Change()
If hsb_throt.Value > CInt(txt_throt_max.Text) Then
hsb_throt.Value = CInt(txt_throt_max.Text)
ElseIf hsb_throt.Value < CInt(txt_throt_min.Text) Then
hsb_throt.Value = CInt(txt_throt_min.Text)
End If
txt_throt_cur.Text = CStr(hsb_throt.Value)
End Sub
Private Sub
lbl_mousepad_DblClick()
'recenter steering and throttle on a double-click
hsb_steer.Value = CInt(txt_steer_cent.Text)
hsb_throt.Value = CInt(txt_throt_cent.Text)
End Sub
Private Sub
lbl_mousepad_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As
Single)
lin_mouse_dir.Visible = True
lin_mouse_dir.X1 = lbl_mousepad.Left + X
lin_mouse_dir.X2 = lbl_mousepad.Left + X
lin_mouse_dir.Y1 = lbl_mousepad.Top + Y
lin_mouse_dir.Y2 = lbl_mousepad.Top + Y
End Sub
Private Sub
lbl_mousepad_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As
Single)
If lin_mouse_dir.Visible Then
lin_mouse_dir.X2 = lbl_mousepad.Left + X
lin_mouse_dir.Y2 = lbl_mousepad.Top + Y
Dim Delta_X As Single, Delta_Y As Single
Delta_X = (lin_mouse_dir.X2 - lin_mouse_dir.X1) / 4
Delta_Y = -(lin_mouse_dir.Y2 - lin_mouse_dir.Y1) / 4
If Delta_X > (CInt(txt_steer_max.Text) -
CInt(txt_steer_cent.Text)) Then
Delta_X = CInt(txt_steer_max.Text) -
CInt(txt_steer_cent.Text)
ElseIf Delta_X < (CInt(txt_steer_min.Text) - CInt(txt_steer_cent.Text))
Then
Delta_X = CInt(txt_steer_min.Text) -
CInt(txt_steer_cent.Text)
End If
If Delta_Y > (CInt(txt_throt_max.Text) -
CInt(txt_throt_cent.Text)) Then
Delta_Y = CInt(txt_throt_max.Text) - CInt(txt_throt_cent.Text)
ElseIf Delta_Y < (CInt(txt_throt_min.Text) -
CInt(txt_throt_cent.Text)) Then
Delta_Y = CInt(txt_throt_min.Text) -
CInt(txt_throt_cent.Text)
End If
hsb_steer.Value = CInt(txt_steer_cent.Text) + Delta_X
hsb_throt.Value = CInt(txt_throt_cent.Text) + Delta_Y
If chk_steer_rev.Value = 1 Then
txt_val_steer = CStr(CInt(txt_steer_max.Text) -
hsb_steer.Value)
Else
txt_val_steer = CStr(hsb_steer.Value)
End If
If chk_throt_rev.Value = 1 Then
txt_val_throt = CStr(CInt(txt_throt_max.Text) -
hsb_throt.Value)
Else
txt_val_throt = CStr(hsb_throt.Value)
End If
End If
End Sub
Private Sub
lbl_mousepad_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As
Single)
lin_mouse_dir.Visible = False
End Sub
Private Sub Timer1_Timer()
'this timer pulls the steering and throttle values back to the
center
'when the user has let go of the mouse control
If (lin_mouse_dir.Visible = False) Then
If Not (hsb_steer.Value = CInt(txt_steer_cent.Text)) Then
Select Case (hsb_steer.Value -
CInt(txt_steer_cent.Text))
Case Is > 100
hsb_steer.Value = hsb_steer.Value - 30
Case 21 To 100
hsb_steer.Value = hsb_steer.Value - 10
Case 1 To 20
hsb_steer.Value = hsb_steer.Value - 1
Case -20 To -1
hsb_steer.Value = hsb_steer.Value + 1
Case -100 To -21
hsb_steer.Value = hsb_steer.Value + 10
Case Is < -100
hsb_steer.Value = hsb_steer.Value + 30
End Select
End If
If Not
(hsb_throt.Value = CInt(txt_throt_cent.Text)) Then
Select Case (hsb_throt.Value -
CInt(txt_throt_cent.Text))
Case Is > 100
hsb_throt.Value = hsb_throt.Value - 30
Case 16 To 100
hsb_throt.Value = hsb_throt.Value - 15
Case 1 To 15
hsb_throt.Value = hsb_throt.Value - 1
Case -15 To -1
hsb_throt.Value = hsb_throt.Value + 1
Case -100 To -16
hsb_throt.Value = hsb_throt.Value + 15
Case Is < -100
hsb_throt.Value = hsb_throt.Value + 30
End Select
End If
If chk_steer_rev.Value = 1 Then
txt_val_steer = CStr(CInt(txt_steer_max.Text) -
hsb_steer.Value)
Else
txt_val_steer = CStr(hsb_steer.Value)
End If
If chk_throt_rev.Value = 1 Then
txt_val_throt = CStr(CInt(txt_throt_max.Text) -
hsb_throt.Value)
Else
txt_val_throt = CStr(hsb_throt.Value)
End If
End If
'send steering and throttle values to the server
'only if they have changed
If Not (send_controls(0) = CInt(txt_val_steer.Text) _
And send_controls(1) = CInt(txt_val_throt.Text)) Then
send_controls(0) = CByte(txt_val_steer.Text)
send_controls(1) = CByte(txt_val_throt.Text)
If (wsk_tcp.State = sckConnected) Then
wsk_tcp.SendData send_controls
End If
End If
End Sub
Private Sub
txt_steer_cur_LostFocus()
If CInt(txt_steer_cur.Text) > CInt(txt_steer_max.Text) Then
txt_steer_cur.Text = txt_steer_max.Text
ElseIf CInt(txt_steer_cur.Text) < CInt(txt_steer_min.Text)
Then
txt_steer_cur.Text = txt_steer_min.Text
End If
hsb_steer.Value = CInt(txt_steer_cur.Text)
End Sub
Private Sub
txt_steer_max_LostFocus()
If CInt(txt_steer_max.Text) > STEER_MAX Then
txt_steer_max.Text = CStr(STEER_MAX)
ElseIf CInt(txt_steer_max.Text) < CInt(txt_steer_cent.Text)
Then
txt_steer_max.Text = txt_steer_cent.Text
End If
End Sub
Private Sub
txt_steer_min_LostFocus()
If CInt(txt_steer_min.Text) < STEER_MIN Then
txt_steer_min.Text = CStr(STEER_MIN)
ElseIf CInt(txt_steer_min.Text) > CInt(txt_steer_cent.Text)
Then
txt_steer_min.Text = txt_steer_cent.Text
End If
End Sub
Private Sub
txt_throt_cur_LostFocus()
If CInt(txt_throt_cur.Text) > CInt(txt_throt_max.Text) Then
txt_throt_cur.Text = txt_throt_max.Text
ElseIf CInt(txt_throt_cur.Text) < CInt(txt_throt_min.Text)
Then
txt_throt_cur.Text = txt_throt_min.Text
End If
hsb_throt.Value = CInt(txt_throt_cur.Text)
End Sub
Private Sub
txt_throt_max_LostFocus()
If CInt(txt_throt_max.Text) > THROT_MAX Then
txt_throt_max.Text = CStr(THROT_MAX)
ElseIf CInt(txt_throt_max.Text) < CInt(txt_throt_cent.Text)
Then
txt_throt_max.Text = txt_throt_cent.Text
End If
End Sub
Private Sub
txt_throt_min_LostFocus()
If CInt(txt_throt_min.Text) < THROT_MIN Then
txt_throt_min.Text = CStr(THROT_MIN)
ElseIf CInt(txt_throt_min.Text) > CInt(txt_throt_cent.Text)
Then
txt_throt_min.Text = txt_throt_cent.Text
End If
End Sub
Private Sub
txt_steer_cent_LostFocus()
'make sure the steering center value is between the min and max
values
If CInt(txt_steer_cent.Text) > CInt(txt_steer_max.Text) Then
txt_steer_cent.Text = txt_steer_max.Text
ElseIf CInt(txt_steer_cent.Text) < CInt(txt_steer_min.Text)
Then
txt_steer_cent.Text = txt_steer_min.Text
End If
End Sub
Private Sub
txt_throt_cent_LostFocus()
'make sure the throttle center value is between the min and max
values
If CInt(txt_throt_cent.Text) > CInt(txt_throt_max.Text) Then
txt_throt_cent.Text = txt_throt_max.Text
ElseIf CInt(txt_throt_cent.Text) < CInt(txt_throt_min.Text)
Then
txt_throt_cent.Text = txt_throt_min.Text
End If
End Sub
The Visual Basic BMP
header extraction code
Private Sub Form_Load()
'use this to extract the header from a 128-by-120 pixel 8-bit
grayscale bitmap file
'once the form appears on your screen, the extraction is
complete
Const ForReading = 1, ForWriting = 2, ForAppending = 8
Const TristateUseDefault = -2, TristateTrue = -1, TristateFalse
= 0
Dim fs, f, ts, fs2, f2, ts2, s
Set fs = CreateObject("Scripting.FileSystemObject")
Set f = fs.GetFile("dummy.bmp") 'file to extract
header from, place it in this project directory
Set ts = f.OpenAsTextStream(ForReading, TristateUseDefault)
Set fs2 = CreateObject("Scripting.FileSystemObject")
Set f2 = fs2.GetFile("header.bmp") 'file which will
contain the header, create an empty file with this name
Set ts2 = f2.OpenAsTextStream(ForWriting, TristateUseDefault)
'read the entire header, which is 1078 bytes,
'the other 15360 bytes are the 128 x 120 pixels
s = ts.Read(1078)
ts2.Write s
ts.Close
ts2.Close
End Sub
Written by Carl Ebeling
Description: Stream interface between Microcontroller and
Xilinx chip for the XS40 board
Bus
Interface Signals:
STROBE -
“Clock”, generated by controller
Com[1:0] – Command indicates type of transfer
Data[3:0] - Data transferred on the bus
(refer to the ucf file to see which
pins correspond to the signals)
FPGA
Interface Signals:
Inputs
ReadData[7:0] - Data returned to the microcontroller
Busy - Assert to hold up
Read/Write
Outputs
Address[15:0] - Address
WriteData[7:0] – Data written by the microcontroller
Read - Asserted for a read
request (1 cycle)
Write - Asserted for a write
request (1 cycle)
Operation:
This interface allows the
microcontroller to write and read data to/from the FPGA. The microcontroller is always the bus
master. If the FPGA needs attention,
then it would have to use an interrupt to cause the microcontroller to wake up
and do something. (This interrupt is not
part of the XBus interface.) Each
read/write transaction uses 16 bits of address and 8 bits of data. The address can be used in the FPGA to
reference memory and registers. The
default is to map the low 32K bytes of the address space to memory and the upper
32K bytes to user registers.
Since there are a
limited number of pins, only 4 bits of address/data are transferred at a time,
and several transfers on the data bus are needed to perform a single write or
read transaction to the FPGA. It takes 4
transfers to send a 16 bit address on the bus and 2 transfers to send a byte of
data. The interface collects the
address and data from the controller and then it executes a single read or
write transaction to the FPGA. Data can
be read/written to consecutive addresses by sending the first address and then
using an implicit auto-increment to generate the remaining addresses.
A
transfer on the bus is indicated by a rising edge on the STROBE signal. That is, a rising edge on STROBE indicates a bus transfer. The
controller indicates the type of transfer using the COM signals as follows:
00 –
NOP.
01 – Part of the address is being sent.
11 – A Read is being performed.
10 – A Write is a being performed.
The timing diagram above shows how transactions take place on the bus. The top part shows a write followed by a
read using six cycles each. In the case
of a read, the controller must tristate the data bus when issuing the read command. The FPGA drives the data bus when it sees
the Read command and tristates it for all other commands. Note that for this interface, the
microcontroller is the master and the FPGA is the slave.
The lower part shows consecutive writes to sequential addresses. Each write (or read) without a new address causes the address to be auto-incremented in the FPGA interface. This allows consecutive reads and writes to be done much more quickly.
The FPGA runs much faster than the microcontroller, but it may still have to stall the microcontroller if a read or write to memory is done and the memory is busy, e.g. doing reads for the VGA interface. The FPGA causes the microcontroller to wait by pulling down on the STROBE signal, releasing it only when it is ready. The microcontroller tests the STROBE after it asserts it when reading and writing, and waits if it sees it is pulled low.
The XBus interface in the FPGA collects the address and issues reads and writes using the Read and Write signals. If the user cannot perform the read or write because the memory is busy, then the Busy signal must be asserted until the read or write can be done. If the user circuit cannot perform the read or write when the strobe is asserted, then it must assert the Busy signal, and continue asserting it until the read or write has completed. For writes, the Busy signal should be reset when the data is written. For reads, the Busy signal can be reset the cycle before the read data is ready. The interface drives the bus as long as Write is asserted and tristates it otherwise.
For reads, the interface sends the data returned on the data bus to the microcontroller. Since a read can take a long time, the interface pulls STROBE low until the data is ready. The microcontroller must wait until STROBE rises before reading the data on the data bus. Writes can also take a long time, and thus STROBE must be checked after a Write before another transaction is started. Note that STROBE is pulled low on the first Read cycle and on the second Write cycle. This is because in case of write the interface needs to get the full data before performing the write operation to the memory whereas in case of read it has to provide the microcontroller the first nibble during the first cycle and hence has to remain in the first cycle until it retrieves the data from the address. In each case the microcontroller must wait for STROBE to go high before proceeding with the next bus transfer.
IMPORTANT:
DO NOT FORGET TO USE PULL UP 1K-PULLUP RESISTORS
FOR DATA LINES AND FOR STROBE SIGNAL WHILE USING THE INTERFACE.
Notes:
1. STROBE is pulled low until the memory has been able to do the write
2. STROBE is pulled low until the memory has been able to do the read
3. When the Read command is asserted, the controller stops driving the bus and the FPGA starts driving the bus. The read data is not available until after STROBE goes high.
This is the documentation we were
provided for the EEPROM auto-config function of the RC-1 camera. We’ve also included schematics for the
push-button debouncing circuit needed for the “GO” signal to the camera.
NOTE: Documentation was not found on
the LAN. Please see the printed copy of
our documentation for this Appendix!
Once
everything is wired up, hooked up, and powered up correctly:
XBus: at first we tested the XBus reads and writes to
the registers, then the XBus reads and writes to memory, both times displaying
the result to the LED. Later, we used
extra pins on our board and put out signals to pins that we wanted to study
using the logic analyzer, oscilloscope, logic probe, etc. to test the XBus.
To
test the servo control module on the FPGA we used the Xilinx simulator to test
this verilog module. Looking at the
waveforms gave us enough information to debug it.
Camera: We spent
most of our time testing this part of the project. First, we tried running the modified vgacam project to display
the image to the VGA monitor. Using a
logic probe to look at signals, we also read the data stored in memory and
dumped it into a bitmap file to look at it.
Due to the fact that we were short some pins, we had to use another
board, and EEPROM to configure the camera, and this part took us a whole week
to make it work.
RS-232
Serial Port: We used the Vision2 debugger tool to debug
the serial interface C code and also dumped the pixel data out to the
Hyperterminal to make sure it sent the right pixel before testing this part
with the web server. Another test we
did was we wrote binary data to the 32k memory directly, and then output the
memory through the serial port. Again,
using Hyperterminal, we examined the data the computer received from the serial
port.
Server/Client
Communication: Most of the time we just used the actual
client and server code to test this part.
We ran the server application and tested to see if it would receive the
correct image from the XS40 board. Then
we compared the image on the server to the image that the client received to
see if the Internet transmission was working.
We also tested to see if the client would send the correct steering and
throttle values to the server by running the actual applications. To test the car controls on the XS40 board,
we wrote a modified version of our server application that was able to set 1
set of steering and throttle values to the serial port at a time.
The
whole testing process required the most time for this project. We had to write design test projects to test
out the different components. We used
all the debugger tools for hardware and software that we could find in the lab,
and also learned to think about the way things work so we could address the
problems.