Introduction

In this laboratory, you will explore direct interaction between the FPGA and the “outside world.” Instead of using the macros in the Platform Abstraction Layer (PAL) to access the input and output devices, this project will show you the nitty-gritty of actually manipulating the voltage values on the FPGA pins.

As you know, the Simulation design flow allows you to debug your code both by monitoring its external behavior on the PAL Virtual Console, and by examining its internal state using the DK simulator. But for this project, you are using a device that is not supported by the PAL Virtual Console, namely a small servomotor that is controlled using pulse width modulation (PWM). Not only do you not have the PAL Virtual Console for feedback, but you also have to deal with the fact that the DK simulator does not provide the real-time timing information you need to check whether your pulse width modulation code is actually modulating pulse widths correctly. So this project introduces you to the use of two tools for testing (and debugging) the timing of your design. One is an extension to the DK simulation software, called the Waveform Analyzer. In addition, you will practice using an oscilloscope to check the actual waveforms generated by your design. Finally, you will connect a servomotor to the RC200E and verify that your program actually does control the motor correctly.

Controller Description

The servomotor you will be controlling, the Futaba S3003, is normally used to operate radio controlled model airplanes, cars, or boats. Normally, when it receives pulses of one width, the motor shaft rotates clockwise about a quarter turn and when it receives pulses of a different width, it rotates in the opposite direction. It has an internal circuit that senses its own displacement clockwise or counterclockwise and returns the motor to its “neutral” position when no pulses are applied. This design is great for steering planes, cars, and boats, but cannot be used to make cars or robots move — at least not very far. So a popular modification is to modify the motor mechanically to remove the stop that prevents 360° rotation, and the linkage between the shaft position sensor (a potentiometer) and the shaft. The motors in the lab have been modified this way.

The servomotor has three wires connected to it: a black one that goes to ground, a red one that connects to a positive voltage betwen 4.8 and 6.0 volts, and a white one that receives control pulses. We have rigged connectors so the motors can be connected to the 50-pin “expansion header” on the side of the RC200. Pin 46 of the expansion header provides +5VDC, eight pins supply ground, and 34 of the pins are connected to the I/O pins of the FPGA. When you plug the motor connector into the expansion header, the red wire goes to Pin 46, the black wire goes to Pin 50 (one of the ground connections), and the white wire goes to Pin 38, which is connected to I/O pin “V2” of the FPGA.

To make the motor work, it must receive a 50 Hz pulse train. If the width of the pulses is 1 msec the motor rotates in one direction; if the width of the pulses is 2 msec, the motor rotates in the opposite direction, and if the width of the pulses is 1.5 msec, the motor stops rotating. When your controller is finished, it will generate a continuous 50 Hz pulse stream on FPGA Pin V2. The two pushbuttons on the side of the RC200E will modify the width of the pulses as follows: initially, the pulse width will be 1.5 msec; pressing button 0 will increment or decrement the pulse width by 10 µsec, and pressing button 1 will control whether button 0 adds to or subtracts from the pulse width. By providing such fine control over the pulse widths, you should be able to control not only the direction of rotation, but also the speed.

