blob: 98b3adecbe0489da804cf4799705285f65673d7f [file] [log] [blame]
jerrymf1579332013-02-07 01:56:28 +00001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2008. All Rights Reserved. */
3/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
5/*----------------------------------------------------------------------------*/
6
7#include "Counter.h"
8#include "AnalogTrigger.h"
9#include "DigitalInput.h"
10#include "NetworkCommunication/UsageReporting.h"
11#include "Resource.h"
12#include "WPIErrors.h"
13
14static Resource *counters = NULL;
15
16/**
17 * Create an instance of a counter object.
18 * This creates a ChipObject counter and initializes status variables appropriately
19 */
20void Counter::InitCounter(Mode mode)
21{
22 Resource::CreateResourceObject(&counters, tCounter::kNumSystems);
23 UINT32 index = counters->Allocate("Counter");
24 if (index == ~0ul)
25 {
26 CloneError(counters);
27 return;
28 }
29 m_index = index;
30 tRioStatusCode localStatus = NiFpga_Status_Success;
31 m_counter = tCounter::create(m_index, &localStatus);
32 m_counter->writeConfig_Mode(mode, &localStatus);
33 m_upSource = NULL;
34 m_downSource = NULL;
35 m_allocatedUpSource = false;
36 m_allocatedDownSource = false;
37 m_counter->writeTimerConfig_AverageSize(1, &localStatus);
38 wpi_setError(localStatus);
39
40 nUsageReporting::report(nUsageReporting::kResourceType_Counter, index, mode);
41}
42
43/**
44 * Create an instance of a counter where no sources are selected.
45 * Then they all must be selected by calling functions to specify the upsource and the downsource
46 * independently.
47 */
48Counter::Counter() :
49 m_upSource(NULL),
50 m_downSource(NULL),
51 m_counter(NULL)
52{
53 InitCounter();
54}
55
56/**
57 * Create an instance of a counter from a Digital Input.
58 * This is used if an existing digital input is to be shared by multiple other objects such
59 * as encoders.
60 */
61Counter::Counter(DigitalSource *source) :
62 m_upSource(NULL),
63 m_downSource(NULL),
64 m_counter(NULL)
65{
66 InitCounter();
67 SetUpSource(source);
68 ClearDownSource();
69}
70
71Counter::Counter(DigitalSource &source) :
72 m_upSource(NULL),
73 m_downSource(NULL),
74 m_counter(NULL)
75{
76 InitCounter();
77 SetUpSource(&source);
78 ClearDownSource();
79}
80
81/**
82 * Create an instance of a Counter object.
83 * Create an up-Counter instance given a channel. The default digital module is assumed.
84 */
85Counter::Counter(UINT32 channel) :
86 m_upSource(NULL),
87 m_downSource(NULL),
88 m_counter(NULL)
89{
90 InitCounter();
91 SetUpSource(channel);
92 ClearDownSource();
93}
94
95/**
96 * Create an instance of a Counter object.
97 * Create an instance of an up-Counter given a digital module and a channel.
98 * @param moduleNumber The digital module (1 or 2).
99 * @param channel The channel in the digital module
100 */
101Counter::Counter(UINT8 moduleNumber, UINT32 channel) :
102 m_upSource(NULL),
103 m_downSource(NULL),
104 m_counter(NULL)
105{
106 InitCounter();
107 SetUpSource(moduleNumber, channel);
108 ClearDownSource();
109}
110
111/**
112 * Create an instance of a Counter object.
113 * Create an instance of a simple up-Counter given an analog trigger.
114 * Use the trigger state output from the analog trigger.
115 */
116Counter::Counter(AnalogTrigger *trigger) :
117 m_upSource(NULL),
118 m_downSource(NULL),
119 m_counter(NULL)
120{
121 InitCounter();
122 SetUpSource(trigger->CreateOutput(AnalogTriggerOutput::kState));
123 ClearDownSource();
124 m_allocatedUpSource = true;
125}
126
127Counter::Counter(AnalogTrigger &trigger) :
128 m_upSource(NULL),
129 m_downSource(NULL),
130 m_counter(NULL)
131{
132 InitCounter();
133 SetUpSource(trigger.CreateOutput(AnalogTriggerOutput::kState));
134 ClearDownSource();
135 m_allocatedUpSource = true;
136}
137
138Counter::Counter(EncodingType encodingType, DigitalSource *upSource, DigitalSource *downSource, bool inverted) :
139 m_upSource(NULL),
140 m_downSource(NULL),
141 m_counter(NULL)
142{
143 if (encodingType != k1X && encodingType != k2X)
144 {
145 wpi_setWPIErrorWithContext(ParameterOutOfRange, "Counter only supports 1X and 2X quadrature decoding.");
146 return;
147 }
148 InitCounter(kExternalDirection);
149 SetUpSource(upSource);
150 SetDownSource(downSource);
151 tRioStatusCode localStatus = NiFpga_Status_Success;
152
153 if (encodingType == k1X)
154 {
155 SetUpSourceEdge(true, false);
156 m_counter->writeTimerConfig_AverageSize(1, &localStatus);
157 }
158 else
159 {
160 SetUpSourceEdge(true, true);
161 m_counter->writeTimerConfig_AverageSize(2, &localStatus);
162 }
163
164 wpi_setError(localStatus);
165 SetDownSourceEdge(inverted, true);
166}
167
168/**
169 * Delete the Counter object.
170 */
171Counter::~Counter()
172{
173 SetUpdateWhenEmpty(true);
174 if (m_allocatedUpSource)
175 {
176 delete m_upSource;
177 m_upSource = NULL;
178 }
179 if (m_allocatedDownSource)
180 {
181 delete m_downSource;
182 m_downSource = NULL;
183 }
184 delete m_counter;
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 *
192 * @param moduleNumber The digital module (1 or 2).
193 * @param channel The digital channel (1..14).
194 */
195void Counter::SetUpSource(UINT8 moduleNumber, UINT32 channel)
196{
197 if (StatusIsFatal()) return;
198 SetUpSource(new DigitalInput(moduleNumber, channel));
199 m_allocatedUpSource = true;
200}
201
202/**
203 * Set the upsource for the counter as a digital input channel.
204 * The slot will be the default digital module slot.
205 */
206void Counter::SetUpSource(UINT32 channel)
207{
208 if (StatusIsFatal()) return;
209 SetUpSource(GetDefaultDigitalModule(), channel);
210 m_allocatedUpSource = true;
211}
212
213/**
214 * Set the up counting source to be an analog trigger.
215 * @param analogTrigger The analog trigger object that is used for the Up Source
216 * @param triggerType The analog trigger output that will trigger the counter.
217 */
218void Counter::SetUpSource(AnalogTrigger *analogTrigger, AnalogTriggerOutput::Type triggerType)
219{
220 if (StatusIsFatal()) return;
221 SetUpSource(analogTrigger->CreateOutput(triggerType));
222 m_allocatedUpSource = true;
223}
224
225/**
226 * Set the up counting source to be an analog trigger.
227 * @param analogTrigger The analog trigger object that is used for the Up Source
228 * @param triggerType The analog trigger output that will trigger the counter.
229 */
230void Counter::SetUpSource(AnalogTrigger &analogTrigger, AnalogTriggerOutput::Type triggerType)
231{
232 SetUpSource(&analogTrigger, triggerType);
233}
234
235/**
236 * Set the source object that causes the counter to count up.
237 * Set the up counting DigitalSource.
238 */
239void Counter::SetUpSource(DigitalSource *source)
240{
241 if (StatusIsFatal()) return;
242 if (m_allocatedUpSource)
243 {
244 delete m_upSource;
245 m_upSource = NULL;
246 m_allocatedUpSource = false;
247 }
248 m_upSource = source;
249 if (m_upSource->StatusIsFatal())
250 {
251 CloneError(m_upSource);
252 }
253 else
254 {
255 tRioStatusCode localStatus = NiFpga_Status_Success;
256 m_counter->writeConfig_UpSource_Module(source->GetModuleForRouting(), &localStatus);
257 m_counter->writeConfig_UpSource_Channel(source->GetChannelForRouting(), &localStatus);
258 m_counter->writeConfig_UpSource_AnalogTrigger(source->GetAnalogTriggerForRouting(), &localStatus);
259
260 if(m_counter->readConfig_Mode(&localStatus) == kTwoPulse ||
261 m_counter->readConfig_Mode(&localStatus) == kExternalDirection)
262 {
263 SetUpSourceEdge(true, false);
264 }
265 m_counter->strobeReset(&localStatus);
266 wpi_setError(localStatus);
267 }
268}
269
270/**
271 * Set the source object that causes the counter to count up.
272 * Set the up counting DigitalSource.
273 */
274void Counter::SetUpSource(DigitalSource &source)
275{
276 SetUpSource(&source);
277}
278
279/**
280 * Set the edge sensitivity on an up counting source.
281 * Set the up source to either detect rising edges or falling edges.
282 */
283void Counter::SetUpSourceEdge(bool risingEdge, bool fallingEdge)
284{
285 if (StatusIsFatal()) return;
286 if (m_upSource == NULL)
287 {
288 wpi_setWPIErrorWithContext(NullParameter, "Must set non-NULL UpSource before setting UpSourceEdge");
289 }
290 tRioStatusCode localStatus = NiFpga_Status_Success;
291 m_counter->writeConfig_UpRisingEdge(risingEdge, &localStatus);
292 m_counter->writeConfig_UpFallingEdge(fallingEdge, &localStatus);
293 wpi_setError(localStatus);
294}
295
296/**
297 * Disable the up counting source to the counter.
298 */
299void Counter::ClearUpSource()
300{
301 if (StatusIsFatal()) return;
302 if (m_allocatedUpSource)
303 {
304 delete m_upSource;
305 m_upSource = NULL;
306 m_allocatedUpSource = false;
307 }
308 tRioStatusCode localStatus = NiFpga_Status_Success;
309 bool state = m_counter->readConfig_Enable(&localStatus);
310 m_counter->writeConfig_Enable(false, &localStatus);
311 m_counter->writeConfig_UpFallingEdge(false, &localStatus);
312 m_counter->writeConfig_UpRisingEdge(false, &localStatus);
313 // Index 0 of digital is always 0.
314 m_counter->writeConfig_UpSource_Channel(0, &localStatus);
315 m_counter->writeConfig_UpSource_AnalogTrigger(false, &localStatus);
316 m_counter->writeConfig_Enable(state, &localStatus);
317 wpi_setError(localStatus);
318}
319
320/**
321 * Set the down counting source to be a digital input channel.
322 * The slot will be set to the default digital module slot.
323 */
324void Counter::SetDownSource(UINT32 channel)
325{
326 if (StatusIsFatal()) return;
327 SetDownSource(new DigitalInput(channel));
328 m_allocatedDownSource = true;
329}
330
331/**
332 * Set the down counting source to be a digital input slot and channel.
333 *
334 * @param moduleNumber The digital module (1 or 2).
335 * @param channel The digital channel (1..14).
336 */
337void Counter::SetDownSource(UINT8 moduleNumber, UINT32 channel)
338{
339 if (StatusIsFatal()) return;
340 SetDownSource(new DigitalInput(moduleNumber, channel));
341 m_allocatedDownSource = true;
342}
343
344/**
345 * Set the down counting source to be an analog trigger.
346 * @param analogTrigger The analog trigger object that is used for the Down Source
347 * @param triggerType The analog trigger output that will trigger the counter.
348 */
349void Counter::SetDownSource(AnalogTrigger *analogTrigger, AnalogTriggerOutput::Type triggerType)
350{
351 if (StatusIsFatal()) return;
352 SetDownSource(analogTrigger->CreateOutput(triggerType));
353 m_allocatedDownSource = true;
354}
355
356/**
357 * Set the down counting source to be an analog trigger.
358 * @param analogTrigger The analog trigger object that is used for the Down Source
359 * @param triggerType The analog trigger output that will trigger the counter.
360 */
361void Counter::SetDownSource(AnalogTrigger &analogTrigger, AnalogTriggerOutput::Type triggerType)
362{
363 SetDownSource(&analogTrigger, triggerType);
364}
365
366/**
367 * Set the source object that causes the counter to count down.
368 * Set the down counting DigitalSource.
369 */
370void Counter::SetDownSource(DigitalSource *source)
371{
372 if (StatusIsFatal()) return;
373 if (m_allocatedDownSource)
374 {
375 delete m_downSource;
376 m_downSource = NULL;
377 m_allocatedDownSource = false;
378 }
379 m_downSource = source;
380 if (m_downSource->StatusIsFatal())
381 {
382 CloneError(m_downSource);
383 }
384 else
385 {
386 tRioStatusCode localStatus = NiFpga_Status_Success;
387 unsigned char mode = m_counter->readConfig_Mode(&localStatus);
388 if (mode != kTwoPulse && mode != kExternalDirection)
389 {
390 wpi_setWPIErrorWithContext(ParameterOutOfRange, "Counter only supports DownSource in TwoPulse and ExternalDirection modes.");
391 return;
392 }
393 m_counter->writeConfig_DownSource_Module(source->GetModuleForRouting(), &localStatus);
394 m_counter->writeConfig_DownSource_Channel(source->GetChannelForRouting(), &localStatus);
395 m_counter->writeConfig_DownSource_AnalogTrigger(source->GetAnalogTriggerForRouting(), &localStatus);
396
397 SetDownSourceEdge(true, false);
398 m_counter->strobeReset(&localStatus);
399 wpi_setError(localStatus);
400 }
401}
402
403/**
404 * Set the source object that causes the counter to count down.
405 * Set the down counting DigitalSource.
406 */
407void Counter::SetDownSource(DigitalSource &source)
408{
409 SetDownSource(&source);
410}
411
412/**
413 * Set the edge sensitivity on a down counting source.
414 * Set the down source to either detect rising edges or falling edges.
415 */
416void Counter::SetDownSourceEdge(bool risingEdge, bool fallingEdge)
417{
418 if (StatusIsFatal()) return;
419 if (m_downSource == NULL)
420 {
421 wpi_setWPIErrorWithContext(NullParameter, "Must set non-NULL DownSource before setting DownSourceEdge");
422 }
423 tRioStatusCode localStatus = NiFpga_Status_Success;
424 m_counter->writeConfig_DownRisingEdge(risingEdge, &localStatus);
425 m_counter->writeConfig_DownFallingEdge(fallingEdge, &localStatus);
426 wpi_setError(localStatus);
427}
428
429/**
430 * Disable the down counting source to the counter.
431 */
432void Counter::ClearDownSource()
433{
434 if (StatusIsFatal()) return;
435 if (m_allocatedDownSource)
436 {
437 delete m_downSource;
438 m_downSource = NULL;
439 m_allocatedDownSource = false;
440 }
441 tRioStatusCode localStatus = NiFpga_Status_Success;
442 bool state = m_counter->readConfig_Enable(&localStatus);
443 m_counter->writeConfig_Enable(false, &localStatus);
444 m_counter->writeConfig_DownFallingEdge(false, &localStatus);
445 m_counter->writeConfig_DownRisingEdge(false, &localStatus);
446 // Index 0 of digital is always 0.
447 m_counter->writeConfig_DownSource_Channel(0, &localStatus);
448 m_counter->writeConfig_DownSource_AnalogTrigger(false, &localStatus);
449 m_counter->writeConfig_Enable(state, &localStatus);
450 wpi_setError(localStatus);
451}
452
453/**
454 * Set standard up / down counting mode on this counter.
455 * Up and down counts are sourced independently from two inputs.
456 */
457void Counter::SetUpDownCounterMode()
458{
459 if (StatusIsFatal()) return;
460 tRioStatusCode localStatus = NiFpga_Status_Success;
461 m_counter->writeConfig_Mode(kTwoPulse, &localStatus);
462 wpi_setError(localStatus);
463}
464
465/**
466 * Set external direction mode on this counter.
467 * Counts are sourced on the Up counter input.
468 * The Down counter input represents the direction to count.
469 */
470void Counter::SetExternalDirectionMode()
471{
472 if (StatusIsFatal()) return;
473 tRioStatusCode localStatus = NiFpga_Status_Success;
474 m_counter->writeConfig_Mode(kExternalDirection, &localStatus);
475 wpi_setError(localStatus);
476}
477
478/**
479 * Set Semi-period mode on this counter.
480 * Counts up on both rising and falling edges.
481 */
482void Counter::SetSemiPeriodMode(bool highSemiPeriod)
483{
484 if (StatusIsFatal()) return;
485 tRioStatusCode localStatus = NiFpga_Status_Success;
486 m_counter->writeConfig_Mode(kSemiperiod, &localStatus);
487 m_counter->writeConfig_UpRisingEdge(highSemiPeriod, &localStatus);
488 SetUpdateWhenEmpty(false);
489 wpi_setError(localStatus);
490}
491
492/**
493 * Configure the counter to count in up or down based on the length of the input pulse.
494 * This mode is most useful for direction sensitive gear tooth sensors.
495 * @param threshold The pulse length beyond which the counter counts the opposite direction. Units are seconds.
496 */
497void Counter::SetPulseLengthMode(float threshold)
498{
499 if (StatusIsFatal()) return;
500 tRioStatusCode localStatus = NiFpga_Status_Success;
501 m_counter->writeConfig_Mode(kPulseLength, &localStatus);
502 m_counter->writeConfig_PulseLengthThreshold((UINT32)(threshold * 1.0e6) * kSystemClockTicksPerMicrosecond, &localStatus);
503 wpi_setError(localStatus);
504}
505
506/**
507 * Start the Counter counting.
508 * This enables the counter and it starts accumulating counts from the associated
509 * input channel. The counter value is not reset on starting, and still has the previous value.
510 */
511void Counter::Start()
512{
513 if (StatusIsFatal()) return;
514 tRioStatusCode localStatus = NiFpga_Status_Success;
515 m_counter->writeConfig_Enable(1, &localStatus);
516 wpi_setError(localStatus);
517}
518
519/**
520 * Read the current counter value.
521 * Read the value at this instant. It may still be running, so it reflects the current value. Next
522 * time it is read, it might have a different value.
523 */
524INT32 Counter::Get()
525{
526 if (StatusIsFatal()) return 0;
527 tRioStatusCode localStatus = NiFpga_Status_Success;
528 INT32 value = m_counter->readOutput_Value(&localStatus);
529 wpi_setError(localStatus);
530 return value;
531}
532
533/**
534 * Reset the Counter to zero.
535 * Set the counter value to zero. This doesn't effect the running state of the counter, just sets
536 * the current value to zero.
537 */
538void Counter::Reset()
539{
540 if (StatusIsFatal()) return;
541 tRioStatusCode localStatus = NiFpga_Status_Success;
542 m_counter->strobeReset(&localStatus);
543 wpi_setError(localStatus);
544}
545
546/**
547 * Stop the Counter.
548 * Stops the counting but doesn't effect the current value.
549 */
550void Counter::Stop()
551{
552 if (StatusIsFatal()) return;
553 tRioStatusCode localStatus = NiFpga_Status_Success;
554 m_counter->writeConfig_Enable(0, &localStatus);
555 wpi_setError(localStatus);
556}
557
558/*
559 * Get the Period of the most recent count.
560 * Returns the time interval of the most recent count. This can be used for velocity calculations
561 * to determine shaft speed.
562 * @returns The period of the last two pulses in units of seconds.
563 */
564double Counter::GetPeriod()
565{
566 if (StatusIsFatal()) return 0.0;
567 tRioStatusCode localStatus = NiFpga_Status_Success;
568 tCounter::tTimerOutput output = m_counter->readTimerOutput(&localStatus);
569 double period;
570 if (output.Stalled)
571 {
572 // Return infinity
573 double zero = 0.0;
574 period = 1.0 / zero;
575 }
576 else
577 {
578 // output.Period is a fixed point number that counts by 2 (24 bits, 25 integer bits)
579 period = (double)(output.Period << 1) / (double)output.Count;
580 }
581 wpi_setError(localStatus);
582 return period * 1.0e-6;
583}
584
585/**
586 * Set the maximum period where the device is still considered "moving".
587 * Sets the maximum period where the device is considered moving. This value is used to determine
588 * the "stopped" state of the counter using the GetStopped method.
589 * @param maxPeriod The maximum period where the counted device is considered moving in
590 * seconds.
591 */
592void Counter::SetMaxPeriod(double maxPeriod)
593{
594 if (StatusIsFatal()) return;
595 tRioStatusCode localStatus = NiFpga_Status_Success;
596 m_counter->writeTimerConfig_StallPeriod((UINT32)(maxPeriod * 1.0e6), &localStatus);
597 wpi_setError(localStatus);
598}
599
600/**
601 * Select whether you want to continue updating the event timer output when there are no samples captured.
602 * The output of the event timer has a buffer of periods that are averaged and posted to
603 * a register on the FPGA. When the timer detects that the event source has stopped
604 * (based on the MaxPeriod) the buffer of samples to be averaged is emptied. If you
605 * enable the update when empty, you will be notified of the stopped source and the event
606 * time will report 0 samples. If you disable update when empty, the most recent average
607 * will remain on the output until a new sample is acquired. You will never see 0 samples
608 * output (except when there have been no events since an FPGA reset) and you will likely not
609 * see the stopped bit become true (since it is updated at the end of an average and there are
610 * no samples to average).
611 */
612void Counter::SetUpdateWhenEmpty(bool enabled)
613{
614 if (StatusIsFatal()) return;
615 tRioStatusCode localStatus = NiFpga_Status_Success;
616 m_counter->writeTimerConfig_UpdateWhenEmpty(enabled, &localStatus);
617 wpi_setError(localStatus);
618}
619
620/**
621 * Determine if the clock is stopped.
622 * Determine if the clocked input is stopped based on the MaxPeriod value set using the
623 * SetMaxPeriod method. If the clock exceeds the MaxPeriod, then the device (and counter) are
624 * assumed to be stopped and it returns true.
625 * @return Returns true if the most recent counter period exceeds the MaxPeriod value set by
626 * SetMaxPeriod.
627 */
628bool Counter::GetStopped()
629{
630 if (StatusIsFatal()) return false;
631 tRioStatusCode localStatus = NiFpga_Status_Success;
632 return m_counter->readTimerOutput_Stalled(&localStatus);
633 wpi_setError(localStatus);
634}
635
636/**
637 * The last direction the counter value changed.
638 * @return The last direction the counter value changed.
639 */
640bool Counter::GetDirection()
641{
642 if (StatusIsFatal()) return false;
643 tRioStatusCode localStatus = NiFpga_Status_Success;
644 bool value = m_counter->readOutput_Direction(&localStatus);
645 wpi_setError(localStatus);
646 return value;
647}
648
649/**
650 * Set the Counter to return reversed sensing on the direction.
651 * This allows counters to change the direction they are counting in the case of 1X and 2X
652 * quadrature encoding only. Any other counter mode isn't supported.
653 * @param reverseDirection true if the value counted should be negated.
654 */
655void Counter::SetReverseDirection(bool reverseDirection)
656{
657 if (StatusIsFatal()) return;
658 tRioStatusCode localStatus = NiFpga_Status_Success;
659 if (m_counter->readConfig_Mode(&localStatus) == kExternalDirection)
660 {
661 if (reverseDirection)
662 SetDownSourceEdge(true, true);
663 else
664 SetDownSourceEdge(false, true);
665 }
666 wpi_setError(localStatus);
667}
668
669
670void Counter::UpdateTable() {
671 if (m_table != NULL) {
672 m_table->PutNumber("Value", Get());
673 }
674}
675
676void Counter::StartLiveWindowMode() {
677
678}
679
680void Counter::StopLiveWindowMode() {
681
682}
683
684std::string Counter::GetSmartDashboardType() {
685 return "Counter";
686}
687
688void Counter::InitTable(ITable *subTable) {
689 m_table = subTable;
690 UpdateTable();
691}
692
693ITable * Counter::GetTable() {
694 return m_table;
695}
696