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}