Procedure

  1. Start a new project.

    Create a new project named “Motor_Controller” for this laboratory. Configure it for Simulation and EDIF in the usual way, with the following clock values:

    Use 1 MHz as the target clock rate for the Debug build configuration and 50 MHz as the target clock rate for EDIF. Note that the target rate for simulation is not totally arbitrary; correct operation of the Waveform Analyzer program (see below) will depend on this value matching the setup parameters specified here. You will need some conditional compilation for this assignment, so you find it easier to use #define statements to set the clock values rather than using the Preprocessor tab in the project settings panel. Use Laboratory 2 as a model for this.

  2. Add usecDelay() to your delayProcs library.

    You need finer timing control for this project than your msecDelay() provides. Update delayProcs.hch to include the function prototype for a macro proc that delays for a specified number of microseconds (µsec) rather than milliseconds. Add a second Handel-C source file to the library project with your new usecDelay() procedure in it.

  3. Construct a modified version of Laboratory 2

    Superficially, this project starts out very similarly to Laboratory 2 because we will use the two pushbuttons to control the operation of a two digit counter. But in this project the buttons will be used to control the width of the pulses going to the servomotor and the counter will show how wide those pulses are. Here is how the values displayed on the seven segment displays will ultimately map to pulse widths:

    DisplayPulse Width in Milliseconds
    001.000
    011.010
    501.500
    991.990

    We want to use the two buttons to change the pulse width so you can see the effect on the motor. Here is a suggested way to do that: one button toggles whether the display steps upward or downward when it changes, and the other button controls whether the display is actively stepping (while the button is pressed) or not (while the button is not pressed). Compared to Lab 2, the first button works like the run button, and might meaningfully be called “up/down” for this project. The second button works like the fast button, and might be called “step/stop” for this project. Here are some features to note:

    • Instead of starting at 00 the counter should start at 50, which corresponds to 1.5 msec, the neutral point where the motor stops.
    • Instead of changing once a second or ten times a second, it will be convenient (for the user) if the value changes at some rate in between that range when stepping through the different values. Something in the range of 2-5 steps per second would be good; try to design your code so that this is an easy parameter to “tweak”. If you want a challenge, design it so the stepping rate is slow when the step/stop button is first pressed, and then speeds up if the button is held on for a while.
    • When stepping, have the direction automatically toggle when either limit is reached. That is, when stepping upward, switch to downward when the value 99 is reached and when stepping downward switch to upward when the value 00 is reached.

    Make sure your code works correctly at this point for both simulation and on the RC200E.

  4. Simulate code that outputs to a pin.

    Define a 1-bit unsigned integer named controlPin, and another variable named pulseWidth that can hold a value that ranges from 100 to 200. The former will output the pulses to the motor, and the latter will tell how wide each pulse is, in hundredth of a millisecond (10 µsec) units. That is, a pulseWidth value of 100 will correspond to 1 msec, 150 will be 1.5 msec, and 199 will be 1.99 msec. (Our design doesn’t allow the value to reach a full 2 msec.) Set up your code so that pulseWidth and the seven segment displays are synchronized. when the seven segment displays show 00, pulseWidth should be 100, 50 on the display corresponds to a pulseWidth of 150, and 99 on the display corresponds to a pulseWidth of 199.

    Avoid using division! Avoid the temptation to use a single register to hold the value of pulseWidth and to use divide and mod operators (/ and %) to break it into the tens and units decimal digits. [This note is left over from a previous semester where students did not do the two digit timer project. It wouldn’t make sense for you to set up the relationship between pulseWidth, units, and tens this way but I’m leaving this note in because I want to make the point about division anyway.] Using division and modulo would work, but the design will be bigger and take longer to build than if you avoid these operations. Multiplication, on the other hand, can be done relatively efficiently on the FPGA, especially if the widths of the operands are 18 bits or less.

    To set up the 50 Hz pulse train, write two parallel endless loops, and connect them by a 0-bit wide channel (chan). The Handel-C language manual describes channels: they are used to pass information from one thread to another with automatic synchronization so that the actual transfer takes place on the same clock cycle regardless of whether the reading thread or the writing thread gets to its side of the channel first. We need the synchronization feature but not the actual information-exchange feature, so you can declare 0-bit wide variables (no information) that get written to and read from the channel. The first loop writes to the channel at a 50 Hz rate. The second loop, reads from the channel, sets controlPin to 1, delays the number of microseconds given by the current value of pulseWidth, sets controlPin to zero, and then waits at the channel again. Implicit in this algorithm is the fact that the pulse widths will always be less than the period of the 50 Hz pulse train, so the 50 Hz thread will never block waiting for the pulse generator thread to read from the channel.

    Remember, usecDelay() is a macro that gets expanded at compile time: the values passed as arguments must be constants known at compile time, not variables. You cannot simply pass (1000 + pulseWidth * 10) as the number of microseconds to delay. Rather, you need a loop that calls usecDelay(ClockRate, 10) the appropriate number of times.

    Now you need to add code that connects controlPin to the simulator. Look at the Waveform Anlyzer manual in the Celoxica/DK/Documentation directory. The Waveform Analyzier can do two different things: (1) show the timing of your program’s outputs (what we want) and (2) generate waveforms for input to your program (not of interest to us for this project). So, you are interested in only Chapter 1 of the manual for this assignment, and need to read just the first ten pages before continuing.


    Using the Waveform Analyzer manual as a guide, set up your code so you can monitor the value of controlPin. You will have to synchronize the DK simulator’clock with the Waveform Analyzer’s clock, and you will have to connect the value of controlPin to the Waveform Analyzer’s “terminal” interface.

    #ifdef USE_SIM #define PAL_ACTUAL_CLOCK_RATE 1000000 set clock = external "P1" with { extlib = "DKSync.dll", extinst = "1000", // Period of 1MHz simulated clock in nsec extfunc = "DKSyncGetSet" }; interface bus_out() motor( unsigned 1 out = controlPin ) with { extlib = "DKConnect.dll", extinst = "t(1)", extfunc = "DKConnectGetSet" }; #else #define PAL_TARGET_CLOCK_RATE 50000000 #endif #include <pal_master.hch> macro expr ClockRate = PAL_ACTUAL_CLOCK_RATE;

    I apologize for giving you so much code!
    Be sure to study it rather than just cutting and pasting blindly.

    When you set up your Waveform Analyzer trace window, be sure to specify the correct clock period (in nsec), and set the “Default No. Points” so that one sweep across the window will correspond to exactly one period of the 50 Hz pulse train. The idea is that you want each pulse to display in the same place in the window on each sweep. (I’m omitting the details on this; try to figure the numbers out yourself.)

    When you have this step working correctly, you should be able to observe the PAL Virtual Console and the Waveform Analyzer window at the same time. Clicking on buttons on the virtual console should control the value displayed on the seven segment displays, and you should be able to see the effect on the waveform being traced out in the Waveform Analyzer window. Make sure the pulses don’t drift across the window, even a little bit. If they do move it means the 50 Hz thread is not taking exactly 1/50" per cycle. Use Waveform Analyzer cursors to verify that the pulses are of the correct width. Because the simulated clock has a period of one µsec, you will be able to detect any inaccuracy in either your pulse width thread (wrong number of calls to usecDelay()) and/or the implementation of usecDelay() itself.

    Because of the pixel resolution of the monitor, you might not be able to get the cursors to line up exactly with the edges of the pulses and the pulse width measurements may be off by a small amount as a result. However, (1) you should be able to see that the pulse widths change by exactly 100 µsec for each step up or down, and (2) the pulses should always start at exactly the same point in the waveform window and not drift left or right.

  5. Familiarize yourself with the oscilloscopes used in the lab.

    Look through the Tektronix “XYZ’s of Oscilloscopes” primer. You will be working with a Digital Storage Oscilloscope (DSO) to look at waveforms output by the FPGA to the expansion header pins on the side of the RC200E. Look at pages 13-14 for a description of DSOs, pages 18-32 for a description of the various controls on an oscilloscope, and pages 46-47 on probe compensation.

    Set up an oscilloscope and compensate the probe.

  6. Configure for EDIF with controlPin connected to an expansion header pin, and monitor the pin using the oscilloscope.

    Use conditional compilation to compile the following interface definition if USE_SIM is not defined:

    interface bus_out() motor( unsigned 1 out = controlPin ) with { data = { "V2" } };

    V2 is the name of the pin on the FPGA that is connected to pin number 38 on the expansion header. (The manual that documents this is in …\Celoxica\PDK\Documentation\PSL\RC20x\Manuals.)

    Carefully connect the tip of the oscilloscope probe to pin 38 of the expansion header. It’s the sixth pin from the left on the bottom row; the second one past the empty spot with 42 printed on the circuit board. You will probably get a perfectly good signal with the probe’s ground clip disconnected, so don’t worry about connecting it. Download your program to the FPGA and perform the following exercises with the oscilloscope:

    First press the “Auto Set” button to have the oscilloscope detect your signal automatically. If necessary, use the trigger menu to select positive edge triggering from channel 1 and use the channel 1 menu to select DC coupling. Bandwidth limiting should be off, volts per division can be either coarse or fine, the probe should auto-detect 10X attenuation; if not, set it to 10X and be sure the switch on the side of the probe is set to 10X too.

    1. Verify that your pulse train is operating at 50 Hz.
    2. Verify that the initial pulse width is exactly 1.5 msec. Use the “Measure” and “Cursor” buttons to measure the pulse width.
    3. Verify that the pulse width changes by 10 µsec each time you increment or decrement the seven segment display value by one.
  7. Connect a servomotor to the RC200E and verify that your controller actually works.

    If you are sure the pulse parameters are correct on the oscilloscope but the motor “creeps” in one direction or the other when you first start your program, there is a screwdriver adjustment I can make that will stop it.