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.tAnalogTrigger;
012    import edu.wpi.first.wpilibj.parsing.IInputOutput;
013    import edu.wpi.first.wpilibj.util.AllocationException;
014    import edu.wpi.first.wpilibj.util.BoundaryException;
015    import edu.wpi.first.wpilibj.util.CheckedAllocationException;
016    
017    /**
018     * Class for creating and configuring Analog Triggers
019     * @author dtjones
020     */
021    public class AnalogTrigger implements IInputOutput{
022    
023        /**
024         * Exceptions dealing with improper operation of the Analog trigger
025         */
026        public class AnalogTriggerException extends RuntimeException {
027    
028            /**
029             * Create a new exception with the given message
030             * @param message the message to pass with the exception
031             */
032            public AnalogTriggerException(String message) {
033                super(message);
034            }
035            
036        }
037    
038        private static Resource triggers = new Resource(tAnalogTrigger.kNumSystems);
039        /**
040         * Where the analog trigger is attached
041         */
042        protected int m_index;
043        private tAnalogTrigger m_trigger;
044        private AnalogModule m_analogModule;
045        private int m_channel;
046    
047        /**
048         * Initialize an analog trigger from a module number and channel.
049         * This is the common code for the two constructors that use a module number and channel.
050         * @param moduleNumber The number of the analog module to create this trigger on.
051         * @param channel the port to use for the analog trigger
052         */
053        protected void initTrigger(final int moduleNumber, final int channel) {
054            m_channel = channel;
055            m_analogModule = AnalogModule.getInstance(moduleNumber);
056            try {
057                m_index = triggers.allocate();
058            } catch (CheckedAllocationException e) {
059                throw new AllocationException("No analog triggers are available to allocate");
060            }
061            m_trigger = new tAnalogTrigger((byte) m_index);
062            m_trigger.writeSourceSelect_Channel((byte) (m_channel - 1));
063            m_trigger.writeSourceSelect_Module((byte) moduleNumber - 1);
064            UsageReporting.report(UsageReporting.kResourceType_AnalogTrigger, channel, moduleNumber-1);
065        }
066    
067        /**
068         * Constructor for an analog trigger given a channel number.
069         * The default module is used in this case.
070         * @param channel the port to use for the analog trigger
071         */
072        public AnalogTrigger(final int channel) {
073            initTrigger(AnalogModule.getDefaultAnalogModule(), channel);
074        }
075    
076        /**
077         * Constructor for an analog trigger given both the module number and channel.
078         * @param moduleNumber The number of the analog module to create this trigger on.
079         * @param channel the port to use for the analog trigger
080         */
081        public AnalogTrigger(final int moduleNumber, final int channel) {
082            initTrigger(moduleNumber, channel);
083        }
084    
085        /**
086         * Construct an analog trigger given an analog channel.
087         * This should be used in the case of sharing an analog channel between the trigger
088         * and an analog input object.
089         * @param channel the AnalogChannel to use for the analog trigger
090         */
091        public AnalogTrigger(AnalogChannel channel) {
092            initTrigger(channel.getModuleNumber(), channel.getChannel());
093        }
094    
095        /**
096         * Release the resources used by this object
097         */
098        public void free() {
099            triggers.free(m_index);
100            m_trigger.Release();
101            m_trigger = null;
102        }
103    
104        /**
105         * Set the upper and lower limits of the analog trigger.
106         * The limits are given in ADC codes.  If oversampling is used, the units must be scaled
107         * appropriately.
108         * @param lower the lower raw limit
109         * @param upper the upper raw limit
110         */
111        public void setLimitsRaw(final int lower, final int upper) {
112            if (lower > upper) {
113                throw new BoundaryException("Lower bound is greater than upper");
114            }
115            m_trigger.writeLowerLimit(lower);
116            m_trigger.writeUpperLimit(upper);
117        }
118    
119        /**
120         * Set the upper and lower limits of the analog trigger.
121         * The limits are given as floating point voltage values.
122         * @param lower the lower voltage limit
123         * @param upper the upper voltage limit
124         */
125        public void setLimitsVoltage(double lower, double upper) {
126            if (lower > upper) {
127                throw new BoundaryException("Lower bound is greater than upper bound");
128            }
129            // TODO: This depends on the averaged setting.  Only raw values will work as is.
130            m_trigger.writeLowerLimit(m_analogModule.voltsToValue(m_channel, lower));
131            m_trigger.writeUpperLimit(m_analogModule.voltsToValue(m_channel, upper));
132        }
133    
134        /**
135         * Configure the analog trigger to use the averaged vs. raw values.
136         * If the value is true, then the averaged value is selected for the analog trigger, otherwise
137         * the immediate value is used.
138         * @param useAveragedValue true to use an averaged value, false otherwise
139         */
140        public void setAveraged(boolean useAveragedValue) {
141            if (m_trigger.readSourceSelect_Filter() && useAveragedValue) {
142                throw new AnalogTriggerException("Cannot set to Averaged if the analog trigger is filtered");
143            }
144            m_trigger.writeSourceSelect_Averaged(useAveragedValue);
145        }
146    
147        /**
148         * Configure the analog trigger to use a filtered value.
149         * The analog trigger will operate with a 3 point average rejection filter. This is designed to
150         * help with 360 degree pot applications for the period where the pot crosses through zero.
151         * @param useFilteredValue true to use a filterd value, false otherwise
152         */
153        public void setFiltered(boolean useFilteredValue) {
154            if (m_trigger.readSourceSelect_Averaged() && useFilteredValue) {
155                throw new AnalogTriggerException("Cannot set to Filtered if the analog trigger is averaged");
156            }
157            m_trigger.writeSourceSelect_Filter(useFilteredValue);
158        }
159    
160        /**
161         * Return the index of the analog trigger.
162         * This is the FPGA index of this analog trigger instance.
163         * @return The index of the analog trigger.
164         */
165        public int getIndex() {
166            return m_index;
167        }
168    
169        /**
170         * Return the InWindow output of the analog trigger.
171         * True if the analog input is between the upper and lower limits.
172         * @return The InWindow output of the analog trigger.
173         */
174        public boolean getInWindow() {
175            return tAnalogTrigger.readOutput_InHysteresis((byte) m_index);
176        }
177    
178        /**
179         * Return the TriggerState output of the analog trigger.
180         * True if above upper limit.
181         * False if below lower limit.
182         * If in Hysteresis, maintain previous state.
183         * @return The TriggerState output of the analog trigger.
184         */
185        public boolean getTriggerState() {
186            return tAnalogTrigger.readOutput_OverLimit((byte) m_index);
187        }
188    
189        /**
190         * Creates an AnalogTriggerOutput object.
191         * Gets an output object that can be used for routing.
192         * Caller is responsible for deleting the AnalogTriggerOutput object.
193         * @param type An enum of the type of output object to create.
194         * @return A pointer to a new AnalogTriggerOutput object.
195         */
196        AnalogTriggerOutput createOutput(AnalogTriggerOutput.Type type) {
197            return new AnalogTriggerOutput(this, type);
198        }
199    }