blob: 3614b8cfe7dcb1c36f3b06ce56bf954017b1400c [file] [log] [blame]
Parker Schuhd3b7a8872018-02-19 16:42:27 -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 "frc971/wpilib/ahal/Counter.h"
9
Austin Schuhf6b94632019-02-02 22:11:27 -080010#include "hal/HAL.h"
Parker Schuhd3b7a8872018-02-19 16:42:27 -080011#include "frc971/wpilib/ahal/AnalogTrigger.h"
12#include "frc971/wpilib/ahal/DigitalInput.h"
13#include "frc971/wpilib/ahal/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 reference to the existing AnalogTrigger object.
99 */
100Counter::Counter(const AnalogTrigger &trigger) : Counter(kTwoPulse) {
101 SetUpSource(trigger.CreateOutput(AnalogTriggerType::kState));
102 ClearDownSource();
103}
104
105/**
106 * Create an instance of a Counter object.
107 *
108 * Creates a full up-down counter given two Digital Sources.
109 *
110 * @param encodingType The quadrature decoding mode (1x or 2x)
111 * @param upSource The pointer to the DigitalSource to set as the up source
112 * @param downSource The pointer to the DigitalSource to set as the down
113 * source
114 * @param inverted True to invert the output (reverse the direction)
115 */
116Counter::Counter(EncodingType encodingType, DigitalSource *upSource,
117 DigitalSource *downSource, bool inverted)
118 : Counter(encodingType, std::shared_ptr<DigitalSource>(
119 upSource, NullDeleter<DigitalSource>()),
120 std::shared_ptr<DigitalSource>(downSource,
121 NullDeleter<DigitalSource>()),
122 inverted) {}
123
124/**
125 * Create an instance of a Counter object.
126 *
127 * Creates a full up-down counter given two Digital Sources.
128 *
129 * @param encodingType The quadrature decoding mode (1x or 2x)
130 * @param upSource The pointer to the DigitalSource to set as the up source
131 * @param downSource The pointer to the DigitalSource to set as the down
132 * source
133 * @param inverted True to invert the output (reverse the direction)
134 */
135Counter::Counter(EncodingType encodingType,
136 std::shared_ptr<DigitalSource> upSource,
137 std::shared_ptr<DigitalSource> downSource, bool inverted)
138 : Counter(kExternalDirection) {
139 if (encodingType != k1X && encodingType != k2X) {
140 wpi_setWPIErrorWithContext(
141 ParameterOutOfRange,
142 "Counter only supports 1X and 2X quadrature decoding.");
143 return;
144 }
145 SetUpSource(upSource);
146 SetDownSource(downSource);
147 int32_t status = 0;
148
149 if (encodingType == k1X) {
150 SetUpSourceEdge(true, false);
151 HAL_SetCounterAverageSize(m_counter, 1, &status);
152 } else {
153 SetUpSourceEdge(true, true);
154 HAL_SetCounterAverageSize(m_counter, 2, &status);
155 }
156
157 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
158 SetDownSourceEdge(inverted, true);
159}
160
161/**
162 * Delete the Counter object.
163 */
164Counter::~Counter() {
165 SetUpdateWhenEmpty(true);
166
167 int32_t status = 0;
168 HAL_FreeCounter(m_counter, &status);
169 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
170 m_counter = HAL_kInvalidHandle;
171}
172
173/**
174 * Set the upsource for the counter as a digital input channel.
175 *
176 * @param channel The DIO channel to use as the up source. 0-9 are on-board,
177 * 10-25 are on the MXP
178 */
179void Counter::SetUpSource(int channel) {
180 SetUpSource(std::make_shared<DigitalInput>(channel));
181}
182
183/**
184 * Set the up counting source to be an analog trigger.
185 *
186 * @param analogTrigger The analog trigger object that is used for the Up Source
187 * @param triggerType The analog trigger output that will trigger the counter.
188 */
189void Counter::SetUpSource(AnalogTrigger *analogTrigger,
190 AnalogTriggerType triggerType) {
191 SetUpSource(std::shared_ptr<AnalogTrigger>(analogTrigger,
192 NullDeleter<AnalogTrigger>()),
193 triggerType);
194}
195
196/**
197 * Set the up counting source to be an analog trigger.
198 *
199 * @param analogTrigger The analog trigger object that is used for the Up Source
200 * @param triggerType The analog trigger output that will trigger the counter.
201 */
202void Counter::SetUpSource(std::shared_ptr<AnalogTrigger> analogTrigger,
203 AnalogTriggerType triggerType) {
204 SetUpSource(analogTrigger->CreateOutput(triggerType));
205}
206
207/**
208 * Set the source object that causes the counter to count up.
209 *
210 * Set the up counting DigitalSource.
211 *
212 * @param source Pointer to the DigitalSource object to set as the up source
213 */
214void Counter::SetUpSource(std::shared_ptr<DigitalSource> source) {
215 m_upSource = source;
216 int32_t status = 0;
217 HAL_SetCounterUpSource(
218 m_counter, source->GetPortHandleForRouting(),
219 (HAL_AnalogTriggerType)source->GetAnalogTriggerTypeForRouting(), &status);
220 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
221}
222
223void Counter::SetUpSource(DigitalSource *source) {
224 SetUpSource(
225 std::shared_ptr<DigitalSource>(source, NullDeleter<DigitalSource>()));
226}
227
228/**
229 * Set the source object that causes the counter to count up.
230 *
231 * Set the up counting DigitalSource.
232 *
233 * @param source Reference to the DigitalSource object to set as the up source
234 */
235void Counter::SetUpSource(DigitalSource &source) {
236 SetUpSource(
237 std::shared_ptr<DigitalSource>(&source, NullDeleter<DigitalSource>()));
238}
239
240/**
241 * Set the edge sensitivity on an up counting source.
242 *
243 * Set the up source to either detect rising edges or falling edges or both.
244 *
245 * @param risingEdge True to trigger on rising edges
246 * @param fallingEdge True to trigger on falling edges
247 */
248void Counter::SetUpSourceEdge(bool risingEdge, bool fallingEdge) {
249 if (m_upSource == nullptr) {
250 wpi_setWPIErrorWithContext(
251 NullParameter,
252 "Must set non-nullptr UpSource before setting UpSourceEdge");
253 }
254 int32_t status = 0;
255 HAL_SetCounterUpSourceEdge(m_counter, risingEdge, fallingEdge, &status);
256 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
257}
258
259/**
260 * Disable the up counting source to the counter.
261 */
262void Counter::ClearUpSource() {
263 m_upSource.reset();
264 int32_t status = 0;
265 HAL_ClearCounterUpSource(m_counter, &status);
266 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
267}
268
269/**
270 * Set the down counting source to be a digital input channel.
271 *
272 * @param channel The DIO channel to use as the up source. 0-9 are on-board,
273 * 10-25 are on the MXP
274 */
275void Counter::SetDownSource(int channel) {
276 SetDownSource(std::make_shared<DigitalInput>(channel));
277}
278
279/**
280 * Set the down counting source to be an analog trigger.
281 *
282 * @param analogTrigger The analog trigger object that is used for the Down
283 * Source
284 * @param triggerType The analog trigger output that will trigger the counter.
285 */
286void Counter::SetDownSource(AnalogTrigger *analogTrigger,
287 AnalogTriggerType triggerType) {
288 SetDownSource(std::shared_ptr<AnalogTrigger>(analogTrigger,
289 NullDeleter<AnalogTrigger>()),
290 triggerType);
291}
292
293/**
294 * Set the down counting source to be an analog trigger.
295 *
296 * @param analogTrigger The analog trigger object that is used for the Down
297 * Source
298 * @param triggerType The analog trigger output that will trigger the counter.
299 */
300void Counter::SetDownSource(std::shared_ptr<AnalogTrigger> analogTrigger,
301 AnalogTriggerType triggerType) {
302 SetDownSource(analogTrigger->CreateOutput(triggerType));
303}
304
305/**
306 * Set the source object that causes the counter to count down.
307 *
308 * Set the down counting DigitalSource.
309 *
310 * @param source Pointer to the DigitalSource object to set as the down source
311 */
312void Counter::SetDownSource(std::shared_ptr<DigitalSource> source) {
313 m_downSource = source;
314 int32_t status = 0;
315 HAL_SetCounterDownSource(
316 m_counter, source->GetPortHandleForRouting(),
317 (HAL_AnalogTriggerType)source->GetAnalogTriggerTypeForRouting(), &status);
318 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
319}
320
321void Counter::SetDownSource(DigitalSource *source) {
322 SetDownSource(
323 std::shared_ptr<DigitalSource>(source, NullDeleter<DigitalSource>()));
324}
325
326/**
327 * Set the source object that causes the counter to count down.
328 *
329 * Set the down counting DigitalSource.
330 *
331 * @param source Reference to the DigitalSource object to set as the down source
332 */
333void Counter::SetDownSource(DigitalSource &source) {
334 SetDownSource(
335 std::shared_ptr<DigitalSource>(&source, NullDeleter<DigitalSource>()));
336}
337
338/**
339 * Set the edge sensitivity on a down counting source.
340 *
341 * Set the down source to either detect rising edges or falling edges.
342 *
343 * @param risingEdge True to trigger on rising edges
344 * @param fallingEdge True to trigger on falling edges
345 */
346void Counter::SetDownSourceEdge(bool risingEdge, bool fallingEdge) {
347 if (m_downSource == nullptr) {
348 wpi_setWPIErrorWithContext(
349 NullParameter,
350 "Must set non-nullptr DownSource before setting DownSourceEdge");
351 }
352 int32_t status = 0;
353 HAL_SetCounterDownSourceEdge(m_counter, risingEdge, fallingEdge, &status);
354 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
355}
356
357/**
358 * Disable the down counting source to the counter.
359 */
360void Counter::ClearDownSource() {
361 m_downSource.reset();
362 int32_t status = 0;
363 HAL_ClearCounterDownSource(m_counter, &status);
364 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
365}
366
367/**
368 * Set standard up / down counting mode on this counter.
369 *
370 * Up and down counts are sourced independently from two inputs.
371 */
372void Counter::SetUpDownCounterMode() {
373 int32_t status = 0;
374 HAL_SetCounterUpDownMode(m_counter, &status);
375 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
376}
377
378/**
379 * Set external direction mode on this counter.
380 *
381 * Counts are sourced on the Up counter input.
382 * The Down counter input represents the direction to count.
383 */
384void Counter::SetExternalDirectionMode() {
385 int32_t status = 0;
386 HAL_SetCounterExternalDirectionMode(m_counter, &status);
387 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
388}
389
390/**
391 * Set Semi-period mode on this counter.
392 *
393 * Counts up on both rising and falling edges.
394 */
395void Counter::SetSemiPeriodMode(bool highSemiPeriod) {
396 int32_t status = 0;
397 HAL_SetCounterSemiPeriodMode(m_counter, highSemiPeriod, &status);
398 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
399}
400
401/**
402 * Configure the counter to count in up or down based on the length of the input
403 * pulse.
404 *
405 * This mode is most useful for direction sensitive gear tooth sensors.
406 *
407 * @param threshold The pulse length beyond which the counter counts the
408 * opposite direction. Units are seconds.
409 */
410void Counter::SetPulseLengthMode(double threshold) {
411 int32_t status = 0;
412 HAL_SetCounterPulseLengthMode(m_counter, threshold, &status);
413 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
414}
415
416/**
417 * Get the Samples to Average which specifies the number of samples of the timer
418 * to average when calculating the period.
419 *
420 * Perform averaging to account for mechanical imperfections or as oversampling
421 * to increase resolution.
422 *
423 * @return The number of samples being averaged (from 1 to 127)
424 */
425int Counter::GetSamplesToAverage() const {
426 int32_t status = 0;
427 int samples = HAL_GetCounterSamplesToAverage(m_counter, &status);
428 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
429 return samples;
430}
431
432/**
433 * Set the Samples to Average which specifies the number of samples of the timer
434 * to average when calculating the period. Perform averaging to account for
435 * mechanical imperfections or as oversampling to increase resolution.
436 *
437 * @param samplesToAverage The number of samples to average from 1 to 127.
438 */
439void Counter::SetSamplesToAverage(int samplesToAverage) {
440 if (samplesToAverage < 1 || samplesToAverage > 127) {
441 wpi_setWPIErrorWithContext(
442 ParameterOutOfRange,
443 "Average counter values must be between 1 and 127");
444 }
445 int32_t status = 0;
446 HAL_SetCounterSamplesToAverage(m_counter, samplesToAverage, &status);
447 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
448}
449
450/**
451 * Read the current counter value.
452 *
453 * Read the value at this instant. It may still be running, so it reflects the
454 * current value. Next time it is read, it might have a different value.
455 */
456int Counter::Get() const {
457 int32_t status = 0;
458 int value = HAL_GetCounter(m_counter, &status);
459 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
460 return value;
461}
462
463/**
464 * Reset the Counter to zero.
465 *
466 * Set the counter value to zero. This doesn't effect the running state of the
467 * counter, just sets the current value to zero.
468 */
469void Counter::Reset() {
470 int32_t status = 0;
471 HAL_ResetCounter(m_counter, &status);
472 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
473}
474
475/**
476 * Get the Period of the most recent count.
477 *
478 * Returns the time interval of the most recent count. This can be used for
479 * velocity calculations to determine shaft speed.
480 *
481 * @returns The period between the last two pulses in units of seconds.
482 */
483double Counter::GetPeriod() const {
484 int32_t status = 0;
485 double value = HAL_GetCounterPeriod(m_counter, &status);
486 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
487 return value;
488}
489
490/**
491 * Set the maximum period where the device is still considered "moving".
492 *
493 * Sets the maximum period where the device is considered moving. This value is
494 * used to determine the "stopped" state of the counter using the GetStopped
495 * method.
496 *
497 * @param maxPeriod The maximum period where the counted device is considered
498 * moving in seconds.
499 */
500void Counter::SetMaxPeriod(double maxPeriod) {
501 int32_t status = 0;
502 HAL_SetCounterMaxPeriod(m_counter, maxPeriod, &status);
503 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
504}
505
506/**
507 * Select whether you want to continue updating the event timer output when
508 * there are no samples captured.
509 *
510 * The output of the event timer has a buffer of periods that are averaged and
511 * posted to a register on the FPGA. When the timer detects that the event
512 * source has stopped (based on the MaxPeriod) the buffer of samples to be
513 * averaged is emptied. If you enable the update when empty, you will be
514 * notified of the stopped source and the event time will report 0 samples.
515 * If you disable update when empty, the most recent average will remain on
516 * the output until a new sample is acquired. You will never see 0 samples
517 * output (except when there have been no events since an FPGA reset) and you
518 * will likely not see the stopped bit become true (since it is updated at the
519 * end of an average and there are no samples to average).
520 *
521 * @param enabled True to enable update when empty
522 */
523void Counter::SetUpdateWhenEmpty(bool enabled) {
524 int32_t status = 0;
525 HAL_SetCounterUpdateWhenEmpty(m_counter, enabled, &status);
526 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
527}
528
529/**
530 * Determine if the clock is stopped.
531 *
532 * Determine if the clocked input is stopped based on the MaxPeriod value set
533 * using the SetMaxPeriod method. If the clock exceeds the MaxPeriod, then the
534 * device (and counter) are assumed to be stopped and it returns true.
535 *
536 * @return Returns true if the most recent counter period exceeds the MaxPeriod
537 * value set by SetMaxPeriod.
538 */
539bool Counter::GetStopped() const {
540 int32_t status = 0;
541 bool value = HAL_GetCounterStopped(m_counter, &status);
542 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
543 return value;
544}
545
546/**
547 * The last direction the counter value changed.
548 *
549 * @return The last direction the counter value changed.
550 */
551bool Counter::GetDirection() const {
552 int32_t status = 0;
553 bool value = HAL_GetCounterDirection(m_counter, &status);
554 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
555 return value;
556}
557
558/**
559 * Set the Counter to return reversed sensing on the direction.
560 *
561 * This allows counters to change the direction they are counting in the case of
562 * 1X and 2X quadrature encoding only. Any other counter mode isn't supported.
563 *
564 * @param reverseDirection true if the value counted should be negated.
565 */
566void Counter::SetReverseDirection(bool reverseDirection) {
567 int32_t status = 0;
568 HAL_SetCounterReverseDirection(m_counter, reverseDirection, &status);
569 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
570}