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    
008    package edu.wpi.first.wpilibj;
009    
010    import edu.wpi.first.wpilibj.communication.UsageReporting;
011    import edu.wpi.first.wpilibj.fpga.tDIO;
012    import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
013    import edu.wpi.first.wpilibj.parsing.IInputOutput;
014    import edu.wpi.first.wpilibj.tables.ITable;
015    import edu.wpi.first.wpilibj.tables.ITableListener;
016    
017    /**
018     * Class to write digital outputs.
019     * This class will wrtie digital outputs. Other devices
020     * that are implemented elsewhere will automatically
021     * allocate digital inputs and outputs as required.
022     */
023    public class DigitalOutput extends DigitalSource implements IInputOutput, LiveWindowSendable {
024    
025        private int m_channel;
026        private int m_pwmGenerator;
027        private DigitalModule m_module;
028    
029        private void initDigitalOutput(int moduleNumber, int channel) {
030            m_channel = channel;
031            m_pwmGenerator = ~0;
032            m_module = DigitalModule.getInstance(moduleNumber);
033            m_module.allocateDIO(m_channel, false);
034    
035            UsageReporting.report(UsageReporting.kResourceType_DigitalOutput, channel, moduleNumber-1);
036        }
037    
038        /**
039         * Create an instance of a digital output.
040         * Create an instance of a digital output given a module number and channel.
041         * @param moduleNumber The number of the digital module to use
042         * @param channel the port to use for the digital output
043         */
044        public DigitalOutput(int moduleNumber, int channel) {
045            initDigitalOutput(moduleNumber, channel);
046        }
047    
048        /**
049         * Create an instance of a digital output.
050         * Create a digital output given a channel. The default module is used.
051         * @param channel the port to use for the digital output
052         */
053        public DigitalOutput(int channel) {
054            initDigitalOutput(getDefaultDigitalModule(), channel);
055        }
056    
057        /**
058         * Free the resources associated with a digital output.
059         */
060        public void free() {
061            disablePWM();
062            m_module.freeDIO(m_channel);
063        }
064    
065        /**
066         * Set the value of a digital output.
067         * @param value true is on, off is false
068         */
069        public void set(boolean value) {
070            m_module.setDIO(m_channel, value);
071        }
072    
073        /**
074         * @return The GPIO channel number that this object represents.
075         */
076        public int getChannel() {
077            return m_channel;
078        }
079    
080        /**
081         * Output a single pulse on the digital output line.
082         * Send a single pulse on the digital output line where the pulse diration is specified in seconds.
083         * Maximum pulse length is 0.0016 seconds.
084         * @param length The pulselength in seconds
085         */
086        public void pulse(double length) {
087            m_module.pulse(m_channel, (char) (1e9 * length / (tDIO.readLoopTiming() * 25)));
088        }
089    
090        /**
091         * Determine if the pulse is still going.
092         * Determine if a previously started pulse is still going.
093         * @return true if pulsing
094         */
095        public boolean isPulsing() {
096            return m_module.isPulsing(m_channel);
097        }
098    
099        /**
100         * Change the PWM frequency of the PWM output on a Digital Output line.
101         *
102         * The valid range is from 0.6 Hz to 19 kHz.  The frequency resolution is logarithmic.
103         *
104         * There is only one PWM frequency per digital module.
105         *
106         * @param rate The frequency to output all digital output PWM signals on this module.
107         */
108        public void setPWMRate(double rate)
109    {
110            m_module.setDO_PWMRate(rate);
111        }
112    
113        /**
114         * Enable a PWM Output on this line.
115         *
116         * Allocate one of the 4 DO PWM generator resources from this module.
117         *
118         * Supply the initial duty-cycle to output so as to avoid a glitch when first starting.
119         *
120         * The resolution of the duty cycle is 8-bit for low frequencies (1kHz or less)
121         * but is reduced 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 != ~0) return;
127            m_pwmGenerator = m_module.allocateDO_PWM();
128            m_module.setDO_PWMDutyCycle(m_pwmGenerator, initialDutyCycle);
129            m_module.setDO_PWMOutputChannel(m_pwmGenerator, m_channel);
130        }
131    
132        /**
133         * Change this line from a PWM output back to a static Digital Output line.
134         *
135         * Free up one of the 4 DO PWM generator resources that were in use.
136         */
137        public void disablePWM() {
138            // Disable the output by routing to a dead bit.
139            m_module.setDO_PWMOutputChannel(m_pwmGenerator, kDigitalChannels);
140            m_module.freeDO_PWM(m_pwmGenerator);
141            m_pwmGenerator = ~0;
142        }
143    
144        /**
145         * Change the duty-cycle that is being generated on the line.
146         *
147         * The resolution of the duty cycle is 8-bit for low frequencies (1kHz or less)
148         * but is reduced the higher the frequency of the PWM signal is.
149         *
150         * @param dutyCycle The duty-cycle to change to. [0..1]
151         */
152        public void updateDutyCycle(double dutyCycle) {
153            m_module.setDO_PWMDutyCycle(m_pwmGenerator, dutyCycle);
154        }
155    
156        /**
157         * @return The value to be written to the channel field of a routing mux.
158         */
159        public int getChannelForRouting() {
160            return DigitalModule.remapDigitalChannel(getChannel() - 1);
161        }
162    
163        /**
164         * @return The value to be written to the module field of a routing mux.
165         */
166        public int getModuleForRouting() {
167            return m_module.getModuleNumber() - 1;
168        }
169    
170        /**
171         * @return The value to be written to the analog trigger field of a routing mux.
172         */
173        public boolean getAnalogTriggerForRouting() {
174            return false;
175        }
176        /*
177         * Live Window code, only does anything if live window is activated.
178         */
179        public String getSmartDashboardType(){
180            return "Digital Output";
181        }
182        private ITable m_table;
183        private ITableListener m_table_listener;
184        
185        /**
186         * {@inheritDoc}
187         */
188        public void initTable(ITable subtable) {
189            m_table = subtable;
190            updateTable();
191        }
192        
193        /**
194         * {@inheritDoc}
195         */
196        public ITable getTable(){
197            return m_table;
198        }
199        
200        /**
201         * {@inheritDoc}
202         */
203        public void updateTable() {
204            // TODO: Put current value.
205        }
206        
207        /**
208         * {@inheritDoc}
209         */
210        public void startLiveWindowMode() {
211            m_table_listener = new ITableListener() {
212                public void valueChanged(ITable itable, String key, Object value, boolean bln) {
213                    set(((Boolean) value).booleanValue());
214                }
215            };
216            m_table.addTableListener("Value", m_table_listener, true);
217        }
218        
219        /**
220         * {@inheritDoc}
221         */
222        public void stopLiveWindowMode() {
223            // TODO: Broken, should only remove the listener from "Value" only.
224            m_table.removeTableListener(m_table_listener);
225        }
226    }