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