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   * Generate a single pulse. There can only be a single pulse going at any time.
084   *
085   * @param pulseLength The length of the pulse.
086   */
087  public void pulse(final double pulseLength) {
088    DIOJNI.pulse(m_handle, pulseLength);
089  }
090
091  /**
092   * Determine if the pulse is still going. Determine if a previously started pulse is still going.
093   *
094   * @return true if pulsing
095   */
096  public boolean isPulsing() {
097    return DIOJNI.isPulsing(m_handle);
098  }
099
100  /**
101   * Change the PWM frequency of the PWM output on a Digital Output line.
102   *
103   * <p>The valid range is from 0.6 Hz to 19 kHz. The frequency resolution is logarithmic.
104   *
105   * <p>There is only one PWM frequency for all channels.
106   *
107   * @param rate The frequency to output all digital output PWM signals.
108   */
109  public void setPWMRate(double rate) {
110    DIOJNI.setDigitalPWMRate(rate);
111  }
112
113  /**
114   * Enable a PWM Output on this line.
115   *
116   * <p>Allocate one of the 6 DO PWM generator resources.
117   *
118   * <p>Supply the initial duty-cycle to output so as to avoid a glitch when first starting.
119   *
120   * <p>The resolution of the duty cycle is 8-bit for low frequencies (1kHz or less) but is reduced
121   * the higher the frequency of the PWM signal is.
122   *
123   * @param initialDutyCycle The duty-cycle to start generating. [0..1]
124   */
125  public void enablePWM(double initialDutyCycle) {
126    if (m_pwmGenerator != invalidPwmGenerator) {
127      return;
128    }
129    m_pwmGenerator = DIOJNI.allocateDigitalPWM();
130    DIOJNI.setDigitalPWMDutyCycle(m_pwmGenerator, initialDutyCycle);
131    DIOJNI.setDigitalPWMOutputChannel(m_pwmGenerator, m_channel);
132  }
133
134  /**
135   * Change this line from a PWM output back to a static Digital Output line.
136   *
137   * <p>Free up one of the 6 DO PWM generator resources that were in use.
138   */
139  public void disablePWM() {
140    if (m_pwmGenerator == invalidPwmGenerator) {
141      return;
142    }
143    // Disable the output by routing to a dead bit.
144    DIOJNI.setDigitalPWMOutputChannel(m_pwmGenerator, SensorUtil.kDigitalChannels);
145    DIOJNI.freeDigitalPWM(m_pwmGenerator);
146    m_pwmGenerator = invalidPwmGenerator;
147  }
148
149  /**
150   * Change the duty-cycle that is being generated on the line.
151   *
152   * <p>The resolution of the duty cycle is 8-bit for low frequencies (1kHz or less) but is reduced
153   * the higher the frequency of the PWM signal is.
154   *
155   * @param dutyCycle The duty-cycle to change to. [0..1]
156   */
157  public void updateDutyCycle(double dutyCycle) {
158    if (m_pwmGenerator == invalidPwmGenerator) {
159      return;
160    }
161    DIOJNI.setDigitalPWMDutyCycle(m_pwmGenerator, dutyCycle);
162  }
163
164  /**
165   * Indicates this input is used by a simulated device.
166   *
167   * @param device simulated device handle
168   */
169  public void setSimDevice(SimDevice device) {
170    DIOJNI.setDIOSimDevice(m_handle, device.getNativeHandle());
171  }
172
173  @Override
174  public void initSendable(SendableBuilder builder) {
175    builder.setSmartDashboardType("Digital Output");
176    builder.addBooleanProperty("Value", this::get, this::set);
177  }
178
179  /**
180   * Is this an analog trigger.
181   *
182   * @return true if this is an analog trigger
183   */
184  @Override
185  public boolean isAnalogTrigger() {
186    return false;
187  }
188
189  /**
190   * Get the analog trigger type.
191   *
192   * @return false
193   */
194  @Override
195  public int getAnalogTriggerTypeForRouting() {
196    return 0;
197  }
198
199  /**
200   * Get the HAL Port Handle.
201   *
202   * @return The HAL Handle to the specified source.
203   */
204  @Override
205  public int getPortHandleForRouting() {
206    return m_handle;
207  }
208}