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