Download the Final Report (.doc)

Final Report

Introduction

The purpose of our project was to create a control system for a mini race track that contained two electrically driven cars. We wanted to interface with the track both on the input side, controlling the motor speed with Nintendo controllers, and the output side, keeping track of a diagram and statistics about the cars.

High Level Design

To measure the position of the cars we decided to use phototransistors triggered by overhead lasers. The lasers were positioned above the sensors such that when a car went through, it would break the beam and drive an active high signal. This data is then read into the software, which used it to determine the position of the cars and display it on the LCD. The software also calculated the number of laps based on the sensor data and displays it on the screen.

An NES controller controls the speed of the car. Our software polled this controller and used the button presses to change the generated PWM signal. Our PWM signal is generated by software using processor timers to turn an output pin on and off. This pin was fed to the motor control circuit, which controlled the voltage that the track was given from a separate power supply.

High-Level Block Diagram

Hardware Design

LCD
Interfacing with the LCD, in both a read and write transaction, required a complicated timing diagram with two or more wait states. These were accomplished cleanly by using a series of flip-flops with alternating clock edges. The processor clock was used.

Although the provided timing diagram called for a clock with half the frequency of the processor's clock, this was circumvented by doubling the number of the clock edge at which each signal was asserted. Because the LCD used a single set of pins for both data inputs and outputs, a bidirectional set of FPGA pins was connected to these. A switch on the expansion board and one in the bidirectional FPGA pin macro, toggled together, controlled the direction of the pins. The other signals used by the LCD were all output signals.

Controllers
N64
The N64 controller interface is designed on a basic command-oriented philosophy. Unlike a constant hardware polling scheme, this design would theoretically allow for accessing peripheral devices such as a rumble pack or memory card. The entire hardware is triggered by a TS from the processor. It then latches in the data from the data bus at the appropriate time, and shifts it out via a shift register. A Verilog macro is used to convert the data into the format required by the controller (one low bit, 2 copies of the data bit, and one high bit) and add the stop bit. This entire process is driven by a 1 MHz clock created by a simple clock divider. After a command has been sent, the controller responds in the same format, with one bit for each button and 8 bits each for the x and y position of the joystick.

This data is captured by using a 33-bit shift register, with the clock being the data line inverted and delayed by 2 microseconds. The 33rd bit is used just to capture the stop bit, and is not read back by the processor. We initially were concerned about enabling the shift register only while the controller was responding, but then realized that it didn’t matter because earlier data (such as our command) would always get pushed out by the response. The data is then read with a simple read command, similar to what we used during lab for reading our memory or switches.

Electrically, we basically followed the circuit on the website (but much of it was figured out before we knew it was there). We found that 3.3V was enough to drive a high, which meant that we didn't need to use pullup resistors and could thus share the line with the NES controller (see port layout below).

NES
Once the N64 interface was designed and working, the NES interface was relatively simple to add. We simply send out the required PULSE and LATCH signals on a write to the address. A shift register that is clocked on the falling edges of both PULSE and LATCH is used to capture the (inverted) data from the controller, and then is read back with a simple read (as described above).

Interchangeability
One of the big anticipated features of our project was controller interchangability. We decided to implement this at the hardware level, which meant that while software could query what type of controller was being used if it wanted to, it didn't have to if it only relied on the buttons present on both controllers. Also, the controller could be changed at any time, even when the software was running. The way we implemented this was to have 2 extra input pins per port for detection. If an NES controller was hooked up, it would hold one of these lines high (by tying to its VCC input), and the N64 controller would do the same thing (with the VCC-5V input, not the 3.3V input that it used) on the other line. We had pull-down resistors on the input pins to hold them low when the controllers weren't driving them high. The Xilinx hardware then used these input pins to determine what command protocol to use, and also fed them through to the 2 unused lines on the data bus (the N64 controller only uses 30 bits) for the software's use.

Port Layout
Laying out the ports for the controllers was an interesing task. At first we wanted to use NES connectors, but were unable to obtain the connectors. We also considered DB9 (serial) connectors, but in the end went with header pins along with a connector as the simplest solution. Port layout follows (note that we never use 2 consecutive pins because they would actually be in the same row on the protoboard and electrically connected. Also note that we switched to even numbers half-way through in order to avoi repeating wire colors):

