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.livewindow.LiveWindowSendable;
014import edu.wpi.first.wpilibj.tables.ITable;
015import edu.wpi.first.wpilibj.tables.ITableListener;
016
017/**
018 * Class to write digital outputs. This class will write digital outputs. Other devices that are
019 * implemented elsewhere will automatically allocate digital inputs and outputs as required.
020 */
021public class DigitalOutput extends DigitalSource implements LiveWindowSendable {
022
023  private static final int invalidPwmGenerator = 0;
024  private int m_pwmGenerator = invalidPwmGenerator;
025
026  private int m_channel = 0;
027  private int m_handle = 0;
028
029  /**
030   * Create an instance of a digital output. Create an instance of a digital output given a
031   * channel.
032   *
033   * @param channel the DIO channel to use for the digital output. 0-9 are on-board, 10-25 are on
034   *                the MXP
035   */
036  public DigitalOutput(int channel) {
037    checkDigitalChannel(channel);
038    m_channel = channel;
039
040    m_handle = DIOJNI.initializeDIOPort(DIOJNI.getPort((byte)channel), false);
041
042    HAL.report(tResourceType.kResourceType_DigitalOutput, channel);
043  }
044
045  /**
046   * Free the resources associated with a digital output.
047   */
048  @Override
049  public void free() {
050    // disable the pwm only if we have allocated it
051    if (m_pwmGenerator != invalidPwmGenerator) {
052      disablePWM();
053    }
054    DIOJNI.freeDIOPort(m_handle);
055    m_handle = 0;
056  }
057
058  /**
059   * Set the value of a digital output.
060   *
061   * @param value true is on, off is false
062   */
063  public void set(boolean value) {
064    DIOJNI.setDIO(m_handle, (short) (value ? 1 : 0));
065  }
066
067  /**
068   * Gets the value being output from the Digital Output.
069   *
070   * @return the state of the digital output.
071   */
072  public boolean get() {
073    return DIOJNI.getDIO(m_handle);
074  }
075
076  /**
077   * @return The GPIO channel number that this object represents.
078   */
079  @Override
080  public int getChannel() {
081    return m_channel;
082  }
083
084  /**
085   * Generate a single pulse. There can only be a single pulse going at any time.
086   *
087   * @param pulseLength The length of the pulse.
088   */
089  public void pulse(final double pulseLength) {
090    DIOJNI.pulse(m_handle, pulseLength);
091  }
092
093  /**
094   * Generate a single pulse. Write a pulse to the specified digital output channel. There can only
095   * be a single pulse going at any time.
096   *
097   * @param channel     Unused
098   * @param pulseLength The length of the pulse.
099   * @deprecated Use {@link #pulse(double)} instead.
100   */
101  @Deprecated
102  @SuppressWarnings("PMD.UnusedFormalParameter")
103  public void pulse(final int channel, final double pulseLength) {
104    DIOJNI.pulse(m_handle, pulseLength);
105  }
106
107  /**
108   * @param channel     Unused
109   * @param pulseLength The length of the pulse.
110   * @deprecated Generate a single pulse. Write a pulse to the specified digital output channel.
111   *             There can only be a single pulse going at any time.
112   */
113  @Deprecated
114  @SuppressWarnings("PMD.UnusedFormalParameter")
115  public void pulse(final int channel, final int pulseLength) {
116    double convertedPulse = pulseLength / 1.0e9 * (DIOJNI.getLoopTiming() * 25);
117    System.err
118        .println("You should use the double version of pulse for portability.  This is deprecated");
119    DIOJNI.pulse(m_handle, convertedPulse);
120  }
121
122  /**
123   * Determine if the pulse is still going. Determine if a previously started pulse is still going.
124   *
125   * @return true if pulsing
126   */
127  public boolean isPulsing() {
128    return DIOJNI.isPulsing(m_handle);
129  }
130
131  /**
132   * Change the PWM frequency of the PWM output on a Digital Output line.
133   *
134   * <p>The valid range is from 0.6 Hz to 19 kHz. The frequency resolution is logarithmic.
135   *
136   * <p>There is only one PWM frequency for all channnels.
137   *
138   * @param rate The frequency to output all digital output PWM signals.
139   */
140  public void setPWMRate(double rate) {
141    DIOJNI.setDigitalPWMRate(rate);
142  }
143
144  /**
145   * Enable a PWM Output on this line.
146   *
147   * <p>Allocate one of the 6 DO PWM generator resources.
148   *
149   * <p>Supply the initial duty-cycle to output so as to avoid a glitch when first starting.
150   *
151   * <p>The resolution of the duty cycle is 8-bit for low frequencies (1kHz or less) but is reduced
152   * the higher the frequency of the PWM signal is.
153   *
154   * @param initialDutyCycle The duty-cycle to start generating. [0..1]
155   */
156  public void enablePWM(double initialDutyCycle) {
157    if (m_pwmGenerator != invalidPwmGenerator) {
158      return;
159    }
160    m_pwmGenerator = DIOJNI.allocateDigitalPWM();
161    DIOJNI.setDigitalPWMDutyCycle(m_pwmGenerator, initialDutyCycle);
162    DIOJNI.setDigitalPWMOutputChannel(m_pwmGenerator, m_channel);
163  }
164
165  /**
166   * Change this line from a PWM output back to a static Digital Output line.
167   *
168   * <p>Free up one of the 6 DO PWM generator resources that were in use.
169   */
170  public void disablePWM() {
171    if (m_pwmGenerator == invalidPwmGenerator) {
172      return;
173    }
174    // Disable the output by routing to a dead bit.
175    DIOJNI.setDigitalPWMOutputChannel(m_pwmGenerator, kDigitalChannels);
176    DIOJNI.freeDigitalPWM(m_pwmGenerator);
177    m_pwmGenerator = invalidPwmGenerator;
178  }
179
180  /**
181   * Change the duty-cycle that is being generated on the line.
182   *
183   * <p>The resolution of the duty cycle is 8-bit for low frequencies (1kHz or less) but is reduced
184   * the
185   * higher the frequency of the PWM signal is.
186   *
187   * @param dutyCycle The duty-cycle to change to. [0..1]
188   */
189  public void updateDutyCycle(double dutyCycle) {
190    if (m_pwmGenerator == invalidPwmGenerator) {
191      return;
192    }
193    DIOJNI.setDigitalPWMDutyCycle(m_pwmGenerator, dutyCycle);
194  }
195
196  /**
197   * Get the analog trigger type.
198   *
199   * @return false
200   */
201  @Override
202  public int getAnalogTriggerTypeForRouting() {
203    return 0;
204  }
205
206  /**
207   * Is this an analog trigger.
208   *
209   * @return true if this is an analog trigger
210   */
211  @Override
212  public boolean isAnalogTrigger() {
213    return false;
214  }
215
216  /**
217   * Get the HAL Port Handle.
218   *
219   * @return The HAL Handle to the specified source.
220   */
221  @Override
222  public int getPortHandleForRouting() {
223    return m_handle;
224  }
225
226  /*
227   * Live Window code, only does anything if live window is activated.
228   */
229  @Override
230  public String getSmartDashboardType() {
231    return "Digital Output";
232  }
233
234  private ITable m_table;
235  private ITableListener m_tableListener;
236
237
238  @Override
239  public void initTable(ITable subtable) {
240    m_table = subtable;
241    updateTable();
242  }
243
244
245  @Override
246  public ITable getTable() {
247    return m_table;
248  }
249
250
251  @Override
252  public void updateTable() {
253    // TODO: Put current value.
254  }
255
256
257  @Override
258  public void startLiveWindowMode() {
259    m_tableListener = new ITableListener() {
260      @Override
261      public void valueChanged(ITable itable, String key, Object value, boolean bln) {
262        set((Boolean) value);
263      }
264    };
265    m_table.addTableListener("Value", m_tableListener, true);
266  }
267
268
269  @Override
270  public void stopLiveWindowMode() {
271    // TODO: Broken, should only remove the listener from "Value" only.
272    m_table.removeTableListener(m_tableListener);
273  }
274}