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 org.wpilib.hardware.discrete;
006
007import org.wpilib.hardware.hal.DIOJNI;
008import org.wpilib.hardware.hal.HAL;
009import org.wpilib.hardware.hal.SimDevice;
010import org.wpilib.util.sendable.Sendable;
011import org.wpilib.util.sendable.SendableBuilder;
012import org.wpilib.util.sendable.SendableRegistry;
013
014/**
015 * Class to write digital outputs. This class will write digital outputs. Other devices that are
016 * implemented elsewhere will automatically allocate digital inputs and outputs as required.
017 */
018public class DigitalOutput implements AutoCloseable, Sendable {
019  private static final int invalidPwmGenerator = 0;
020  private int m_pwmGenerator = invalidPwmGenerator;
021
022  private final int m_channel;
023  private int m_handle;
024
025  /**
026   * Create an instance of a digital output. Create an instance of a digital output given a channel.
027   *
028   * @param channel the SmartIO channel to use for the digital output.
029   */
030  @SuppressWarnings("this-escape")
031  public DigitalOutput(int channel) {
032    m_channel = channel;
033
034    m_handle = DIOJNI.initializeDIOPort(channel, false);
035
036    HAL.reportUsage("IO", channel, "DigitalOutput");
037    SendableRegistry.add(this, "DigitalOutput", channel);
038  }
039
040  @Override
041  public void close() {
042    SendableRegistry.remove(this);
043    // Disable the pwm only if we have allocated it
044    if (m_pwmGenerator != invalidPwmGenerator) {
045      disablePWM();
046    }
047    DIOJNI.freeDIOPort(m_handle);
048    m_handle = 0;
049  }
050
051  /**
052   * Set the value of a digital output.
053   *
054   * @param value true is on, off is false
055   */
056  public void set(boolean value) {
057    DIOJNI.setDIO(m_handle, value);
058  }
059
060  /**
061   * Gets the value being output from the Digital Output.
062   *
063   * @return the state of the digital output.
064   */
065  public boolean get() {
066    return DIOJNI.getDIO(m_handle);
067  }
068
069  /**
070   * Get the GPIO channel number that this object represents.
071   *
072   * @return The GPIO channel number.
073   */
074  public int getChannel() {
075    return m_channel;
076  }
077
078  /**
079   * Output a single pulse on the digital output line.
080   *
081   * <p>Send a single pulse on the digital output line where the pulse duration is specified in
082   * seconds. Maximum of 65535 microseconds.
083   *
084   * @param pulseLength The pulse length in seconds
085   */
086  public void pulse(final double pulseLength) {
087    DIOJNI.pulse(m_handle, pulseLength);
088  }
089
090  /**
091   * Determine if the pulse is still going. Determine if a previously started pulse is still going.
092   *
093   * @return true if pulsing
094   */
095  public boolean isPulsing() {
096    return DIOJNI.isPulsing(m_handle);
097  }
098
099  /**
100   * Change the PWM frequency of the PWM output on a Digital Output line.
101   *
102   * <p>The valid range is from 0.6 Hz to 19 kHz. The frequency resolution is logarithmic.
103   *
104   * <p>There is only one PWM frequency for all channels.
105   *
106   * @param rate The frequency to output all digital output PWM signals.
107   */
108  public void setPWMRate(double rate) {
109    DIOJNI.setDigitalPWMRate(rate);
110  }
111
112  /**
113   * Enable a PWM PPS (Pulse Per Second) Output on this line.
114   *
115   * <p>Allocate one of the 6 DO PWM generator resources.
116   *
117   * <p>Supply the duty-cycle to output.
118   *
119   * <p>The resolution of the duty cycle is 8-bit.
120   *
121   * @param dutyCycle The duty-cycle to start generating. [0..1]
122   */
123  public void enablePPS(double dutyCycle) {
124    if (m_pwmGenerator != invalidPwmGenerator) {
125      return;
126    }
127    m_pwmGenerator = DIOJNI.allocateDigitalPWM();
128    DIOJNI.setDigitalPWMPPS(m_pwmGenerator, dutyCycle);
129    DIOJNI.setDigitalPWMOutputChannel(m_pwmGenerator, m_channel);
130  }
131
132  /**
133   * Enable a PWM Output on this line.
134   *
135   * <p>Allocate one of the 6 DO PWM generator resources.
136   *
137   * <p>Supply the initial duty-cycle to output in order to avoid a glitch when first starting.
138   *
139   * <p>The resolution of the duty cycle is 8-bit for low frequencies (1kHz or less) but is reduced
140   * the higher the frequency of the PWM signal is.
141   *
142   * @param initialDutyCycle The duty-cycle to start generating. [0..1]
143   */
144  public void enablePWM(double initialDutyCycle) {
145    if (m_pwmGenerator != invalidPwmGenerator) {
146      return;
147    }
148    m_pwmGenerator = DIOJNI.allocateDigitalPWM();
149    DIOJNI.setDigitalPWMDutyCycle(m_pwmGenerator, initialDutyCycle);
150    DIOJNI.setDigitalPWMOutputChannel(m_pwmGenerator, m_channel);
151  }
152
153  /**
154   * Change this line from a PWM output back to a static Digital Output line.
155   *
156   * <p>Free up one of the 6 DO PWM generator resources that were in use.
157   */
158  public void disablePWM() {
159    if (m_pwmGenerator == invalidPwmGenerator) {
160      return;
161    }
162    DIOJNI.freeDigitalPWM(m_pwmGenerator);
163    m_pwmGenerator = invalidPwmGenerator;
164  }
165
166  /**
167   * Change the duty-cycle that is being generated on the line.
168   *
169   * <p>The resolution of the duty cycle is 8-bit for low frequencies (1kHz or less) but is reduced
170   * the higher the frequency of the PWM signal is.
171   *
172   * @param dutyCycle The duty-cycle to change to. [0..1]
173   */
174  public void updateDutyCycle(double dutyCycle) {
175    if (m_pwmGenerator == invalidPwmGenerator) {
176      return;
177    }
178    DIOJNI.setDigitalPWMDutyCycle(m_pwmGenerator, dutyCycle);
179  }
180
181  /**
182   * Indicates this input is used by a simulated device.
183   *
184   * @param device simulated device handle
185   */
186  public void setSimDevice(SimDevice device) {
187    DIOJNI.setDIOSimDevice(m_handle, device.getNativeHandle());
188  }
189
190  @Override
191  public void initSendable(SendableBuilder builder) {
192    builder.setSmartDashboardType("Digital Output");
193    builder.addBooleanProperty("Value", this::get, this::set);
194  }
195}