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