Introduction

In this laboratory, you will explore direct interaction between the FPGA and the “outside world.” Until now, you performed all input and output operations using the macros in the Platform Abstraction Layer (PAL), which sheilded you from the nitty-gritty of actually manipulating the voltage values on the FPGA pins. With this project your PAL-provided innocence expires.

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 M3. 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 differences:

    1. Instead of defining PAL_TARGET_CLOCK_RATE with a #define preprocessor directive, do it in the Preprocessor tab of the Project Settings panel. Where you specify USE_xxx, add PAL_TARGET_CLOCK_RATE=50000000 for the EDIF configuration. But for Simulation, specify a 1 MHz clock instead of 50 MHz. The reason for using such a low clock rate for simulation is to help make using the Waveform Analyzer a bit easier as discussed below. By specifying the two different clock rates here, you avoid the need for conditional compilation in your source code later.
    2. On the same Preprocessor tab, put the relative path to the directory containing your delayProcs.hch file in the “Additional Include Directories” box. Now you will be able to include your header file using angle brackets instead of quotes, and won’t have to type in the path to the directory in your code either
    3. Finally, on the Linker tab, put the relative pathname to your library file’s directory in the Additional Library Path box, and remove the path component from the library name.

    The previous assignment should have used the second two items above, but it’s not too late to make things right!

  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.

    Optional:

    Use a Handel-C assert() statement to generate the width of the counter register. If the number of ticks is not greater than 0, the assertion should fail, reporting the delay interval and clock speed passed as parameters. Test the assertion by calling the macro with a short interval and slow clock speed.

  3. Use the buttons to change values and seven segment displays to show them.

    Create a source file for the laboratory named motor_controller.hcc if you have not done so already. Write code so that pressing button 0 makes the seven segment displays count upward from 00 to 99 in decimal. Think in terms of two Binary Coded Decimal (BCD) digits. Each button press and release must make the counter increment by exactly one; this is an exercise in “debouncing” the button, as described in class. You may cause the count to increment on either the press or the release, but not both. Now make it so that pressing button 1 makes the displays count downwards instead of upwards. Turn on the right decimal point when counting up and turn it off when counting down. Automatically switch directions if the user tries to count up beyond 99 or down below 00.

    Even if the code works properly on the RC200E, also make sure it simulates correctly. You will be using the simulator explcitly below, so this part has to work correctly both ways from the beginning.

    Do not use division! Avoid the temptation to use a single register to hold the value of the counter and to use divide and mod to break it into decimal digits. Or don’t believe me, try it, and see how much of the FPGA your design uses and how long it takes to build!

  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 hundredths of a millisecond. That is, a pulseWidth value of 100 will be 1 msec, 150 will be 1.5 msec, and 200 will be 2 msec. Add logic to 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.

    Write two parallel endless loops, and connect them by a 0-bit wide channel. The first loop is to write 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 waits at the channel again.

    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 both show the timing ouf your program’s outputs (what we want) and 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.

    Managing the clock is tricky. If you just use pal_master.hch to generate the value of PAL_ACTUAL_CLOCK_RATE from PAL_TARGET_CLOCK_RATE, you will get multiple clocks defined for your code: one set clock in pal_master.hch, and a second one that you will need to code following the guidelines of the Waveform Analyzer manual. The trick is to use conditional compilation and to include pal_master after you define PAL_ACTUAL_CLOCK_RATE explicitly yourself:

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

    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.

    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 Button 0 on the virtual console should change 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. Use Waveform Analyzer cursors to verify that the pulses are of the correct width.

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

    Look through the Tektronix “XYZ’s of Oscilloscopes” primer provided to you. 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_RC200E is 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 ground clip disconnected. 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 press Button 0.
  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.