001// Copyright (c) FIRST and other WPILib contributors.
002// Open Source Software; you can modify and/or share it under the terms of
003// the WPILib BSD license file in the root directory of this project.
004
005package edu.wpi.first.wpilibj;
006
007import edu.wpi.first.hal.DIOJNI;
008import edu.wpi.first.hal.FRCNetComm.tResourceType;
009import edu.wpi.first.hal.HAL;
010import edu.wpi.first.hal.SimDevice;
011import edu.wpi.first.util.sendable.Sendable;
012import edu.wpi.first.util.sendable.SendableBuilder;
013import edu.wpi.first.util.sendable.SendableRegistry;
014
015/**
016 * Class to write digital outputs. This class will write digital outputs. Other devices that are
017 * implemented elsewhere will automatically allocate digital inputs and outputs as required.
018 */
019public class DigitalOutput extends DigitalSource implements Sendable {
020  private static final int invalidPwmGenerator = 0;
021  private int m_pwmGenerator = invalidPwmGenerator;
022
023  private final int m_channel;
024  private int m_handle;
025
026  /**
027   * Create an instance of a digital output. Create an instance of a digital output given a channel.
028   *
029   * @param channel the DIO channel to use for the digital output. 0-9 are on-board, 10-25 are on
030   *     the MXP
031   */
032  public DigitalOutput(int channel) {
033    SensorUtil.checkDigitalChannel(channel);
034    m_channel = channel;
035
036    m_handle = DIOJNI.initializeDIOPort(HAL.getPort((byte) channel), false);
037
038    HAL.report(tResourceType.kResourceType_DigitalOutput, channel + 1);
039    SendableRegistry.addLW(this, "DigitalOutput", channel);
040  }
041
042  @Override
043  public void close() {
044    super.close();
045    SendableRegistry.remove(this);
046    // Disable the pwm only if we have allocated it
047    if (m_pwmGenerator != invalidPwmGenerator) {
048      disablePWM();
049    }
050    DIOJNI.freeDIOPort(m_handle);
051    m_handle = 0;
052  }
053
054  /**
055   * Set the value of a digital output.
056   *
057   * @param value true is on, off is false
058   */
059  public void set(boolean value) {
060    DIOJNI.setDIO(m_handle, value);
061  }
062
063  /**
064   * Gets the value being output from the Digital Output.
065   *
066   * @return the state of the digital output.
067   */
068  public boolean get() {
069    return DIOJNI.getDIO(m_handle);
070  }
071
072  /**
073   * Get the GPIO channel number that this object represents.
074   *
075   * @return The GPIO channel number.
076   */
077  @Override
078  public int getChannel() {
079    return m_channel;
080  }
081
082  /**
083   * Output a single pulse on the digital output line.
084   *
085   * <p>Send a single pulse on the digital output line where the pulse duration is specified in
086   * seconds. Maximum of 65535 microseconds.
087   *
088   * @param pulseLengthSeconds The pulse length in seconds
089   */
090  public void pulse(final double pulseLengthSeconds) {
091    DIOJNI.pulse(m_handle, pulseLengthSeconds);
092  }
093
094  /**
095   * Determine if the pulse is still going. Determine if a previously started pulse is still going.
096   *
097   * @return true if pulsing
098   */
099  public boolean isPulsing() {
100    return DIOJNI.isPulsing(m_handle);
101  }
102
103  /**
104   * Change the PWM frequency of the PWM output on a Digital Output line.
105   *
106   * <p>The valid range is from 0.6 Hz to 19 kHz. The frequency resolution is logarithmic.
107   *
108   * <p>There is only one PWM frequency for all channels.
109   *
110   * @param rate The frequency to output all digital output PWM signals.
111   */
112  public void setPWMRate(double rate) {
113    DIOJNI.setDigitalPWMRate(rate);
114  }
115
116  /**
117   * Enable a PWM PPS (Pulse Per Second) Output on this line.
118   *
119   * <p>Allocate one of the 6 DO PWM generator resources.
120   *
121   * <p>Supply the duty-cycle to output.
122   *
123   * <p>The resolution of the duty cycle is 8-bit.
124   *
125   * @param dutyCycle The duty-cycle to start generating. [0..1]
126   */
127  public void enablePPS(double dutyCycle) {
128    if (m_pwmGenerator != invalidPwmGenerator) {
129      return;
130    }
131    m_pwmGenerator = DIOJNI.allocateDigitalPWM();
132    DIOJNI.setDigitalPWMPPS(m_pwmGenerator, dutyCycle);
133    DIOJNI.setDigitalPWMOutputChannel(m_pwmGenerator, m_channel);
134  }
135
136  /**
137   * Enable a PWM Output on this line.
138   *
139   * <p>Allocate one of the 6 DO PWM generator resources.
140   *
141   * <p>Supply the initial duty-cycle to output so as to avoid a glitch when first starting.
142   *
143   * <p>The resolution of the duty cycle is 8-bit for low frequencies (1kHz or less) but is reduced
144   * the higher the frequency of the PWM signal is.
145   *
146   * @param initialDutyCycle The duty-cycle to start generating. [0..1]
147   */
148  public void enablePWM(double initialDutyCycle) {
149    if (m_pwmGenerator != invalidPwmGenerator) {
150      return;
151    }
152    m_pwmGenerator = DIOJNI.allocateDigitalPWM();
153    DIOJNI.setDigitalPWMDutyCycle(m_pwmGenerator, initialDutyCycle);
154    DIOJNI.setDigitalPWMOutputChannel(m_pwmGenerator, m_channel);
155  }
156
157  /**
158   * Change this line from a PWM output back to a static Digital Output line.
159   *
160   * <p>Free up one of the 6 DO PWM generator resources that were in use.
161   */
162  public void disablePWM() {
163    if (m_pwmGenerator == invalidPwmGenerator) {
164      return;
165    }
166    // Disable the output by routing to a dead bit.
167    DIOJNI.setDigitalPWMOutputChannel(m_pwmGenerator, SensorUtil.kDigitalChannels);
168    DIOJNI.freeDigitalPWM(m_pwmGenerator);
169    m_pwmGenerator = invalidPwmGenerator;
170  }
171
172  /**
173   * Change the duty-cycle that is being generated on the line.
174   *
175   * <p>The resolution of the duty cycle is 8-bit for low frequencies (1kHz or less) but is reduced
176   * the higher the frequency of the PWM signal is.
177   *
178   * @param dutyCycle The duty-cycle to change to. [0..1]
179   */
180  public void updateDutyCycle(double dutyCycle) {
181    if (m_pwmGenerator == invalidPwmGenerator) {
182      return;
183    }
184    DIOJNI.setDigitalPWMDutyCycle(m_pwmGenerator, dutyCycle);
185  }
186
187  /**
188   * Indicates this input is used by a simulated device.
189   *
190   * @param device simulated device handle
191   */
192  public void setSimDevice(SimDevice device) {
193    DIOJNI.setDIOSimDevice(m_handle, device.getNativeHandle());
194  }
195
196  @Override
197  public void initSendable(SendableBuilder builder) {
198    builder.setSmartDashboardType("Digital Output");
199    builder.addBooleanProperty("Value", this::get, this::set);
200  }
201
202  /**
203   * Is this an analog trigger.
204   *
205   * @return true if this is an analog trigger
206   */
207  @Override
208  public boolean isAnalogTrigger() {
209    return false;
210  }
211
212  /**
213   * Get the analog trigger type.
214   *
215   * @return false
216   */
217  @Override
218  public int getAnalogTriggerTypeForRouting() {
219    return 0;
220  }
221
222  /**
223   * Get the HAL Port Handle.
224   *
225   * @return The HAL Handle to the specified source.
226   */
227  @Override
228  public int getPortHandleForRouting() {
229    return m_handle;
230  }
231}