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

 

 


Table of Contents

 

 

Summary Information

 

Project Abstract

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


Project Abstract

 

 

          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.


Project Status

 

 

          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.


Project Specification

 

 

          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?

 


Project Implementation

 

 

Micro-Controller

 

 

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.

 

 

Camera Interface

 

 

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.

 

 

Servo Interface

 

 

          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

 

Gnd+5 Volts50 Hz refreshServo control wire format

Left-Right Arrow: 1 ms 


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:

 

 

User Interface

 

 

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

 
        

 

 

 

 

 

 

 

 

 

Notes on the bitmap file format

 

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

Timer1

 

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

 
                


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.

 

 

Duy Le

 

 

          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.

 

 

Ngochan Nguyen

 

 

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.

 

 

Valdis Riekstins

 

 

          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.


Presentation 1



Presentation 2



Final Presentation



Appendix A: Xilinx Schematics

 

 

          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

 


Appendix B: Microcontroller C Code

 

 

The serial port header file

 

unsigned char XRead(unsigned int address);

void XWrite(unsigned int address, unsigned char value);

void XInit();

unsigned char XReadCont();
The serial port C code

 

#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();


The XBus C file

 

#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;

}


Appendix C: User Interface VB Code

 

 

The Visual Basic server code

 

The server globals and constants module

 

'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 client user interface globals and constants module

 

'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


Appendix D: XBus Documentation

 

 

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.

 


Appendix E: EEPROM RC-1 Configuration Documentation

 

 

          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!


Appendix F: Operating Instructions

 

 

Once everything is wired up, hooked up, and powered up correctly:

 

  1. Configure the camera using the EEPROM.  This can be done by pressing the switch to auto-load the configuration data.
  2. Reset the microcontroller by repeatedly pressing the appropriate switch until the LED turns off.
  3. Run xsload vgacam.bit
  4. Enter ‘xsport 1’, followed by ‘xsport 0’.  If the serial port doesn’t appear to be sending any data, enter the two commands again.  Use the logic probe to check the signal on the serial port.
  5. Load up the server and press the “Start Server” button.
  6. Now a person can drive the car on the Internet.  They just need to enter the URL for the client program, and then they will be able to connect to the server.  By pressing the “Start the Engine” button on the client interface, they can begin driving.

Appendix G: Testing Procedures

 

 

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.