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