blob: 9273ea93c38a4c527364a54799e4c663da7a5d9b [file] [log] [blame]
Brian Silvermanf7f267a2017-02-04 16:16:08 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2016-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 "HAL/Counter.h"
9
10#include "ConstantsInternal.h"
11#include "DigitalInternal.h"
12#include "HAL/HAL.h"
13#include "HAL/handles/LimitedHandleResource.h"
14#include "PortsInternal.h"
15
16using namespace hal;
17
18namespace {
19struct Counter {
20 std::unique_ptr<tCounter> counter;
21 uint8_t index;
22};
23}
24
25static LimitedHandleResource<HAL_CounterHandle, Counter, kNumCounters,
26 HAL_HandleEnum::Counter>
27 counterHandles;
28
29extern "C" {
30HAL_CounterHandle HAL_InitializeCounter(HAL_Counter_Mode mode, int32_t* index,
31 int32_t* status) {
32 auto handle = counterHandles.Allocate();
33 if (handle == HAL_kInvalidHandle) { // out of resources
34 *status = NO_AVAILABLE_RESOURCES;
35 return HAL_kInvalidHandle;
36 }
37 auto counter = counterHandles.Get(handle);
38 if (counter == nullptr) { // would only occur on thread issues
39 *status = HAL_HANDLE_ERROR;
40 return HAL_kInvalidHandle;
41 }
42 counter->index = static_cast<uint8_t>(getHandleIndex(handle));
43 *index = counter->index;
44
45 counter->counter.reset(tCounter::create(counter->index, status));
46 counter->counter->writeConfig_Mode(mode, status);
47 counter->counter->writeTimerConfig_AverageSize(1, status);
48 return handle;
49}
50
51void HAL_FreeCounter(HAL_CounterHandle counterHandle, int32_t* status) {
52 counterHandles.Free(counterHandle);
53}
54
55void HAL_SetCounterAverageSize(HAL_CounterHandle counterHandle, int32_t size,
56 int32_t* status) {
57 auto counter = counterHandles.Get(counterHandle);
58 if (counter == nullptr) {
59 *status = HAL_HANDLE_ERROR;
60 return;
61 }
62 counter->counter->writeTimerConfig_AverageSize(size, status);
63}
64
65/**
66 * Set the source object that causes the counter to count up.
67 * Set the up counting DigitalSource.
68 */
69void HAL_SetCounterUpSource(HAL_CounterHandle counterHandle,
70 HAL_Handle digitalSourceHandle,
71 HAL_AnalogTriggerType analogTriggerType,
72 int32_t* status) {
73 auto counter = counterHandles.Get(counterHandle);
74 if (counter == nullptr) {
75 *status = HAL_HANDLE_ERROR;
76 return;
77 }
78
79 bool routingAnalogTrigger = false;
80 uint8_t routingChannel = 0;
81 uint8_t routingModule = 0;
82 bool success =
83 remapDigitalSource(digitalSourceHandle, analogTriggerType, routingChannel,
84 routingModule, routingAnalogTrigger);
85 if (!success) {
86 *status = HAL_HANDLE_ERROR;
87 return;
88 }
89
90 counter->counter->writeConfig_UpSource_Module(routingModule, status);
91 counter->counter->writeConfig_UpSource_Channel(routingChannel, status);
92 counter->counter->writeConfig_UpSource_AnalogTrigger(routingAnalogTrigger,
93 status);
94
95 if (counter->counter->readConfig_Mode(status) == HAL_Counter_kTwoPulse ||
96 counter->counter->readConfig_Mode(status) ==
97 HAL_Counter_kExternalDirection) {
98 HAL_SetCounterUpSourceEdge(counterHandle, true, false, status);
99 }
100 counter->counter->strobeReset(status);
101}
102
103/**
104 * Set the edge sensitivity on an up counting source.
105 * Set the up source to either detect rising edges or falling edges.
106 */
107void HAL_SetCounterUpSourceEdge(HAL_CounterHandle counterHandle,
108 HAL_Bool risingEdge, HAL_Bool fallingEdge,
109 int32_t* status) {
110 auto counter = counterHandles.Get(counterHandle);
111 if (counter == nullptr) {
112 *status = HAL_HANDLE_ERROR;
113 return;
114 }
115 counter->counter->writeConfig_UpRisingEdge(risingEdge, status);
116 counter->counter->writeConfig_UpFallingEdge(fallingEdge, status);
117}
118
119/**
120 * Disable the up counting source to the counter.
121 */
122void HAL_ClearCounterUpSource(HAL_CounterHandle counterHandle,
123 int32_t* status) {
124 auto counter = counterHandles.Get(counterHandle);
125 if (counter == nullptr) {
126 *status = HAL_HANDLE_ERROR;
127 return;
128 }
129 counter->counter->writeConfig_UpFallingEdge(false, status);
130 counter->counter->writeConfig_UpRisingEdge(false, status);
131 // Index 0 of digital is always 0.
132 counter->counter->writeConfig_UpSource_Channel(0, status);
133 counter->counter->writeConfig_UpSource_AnalogTrigger(false, status);
134}
135
136/**
137 * Set the source object that causes the counter to count down.
138 * Set the down counting DigitalSource.
139 */
140void HAL_SetCounterDownSource(HAL_CounterHandle counterHandle,
141 HAL_Handle digitalSourceHandle,
142 HAL_AnalogTriggerType analogTriggerType,
143 int32_t* status) {
144 auto counter = counterHandles.Get(counterHandle);
145 if (counter == nullptr) {
146 *status = HAL_HANDLE_ERROR;
147 return;
148 }
149 uint8_t mode = counter->counter->readConfig_Mode(status);
150 if (mode != HAL_Counter_kTwoPulse && mode != HAL_Counter_kExternalDirection) {
151 // TODO: wpi_setWPIErrorWithContext(ParameterOutOfRange, "Counter only
152 // supports DownSource in TwoPulse and ExternalDirection modes.");
153 *status = PARAMETER_OUT_OF_RANGE;
154 return;
155 }
156
157 bool routingAnalogTrigger = false;
158 uint8_t routingChannel = 0;
159 uint8_t routingModule = 0;
160 bool success =
161 remapDigitalSource(digitalSourceHandle, analogTriggerType, routingChannel,
162 routingModule, routingAnalogTrigger);
163 if (!success) {
164 *status = HAL_HANDLE_ERROR;
165 return;
166 }
167
168 counter->counter->writeConfig_DownSource_Module(routingModule, status);
169 counter->counter->writeConfig_DownSource_Channel(routingChannel, status);
170 counter->counter->writeConfig_DownSource_AnalogTrigger(routingAnalogTrigger,
171 status);
172
173 HAL_SetCounterDownSourceEdge(counterHandle, true, false, status);
174 counter->counter->strobeReset(status);
175}
176
177/**
178 * Set the edge sensitivity on a down counting source.
179 * Set the down source to either detect rising edges or falling edges.
180 */
181void HAL_SetCounterDownSourceEdge(HAL_CounterHandle counterHandle,
182 HAL_Bool risingEdge, HAL_Bool fallingEdge,
183 int32_t* status) {
184 auto counter = counterHandles.Get(counterHandle);
185 if (counter == nullptr) {
186 *status = HAL_HANDLE_ERROR;
187 return;
188 }
189 counter->counter->writeConfig_DownRisingEdge(risingEdge, status);
190 counter->counter->writeConfig_DownFallingEdge(fallingEdge, status);
191}
192
193/**
194 * Disable the down counting source to the counter.
195 */
196void HAL_ClearCounterDownSource(HAL_CounterHandle counterHandle,
197 int32_t* status) {
198 auto counter = counterHandles.Get(counterHandle);
199 if (counter == nullptr) {
200 *status = HAL_HANDLE_ERROR;
201 return;
202 }
203 counter->counter->writeConfig_DownFallingEdge(false, status);
204 counter->counter->writeConfig_DownRisingEdge(false, status);
205 // Index 0 of digital is always 0.
206 counter->counter->writeConfig_DownSource_Channel(0, status);
207 counter->counter->writeConfig_DownSource_AnalogTrigger(false, status);
208}
209
210/**
211 * Set standard up / down counting mode on this counter.
212 * Up and down counts are sourced independently from two inputs.
213 */
214void HAL_SetCounterUpDownMode(HAL_CounterHandle counterHandle,
215 int32_t* status) {
216 auto counter = counterHandles.Get(counterHandle);
217 if (counter == nullptr) {
218 *status = HAL_HANDLE_ERROR;
219 return;
220 }
221 counter->counter->writeConfig_Mode(HAL_Counter_kTwoPulse, status);
222}
223
224/**
225 * Set external direction mode on this counter.
226 * Counts are sourced on the Up counter input.
227 * The Down counter input represents the direction to count.
228 */
229void HAL_SetCounterExternalDirectionMode(HAL_CounterHandle counterHandle,
230 int32_t* status) {
231 auto counter = counterHandles.Get(counterHandle);
232 if (counter == nullptr) {
233 *status = HAL_HANDLE_ERROR;
234 return;
235 }
236 counter->counter->writeConfig_Mode(HAL_Counter_kExternalDirection, status);
237}
238
239/**
240 * Set Semi-period mode on this counter.
241 * Counts up on both rising and falling edges.
242 */
243void HAL_SetCounterSemiPeriodMode(HAL_CounterHandle counterHandle,
244 HAL_Bool highSemiPeriod, int32_t* status) {
245 auto counter = counterHandles.Get(counterHandle);
246 if (counter == nullptr) {
247 *status = HAL_HANDLE_ERROR;
248 return;
249 }
250 counter->counter->writeConfig_Mode(HAL_Counter_kSemiperiod, status);
251 counter->counter->writeConfig_UpRisingEdge(highSemiPeriod, status);
252 HAL_SetCounterUpdateWhenEmpty(counterHandle, false, status);
253}
254
255/**
256 * Configure the counter to count in up or down based on the length of the input
257 * pulse.
258 * This mode is most useful for direction sensitive gear tooth sensors.
259 * @param threshold The pulse length beyond which the counter counts the
260 * opposite direction. Units are seconds.
261 */
262void HAL_SetCounterPulseLengthMode(HAL_CounterHandle counterHandle,
263 double threshold, int32_t* status) {
264 auto counter = counterHandles.Get(counterHandle);
265 if (counter == nullptr) {
266 *status = HAL_HANDLE_ERROR;
267 return;
268 }
269 counter->counter->writeConfig_Mode(HAL_Counter_kPulseLength, status);
270 counter->counter->writeConfig_PulseLengthThreshold(
271 static_cast<uint32_t>(threshold * 1.0e6) *
272 kSystemClockTicksPerMicrosecond,
273 status);
274}
275
276/**
277 * Get the Samples to Average which specifies the number of samples of the timer
278 * to
279 * average when calculating the period. Perform averaging to account for
280 * mechanical imperfections or as oversampling to increase resolution.
281 * @return SamplesToAverage The number of samples being averaged (from 1 to 127)
282 */
283int32_t HAL_GetCounterSamplesToAverage(HAL_CounterHandle counterHandle,
284 int32_t* status) {
285 auto counter = counterHandles.Get(counterHandle);
286 if (counter == nullptr) {
287 *status = HAL_HANDLE_ERROR;
288 return 0;
289 }
290 return counter->counter->readTimerConfig_AverageSize(status);
291}
292
293/**
294 * Set the Samples to Average which specifies the number of samples of the timer
295 * to average when calculating the period. Perform averaging to account for
296 * mechanical imperfections or as oversampling to increase resolution.
297 * @param samplesToAverage The number of samples to average from 1 to 127.
298 */
299void HAL_SetCounterSamplesToAverage(HAL_CounterHandle counterHandle,
300 int32_t samplesToAverage, int32_t* status) {
301 auto counter = counterHandles.Get(counterHandle);
302 if (counter == nullptr) {
303 *status = HAL_HANDLE_ERROR;
304 return;
305 }
306 if (samplesToAverage < 1 || samplesToAverage > 127) {
307 *status = PARAMETER_OUT_OF_RANGE;
308 }
309 counter->counter->writeTimerConfig_AverageSize(samplesToAverage, status);
310}
311
312/**
313 * Reset the Counter to zero.
314 * Set the counter value to zero. This doesn't effect the running state of the
315 * counter, just sets the current value to zero.
316 */
317void HAL_ResetCounter(HAL_CounterHandle counterHandle, int32_t* status) {
318 auto counter = counterHandles.Get(counterHandle);
319 if (counter == nullptr) {
320 *status = HAL_HANDLE_ERROR;
321 return;
322 }
323 counter->counter->strobeReset(status);
324}
325
326/**
327 * Read the current counter value.
328 * Read the value at this instant. It may still be running, so it reflects the
329 * current value. Next time it is read, it might have a different value.
330 */
331int32_t HAL_GetCounter(HAL_CounterHandle counterHandle, int32_t* status) {
332 auto counter = counterHandles.Get(counterHandle);
333 if (counter == nullptr) {
334 *status = HAL_HANDLE_ERROR;
335 return 0;
336 }
337 int32_t value = counter->counter->readOutput_Value(status);
338 return value;
339}
340
341/*
342 * Get the Period of the most recent count.
343 * Returns the time interval of the most recent count. This can be used for
344 * velocity calculations to determine shaft speed.
345 * @returns The period of the last two pulses in units of seconds.
346 */
347double HAL_GetCounterPeriod(HAL_CounterHandle counterHandle, int32_t* status) {
348 auto counter = counterHandles.Get(counterHandle);
349 if (counter == nullptr) {
350 *status = HAL_HANDLE_ERROR;
351 return 0.0;
352 }
353 tCounter::tTimerOutput output = counter->counter->readTimerOutput(status);
354 double period;
355 if (output.Stalled) {
356 // Return infinity
357 double zero = 0.0;
358 period = 1.0 / zero;
359 } else {
360 // output.Period is a fixed point number that counts by 2 (24 bits, 25
361 // integer bits)
362 period = static_cast<double>(output.Period << 1) /
363 static_cast<double>(output.Count);
364 }
365 return static_cast<double>(period *
366 2.5e-8); // result * timebase (currently 25ns)
367}
368
369/**
370 * Set the maximum period where the device is still considered "moving".
371 * Sets the maximum period where the device is considered moving. This value is
372 * used to determine the "stopped" state of the counter using the GetStopped
373 * method.
374 * @param maxPeriod The maximum period where the counted device is considered
375 * moving in seconds.
376 */
377void HAL_SetCounterMaxPeriod(HAL_CounterHandle counterHandle, double maxPeriod,
378 int32_t* status) {
379 auto counter = counterHandles.Get(counterHandle);
380 if (counter == nullptr) {
381 *status = HAL_HANDLE_ERROR;
382 return;
383 }
384 counter->counter->writeTimerConfig_StallPeriod(
385 static_cast<uint32_t>(maxPeriod * 4.0e8), status);
386}
387
388/**
389 * Select whether you want to continue updating the event timer output when
390 * there are no samples captured. The output of the event timer has a buffer of
391 * periods that are averaged and posted to a register on the FPGA. When the
392 * timer detects that the event source has stopped (based on the MaxPeriod) the
393 * buffer of samples to be averaged is emptied. If you enable the update when
394 * empty, you will be notified of the stopped source and the event time will
395 * report 0 samples. If you disable update when empty, the most recent average
396 * will remain on the output until a new sample is acquired. You will never see
397 * 0 samples output (except when there have been no events since an FPGA reset)
398 * and you will likely not see the stopped bit become true (since it is updated
399 * at the end of an average and there are no samples to average).
400 */
401void HAL_SetCounterUpdateWhenEmpty(HAL_CounterHandle counterHandle,
402 HAL_Bool enabled, int32_t* status) {
403 auto counter = counterHandles.Get(counterHandle);
404 if (counter == nullptr) {
405 *status = HAL_HANDLE_ERROR;
406 return;
407 }
408 counter->counter->writeTimerConfig_UpdateWhenEmpty(enabled, status);
409}
410
411/**
412 * Determine if the clock is stopped.
413 * Determine if the clocked input is stopped based on the MaxPeriod value set
414 * using the SetMaxPeriod method. If the clock exceeds the MaxPeriod, then the
415 * device (and counter) are assumed to be stopped and it returns true.
416 * @return Returns true if the most recent counter period exceeds the MaxPeriod
417 * value set by SetMaxPeriod.
418 */
419HAL_Bool HAL_GetCounterStopped(HAL_CounterHandle counterHandle,
420 int32_t* status) {
421 auto counter = counterHandles.Get(counterHandle);
422 if (counter == nullptr) {
423 *status = HAL_HANDLE_ERROR;
424 return false;
425 }
426 return counter->counter->readTimerOutput_Stalled(status);
427}
428
429/**
430 * The last direction the counter value changed.
431 * @return The last direction the counter value changed.
432 */
433HAL_Bool HAL_GetCounterDirection(HAL_CounterHandle counterHandle,
434 int32_t* status) {
435 auto counter = counterHandles.Get(counterHandle);
436 if (counter == nullptr) {
437 *status = HAL_HANDLE_ERROR;
438 return false;
439 }
440 bool value = counter->counter->readOutput_Direction(status);
441 return value;
442}
443
444/**
445 * Set the Counter to return reversed sensing on the direction.
446 * This allows counters to change the direction they are counting in the case of
447 * 1X and 2X quadrature encoding only. Any other counter mode isn't supported.
448 * @param reverseDirection true if the value counted should be negated.
449 */
450void HAL_SetCounterReverseDirection(HAL_CounterHandle counterHandle,
451 HAL_Bool reverseDirection,
452 int32_t* status) {
453 auto counter = counterHandles.Get(counterHandle);
454 if (counter == nullptr) {
455 *status = HAL_HANDLE_ERROR;
456 return;
457 }
458 if (counter->counter->readConfig_Mode(status) ==
459 HAL_Counter_kExternalDirection) {
460 if (reverseDirection)
461 HAL_SetCounterDownSourceEdge(counterHandle, true, true, status);
462 else
463 HAL_SetCounterDownSourceEdge(counterHandle, false, true, status);
464 }
465}
466}