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