blob: 9b0e5adf722716c79773a87ebe460b1b357e690e [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 "FPGAEncoder.h"
9
10#include <memory>
11
12#include "DigitalInternal.h"
13#include "HAL/handles/LimitedHandleResource.h"
14#include "PortsInternal.h"
15
16using namespace hal;
17
18namespace {
19struct Encoder {
20 std::unique_ptr<tEncoder> encoder;
21 uint8_t index;
22};
23}
24
25static const double DECODING_SCALING_FACTOR = 0.25;
26
27static LimitedHandleResource<HAL_FPGAEncoderHandle, Encoder, kNumEncoders,
28 HAL_HandleEnum::FPGAEncoder>
29 fpgaEncoderHandles;
30
31extern "C" {
32HAL_FPGAEncoderHandle HAL_InitializeFPGAEncoder(
33 HAL_Handle digitalSourceHandleA, HAL_AnalogTriggerType analogTriggerTypeA,
34 HAL_Handle digitalSourceHandleB, HAL_AnalogTriggerType analogTriggerTypeB,
35 HAL_Bool reverseDirection, int32_t* index, int32_t* status) {
36 bool routingAnalogTriggerA = false;
37 uint8_t routingChannelA = 0;
38 uint8_t routingModuleA = 0;
39 bool successA = remapDigitalSource(digitalSourceHandleA, analogTriggerTypeA,
40 routingChannelA, routingModuleA,
41 routingAnalogTriggerA);
42 bool routingAnalogTriggerB = false;
43 uint8_t routingChannelB = 0;
44 uint8_t routingModuleB = 0;
45 bool successB = remapDigitalSource(digitalSourceHandleB, analogTriggerTypeB,
46 routingChannelB, routingModuleB,
47 routingAnalogTriggerB);
48
49 if (!successA || !successB) {
50 *status = HAL_HANDLE_ERROR;
51 return HAL_kInvalidHandle;
52 }
53
54 auto handle = fpgaEncoderHandles.Allocate();
55 if (handle == HAL_kInvalidHandle) { // out of resources
56 *status = NO_AVAILABLE_RESOURCES;
57 return HAL_kInvalidHandle;
58 }
59
60 auto encoder = fpgaEncoderHandles.Get(handle);
61 if (encoder == nullptr) { // will only error on thread issue
62 *status = HAL_HANDLE_ERROR;
63 return HAL_kInvalidHandle;
64 }
65
66 encoder->index = static_cast<uint8_t>(getHandleIndex(handle));
67 *index = encoder->index;
68 // TODO: if (index == ~0ul) { CloneError(quadEncoders); return; }
69 encoder->encoder.reset(tEncoder::create(encoder->index, status));
70 encoder->encoder->writeConfig_ASource_Module(routingModuleA, status);
71 encoder->encoder->writeConfig_ASource_Channel(routingChannelA, status);
72 encoder->encoder->writeConfig_ASource_AnalogTrigger(routingAnalogTriggerA,
73 status);
74 encoder->encoder->writeConfig_BSource_Module(routingModuleB, status);
75 encoder->encoder->writeConfig_BSource_Channel(routingChannelB, status);
76 encoder->encoder->writeConfig_BSource_AnalogTrigger(routingAnalogTriggerB,
77 status);
78 encoder->encoder->strobeReset(status);
79 encoder->encoder->writeConfig_Reverse(reverseDirection, status);
80 encoder->encoder->writeTimerConfig_AverageSize(4, status);
81
82 return handle;
83}
84
85void HAL_FreeFPGAEncoder(HAL_FPGAEncoderHandle fpgaEncoderHandle,
86 int32_t* status) {
87 fpgaEncoderHandles.Free(fpgaEncoderHandle);
88}
89
90/**
91 * Reset the Encoder distance to zero.
92 * Resets the current count to zero on the encoder.
93 */
94void HAL_ResetFPGAEncoder(HAL_FPGAEncoderHandle fpgaEncoderHandle,
95 int32_t* status) {
96 auto encoder = fpgaEncoderHandles.Get(fpgaEncoderHandle);
97 if (encoder == nullptr) {
98 *status = HAL_HANDLE_ERROR;
99 return;
100 }
101 encoder->encoder->strobeReset(status);
102}
103
104/**
105 * Gets the fpga value from the encoder.
106 * The fpga value is the actual count unscaled by the 1x, 2x, or 4x scale
107 * factor.
108 * @return Current fpga count from the encoder
109 */
110int32_t HAL_GetFPGAEncoder(HAL_FPGAEncoderHandle fpgaEncoderHandle,
111 int32_t* status) {
112 auto encoder = fpgaEncoderHandles.Get(fpgaEncoderHandle);
113 if (encoder == nullptr) {
114 *status = HAL_HANDLE_ERROR;
115 return 0;
116 }
117 return encoder->encoder->readOutput_Value(status);
118}
119
120/**
121 * Returns the period of the most recent pulse.
122 * Returns the period of the most recent Encoder pulse in seconds.
123 * This method compenstates for the decoding type.
124 *
125 * @deprecated Use GetRate() in favor of this method. This returns unscaled
126 * periods and GetRate() scales using value from SetDistancePerPulse().
127 *
128 * @return Period in seconds of the most recent pulse.
129 */
130double HAL_GetFPGAEncoderPeriod(HAL_FPGAEncoderHandle fpgaEncoderHandle,
131 int32_t* status) {
132 auto encoder = fpgaEncoderHandles.Get(fpgaEncoderHandle);
133 if (encoder == nullptr) {
134 *status = HAL_HANDLE_ERROR;
135 return 0.0;
136 }
137 tEncoder::tTimerOutput output = encoder->encoder->readTimerOutput(status);
138 double value;
139 if (output.Stalled) {
140 // Return infinity
141 double zero = 0.0;
142 value = 1.0 / zero;
143 } else {
144 // output.Period is a fixed point number that counts by 2 (24 bits, 25
145 // integer bits)
146 value = static_cast<double>(output.Period << 1) /
147 static_cast<double>(output.Count);
148 }
149 double measuredPeriod = value * 2.5e-8;
150 return measuredPeriod / DECODING_SCALING_FACTOR;
151}
152
153/**
154 * Sets the maximum period for stopped detection.
155 * Sets the value that represents the maximum period of the Encoder before it
156 * will assume that the attached device is stopped. This timeout allows users
157 * to determine if the wheels or other shaft has stopped rotating.
158 * This method compensates for the decoding type.
159 *
160 * @deprecated Use SetMinRate() in favor of this method. This takes unscaled
161 * periods and SetMinRate() scales using value from SetDistancePerPulse().
162 *
163 * @param maxPeriod The maximum time between rising and falling edges before the
164 * FPGA will
165 * report the device stopped. This is expressed in seconds.
166 */
167void HAL_SetFPGAEncoderMaxPeriod(HAL_FPGAEncoderHandle fpgaEncoderHandle,
168 double maxPeriod, int32_t* status) {
169 auto encoder = fpgaEncoderHandles.Get(fpgaEncoderHandle);
170 if (encoder == nullptr) {
171 *status = HAL_HANDLE_ERROR;
172 return;
173 }
174 encoder->encoder->writeTimerConfig_StallPeriod(
175 static_cast<uint32_t>(maxPeriod * 4.0e8 * DECODING_SCALING_FACTOR),
176 status);
177}
178
179/**
180 * Determine if the encoder is stopped.
181 * Using the MaxPeriod value, a boolean is returned that is true if the encoder
182 * is considered stopped and false if it is still moving. A stopped encoder is
183 * one where the most recent pulse width exceeds the MaxPeriod.
184 * @return True if the encoder is considered stopped.
185 */
186HAL_Bool HAL_GetFPGAEncoderStopped(HAL_FPGAEncoderHandle fpgaEncoderHandle,
187 int32_t* status) {
188 auto encoder = fpgaEncoderHandles.Get(fpgaEncoderHandle);
189 if (encoder == nullptr) {
190 *status = HAL_HANDLE_ERROR;
191 return false;
192 }
193 return encoder->encoder->readTimerOutput_Stalled(status) != 0;
194}
195
196/**
197 * The last direction the encoder value changed.
198 * @return The last direction the encoder value changed.
199 */
200HAL_Bool HAL_GetFPGAEncoderDirection(HAL_FPGAEncoderHandle fpgaEncoderHandle,
201 int32_t* status) {
202 auto encoder = fpgaEncoderHandles.Get(fpgaEncoderHandle);
203 if (encoder == nullptr) {
204 *status = HAL_HANDLE_ERROR;
205 return false;
206 }
207 return encoder->encoder->readOutput_Direction(status);
208}
209
210/**
211 * Set the direction sensing for this encoder.
212 * This sets the direction sensing on the encoder so that it could count in the
213 * correct software direction regardless of the mounting.
214 * @param reverseDirection true if the encoder direction should be reversed
215 */
216void HAL_SetFPGAEncoderReverseDirection(HAL_FPGAEncoderHandle fpgaEncoderHandle,
217 HAL_Bool reverseDirection,
218 int32_t* status) {
219 auto encoder = fpgaEncoderHandles.Get(fpgaEncoderHandle);
220 if (encoder == nullptr) {
221 *status = HAL_HANDLE_ERROR;
222 return;
223 }
224 encoder->encoder->writeConfig_Reverse(reverseDirection, status);
225}
226
227/**
228 * Set the Samples to Average which specifies the number of samples of the timer
229 * to average when calculating the period. Perform averaging to account for
230 * mechanical imperfections or as oversampling to increase resolution.
231 * @param samplesToAverage The number of samples to average from 1 to 127.
232 */
233void HAL_SetFPGAEncoderSamplesToAverage(HAL_FPGAEncoderHandle fpgaEncoderHandle,
234 int32_t samplesToAverage,
235 int32_t* status) {
236 auto encoder = fpgaEncoderHandles.Get(fpgaEncoderHandle);
237 if (encoder == nullptr) {
238 *status = HAL_HANDLE_ERROR;
239 return;
240 }
241 if (samplesToAverage < 1 || samplesToAverage > 127) {
242 *status = PARAMETER_OUT_OF_RANGE;
243 }
244 encoder->encoder->writeTimerConfig_AverageSize(samplesToAverage, status);
245}
246
247/**
248 * Get the Samples to Average which specifies the number of samples of the timer
249 * to average when calculating the period. Perform averaging to account for
250 * mechanical imperfections or as oversampling to increase resolution.
251 * @return SamplesToAverage The number of samples being averaged (from 1 to 127)
252 */
253int32_t HAL_GetFPGAEncoderSamplesToAverage(
254 HAL_FPGAEncoderHandle fpgaEncoderHandle, int32_t* status) {
255 auto encoder = fpgaEncoderHandles.Get(fpgaEncoderHandle);
256 if (encoder == nullptr) {
257 *status = HAL_HANDLE_ERROR;
258 return 0;
259 }
260 return encoder->encoder->readTimerConfig_AverageSize(status);
261}
262
263/**
264 * Set an index source for an encoder, which is an input that resets the
265 * encoder's count.
266 */
267void HAL_SetFPGAEncoderIndexSource(HAL_FPGAEncoderHandle fpgaEncoderHandle,
268 HAL_Handle digitalSourceHandle,
269 HAL_AnalogTriggerType analogTriggerType,
270 HAL_Bool activeHigh, HAL_Bool edgeSensitive,
271 int32_t* status) {
272 auto encoder = fpgaEncoderHandles.Get(fpgaEncoderHandle);
273 if (encoder == nullptr) {
274 *status = HAL_HANDLE_ERROR;
275 return;
276 }
277
278 bool routingAnalogTrigger = false;
279 uint8_t routingChannel = 0;
280 uint8_t routingModule = 0;
281 bool success =
282 remapDigitalSource(digitalSourceHandle, analogTriggerType, routingChannel,
283 routingModule, routingAnalogTrigger);
284 if (!success) {
285 *status = HAL_HANDLE_ERROR;
286 return;
287 }
288
289 encoder->encoder->writeConfig_IndexSource_Channel(routingChannel, status);
290 encoder->encoder->writeConfig_IndexSource_Module(routingModule, status);
291 encoder->encoder->writeConfig_IndexSource_AnalogTrigger(routingAnalogTrigger,
292 status);
293 encoder->encoder->writeConfig_IndexActiveHigh(activeHigh, status);
294 encoder->encoder->writeConfig_IndexEdgeSensitive(edgeSensitive, status);
295}
296}