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.tCounter;
012    import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
013    import edu.wpi.first.wpilibj.tables.ITable;
014    import edu.wpi.first.wpilibj.util.AllocationException;
015    import edu.wpi.first.wpilibj.util.BoundaryException;
016    import edu.wpi.first.wpilibj.util.CheckedAllocationException;
017    
018    /**
019     * Class for counting the number of ticks on a digital input channel.
020     * This is a general purpose class for counting repetitive events. It can return the number
021     * of counts, the period of the most recent cycle, and detect when the signal being counted
022     * has stopped by supplying a maximum cycle time.
023     */
024    public class Counter extends SensorBase implements CounterBase, LiveWindowSendable, PIDSource {
025    
026        /**
027         * Mode determines how and what the counter counts
028         */
029        public static class Mode {
030    
031            /**
032             * The integer value representing this enumeration
033             */
034            public final int value;
035            static final int kTwoPulse_val = 0;
036            static final int kSemiperiod_val = 1;
037            static final int kPulseLength_val = 2;
038            static final int kExternalDirection_val = 3;
039            /**
040             * mode: two pulse
041             */
042            public static final Mode kTwoPulse = new Mode(kTwoPulse_val);
043            /**
044             * mode: semi period
045             */
046            public static final Mode kSemiperiod = new Mode(kSemiperiod_val);
047            /**
048             * mode: pulse length
049             */
050            public static final Mode kPulseLength = new Mode(kPulseLength_val);
051            /**
052             * mode: external direction
053             */
054            public static final Mode kExternalDirection = new Mode(kExternalDirection_val);
055    
056            private Mode(int value) {
057                this.value = value;
058            }
059        }
060        private DigitalSource m_upSource;           ///< What makes the counter count up.
061        private DigitalSource m_downSource; ///< What makes the counter count down.
062        private boolean m_allocatedUpSource;
063        private boolean m_allocatedDownSource;
064        private tCounter m_counter;                         ///< The FPGA counter object.
065        private int m_index;                                        ///< The index of this counter.
066        private static Resource counters = new Resource(tCounter.kNumSystems);
067        private PIDSourceParameter m_pidSource;
068        private double m_distancePerPulse;          // distance of travel for each tick
069    
070        private void initCounter(final Mode mode) {
071            m_allocatedUpSource = false;
072            m_allocatedDownSource = false;
073    
074            try {
075                m_index = counters.allocate();
076            } catch (CheckedAllocationException e) {
077                throw new AllocationException("No counters left to be allocated");
078            }
079    
080            m_counter = new tCounter(m_index);
081            m_counter.writeConfig_Mode(mode.value);
082            m_upSource = null;
083            m_downSource = null;
084            m_counter.writeTimerConfig_AverageSize(1);
085    
086           UsageReporting.report(UsageReporting.kResourceType_Counter, m_index, mode.value);
087        }
088    
089        /**
090         * Create an instance of a counter where no sources are selected.
091         * Then they all must be selected by calling functions to specify the upsource and the downsource
092         * independently.
093         */
094        public Counter() {
095            initCounter(Mode.kTwoPulse);
096        }
097    
098        /**
099         * Create an instance of a counter from a Digital Input.
100         * This is used if an existing digital input is to be shared by multiple other objects such
101         * as encoders.
102         * @param source the digital source to count
103         */
104        public Counter(DigitalSource source) {
105            if (source == null)
106                throw new NullPointerException("Source given was null");
107            initCounter(Mode.kTwoPulse);
108            setUpSource(source);
109        }
110    
111        /**
112         * Create an instance of a Counter object.
113         * Create an up-Counter instance given a channel. The default digital module is assumed.
114         * @param channel the digital input channel to count
115         */
116        public Counter(int channel) {
117            initCounter(Mode.kTwoPulse);
118            setUpSource(channel);
119        }
120    
121        /**
122         * Create an instance of a Counter object.
123         * Create an instance of an up-Counter given a digital module and a channel.
124         * @param slot The cRIO chassis slot for the digital module used
125         * @param channel The channel in the digital module
126         */
127        public Counter(int slot, int channel) {
128            initCounter(Mode.kTwoPulse);
129            setUpSource(slot, channel);
130        }
131    
132        /**
133         * Create an instance of a Counter object.
134         * Create an instance of a simple up-Counter given an analog trigger.
135         * Use the trigger state output from the analog trigger.
136         * @param encodingType which edges to count
137         * @param upSource first source to count
138         * @param downSource second source for direction
139         * @param inverted true to invert the count
140         */
141        public Counter(EncodingType encodingType, DigitalSource upSource, DigitalSource downSource, boolean inverted) {
142            initCounter(Mode.kExternalDirection);
143            if (encodingType != EncodingType.k1X && encodingType != EncodingType.k2X) {
144                throw new RuntimeException("Counters only support 1X and 2X quadreature decoding!");
145            }
146            if (upSource == null)
147                throw new NullPointerException("Up Source given was null");
148            setUpSource(upSource);
149            if (downSource == null)
150                throw new NullPointerException("Down Source given was null");
151            setDownSource(downSource);
152    
153            if (encodingType == null)
154                throw new NullPointerException("Encoding type given was null");
155    
156            if (encodingType == EncodingType.k1X) {
157                setUpSourceEdge(true, false);
158            } else {
159                setUpSourceEdge(true, true);
160            }
161    
162            setDownSourceEdge(inverted, true);
163        }
164    
165        /**
166         * Create an instance of a Counter object.
167         * Create an instance of a simple up-Counter given an analog trigger.
168         * Use the trigger state output from the analog trigger.
169         * @param trigger the analog trigger to count
170         */
171        public Counter(AnalogTrigger trigger) {
172            initCounter(Mode.kTwoPulse);
173            setUpSource(trigger.createOutput(AnalogTriggerOutput.Type.kTypeState));
174        }
175    
176        public void free() {
177            setUpdateWhenEmpty(true);
178    
179            clearUpSource();
180            clearDownSource();
181            m_counter.Release();
182    
183            m_upSource = null;
184            m_downSource = null;
185            m_counter = null;
186            counters.free(m_index);
187        }
188    
189        /**
190         * Set the up source for the counter as digital input channel and slot.
191         * @param slot the location of the digital module to use
192         * @param channel the digital port to count
193         */
194        public void setUpSource(int slot, int channel) {
195            setUpSource(new DigitalInput(slot, channel));
196            m_allocatedUpSource = true;
197        }
198    
199        /**
200         * Set the upsource for the counter as a digital input channel.
201         * The slot will be the default digital module slot.
202         * @param channel the digital port to count
203         */
204        public void setUpSource(int channel) {
205            setUpSource(new DigitalInput(channel));
206            m_allocatedUpSource = true;
207        }
208    
209        /**
210         * Set the source object that causes the counter to count up.
211         * Set the up counting DigitalSource.
212         * @param source the digital source to count
213         */
214        public void setUpSource(DigitalSource source) {
215            if (m_upSource != null && m_allocatedUpSource) {
216                m_upSource.free();
217                m_allocatedUpSource = false;
218            }
219            m_upSource = source;
220            m_counter.writeConfig_UpSource_Module(source.getModuleForRouting());
221            m_counter.writeConfig_UpSource_Channel(source.getChannelForRouting());
222            m_counter.writeConfig_UpSource_AnalogTrigger(source.getAnalogTriggerForRouting());
223    
224            if (m_counter.readConfig_Mode() == Mode.kTwoPulse.value ||
225                    m_counter.readConfig_Mode() == Mode.kExternalDirection.value) {
226                setUpSourceEdge(true, false);
227            }
228            m_counter.strobeReset();
229        }
230    
231        /**
232         * Set the up counting source to be an analog trigger.
233         * @param analogTrigger The analog trigger object that is used for the Up Source
234         * @param triggerType The analog trigger output that will trigger the counter.
235         */
236        public void setUpSource(AnalogTrigger analogTrigger, AnalogTriggerOutput.Type triggerType) {
237            setUpSource(analogTrigger.createOutput(triggerType));
238            m_allocatedUpSource = true;
239        }
240    
241        /**
242         * Set the edge sensitivity on an up counting source.
243         * Set the up source to either detect rising edges or falling edges.
244         * @param risingEdge true to count rising edge
245         * @param fallingEdge true to count falling edge
246         */
247        public void setUpSourceEdge(boolean risingEdge, boolean fallingEdge) {
248            if (m_upSource == null) throw new RuntimeException(
249                    "Up Source must be set before setting the edge!");
250            m_counter.writeConfig_UpRisingEdge(risingEdge);
251            m_counter.writeConfig_UpFallingEdge(fallingEdge);
252        }
253    
254        /**
255         * Disable the up counting source to the counter.
256         */
257        public void clearUpSource() {
258            if (m_upSource != null && m_allocatedUpSource) {
259                m_upSource.free();
260                m_allocatedUpSource = false;
261            }
262            m_upSource = null;
263    
264            boolean state = m_counter.readConfig_Enable();
265            m_counter.writeConfig_Enable(false);
266            m_counter.writeConfig_UpFallingEdge(false);
267            m_counter.writeConfig_UpRisingEdge(false);
268            // Index 0 of digital is always 0.
269            m_counter.writeConfig_UpSource_Channel(0);
270            m_counter.writeConfig_UpSource_AnalogTrigger(false);
271            m_counter.writeConfig_Enable(state);
272        }
273    
274        /**
275         * Set the down counting source to be a digital input channel.
276         * The slot will be set to the default digital module slot.
277         * @param channel the digital port to count
278         */
279        public void setDownSource(int channel) {
280            setDownSource(new DigitalInput(channel));
281            m_allocatedDownSource = true;
282        }
283    
284        /**
285         * Set the down counting source to be a digital input slot and channel.
286         * @param slot the location of the digital module to use
287         * @param channel the digital port to count
288         */
289        public void setDownSource(int slot, int channel) {
290            setDownSource(new DigitalInput(slot, channel));
291            m_allocatedDownSource = true;
292        }
293    
294        /**
295         * Set the source object that causes the counter to count down.
296         * Set the down counting DigitalSource.
297         * @param source the digital source to count
298         */
299        public void setDownSource(DigitalSource source) {
300            if (m_downSource != null && m_allocatedDownSource) {
301                m_downSource.free();
302                m_allocatedDownSource = false;
303            }
304            int mode = m_counter.readConfig_Mode();
305            if(mode != Mode.kTwoPulse_val && mode != Mode.kExternalDirection_val) {
306                throw new RuntimeException(
307                        "Down Source only supported in TwoPulse and ExternalDirection modes!");
308            }
309            m_downSource = source;
310            m_counter.writeConfig_DownSource_Module(source.getModuleForRouting());
311            m_counter.writeConfig_DownSource_Channel(source.getChannelForRouting());
312            m_counter.writeConfig_DownSource_AnalogTrigger(source.getAnalogTriggerForRouting());
313    
314            setDownSourceEdge(true, false);
315            m_counter.strobeReset();
316        }
317    
318        /**
319         * Set the down counting source to be an analog trigger.
320         * @param analogTrigger The analog trigger object that is used for the Down Source
321         * @param triggerType The analog trigger output that will trigger the counter.
322         */
323        public void setDownSource(AnalogTrigger analogTrigger, AnalogTriggerOutput.Type triggerType) {
324            setDownSource(analogTrigger.createOutput(triggerType));
325            m_allocatedDownSource = true;
326        }
327    
328        /**
329         * Set the edge sensitivity on a down counting source.
330         * Set the down source to either detect rising edges or falling edges.
331         * @param risingEdge true to count the rising edge
332         * @param fallingEdge true to count the falling edge
333         */
334        public void setDownSourceEdge(boolean risingEdge, boolean fallingEdge) {
335            if (m_downSource == null) throw new RuntimeException(
336                    " Down Source must be set before setting the edge!");
337            m_counter.writeConfig_DownRisingEdge(risingEdge);
338            m_counter.writeConfig_DownFallingEdge(fallingEdge);
339        }
340    
341        /**
342         * Disable the down counting source to the counter.
343         */
344        public void clearDownSource() {
345            if (m_downSource != null && m_allocatedDownSource) {
346                m_downSource.free();
347                m_allocatedDownSource = false;
348            }
349            m_downSource = null;
350    
351            boolean state = m_counter.readConfig_Enable();
352            m_counter.writeConfig_Enable(false);
353            m_counter.writeConfig_DownFallingEdge(false);
354            m_counter.writeConfig_DownRisingEdge(false);
355            // Index 0 of digital is always 0.
356            m_counter.writeConfig_DownSource_Channel(0);
357            m_counter.writeConfig_DownSource_AnalogTrigger(false);
358            m_counter.writeConfig_Enable(state);
359        }
360    
361        /**
362         * Set standard up / down counting mode on this counter.
363         * Up and down counts are sourced independently from two inputs.
364         */
365        public void setUpDownCounterMode() {
366            m_counter.writeConfig_Mode(Mode.kTwoPulse.value);
367        }
368    
369        /**
370         * Set external direction mode on this counter.
371         * Counts are sourced on the Up counter input.
372         * The Down counter input represents the direction to count.
373         */
374        public void setExternalDirectionMode() {
375            m_counter.writeConfig_Mode(Mode.kExternalDirection.value);
376        }
377    
378        /**
379         * Set Semi-period mode on this counter.
380         * Counts up on both rising and falling edges.
381         * @param highSemiPeriod true to count up on both rising and falling
382         */
383        public void setSemiPeriodMode(boolean highSemiPeriod) {
384            m_counter.writeConfig_Mode(Mode.kSemiperiod.value);
385            m_counter.writeConfig_UpRisingEdge(highSemiPeriod);
386            setUpdateWhenEmpty(false);
387        }
388    
389        /**
390         * Configure the counter to count in up or down based on the length of the input pulse.
391         * This mode is most useful for direction sensitive gear tooth sensors.
392         * @param threshold The pulse length beyond which the counter counts the opposite direction.  Units are seconds.
393         */
394        public void setPulseLengthMode(double threshold) {
395            m_counter.writeConfig_Mode(Mode.kPulseLength.value);
396            m_counter.writeConfig_PulseLengthThreshold((short) ((threshold * 1.0e6) * kSystemClockTicksPerMicrosecond));
397        }
398    
399        /**
400         * Start the Counter counting.
401         * This enables the counter and it starts accumulating counts from the associated
402         * input channel. The counter value is not reset on starting, and still has the previous value.
403         */
404        public void start() {
405            m_counter.writeConfig_Enable(true);
406        }
407    
408        /**
409         * Read the current counter value.
410         * Read the value at this instant. It may still be running, so it reflects the current value. Next
411         * time it is read, it might have a different value.
412         */
413        public int get() {
414            return m_counter.readOutput_Value();
415        }
416        
417        /**
418         * Read the current scaled counter value.
419         * Read the value at this instant, scaled by the distance per pulse (defaults to 1).
420         * @return 
421         */
422        public double getDistance() {
423            return m_counter.readOutput_Value() * m_distancePerPulse;
424        }
425    
426        /**
427         * Reset the Counter to zero.
428         * Set the counter value to zero. This doesn't effect the running state of the counter, just sets
429         * the current value to zero.
430         */
431        public void reset() {
432            m_counter.strobeReset();
433        }
434    
435        /**
436         * Stop the Counter.
437         * Stops the counting but doesn't effect the current value.
438         */
439        public void stop() {
440            m_counter.writeConfig_Enable(false);
441        }
442    
443        /**
444         * Set the maximum period where the device is still considered "moving".
445         * Sets the maximum period where the device is considered moving. This value is used to determine
446         * the "stopped" state of the counter using the GetStopped method.
447         * @param maxPeriod The maximum period where the counted device is considered moving in
448         * seconds.
449         */
450        public void setMaxPeriod(double maxPeriod) {
451            m_counter.writeTimerConfig_StallPeriod((int) (maxPeriod * 1.0e6));
452        }
453    
454        /**
455         * Select whether you want to continue updating the event timer output when there are no samples captured.
456         * The output of the event timer has a buffer of periods that are averaged and posted to
457         * a register on the FPGA.  When the timer detects that the event source has stopped
458         * (based on the MaxPeriod) the buffer of samples to be averaged is emptied.  If you
459         * enable the update when empty, you will be notified of the stopped source and the event
460         * time will report 0 samples.  If you disable update when empty, the most recent average
461         * will remain on the output until a new sample is acquired.  You will never see 0 samples
462         * output (except when there have been no events since an FPGA reset) and you will likely not
463         * see the stopped bit become true (since it is updated at the end of an average and there are
464         * no samples to average).
465         * @param enabled true to continue updating
466         */
467        public void setUpdateWhenEmpty(boolean enabled) {
468            m_counter.writeTimerConfig_UpdateWhenEmpty(enabled);
469        }
470    
471        /**
472         * Determine if the clock is stopped.
473         * Determine if the clocked input is stopped based on the MaxPeriod value set using the
474         * SetMaxPeriod method. If the clock exceeds the MaxPeriod, then the device (and counter) are
475         * assumed to be stopped and it returns true.
476         * @return Returns true if the most recent counter period exceeds the MaxPeriod value set by
477         * SetMaxPeriod.
478         */
479        public boolean getStopped() {
480            return m_counter.readTimerOutput_Stalled();
481        }
482    
483        /**
484         * The last direction the counter value changed.
485         * @return The last direction the counter value changed.
486         */
487        public boolean getDirection() {
488            boolean value = m_counter.readOutput_Direction();
489            return value;
490        }
491    
492        /**
493         * Set the Counter to return reversed sensing on the direction.
494         * This allows counters to change the direction they are counting in the case of 1X and 2X
495         * quadrature encoding only. Any other counter mode isn't supported.
496         * @param reverseDirection true if the value counted should be negated.
497         */
498        public void setReverseDirection(boolean reverseDirection) {
499            if (m_counter.readConfig_Mode() == Mode.kExternalDirection.value) {
500                if (reverseDirection) {
501                    setDownSourceEdge(true, true);
502                } else {
503                    setDownSourceEdge(false, true);
504                }
505            }
506        }
507    
508        /**
509         * Get the Period of the most recent count.
510         * Returns the time interval of the most recent count. This can be used for velocity calculations
511         * to determine shaft speed.
512         * @returns The period of the last two pulses in units of seconds.
513         */
514        public double getPeriod() {
515            double period;
516            if (m_counter.readTimerOutput_Stalled()) {
517                return Double.POSITIVE_INFINITY;
518            } else {
519                period = (double) m_counter.readTimerOutput_Period() / (double) m_counter.readTimerOutput_Count();
520            }
521            return period / 1.0e6;
522        }
523        
524        /**
525         * Get the current rate of the Counter.
526         * Read the current rate of the counter accounting for the distance per pulse value. 
527         * The default value for distance per pulse (1) yields units of pulses per second.
528         * @return The rate in units/sec
529         */
530        public double getRate() {
531            return m_distancePerPulse / getPeriod();
532        }
533        
534        /**
535         * Set the Samples to Average which specifies the number of samples of the timer to 
536         * average when calculating the period. Perform averaging to account for 
537         * mechanical imperfections or as oversampling to increase resolution.
538         * @param samplesToAverage The number of samples to average from 1 to 127.
539         */
540        public void setSamplesToAverage (int samplesToAverage) {
541           BoundaryException.assertWithinBounds(samplesToAverage, 1, 127);
542           m_counter.writeTimerConfig_AverageSize(samplesToAverage);
543        }
544        
545        /**
546         * Get the Samples to Average which specifies the number of samples of the timer to 
547         * average when calculating the period. Perform averaging to account for 
548         * mechanical imperfections or as oversampling to increase resolution.
549         * @return SamplesToAverage The number of samples being averaged (from 1 to 127)
550         */
551        public int getSamplesToAverage()
552        {
553            return m_counter.readTimerConfig_AverageSize();
554        }
555        
556        /**
557         * Set the distance per pulse for this counter.
558         * This sets the multiplier used to determine the distance driven based on the count value
559         * from the encoder. Set this value based on the Pulses per Revolution and factor in any 
560         * gearing reductions. This distance can be in any units you like, linear or angular.
561         *
562         * @param distancePerPulse The scale factor that will be used to convert pulses to useful units.
563         */
564        public void setDistancePerPulse(double distancePerPulse) {
565            m_distancePerPulse = distancePerPulse;
566        }
567        
568        /**
569         * Set which parameter of the encoder you are using as a process control variable.
570         * The counter class supports the rate and distance parameters.
571         * @param pidSource An enum to select the parameter.
572         */
573        public void setPIDSourceParameter(PIDSourceParameter pidSource) {
574            BoundaryException.assertWithinBounds(pidSource.value, 0, 1);
575            m_pidSource = pidSource;
576        }
577            
578        public double pidGet() {
579            switch (m_pidSource.value) {
580            case PIDSourceParameter.kDistance_val:
581                return getDistance();
582            case PIDSourceParameter.kRate_val:
583                return getRate();
584            default:
585                return 0.0;
586            }
587        }
588            
589        /**
590         * Live Window code, only does anything if live window is activated.
591         */
592        public String getSmartDashboardType(){
593            return "Counter";
594        }
595        private ITable m_table;
596        
597        /**
598         * {@inheritDoc}
599         */
600        public void initTable(ITable subtable) {
601            m_table = subtable;
602            updateTable();
603        }
604        
605        /**
606         * {@inheritDoc}
607         */
608        public ITable getTable(){
609            return m_table;
610        }
611        
612        /**
613         * {@inheritDoc}
614         */
615        public void updateTable() {
616            if (m_table != null) {
617                m_table.putNumber("Value", m_counter.readOutput_Value());
618            }
619        }
620        
621        /**
622         * {@inheritDoc}
623         */
624        public void startLiveWindowMode() {}
625        
626        /**
627         * {@inheritDoc}
628         */
629        public void stopLiveWindowMode() {}
630    }