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