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.system.SensorUtil; 011import org.wpilib.util.sendable.Sendable; 012import org.wpilib.util.sendable.SendableBuilder; 013import org.wpilib.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 implements AutoCloseable, 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(channel, false); 038 039 HAL.reportUsage("IO", channel, "DigitalOutput"); 040 SendableRegistry.add(this, "DigitalOutput", channel); 041 } 042 043 @Override 044 public void 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 public int getChannel() { 078 return m_channel; 079 } 080 081 /** 082 * Output a single pulse on the digital output line. 083 * 084 * <p>Send a single pulse on the digital output line where the pulse duration is specified in 085 * seconds. Maximum of 65535 microseconds. 086 * 087 * @param pulseLength The pulse length in seconds 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 PPS (Pulse Per Second) Output on this line. 117 * 118 * <p>Allocate one of the 6 DO PWM generator resources. 119 * 120 * <p>Supply the duty-cycle to output. 121 * 122 * <p>The resolution of the duty cycle is 8-bit. 123 * 124 * @param dutyCycle The duty-cycle to start generating. [0..1] 125 */ 126 public void enablePPS(double dutyCycle) { 127 if (m_pwmGenerator != invalidPwmGenerator) { 128 return; 129 } 130 m_pwmGenerator = DIOJNI.allocateDigitalPWM(); 131 DIOJNI.setDigitalPWMPPS(m_pwmGenerator, dutyCycle); 132 DIOJNI.setDigitalPWMOutputChannel(m_pwmGenerator, m_channel); 133 } 134 135 /** 136 * Enable a PWM Output on this line. 137 * 138 * <p>Allocate one of the 6 DO PWM generator resources. 139 * 140 * <p>Supply the initial duty-cycle to output in order to avoid a glitch when first starting. 141 * 142 * <p>The resolution of the duty cycle is 8-bit for low frequencies (1kHz or less) but is reduced 143 * the higher the frequency of the PWM signal is. 144 * 145 * @param initialDutyCycle The duty-cycle to start generating. [0..1] 146 */ 147 public void enablePWM(double initialDutyCycle) { 148 if (m_pwmGenerator != invalidPwmGenerator) { 149 return; 150 } 151 m_pwmGenerator = DIOJNI.allocateDigitalPWM(); 152 DIOJNI.setDigitalPWMDutyCycle(m_pwmGenerator, initialDutyCycle); 153 DIOJNI.setDigitalPWMOutputChannel(m_pwmGenerator, m_channel); 154 } 155 156 /** 157 * Change this line from a PWM output back to a static Digital Output line. 158 * 159 * <p>Free up one of the 6 DO PWM generator resources that were in use. 160 */ 161 public void disablePWM() { 162 if (m_pwmGenerator == invalidPwmGenerator) { 163 return; 164 } 165 // Disable the output by routing to a dead bit. 166 DIOJNI.setDigitalPWMOutputChannel(m_pwmGenerator, SensorUtil.kDigitalChannels); 167 DIOJNI.freeDigitalPWM(m_pwmGenerator); 168 m_pwmGenerator = invalidPwmGenerator; 169 } 170 171 /** 172 * Change the duty-cycle that is being generated on the line. 173 * 174 * <p>The resolution of the duty cycle is 8-bit for low frequencies (1kHz or less) but is reduced 175 * the higher the frequency of the PWM signal is. 176 * 177 * @param dutyCycle The duty-cycle to change to. [0..1] 178 */ 179 public void updateDutyCycle(double dutyCycle) { 180 if (m_pwmGenerator == invalidPwmGenerator) { 181 return; 182 } 183 DIOJNI.setDigitalPWMDutyCycle(m_pwmGenerator, dutyCycle); 184 } 185 186 /** 187 * Indicates this input is used by a simulated device. 188 * 189 * @param device simulated device handle 190 */ 191 public void setSimDevice(SimDevice device) { 192 DIOJNI.setDIOSimDevice(m_handle, device.getNativeHandle()); 193 } 194 195 @Override 196 public void initSendable(SendableBuilder builder) { 197 builder.setSmartDashboardType("Digital Output"); 198 builder.addBooleanProperty("Value", this::get, this::set); 199 } 200}