Sensors
Although we did not end up using this in our final demo because we only used one controller and thus had enough I/O pins, we initially designed an off-board shift register (using 74195 shift registers and a 7404 inverter) to get the sensor data in on only one pin. The initial design called for the sensors to essentially act as an NES controller. This had to be modified slightly because the 74195 shift registers have a synchronous latch, while the NES controller has an asynchronous latch. However, due to the modular design, this only required one extra output signal to be added, and vitually no changes to the rest of the controller hardware on the Xilinx side.

The Track
The track was built using standard slot-car track. A system of photo-transistors (4 sensors per lane) were implanted in the track. To do this a small hold was cut in the track the transistor circuit glued to the bottom of the track. A series of small wooden bridges were created to go over the track. These 4 bridges contained a round cut out so that a laser pointer could be inserted and directed at the sensors.

A PWM circuit was created for both lanes of track that could provided a signal would switch the individual tracks on and off providing movement for the cars. The output of the sensors were then directed off of the track and connected to the processor logic board.

Software Design

The software consisted of two components: a control loop and a timer. The control loop, written in C, first read the data from the controllers and the sensors. If a user pressed the "up" or "down" buttons on the Nintendo controller, the software incremented or decremented a register indicating the speed of that player's motor. If the sensors indicated that the position of either car had changed, the software updated that data; if the position had changed from "no sensor activated" to "sensor 0 activated", that player's number of laps would increase by 1. Next, the software wrote the new information to the LCD, including two drawings of cars where the last sensor for each car had been activated, and text indicating the number of laps. Then the loop began again.

This part of the software was written almost entirely in C. Reading the sensors required a simple read of an address, which could be done in C; reading the controller only required a write and then a read of an address. Writing to the LCD required several steps, including both writes and status reads, but these still only required loads and stores to an external address. So C was sufficient for even the low-level parts of the control loop.

The timer's design was interrupt-driven, and was very similar to Lab 7. As in that project, the contents of an address in memory determined what number the timer would count to before the next interrupt happened. Every time the timer went off, the flip-flop connected to the motor would be toggled, controlling the speed of the car. This part of the code was entirely in assembly, because the software had to write to special-purpose registers and do other actions that would be difficult in C.

Results and Conclusions

Results and Problems
Our design worked very closely to what we expected. We were able to get the sensors tracking the car position with relative ease. We did have some software glitches that at first caused the LCD to display the wrong location of the car. We fixed this quickly.

Pritpaul had a fully functional Xilinx circuit working for the controllers, that allowed for two Nintendo 8 or Nintendo 64 controllers. However, we were unable to integrate this circuit with the rest of the components, so Pritpaul put together a bare-bones version of the circuit with one Nintendo 8 controller. At this point, though, we were running out of time and weren't able to get full functionality out of this circuit. Reading the Nintendo controller involves doing a store and a load with a delay in between; the data showed up properly only if we set a breakpoint between the store and the load. We experimented with different sizes of for loops to create a software delay, but strangely, none of them seemed to work. This problem, too, could be easily corrected with more time. Since the delay is supposed to be a specific number of milliseconds, it could be integrated into the Xilinx circuit to bypass the uncertainties of software delays.

The issue that plagued us longest with regard to the controllers was occasional gliching that we couldn t seem to find the source of. We tried switching from schematic to Verilog for generating the output pattern, but that didn t help. What ended up solving the problem was making sure all non-clock signals used as a clock were driven through a flip-flop, and most of all, putting a BUFG (clock buffer) on all clock signals.

Towards the end of the project, we also ran into an issue where loading the FPGA bit-file would cause the processor to immediately freeze up. After many hours of investigation, we were unable to solve this issue.

Conclusions
It was possible to implement the whole project (with 2 players) with the components we had available. If we were given more time, and didn’t have a major Xlinix bug that kept the controllers from being tested we could have had the whole project working. I believe that we diffidently had enough processing power, but, we were limited on the position sensors based the availability of input pins on our current design.

Media

The Race Track
The Circuit Board
The Controller

References

IO Board Page
http://www.eecs.umich.edu/courses/eecs373/Labs/373%20Project%20IO%20Expansion%20Kit.pdf

Device Links Page
http://www.eecs.umich.edu/courses/eecs373/Labs/devices/devices.html

Controller
http://www.mixdown.ca/n64dev/
http://www.eecs.umich.edu/courses/eecs373/Labs/devices/s05_lab7.pdf