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