001/*----------------------------------------------------------------------------*/
002/* Copyright (c) 2008-2018 FIRST. 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.smartdashboard.SendableBuilder;
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 SendableBase implements Sendable {
020  private static final int invalidPwmGenerator = 0;
021  private int m_pwmGenerator = invalidPwmGenerator;
022
023  private int m_channel = 0;
024  private int m_handle = 0;
025
026  /**
027   * Create an instance of a digital output. Create an instance of a digital output given a
028   * channel.
029   *
030   * @param channel the DIO channel to use for the digital output. 0-9 are on-board, 10-25 are on
031   *                the MXP
032   */
033  public DigitalOutput(int channel) {
034    SensorBase.checkDigitalChannel(channel);
035    m_channel = channel;
036
037    m_handle = DIOJNI.initializeDIOPort(DIOJNI.getPort((byte) channel), false);
038
039    HAL.report(tResourceType.kResourceType_DigitalOutput, channel);
040    setName("DigitalOutput", channel);
041  }
042
043  /**
044   * Free the resources associated with a digital output.
045   */
046  @Override
047  public void free() {
048    super.free();
049    // disable the pwm only if we have allocated it
050    if (m_pwmGenerator != invalidPwmGenerator) {
051      disablePWM();
052    }
053    DIOJNI.freeDIOPort(m_handle);
054    m_handle = 0;
055  }
056
057  /**
058   * Set the value of a digital output.
059   *
060   * @param value true is on, off is false
061   */
062  public void set(boolean value) {
063    DIOJNI.setDIO(m_handle, (short) (value ? 1 : 0));
064  }
065
066  /**
067   * Gets the value being output from the Digital Output.
068   *
069   * @return the state of the digital output.
070   */
071  public boolean get() {
072    return DIOJNI.getDIO(m_handle);
073  }
074
075  /**
076   * Get the GPIO channel number that this object represents.
077   *
078   * @return The GPIO channel number.
079   */
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   * Determine if the pulse is still going. Determine if a previously started pulse is still going.
095   *
096   * @return true if pulsing
097   */
098  public boolean isPulsing() {
099    return DIOJNI.isPulsing(m_handle);
100  }
101
102  /**
103   * Change the PWM frequency of the PWM output on a Digital Output line.
104   *
105   * <p>The valid range is from 0.6 Hz to 19 kHz. The frequency resolution is logarithmic.
106   *
107   * <p>There is only one PWM frequency for all channels.
108   *
109   * @param rate The frequency to output all digital output PWM signals.
110   */
111  public void setPWMRate(double rate) {
112    DIOJNI.setDigitalPWMRate(rate);
113  }
114
115  /**
116   * Enable a PWM Output on this line.
117   *
118   * <p>Allocate one of the 6 DO PWM generator resources.
119   *
120   * <p>Supply the initial duty-cycle to output so as to avoid a glitch when first starting.
121   *
122   * <p>The resolution of the duty cycle is 8-bit for low frequencies (1kHz or less) but is reduced
123   * the higher the frequency of the PWM signal is.
124   *
125   * @param initialDutyCycle The duty-cycle to start generating. [0..1]
126   */
127  public void enablePWM(double initialDutyCycle) {
128    if (m_pwmGenerator != invalidPwmGenerator) {
129      return;
130    }
131    m_pwmGenerator = DIOJNI.allocateDigitalPWM();
132    DIOJNI.setDigitalPWMDutyCycle(m_pwmGenerator, initialDutyCycle);
133    DIOJNI.setDigitalPWMOutputChannel(m_pwmGenerator, m_channel);
134  }
135
136  /**
137   * Change this line from a PWM output back to a static Digital Output line.
138   *
139   * <p>Free up one of the 6 DO PWM generator resources that were in use.
140   */
141  public void disablePWM() {
142    if (m_pwmGenerator == invalidPwmGenerator) {
143      return;
144    }
145    // Disable the output by routing to a dead bit.
146    DIOJNI.setDigitalPWMOutputChannel(m_pwmGenerator, SensorBase.kDigitalChannels);
147    DIOJNI.freeDigitalPWM(m_pwmGenerator);
148    m_pwmGenerator = invalidPwmGenerator;
149  }
150
151  /**
152   * Change the duty-cycle that is being generated on the line.
153   *
154   * <p>The resolution of the duty cycle is 8-bit for low frequencies (1kHz or less) but is reduced
155   * the
156   * higher the frequency of the PWM signal is.
157   *
158   * @param dutyCycle The duty-cycle to change to. [0..1]
159   */
160  public void updateDutyCycle(double dutyCycle) {
161    if (m_pwmGenerator == invalidPwmGenerator) {
162      return;
163    }
164    DIOJNI.setDigitalPWMDutyCycle(m_pwmGenerator, dutyCycle);
165  }
166
167  @Override
168  public void initSendable(SendableBuilder builder) {
169    builder.setSmartDashboardType("Digital Output");
170    builder.addBooleanProperty("Value", this::get, this::set);
171  }
172}