001/*----------------------------------------------------------------------------*/
002/* Copyright (c) FIRST 2008-2017. All Rights Reserved.                        */
003/* Open Source Software - may be modified and shared by FRC teams. The code   */
004/* must be accompanied by the FIRST BSD license file in the root directory of */
005/* the project.                                                               */
006/*----------------------------------------------------------------------------*/
007
008package edu.wpi.first.wpilibj;
009
010import edu.wpi.first.wpilibj.hal.DIOJNI;
011import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
012import edu.wpi.first.wpilibj.hal.HAL;
013import edu.wpi.first.wpilibj.hal.PWMJNI;
014import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
015import edu.wpi.first.wpilibj.tables.ITable;
016import edu.wpi.first.wpilibj.tables.ITableListener;
017
018/**
019 * Class implements the PWM generation in the FPGA.
020 *
021 * <p>The values supplied as arguments for PWM outputs range from -1.0 to 1.0. They are mapped to
022 * the hardware dependent values, in this case 0-2000 for the FPGA. Changes are immediately sent to
023 * the FPGA, and the update occurs at the next FPGA cycle. There is no delay.
024 *
025 * <p>As of revision 0.1.10 of the FPGA, the FPGA interprets the 0-2000 values as follows: - 2000 =
026 * maximum pulse width - 1999 to 1001 = linear scaling from "full forward" to "center" - 1000 =
027 * center value - 999 to 2 = linear scaling from "center" to "full reverse" - 1 = minimum pulse
028 * width (currently .5ms) - 0 = disabled (i.e. PWM output is held low)
029 */
030public class PWM extends SensorBase implements LiveWindowSendable {
031  /**
032   * Represents the amount to multiply the minimum servo-pulse pwm period by.
033   */
034  public enum PeriodMultiplier {
035    /**
036     * Period Multiplier: don't skip pulses.
037     */
038    k1X,
039    /**
040     * Period Multiplier: skip every other pulse.
041     */
042    k2X,
043    /**
044     * Period Multiplier: skip three out of four pulses.
045     */
046    k4X
047  }
048
049  private int m_channel;
050  private int m_handle;
051
052  /**
053   * Allocate a PWM given a channel.
054   *
055   * @param channel The PWM channel number. 0-9 are on-board, 10-19 are on the MXP port
056   */
057  public PWM(final int channel) {
058    checkPWMChannel(channel);
059    m_channel = channel;
060
061    m_handle = PWMJNI.initializePWMPort(DIOJNI.getPort((byte) channel));
062
063    setDisabled();
064
065    PWMJNI.setPWMEliminateDeadband(m_handle, false);
066
067    HAL.report(tResourceType.kResourceType_PWM, channel);
068  }
069
070  /**
071   * Free the PWM channel.
072   *
073   * <p>Free the resource associated with the PWM channel and set the value to 0.
074   */
075  public void free() {
076    if (m_handle == 0) {
077      return;
078    }
079    setDisabled();
080    PWMJNI.freePWMPort(m_handle);
081    m_handle = 0;
082  }
083
084  /**
085   * Optionally eliminate the deadband from a speed controller.
086   *
087   * @param eliminateDeadband If true, set the motor curve on the Jaguar to eliminate the deadband
088   *                          in the middle of the range. Otherwise, keep the full range without
089   *                          modifying any values.
090   */
091  public void enableDeadbandElimination(boolean eliminateDeadband) {
092    PWMJNI.setPWMEliminateDeadband(m_handle, eliminateDeadband);
093  }
094
095  /**
096   * Set the bounds on the PWM values. This sets the bounds on the PWM values for a particular each
097   * type of controller. The values determine the upper and lower speeds as well as the deadband
098   * bracket.
099   *
100   * @param max         The Minimum pwm value
101   * @param deadbandMax The high end of the deadband range
102   * @param center      The center speed (off)
103   * @param deadbandMin The low end of the deadband range
104   * @param min         The minimum pwm value
105   * @deprecated Recommended to set bounds in ms using {@link #setBounds(double, double, double,
106   * double, double)}
107   */
108  @Deprecated
109  public void setRawBounds(final int max, final int deadbandMax, final int center,
110                        final int deadbandMin, final int min) {
111    PWMJNI.setPWMConfigRaw(m_handle, max, deadbandMax, center, deadbandMin, min);
112  }
113
114  /**
115   * Set the bounds on the PWM pulse widths. This sets the bounds on the PWM values for a particular
116   * type of controller. The values determine the upper and lower speeds as well as the deadband
117   * bracket.
118   *
119   * @param max         The max PWM pulse width in ms
120   * @param deadbandMax The high end of the deadband range pulse width in ms
121   * @param center      The center (off) pulse width in ms
122   * @param deadbandMin The low end of the deadband pulse width in ms
123   * @param min         The minimum pulse width in ms
124   */
125  public void setBounds(double max, double deadbandMax, double center, double deadbandMin,
126                           double min) {
127    PWMJNI.setPWMConfig(m_handle, max, deadbandMax, center, deadbandMin, min);
128  }
129
130  /**
131   * Gets the bounds on the PWM pulse widths. This Gets the bounds on the PWM values for a
132   * particular type of controller. The values determine the upper and lower speeds as well
133   * as the deadband bracket.
134   */
135  public PWMConfigDataResult getRawBounds() {
136    return PWMJNI.getPWMConfigRaw(m_handle);
137  }
138
139  /**
140   * Gets the channel number associated with the PWM Object.
141   *
142   * @return The channel number.
143   */
144  public int getChannel() {
145    return m_channel;
146  }
147
148  /**
149   * Set the PWM value based on a position.
150   *
151   * <p>This is intended to be used by servos.
152   *
153   * @param pos The position to set the servo between 0.0 and 1.0.
154   * @pre SetMaxPositivePwm() called.
155   * @pre SetMinNegativePwm() called.
156   */
157  public void setPosition(double pos) {
158    PWMJNI.setPWMPosition(m_handle, pos);
159  }
160
161  /**
162   * Get the PWM value in terms of a position.
163   *
164   * <p>This is intended to be used by servos.
165   *
166   * @return The position the servo is set to between 0.0 and 1.0.
167   * @pre SetMaxPositivePwm() called.
168   * @pre SetMinNegativePwm() called.
169   */
170  public double getPosition() {
171    return PWMJNI.getPWMPosition(m_handle);
172  }
173
174  /**
175   * Set the PWM value based on a speed.
176   *
177   * <p>This is intended to be used by speed controllers.
178   *
179   * @param speed The speed to set the speed controller between -1.0 and 1.0.
180   * @pre SetMaxPositivePwm() called.
181   * @pre SetMinPositivePwm() called.
182   * @pre SetCenterPwm() called.
183   * @pre SetMaxNegativePwm() called.
184   * @pre SetMinNegativePwm() called.
185   */
186  public void setSpeed(double speed) {
187    PWMJNI.setPWMSpeed(m_handle, speed);
188  }
189
190  /**
191   * Get the PWM value in terms of speed.
192   *
193   * <p>This is intended to be used by speed controllers.
194   *
195   * @return The most recently set speed between -1.0 and 1.0.
196   * @pre SetMaxPositivePwm() called.
197   * @pre SetMinPositivePwm() called.
198   * @pre SetMaxNegativePwm() called.
199   * @pre SetMinNegativePwm() called.
200   */
201  public double getSpeed() {
202    return PWMJNI.getPWMSpeed(m_handle);
203  }
204
205  /**
206   * Set the PWM value directly to the hardware.
207   *
208   * <p>Write a raw value to a PWM channel.
209   *
210   * @param value Raw PWM value. Range 0 - 255.
211   */
212  public void setRaw(int value) {
213    PWMJNI.setPWMRaw(m_handle, (short) value);
214  }
215
216  /**
217   * Get the PWM value directly from the hardware.
218   *
219   * <p>Read a raw value from a PWM channel.
220   *
221   * @return Raw PWM control value. Range: 0 - 255.
222   */
223  public int getRaw() {
224    return PWMJNI.getPWMRaw(m_handle);
225  }
226
227  /**
228   * Temporarily disables the PWM output. The next set call will reenable
229   * the output.
230   */
231  public void setDisabled() {
232    PWMJNI.setPWMDisabled(m_handle);
233  }
234
235  /**
236   * Slow down the PWM signal for old devices.
237   *
238   * @param mult The period multiplier to apply to this channel
239   */
240  public void setPeriodMultiplier(PeriodMultiplier mult) {
241    switch (mult) {
242      case k4X:
243        // Squelch 3 out of 4 outputs
244        PWMJNI.setPWMPeriodScale(m_handle, 3);
245        break;
246      case k2X:
247        // Squelch 1 out of 2 outputs
248        PWMJNI.setPWMPeriodScale(m_handle, 1);
249        break;
250      case k1X:
251        // Don't squelch any outputs
252        PWMJNI.setPWMPeriodScale(m_handle, 0);
253        break;
254      default:
255        // Cannot hit this, limited by PeriodMultiplier enum
256    }
257  }
258
259  protected void setZeroLatch() {
260    PWMJNI.latchPWMZero(m_handle);
261  }
262
263  /*
264   * Live Window code, only does anything if live window is activated.
265   */
266  @Override
267  public String getSmartDashboardType() {
268    return "Speed Controller";
269  }
270
271  private ITable m_table;
272  private ITableListener m_tableListener;
273
274  @Override
275  public void initTable(ITable subtable) {
276    m_table = subtable;
277    updateTable();
278  }
279
280  @Override
281  public void updateTable() {
282    if (m_table != null) {
283      m_table.putNumber("Value", getSpeed());
284    }
285  }
286
287  @Override
288  public ITable getTable() {
289    return m_table;
290  }
291
292  @Override
293  public void startLiveWindowMode() {
294    setSpeed(0); // Stop for safety
295    m_tableListener = new ITableListener() {
296      public void valueChanged(ITable itable, String key, Object value, boolean bln) {
297        setSpeed((Double) value);
298      }
299    };
300    m_table.addTableListener("Value", m_tableListener, true);
301  }
302
303  @Override
304  public void stopLiveWindowMode() {
305    setSpeed(0); // Stop for safety
306    // TODO: Broken, should only remove the listener from "Value" only.
307    m_table.removeTableListener(m_tableListener);
308  }
309}