blob: ca134525cadb923596faf15f6c99068f05f18996 [file] [log] [blame]
Brian Silvermanf7f267a2017-02-04 16:16:08 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2008-2017. 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 the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
8#include "Counter.h"
9
10#include "AnalogTrigger.h"
11#include "DigitalInput.h"
12#include "HAL/HAL.h"
13#include "WPIErrors.h"
14
15using namespace frc;
16
17/**
18 * Create an instance of a counter where no sources are selected.
19 *
20 * They all must be selected by calling functions to specify the upsource and
21 * the downsource independently.
22 *
23 * This creates a ChipObject counter and initializes status variables
24 * appropriately.
25 *
26 * The counter will start counting immediately.
27 *
28 * @param mode The counter mode
29 */
30Counter::Counter(Mode mode) {
31 int32_t status = 0;
32 m_counter = HAL_InitializeCounter((HAL_Counter_Mode)mode, &m_index, &status);
33 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
34
35 SetMaxPeriod(.5);
36
37 HAL_Report(HALUsageReporting::kResourceType_Counter, m_index, mode);
38}
39
40/**
41 * Create an instance of a counter from a Digital Source (such as a Digital
42 * Input).
43 *
44 * This is used if an existing digital input is to be shared by multiple other
45 * objects such as encoders or if the Digital Source is not a Digital Input
46 * channel (such as an Analog Trigger).
47 *
48 * The counter will start counting immediately.
49 * @param source A pointer to the existing DigitalSource object. It will be set
50 * as the Up Source.
51 */
52Counter::Counter(DigitalSource* source) : Counter(kTwoPulse) {
53 SetUpSource(source);
54 ClearDownSource();
55}
56
57/**
58 * Create an instance of a counter from a Digital Source (such as a Digital
59 * Input).
60 *
61 * This is used if an existing digital input is to be shared by multiple other
62 * objects such as encoders or if the Digital Source is not a Digital Input
63 * channel (such as an Analog Trigger).
64 *
65 * The counter will start counting immediately.
66 *
67 * @param source A pointer to the existing DigitalSource object. It will be
68 * set as the Up Source.
69 */
70Counter::Counter(std::shared_ptr<DigitalSource> source) : Counter(kTwoPulse) {
71 SetUpSource(source);
72 ClearDownSource();
73}
74
75/**
76 * Create an instance of a Counter object.
77 *
78 * Create an up-Counter instance given a channel.
79 *
80 * The counter will start counting immediately.
81 *
82 * @param channel The DIO channel to use as the up source. 0-9 are on-board,
83 * 10-25 are on the MXP
84 */
85Counter::Counter(int channel) : Counter(kTwoPulse) {
86 SetUpSource(channel);
87 ClearDownSource();
88}
89
90/**
91 * Create an instance of a Counter object.
92 *
93 * Create an instance of a simple up-Counter given an analog trigger.
94 * Use the trigger state output from the analog trigger.
95 *
96 * The counter will start counting immediately.
97 *
98 * @param trigger The pointer to the existing AnalogTrigger object.
99 */
100WPI_DEPRECATED("Use pass-by-reference instead.")
101Counter::Counter(AnalogTrigger* trigger) : Counter(kTwoPulse) {
102 SetUpSource(trigger->CreateOutput(AnalogTriggerType::kState));
103 ClearDownSource();
104}
105
106/**
107 * Create an instance of a Counter object.
108 *
109 * Create an instance of a simple up-Counter given an analog trigger.
110 * Use the trigger state output from the analog trigger.
111 *
112 * The counter will start counting immediately.
113 *
114 * @param trigger The reference to the existing AnalogTrigger object.
115 */
116Counter::Counter(const AnalogTrigger& trigger) : Counter(kTwoPulse) {
117 SetUpSource(trigger.CreateOutput(AnalogTriggerType::kState));
118 ClearDownSource();
119}
120
121/**
122 * Create an instance of a Counter object.
123 *
124 * Creates a full up-down counter given two Digital Sources.
125 *
126 * @param encodingType The quadrature decoding mode (1x or 2x)
127 * @param upSource The pointer to the DigitalSource to set as the up source
128 * @param downSource The pointer to the DigitalSource to set as the down
129 * source
130 * @param inverted True to invert the output (reverse the direction)
131 */
132Counter::Counter(EncodingType encodingType, DigitalSource* upSource,
133 DigitalSource* downSource, bool inverted)
134 : Counter(encodingType, std::shared_ptr<DigitalSource>(
135 upSource, NullDeleter<DigitalSource>()),
136 std::shared_ptr<DigitalSource>(downSource,
137 NullDeleter<DigitalSource>()),
138 inverted) {}
139
140/**
141 * Create an instance of a Counter object.
142 *
143 * Creates a full up-down counter given two Digital Sources.
144 *
145 * @param encodingType The quadrature decoding mode (1x or 2x)
146 * @param upSource The pointer to the DigitalSource to set as the up source
147 * @param downSource The pointer to the DigitalSource to set as the down
148 * source
149 * @param inverted True to invert the output (reverse the direction)
150 */
151Counter::Counter(EncodingType encodingType,
152 std::shared_ptr<DigitalSource> upSource,
153 std::shared_ptr<DigitalSource> downSource, bool inverted)
154 : Counter(kExternalDirection) {
155 if (encodingType != k1X && encodingType != k2X) {
156 wpi_setWPIErrorWithContext(
157 ParameterOutOfRange,
158 "Counter only supports 1X and 2X quadrature decoding.");
159 return;
160 }
161 SetUpSource(upSource);
162 SetDownSource(downSource);
163 int32_t status = 0;
164
165 if (encodingType == k1X) {
166 SetUpSourceEdge(true, false);
167 HAL_SetCounterAverageSize(m_counter, 1, &status);
168 } else {
169 SetUpSourceEdge(true, true);
170 HAL_SetCounterAverageSize(m_counter, 2, &status);
171 }
172
173 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
174 SetDownSourceEdge(inverted, true);
175}
176
177/**
178 * Delete the Counter object.
179 */
180Counter::~Counter() {
181 SetUpdateWhenEmpty(true);
182
183 int32_t status = 0;
184 HAL_FreeCounter(m_counter, &status);
185 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
186 m_counter = HAL_kInvalidHandle;
187}
188
189/**
190 * Set the upsource for the counter as a digital input channel.
191 *
192 * @param channel The DIO channel to use as the up source. 0-9 are on-board,
193 * 10-25 are on the MXP
194 */
195void Counter::SetUpSource(int channel) {
196 if (StatusIsFatal()) return;
197 SetUpSource(std::make_shared<DigitalInput>(channel));
198}
199
200/**
201 * Set the up counting source to be an analog trigger.
202 *
203 * @param analogTrigger The analog trigger object that is used for the Up Source
204 * @param triggerType The analog trigger output that will trigger the counter.
205 */
206void Counter::SetUpSource(AnalogTrigger* analogTrigger,
207 AnalogTriggerType triggerType) {
208 SetUpSource(std::shared_ptr<AnalogTrigger>(analogTrigger,
209 NullDeleter<AnalogTrigger>()),
210 triggerType);
211}
212
213/**
214 * Set the up counting source to be an analog trigger.
215 *
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(std::shared_ptr<AnalogTrigger> analogTrigger,
220 AnalogTriggerType triggerType) {
221 if (StatusIsFatal()) return;
222 SetUpSource(analogTrigger->CreateOutput(triggerType));
223}
224
225/**
226 * Set the source object that causes the counter to count up.
227 *
228 * Set the up counting DigitalSource.
229 *
230 * @param source Pointer to the DigitalSource object to set as the up source
231 */
232void Counter::SetUpSource(std::shared_ptr<DigitalSource> source) {
233 if (StatusIsFatal()) return;
234 m_upSource = source;
235 if (m_upSource->StatusIsFatal()) {
236 CloneError(*m_upSource);
237 } else {
238 int32_t status = 0;
239 HAL_SetCounterUpSource(
240 m_counter, source->GetPortHandleForRouting(),
241 (HAL_AnalogTriggerType)source->GetAnalogTriggerTypeForRouting(),
242 &status);
243 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
244 }
245}
246
247void Counter::SetUpSource(DigitalSource* source) {
248 SetUpSource(
249 std::shared_ptr<DigitalSource>(source, NullDeleter<DigitalSource>()));
250}
251
252/**
253 * Set the source object that causes the counter to count up.
254 *
255 * Set the up counting DigitalSource.
256 *
257 * @param source Reference to the DigitalSource object to set as the up source
258 */
259void Counter::SetUpSource(DigitalSource& source) {
260 SetUpSource(
261 std::shared_ptr<DigitalSource>(&source, NullDeleter<DigitalSource>()));
262}
263
264/**
265 * Set the edge sensitivity on an up counting source.
266 *
267 * Set the up source to either detect rising edges or falling edges or both.
268 *
269 * @param risingEdge True to trigger on rising edges
270 * @param fallingEdge True to trigger on falling edges
271 */
272void Counter::SetUpSourceEdge(bool risingEdge, bool fallingEdge) {
273 if (StatusIsFatal()) return;
274 if (m_upSource == nullptr) {
275 wpi_setWPIErrorWithContext(
276 NullParameter,
277 "Must set non-nullptr UpSource before setting UpSourceEdge");
278 }
279 int32_t status = 0;
280 HAL_SetCounterUpSourceEdge(m_counter, risingEdge, fallingEdge, &status);
281 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
282}
283
284/**
285 * Disable the up counting source to the counter.
286 */
287void Counter::ClearUpSource() {
288 if (StatusIsFatal()) return;
289 m_upSource.reset();
290 int32_t status = 0;
291 HAL_ClearCounterUpSource(m_counter, &status);
292 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
293}
294
295/**
296 * Set the down counting source to be a digital input channel.
297 *
298 * @param channel The DIO channel to use as the up source. 0-9 are on-board,
299 * 10-25 are on the MXP
300 */
301void Counter::SetDownSource(int channel) {
302 if (StatusIsFatal()) return;
303 SetDownSource(std::make_shared<DigitalInput>(channel));
304}
305
306/**
307 * Set the down counting source to be an analog trigger.
308 *
309 * @param analogTrigger The analog trigger object that is used for the Down
310 * Source
311 * @param triggerType The analog trigger output that will trigger the counter.
312 */
313void Counter::SetDownSource(AnalogTrigger* analogTrigger,
314 AnalogTriggerType triggerType) {
315 SetDownSource(std::shared_ptr<AnalogTrigger>(analogTrigger,
316 NullDeleter<AnalogTrigger>()),
317 triggerType);
318}
319
320/**
321 * Set the down counting source to be an analog trigger.
322 *
323 * @param analogTrigger The analog trigger object that is used for the Down
324 * Source
325 * @param triggerType The analog trigger output that will trigger the counter.
326 */
327void Counter::SetDownSource(std::shared_ptr<AnalogTrigger> analogTrigger,
328 AnalogTriggerType triggerType) {
329 if (StatusIsFatal()) return;
330 SetDownSource(analogTrigger->CreateOutput(triggerType));
331}
332
333/**
334 * Set the source object that causes the counter to count down.
335 *
336 * Set the down counting DigitalSource.
337 *
338 * @param source Pointer to the DigitalSource object to set as the down source
339 */
340void Counter::SetDownSource(std::shared_ptr<DigitalSource> source) {
341 if (StatusIsFatal()) return;
342 m_downSource = source;
343 if (m_downSource->StatusIsFatal()) {
344 CloneError(*m_downSource);
345 } else {
346 int32_t status = 0;
347 HAL_SetCounterDownSource(
348 m_counter, source->GetPortHandleForRouting(),
349 (HAL_AnalogTriggerType)source->GetAnalogTriggerTypeForRouting(),
350 &status);
351 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
352 }
353}
354
355void Counter::SetDownSource(DigitalSource* source) {
356 SetDownSource(
357 std::shared_ptr<DigitalSource>(source, NullDeleter<DigitalSource>()));
358}
359
360/**
361 * Set the source object that causes the counter to count down.
362 *
363 * Set the down counting DigitalSource.
364 *
365 * @param source Reference to the DigitalSource object to set as the down source
366 */
367void Counter::SetDownSource(DigitalSource& source) {
368 SetDownSource(
369 std::shared_ptr<DigitalSource>(&source, NullDeleter<DigitalSource>()));
370}
371
372/**
373 * Set the edge sensitivity on a down counting source.
374 *
375 * Set the down source to either detect rising edges or falling edges.
376 *
377 * @param risingEdge True to trigger on rising edges
378 * @param fallingEdge True to trigger on falling edges
379 */
380void Counter::SetDownSourceEdge(bool risingEdge, bool fallingEdge) {
381 if (StatusIsFatal()) return;
382 if (m_downSource == nullptr) {
383 wpi_setWPIErrorWithContext(
384 NullParameter,
385 "Must set non-nullptr DownSource before setting DownSourceEdge");
386 }
387 int32_t status = 0;
388 HAL_SetCounterDownSourceEdge(m_counter, risingEdge, fallingEdge, &status);
389 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
390}
391
392/**
393 * Disable the down counting source to the counter.
394 */
395void Counter::ClearDownSource() {
396 if (StatusIsFatal()) return;
397 m_downSource.reset();
398 int32_t status = 0;
399 HAL_ClearCounterDownSource(m_counter, &status);
400 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
401}
402
403/**
404 * Set standard up / down counting mode on this counter.
405 *
406 * Up and down counts are sourced independently from two inputs.
407 */
408void Counter::SetUpDownCounterMode() {
409 if (StatusIsFatal()) return;
410 int32_t status = 0;
411 HAL_SetCounterUpDownMode(m_counter, &status);
412 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
413}
414
415/**
416 * Set external direction mode on this counter.
417 *
418 * Counts are sourced on the Up counter input.
419 * The Down counter input represents the direction to count.
420 */
421void Counter::SetExternalDirectionMode() {
422 if (StatusIsFatal()) return;
423 int32_t status = 0;
424 HAL_SetCounterExternalDirectionMode(m_counter, &status);
425 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
426}
427
428/**
429 * Set Semi-period mode on this counter.
430 *
431 * Counts up on both rising and falling edges.
432 */
433void Counter::SetSemiPeriodMode(bool highSemiPeriod) {
434 if (StatusIsFatal()) return;
435 int32_t status = 0;
436 HAL_SetCounterSemiPeriodMode(m_counter, highSemiPeriod, &status);
437 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
438}
439
440/**
441 * Configure the counter to count in up or down based on the length of the input
442 * pulse.
443 *
444 * This mode is most useful for direction sensitive gear tooth sensors.
445 *
446 * @param threshold The pulse length beyond which the counter counts the
447 * opposite direction. Units are seconds.
448 */
449void Counter::SetPulseLengthMode(double threshold) {
450 if (StatusIsFatal()) return;
451 int32_t status = 0;
452 HAL_SetCounterPulseLengthMode(m_counter, threshold, &status);
453 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
454}
455
456/**
457 * Get the Samples to Average which specifies the number of samples of the timer
458 * to average when calculating the period.
459 *
460 * Perform averaging to account for mechanical imperfections or as oversampling
461 * to increase resolution.
462 *
463 * @return The number of samples being averaged (from 1 to 127)
464 */
465int Counter::GetSamplesToAverage() const {
466 int32_t status = 0;
467 int samples = HAL_GetCounterSamplesToAverage(m_counter, &status);
468 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
469 return samples;
470}
471
472/**
473 * Set the Samples to Average which specifies the number of samples of the timer
474 * to average when calculating the period. Perform averaging to account for
475 * mechanical imperfections or as oversampling to increase resolution.
476 *
477 * @param samplesToAverage The number of samples to average from 1 to 127.
478 */
479void Counter::SetSamplesToAverage(int samplesToAverage) {
480 if (samplesToAverage < 1 || samplesToAverage > 127) {
481 wpi_setWPIErrorWithContext(
482 ParameterOutOfRange,
483 "Average counter values must be between 1 and 127");
484 }
485 int32_t status = 0;
486 HAL_SetCounterSamplesToAverage(m_counter, samplesToAverage, &status);
487 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
488}
489
490/**
491 * Read the current counter value.
492 *
493 * Read the value at this instant. It may still be running, so it reflects the
494 * current value. Next time it is read, it might have a different value.
495 */
496int Counter::Get() const {
497 if (StatusIsFatal()) return 0;
498 int32_t status = 0;
499 int value = HAL_GetCounter(m_counter, &status);
500 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
501 return value;
502}
503
504/**
505 * Reset the Counter to zero.
506 *
507 * Set the counter value to zero. This doesn't effect the running state of the
508 * counter, just sets the current value to zero.
509 */
510void Counter::Reset() {
511 if (StatusIsFatal()) return;
512 int32_t status = 0;
513 HAL_ResetCounter(m_counter, &status);
514 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
515}
516
517/**
518 * Get the Period of the most recent count.
519 *
520 * Returns the time interval of the most recent count. This can be used for
521 * velocity calculations to determine shaft speed.
522 *
523 * @returns The period between the last two pulses in units of seconds.
524 */
525double Counter::GetPeriod() const {
526 if (StatusIsFatal()) return 0.0;
527 int32_t status = 0;
528 double value = HAL_GetCounterPeriod(m_counter, &status);
529 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
530 return value;
531}
532
533/**
534 * Set the maximum period where the device is still considered "moving".
535 *
536 * Sets the maximum period where the device is considered moving. This value is
537 * used to determine the "stopped" state of the counter using the GetStopped
538 * method.
539 *
540 * @param maxPeriod The maximum period where the counted device is considered
541 * moving in seconds.
542 */
543void Counter::SetMaxPeriod(double maxPeriod) {
544 if (StatusIsFatal()) return;
545 int32_t status = 0;
546 HAL_SetCounterMaxPeriod(m_counter, maxPeriod, &status);
547 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
548}
549
550/**
551 * Select whether you want to continue updating the event timer output when
552 * there are no samples captured.
553 *
554 * The output of the event timer has a buffer of periods that are averaged and
555 * posted to a register on the FPGA. When the timer detects that the event
556 * source has stopped (based on the MaxPeriod) the buffer of samples to be
557 * averaged is emptied. If you enable the update when empty, you will be
558 * notified of the stopped source and the event time will report 0 samples.
559 * If you disable update when empty, the most recent average will remain on
560 * the output until a new sample is acquired. You will never see 0 samples
561 * output (except when there have been no events since an FPGA reset) and you
562 * will likely not see the stopped bit become true (since it is updated at the
563 * end of an average and there are no samples to average).
564 *
565 * @param enabled True to enable update when empty
566 */
567void Counter::SetUpdateWhenEmpty(bool enabled) {
568 if (StatusIsFatal()) return;
569 int32_t status = 0;
570 HAL_SetCounterUpdateWhenEmpty(m_counter, enabled, &status);
571 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
572}
573
574/**
575 * Determine if the clock is stopped.
576 *
577 * Determine if the clocked input is stopped based on the MaxPeriod value set
578 * using the SetMaxPeriod method. If the clock exceeds the MaxPeriod, then the
579 * device (and counter) are assumed to be stopped and it returns true.
580 *
581 * @return Returns true if the most recent counter period exceeds the MaxPeriod
582 * value set by SetMaxPeriod.
583 */
584bool Counter::GetStopped() const {
585 if (StatusIsFatal()) return false;
586 int32_t status = 0;
587 bool value = HAL_GetCounterStopped(m_counter, &status);
588 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
589 return value;
590}
591
592/**
593 * The last direction the counter value changed.
594 *
595 * @return The last direction the counter value changed.
596 */
597bool Counter::GetDirection() const {
598 if (StatusIsFatal()) return false;
599 int32_t status = 0;
600 bool value = HAL_GetCounterDirection(m_counter, &status);
601 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
602 return value;
603}
604
605/**
606 * Set the Counter to return reversed sensing on the direction.
607 *
608 * This allows counters to change the direction they are counting in the case of
609 * 1X and 2X quadrature encoding only. Any other counter mode isn't supported.
610 *
611 * @param reverseDirection true if the value counted should be negated.
612 */
613void Counter::SetReverseDirection(bool reverseDirection) {
614 if (StatusIsFatal()) return;
615 int32_t status = 0;
616 HAL_SetCounterReverseDirection(m_counter, reverseDirection, &status);
617 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
618}
619
620void Counter::UpdateTable() {
621 if (m_table != nullptr) {
622 m_table->PutNumber("Value", Get());
623 }
624}
625
626void Counter::StartLiveWindowMode() {}
627
628void Counter::StopLiveWindowMode() {}
629
630std::string Counter::GetSmartDashboardType() const { return "Counter"; }
631
632void Counter::InitTable(std::shared_ptr<ITable> subTable) {
633 m_table = subTable;
634 UpdateTable();
635}
636
637std::shared_ptr<ITable> Counter::GetTable() const { return m_table; }