001/*----------------------------------------------------------------------------*/ 002/* Copyright (c) FIRST 2008-2017. All Rights Reserved. */ 003/* Open Source Software - may be modified and shared by FRC teams. The code */ 004/* must be accompanied by the FIRST BSD license file in the root directory of */ 005/* the project. */ 006/*----------------------------------------------------------------------------*/ 007 008package edu.wpi.first.wpilibj; 009 010import edu.wpi.first.wpilibj.hal.DIOJNI; 011import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType; 012import edu.wpi.first.wpilibj.hal.HAL; 013import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable; 014import edu.wpi.first.wpilibj.tables.ITable; 015import edu.wpi.first.wpilibj.tables.ITableListener; 016 017/** 018 * Class to write digital outputs. This class will write digital outputs. Other devices that are 019 * implemented elsewhere will automatically allocate digital inputs and outputs as required. 020 */ 021public class DigitalOutput extends DigitalSource implements LiveWindowSendable { 022 023 private static final int invalidPwmGenerator = 0; 024 private int m_pwmGenerator = invalidPwmGenerator; 025 026 private int m_channel = 0; 027 private int m_handle = 0; 028 029 /** 030 * Create an instance of a digital output. Create an instance of a digital output given a 031 * channel. 032 * 033 * @param channel the DIO channel to use for the digital output. 0-9 are on-board, 10-25 are on 034 * the MXP 035 */ 036 public DigitalOutput(int channel) { 037 checkDigitalChannel(channel); 038 m_channel = channel; 039 040 m_handle = DIOJNI.initializeDIOPort(DIOJNI.getPort((byte)channel), false); 041 042 HAL.report(tResourceType.kResourceType_DigitalOutput, channel); 043 } 044 045 /** 046 * Free the resources associated with a digital output. 047 */ 048 @Override 049 public void free() { 050 // disable the pwm only if we have allocated it 051 if (m_pwmGenerator != invalidPwmGenerator) { 052 disablePWM(); 053 } 054 DIOJNI.freeDIOPort(m_handle); 055 m_handle = 0; 056 } 057 058 /** 059 * Set the value of a digital output. 060 * 061 * @param value true is on, off is false 062 */ 063 public void set(boolean value) { 064 DIOJNI.setDIO(m_handle, (short) (value ? 1 : 0)); 065 } 066 067 /** 068 * Gets the value being output from the Digital Output. 069 * 070 * @return the state of the digital output. 071 */ 072 public boolean get() { 073 return DIOJNI.getDIO(m_handle); 074 } 075 076 /** 077 * @return The GPIO channel number that this object represents. 078 */ 079 @Override 080 public int getChannel() { 081 return m_channel; 082 } 083 084 /** 085 * Generate a single pulse. There can only be a single pulse going at any time. 086 * 087 * @param pulseLength The length of the pulse. 088 */ 089 public void pulse(final double pulseLength) { 090 DIOJNI.pulse(m_handle, pulseLength); 091 } 092 093 /** 094 * Generate a single pulse. Write a pulse to the specified digital output channel. There can only 095 * be a single pulse going at any time. 096 * 097 * @param channel Unused 098 * @param pulseLength The length of the pulse. 099 * @deprecated Use {@link #pulse(double)} instead. 100 */ 101 @Deprecated 102 @SuppressWarnings("PMD.UnusedFormalParameter") 103 public void pulse(final int channel, final double pulseLength) { 104 DIOJNI.pulse(m_handle, pulseLength); 105 } 106 107 /** 108 * @param channel Unused 109 * @param pulseLength The length of the pulse. 110 * @deprecated Generate a single pulse. Write a pulse to the specified digital output channel. 111 * There can only be a single pulse going at any time. 112 */ 113 @Deprecated 114 @SuppressWarnings("PMD.UnusedFormalParameter") 115 public void pulse(final int channel, final int pulseLength) { 116 double convertedPulse = pulseLength / 1.0e9 * (DIOJNI.getLoopTiming() * 25); 117 System.err 118 .println("You should use the double version of pulse for portability. This is deprecated"); 119 DIOJNI.pulse(m_handle, convertedPulse); 120 } 121 122 /** 123 * Determine if the pulse is still going. Determine if a previously started pulse is still going. 124 * 125 * @return true if pulsing 126 */ 127 public boolean isPulsing() { 128 return DIOJNI.isPulsing(m_handle); 129 } 130 131 /** 132 * Change the PWM frequency of the PWM output on a Digital Output line. 133 * 134 * <p>The valid range is from 0.6 Hz to 19 kHz. The frequency resolution is logarithmic. 135 * 136 * <p>There is only one PWM frequency for all channnels. 137 * 138 * @param rate The frequency to output all digital output PWM signals. 139 */ 140 public void setPWMRate(double rate) { 141 DIOJNI.setDigitalPWMRate(rate); 142 } 143 144 /** 145 * Enable a PWM Output on this line. 146 * 147 * <p>Allocate one of the 6 DO PWM generator resources. 148 * 149 * <p>Supply the initial duty-cycle to output so as to avoid a glitch when first starting. 150 * 151 * <p>The resolution of the duty cycle is 8-bit for low frequencies (1kHz or less) but is reduced 152 * the higher the frequency of the PWM signal is. 153 * 154 * @param initialDutyCycle The duty-cycle to start generating. [0..1] 155 */ 156 public void enablePWM(double initialDutyCycle) { 157 if (m_pwmGenerator != invalidPwmGenerator) { 158 return; 159 } 160 m_pwmGenerator = DIOJNI.allocateDigitalPWM(); 161 DIOJNI.setDigitalPWMDutyCycle(m_pwmGenerator, initialDutyCycle); 162 DIOJNI.setDigitalPWMOutputChannel(m_pwmGenerator, m_channel); 163 } 164 165 /** 166 * Change this line from a PWM output back to a static Digital Output line. 167 * 168 * <p>Free up one of the 6 DO PWM generator resources that were in use. 169 */ 170 public void disablePWM() { 171 if (m_pwmGenerator == invalidPwmGenerator) { 172 return; 173 } 174 // Disable the output by routing to a dead bit. 175 DIOJNI.setDigitalPWMOutputChannel(m_pwmGenerator, kDigitalChannels); 176 DIOJNI.freeDigitalPWM(m_pwmGenerator); 177 m_pwmGenerator = invalidPwmGenerator; 178 } 179 180 /** 181 * Change the duty-cycle that is being generated on the line. 182 * 183 * <p>The resolution of the duty cycle is 8-bit for low frequencies (1kHz or less) but is reduced 184 * the 185 * higher the frequency of the PWM signal is. 186 * 187 * @param dutyCycle The duty-cycle to change to. [0..1] 188 */ 189 public void updateDutyCycle(double dutyCycle) { 190 if (m_pwmGenerator == invalidPwmGenerator) { 191 return; 192 } 193 DIOJNI.setDigitalPWMDutyCycle(m_pwmGenerator, dutyCycle); 194 } 195 196 /** 197 * Get the analog trigger type. 198 * 199 * @return false 200 */ 201 @Override 202 public int getAnalogTriggerTypeForRouting() { 203 return 0; 204 } 205 206 /** 207 * Is this an analog trigger. 208 * 209 * @return true if this is an analog trigger 210 */ 211 @Override 212 public boolean isAnalogTrigger() { 213 return false; 214 } 215 216 /** 217 * Get the HAL Port Handle. 218 * 219 * @return The HAL Handle to the specified source. 220 */ 221 @Override 222 public int getPortHandleForRouting() { 223 return m_handle; 224 } 225 226 /* 227 * Live Window code, only does anything if live window is activated. 228 */ 229 @Override 230 public String getSmartDashboardType() { 231 return "Digital Output"; 232 } 233 234 private ITable m_table; 235 private ITableListener m_tableListener; 236 237 238 @Override 239 public void initTable(ITable subtable) { 240 m_table = subtable; 241 updateTable(); 242 } 243 244 245 @Override 246 public ITable getTable() { 247 return m_table; 248 } 249 250 251 @Override 252 public void updateTable() { 253 // TODO: Put current value. 254 } 255 256 257 @Override 258 public void startLiveWindowMode() { 259 m_tableListener = new ITableListener() { 260 @Override 261 public void valueChanged(ITable itable, String key, Object value, boolean bln) { 262 set((Boolean) value); 263 } 264 }; 265 m_table.addTableListener("Value", m_tableListener, true); 266 } 267 268 269 @Override 270 public void stopLiveWindowMode() { 271 // TODO: Broken, should only remove the listener from "Value" only. 272 m_table.removeTableListener(m_tableListener); 273 } 274}