blob: 96eab2b70f9212614ca882f26a9f3224f5012a29 [file] [log] [blame]
Brian Silverman41cdd3e2019-01-19 19:48:58 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) 2016-2018 FIRST. 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/DIO.h"
9
10#include <cmath>
11#include <thread>
12
13#include <wpi/raw_ostream.h>
14
15#include "DigitalInternal.h"
16#include "HALInitializer.h"
17#include "PortsInternal.h"
18#include "hal/cpp/fpga_clock.h"
19#include "hal/handles/HandlesInternal.h"
20#include "hal/handles/LimitedHandleResource.h"
21
22using namespace hal;
23
24// Create a mutex to protect changes to the DO PWM config
25static wpi::mutex digitalPwmMutex;
26
27static LimitedHandleResource<HAL_DigitalPWMHandle, uint8_t,
28 kNumDigitalPWMOutputs, HAL_HandleEnum::DigitalPWM>*
29 digitalPWMHandles;
30
31namespace hal {
32namespace init {
33void InitializeDIO() {
34 static LimitedHandleResource<HAL_DigitalPWMHandle, uint8_t,
35 kNumDigitalPWMOutputs,
36 HAL_HandleEnum::DigitalPWM>
37 dpH;
38 digitalPWMHandles = &dpH;
39}
40} // namespace init
41} // namespace hal
42
43extern "C" {
44
45HAL_DigitalHandle HAL_InitializeDIOPort(HAL_PortHandle portHandle,
46 HAL_Bool input, int32_t* status) {
47 hal::init::CheckInit();
48 initializeDigital(status);
49
50 if (*status != 0) return HAL_kInvalidHandle;
51
52 int16_t channel = getPortHandleChannel(portHandle);
53 if (channel == InvalidHandleIndex || channel >= kNumDigitalChannels) {
54 *status = PARAMETER_OUT_OF_RANGE;
55 return HAL_kInvalidHandle;
56 }
57
58 auto handle =
59 digitalChannelHandles->Allocate(channel, HAL_HandleEnum::DIO, status);
60
61 if (*status != 0)
62 return HAL_kInvalidHandle; // failed to allocate. Pass error back.
63
64 auto port = digitalChannelHandles->Get(handle, HAL_HandleEnum::DIO);
65 if (port == nullptr) { // would only occur on thread issue.
66 *status = HAL_HANDLE_ERROR;
67 return HAL_kInvalidHandle;
68 }
69
70 port->channel = static_cast<uint8_t>(channel);
71
72 std::lock_guard<wpi::mutex> lock(digitalDIOMutex);
73
74 tDIO::tOutputEnable outputEnable = digitalSystem->readOutputEnable(status);
75
76 if (port->channel >= kNumDigitalHeaders + kNumDigitalMXPChannels) {
77 if (!getPortHandleSPIEnable(portHandle)) {
78 // if this flag is not set, we actually want DIO.
79 uint32_t bitToSet = 1u << remapSPIChannel(port->channel);
80
81 uint16_t specialFunctions = spiSystem->readEnableDIO(status);
82 // Set the field to enable SPI DIO
83 spiSystem->writeEnableDIO(specialFunctions | bitToSet, status);
84
85 if (input) {
86 outputEnable.SPIPort =
87 outputEnable.SPIPort & (~bitToSet); // clear the field for read
88 } else {
89 outputEnable.SPIPort =
90 outputEnable.SPIPort | bitToSet; // set the bits for write
91 }
92 }
93 } else if (port->channel < kNumDigitalHeaders) {
94 uint32_t bitToSet = 1u << port->channel;
95 if (input) {
96 outputEnable.Headers =
97 outputEnable.Headers & (~bitToSet); // clear the bit for read
98 } else {
99 outputEnable.Headers =
100 outputEnable.Headers | bitToSet; // set the bit for write
101 }
102 } else {
103 uint32_t bitToSet = 1u << remapMXPChannel(port->channel);
104
105 uint16_t specialFunctions =
106 digitalSystem->readEnableMXPSpecialFunction(status);
107 digitalSystem->writeEnableMXPSpecialFunction(specialFunctions & ~bitToSet,
108 status);
109
110 if (input) {
111 outputEnable.MXP =
112 outputEnable.MXP & (~bitToSet); // clear the bit for read
113 } else {
114 outputEnable.MXP = outputEnable.MXP | bitToSet; // set the bit for write
115 }
116 }
117
118 digitalSystem->writeOutputEnable(outputEnable, status);
119
120 return handle;
121}
122
123HAL_Bool HAL_CheckDIOChannel(int32_t channel) {
124 return channel < kNumDigitalChannels && channel >= 0;
125}
126
127void HAL_FreeDIOPort(HAL_DigitalHandle dioPortHandle) {
128 auto port = digitalChannelHandles->Get(dioPortHandle, HAL_HandleEnum::DIO);
129 // no status, so no need to check for a proper free.
130 if (port == nullptr) return;
131 digitalChannelHandles->Free(dioPortHandle, HAL_HandleEnum::DIO);
132
133 // Wait for no other object to hold this handle.
134 auto start = hal::fpga_clock::now();
135 while (port.use_count() != 1) {
136 auto current = hal::fpga_clock::now();
137 if (start + std::chrono::seconds(1) < current) {
138 wpi::outs() << "DIO handle free timeout\n";
139 wpi::outs().flush();
140 break;
141 }
142 std::this_thread::yield();
143 }
144
145 int32_t status = 0;
146 std::lock_guard<wpi::mutex> lock(digitalDIOMutex);
147 if (port->channel >= kNumDigitalHeaders + kNumDigitalMXPChannels) {
148 // Unset the SPI flag
149 int32_t bitToUnset = 1 << remapSPIChannel(port->channel);
150 uint16_t specialFunctions = spiSystem->readEnableDIO(&status);
151 spiSystem->writeEnableDIO(specialFunctions & ~bitToUnset, &status);
152 } else if (port->channel >= kNumDigitalHeaders) {
153 // Unset the MXP flag
154 uint32_t bitToUnset = 1u << remapMXPChannel(port->channel);
155
156 uint16_t specialFunctions =
157 digitalSystem->readEnableMXPSpecialFunction(&status);
158 digitalSystem->writeEnableMXPSpecialFunction(specialFunctions | bitToUnset,
159 &status);
160 }
161}
162
163HAL_DigitalPWMHandle HAL_AllocateDigitalPWM(int32_t* status) {
164 auto handle = digitalPWMHandles->Allocate();
165 if (handle == HAL_kInvalidHandle) {
166 *status = NO_AVAILABLE_RESOURCES;
167 return HAL_kInvalidHandle;
168 }
169
170 auto id = digitalPWMHandles->Get(handle);
171 if (id == nullptr) { // would only occur on thread issue.
172 *status = HAL_HANDLE_ERROR;
173 return HAL_kInvalidHandle;
174 }
175 *id = static_cast<uint8_t>(getHandleIndex(handle));
176
177 return handle;
178}
179
180void HAL_FreeDigitalPWM(HAL_DigitalPWMHandle pwmGenerator, int32_t* status) {
181 digitalPWMHandles->Free(pwmGenerator);
182}
183
184void HAL_SetDigitalPWMRate(double rate, int32_t* status) {
185 // Currently rounding in the log rate domain... heavy weight toward picking a
186 // higher freq.
187 // TODO: Round in the linear rate domain.
188 initializeDigital(status);
189 if (*status != 0) return;
190 uint16_t pwmPeriodPower = static_cast<uint16_t>(
191 std::log(1.0 / (16 * 1.0E-6 * rate)) / std::log(2.0) + 0.5);
192 digitalSystem->writePWMPeriodPower(pwmPeriodPower, status);
193}
194
195void HAL_SetDigitalPWMDutyCycle(HAL_DigitalPWMHandle pwmGenerator,
196 double dutyCycle, int32_t* status) {
197 auto port = digitalPWMHandles->Get(pwmGenerator);
198 if (port == nullptr) {
199 *status = HAL_HANDLE_ERROR;
200 return;
201 }
202 int32_t id = *port;
203 if (dutyCycle > 1.0) dutyCycle = 1.0;
204 if (dutyCycle < 0.0) dutyCycle = 0.0;
205 double rawDutyCycle = 256.0 * dutyCycle;
206 if (rawDutyCycle > 255.5) rawDutyCycle = 255.5;
207 {
208 std::lock_guard<wpi::mutex> lock(digitalPwmMutex);
209 uint16_t pwmPeriodPower = digitalSystem->readPWMPeriodPower(status);
210 if (pwmPeriodPower < 4) {
211 // The resolution of the duty cycle drops close to the highest
212 // frequencies.
213 rawDutyCycle = rawDutyCycle / std::pow(2.0, 4 - pwmPeriodPower);
214 }
215 if (id < 4)
216 digitalSystem->writePWMDutyCycleA(id, static_cast<uint8_t>(rawDutyCycle),
217 status);
218 else
219 digitalSystem->writePWMDutyCycleB(
220 id - 4, static_cast<uint8_t>(rawDutyCycle), status);
221 }
222}
223
224void HAL_SetDigitalPWMOutputChannel(HAL_DigitalPWMHandle pwmGenerator,
225 int32_t channel, int32_t* status) {
226 auto port = digitalPWMHandles->Get(pwmGenerator);
227 if (port == nullptr) {
228 *status = HAL_HANDLE_ERROR;
229 return;
230 }
231 int32_t id = *port;
232 if (channel >= kNumDigitalHeaders &&
233 channel <
234 kNumDigitalHeaders + kNumDigitalMXPChannels) { // If it is on the MXP
235 /* Then to write as a digital PWM channel an offset is needed to write on
236 * the correct channel
237 */
238 channel += kMXPDigitalPWMOffset;
239 }
240 digitalSystem->writePWMOutputSelect(id, channel, status);
241}
242
243void HAL_SetDIO(HAL_DigitalHandle dioPortHandle, HAL_Bool value,
244 int32_t* status) {
245 auto port = digitalChannelHandles->Get(dioPortHandle, HAL_HandleEnum::DIO);
246 if (port == nullptr) {
247 *status = HAL_HANDLE_ERROR;
248 return;
249 }
250 if (value != 0 && value != 1) {
251 if (value != 0) value = 1;
252 }
253 {
254 std::lock_guard<wpi::mutex> lock(digitalDIOMutex);
255 tDIO::tDO currentDIO = digitalSystem->readDO(status);
256
257 if (port->channel >= kNumDigitalHeaders + kNumDigitalMXPChannels) {
258 if (value == 0) {
259 currentDIO.SPIPort =
260 currentDIO.SPIPort & ~(1u << remapSPIChannel(port->channel));
261 } else if (value == 1) {
262 currentDIO.SPIPort =
263 currentDIO.SPIPort | (1u << remapSPIChannel(port->channel));
264 }
265 } else if (port->channel < kNumDigitalHeaders) {
266 if (value == 0) {
267 currentDIO.Headers = currentDIO.Headers & ~(1u << port->channel);
268 } else if (value == 1) {
269 currentDIO.Headers = currentDIO.Headers | (1u << port->channel);
270 }
271 } else {
272 if (value == 0) {
273 currentDIO.MXP =
274 currentDIO.MXP & ~(1u << remapMXPChannel(port->channel));
275 } else if (value == 1) {
276 currentDIO.MXP =
277 currentDIO.MXP | (1u << remapMXPChannel(port->channel));
278 }
279 }
280 digitalSystem->writeDO(currentDIO, status);
281 }
282}
283
284void HAL_SetDIODirection(HAL_DigitalHandle dioPortHandle, HAL_Bool input,
285 int32_t* status) {
286 auto port = digitalChannelHandles->Get(dioPortHandle, HAL_HandleEnum::DIO);
287 if (port == nullptr) {
288 *status = HAL_HANDLE_ERROR;
289 return;
290 }
291 {
292 std::lock_guard<wpi::mutex> lock(digitalDIOMutex);
293 tDIO::tOutputEnable currentDIO = digitalSystem->readOutputEnable(status);
294
295 if (port->channel >= kNumDigitalHeaders + kNumDigitalMXPChannels) {
296 if (input) {
297 currentDIO.SPIPort =
298 currentDIO.SPIPort & ~(1u << remapSPIChannel(port->channel));
299 } else {
300 currentDIO.SPIPort =
301 currentDIO.SPIPort | (1u << remapSPIChannel(port->channel));
302 }
303 } else if (port->channel < kNumDigitalHeaders) {
304 if (input) {
305 currentDIO.Headers = currentDIO.Headers & ~(1u << port->channel);
306 } else {
307 currentDIO.Headers = currentDIO.Headers | (1u << port->channel);
308 }
309 } else {
310 if (input) {
311 currentDIO.MXP =
312 currentDIO.MXP & ~(1u << remapMXPChannel(port->channel));
313 } else {
314 currentDIO.MXP =
315 currentDIO.MXP | (1u << remapMXPChannel(port->channel));
316 }
317 }
318 digitalSystem->writeOutputEnable(currentDIO, status);
319 }
320}
321
322HAL_Bool HAL_GetDIO(HAL_DigitalHandle dioPortHandle, int32_t* status) {
323 auto port = digitalChannelHandles->Get(dioPortHandle, HAL_HandleEnum::DIO);
324 if (port == nullptr) {
325 *status = HAL_HANDLE_ERROR;
326 return false;
327 }
328 tDIO::tDI currentDIO = digitalSystem->readDI(status);
329 // Shift 00000001 over channel-1 places.
330 // AND it against the currentDIO
331 // if it == 0, then return false
332 // else return true
333
334 if (port->channel >= kNumDigitalHeaders + kNumDigitalMXPChannels) {
335 return ((currentDIO.SPIPort >> remapSPIChannel(port->channel)) & 1) != 0;
336 } else if (port->channel < kNumDigitalHeaders) {
337 return ((currentDIO.Headers >> port->channel) & 1) != 0;
338 } else {
339 return ((currentDIO.MXP >> remapMXPChannel(port->channel)) & 1) != 0;
340 }
341}
342
343HAL_Bool HAL_GetDIODirection(HAL_DigitalHandle dioPortHandle, int32_t* status) {
344 auto port = digitalChannelHandles->Get(dioPortHandle, HAL_HandleEnum::DIO);
345 if (port == nullptr) {
346 *status = HAL_HANDLE_ERROR;
347 return false;
348 }
349 tDIO::tOutputEnable currentOutputEnable =
350 digitalSystem->readOutputEnable(status);
351 // Shift 00000001 over port->channel-1 places.
352 // AND it against the currentOutputEnable
353 // if it == 0, then return false
354 // else return true
355
356 if (port->channel >= kNumDigitalHeaders + kNumDigitalMXPChannels) {
357 return ((currentOutputEnable.SPIPort >> remapSPIChannel(port->channel)) &
358 1) != 0;
359 } else if (port->channel < kNumDigitalHeaders) {
360 return ((currentOutputEnable.Headers >> port->channel) & 1) != 0;
361 } else {
362 return ((currentOutputEnable.MXP >> remapMXPChannel(port->channel)) & 1) !=
363 0;
364 }
365}
366
367void HAL_Pulse(HAL_DigitalHandle dioPortHandle, double pulseLength,
368 int32_t* status) {
369 auto port = digitalChannelHandles->Get(dioPortHandle, HAL_HandleEnum::DIO);
370 if (port == nullptr) {
371 *status = HAL_HANDLE_ERROR;
372 return;
373 }
374 tDIO::tPulse pulse;
375
376 if (port->channel >= kNumDigitalHeaders + kNumDigitalMXPChannels) {
377 pulse.SPIPort = 1u << remapSPIChannel(port->channel);
378 } else if (port->channel < kNumDigitalHeaders) {
379 pulse.Headers = 1u << port->channel;
380 } else {
381 pulse.MXP = 1u << remapMXPChannel(port->channel);
382 }
383
384 digitalSystem->writePulseLength(
385 static_cast<uint8_t>(1.0e9 * pulseLength /
386 (pwmSystem->readLoopTiming(status) * 25)),
387 status);
388 digitalSystem->writePulse(pulse, status);
389}
390
391HAL_Bool HAL_IsPulsing(HAL_DigitalHandle dioPortHandle, int32_t* status) {
392 auto port = digitalChannelHandles->Get(dioPortHandle, HAL_HandleEnum::DIO);
393 if (port == nullptr) {
394 *status = HAL_HANDLE_ERROR;
395 return false;
396 }
397 tDIO::tPulse pulseRegister = digitalSystem->readPulse(status);
398
399 if (port->channel >= kNumDigitalHeaders + kNumDigitalMXPChannels) {
400 return (pulseRegister.SPIPort & (1 << remapSPIChannel(port->channel))) != 0;
401 } else if (port->channel < kNumDigitalHeaders) {
402 return (pulseRegister.Headers & (1 << port->channel)) != 0;
403 } else {
404 return (pulseRegister.MXP & (1 << remapMXPChannel(port->channel))) != 0;
405 }
406}
407
408HAL_Bool HAL_IsAnyPulsing(int32_t* status) {
409 initializeDigital(status);
410 if (*status != 0) return false;
411 tDIO::tPulse pulseRegister = digitalSystem->readPulse(status);
412 return pulseRegister.Headers != 0 && pulseRegister.MXP != 0 &&
413 pulseRegister.SPIPort != 0;
414}
415
416void HAL_SetFilterSelect(HAL_DigitalHandle dioPortHandle, int32_t filterIndex,
417 int32_t* status) {
418 auto port = digitalChannelHandles->Get(dioPortHandle, HAL_HandleEnum::DIO);
419 if (port == nullptr) {
420 *status = HAL_HANDLE_ERROR;
421 return;
422 }
423
424 std::lock_guard<wpi::mutex> lock(digitalDIOMutex);
425 if (port->channel >= kNumDigitalHeaders + kNumDigitalMXPChannels) {
426 // Channels 10-15 are SPI channels, so subtract our MXP channels
427 digitalSystem->writeFilterSelectHdr(port->channel - kNumDigitalMXPChannels,
428 filterIndex, status);
429 } else if (port->channel < kNumDigitalHeaders) {
430 digitalSystem->writeFilterSelectHdr(port->channel, filterIndex, status);
431 } else {
432 digitalSystem->writeFilterSelectMXP(remapMXPChannel(port->channel),
433 filterIndex, status);
434 }
435}
436
437int32_t HAL_GetFilterSelect(HAL_DigitalHandle dioPortHandle, int32_t* status) {
438 auto port = digitalChannelHandles->Get(dioPortHandle, HAL_HandleEnum::DIO);
439 if (port == nullptr) {
440 *status = HAL_HANDLE_ERROR;
441 return 0;
442 }
443
444 std::lock_guard<wpi::mutex> lock(digitalDIOMutex);
445 if (port->channel >= kNumDigitalHeaders + kNumDigitalMXPChannels) {
446 // Channels 10-15 are SPI channels, so subtract our MXP channels
447 return digitalSystem->readFilterSelectHdr(
448 port->channel - kNumDigitalMXPChannels, status);
449 } else if (port->channel < kNumDigitalHeaders) {
450 return digitalSystem->readFilterSelectHdr(port->channel, status);
451 } else {
452 return digitalSystem->readFilterSelectMXP(remapMXPChannel(port->channel),
453 status);
454 }
455}
456
457void HAL_SetFilterPeriod(int32_t filterIndex, int64_t value, int32_t* status) {
458 initializeDigital(status);
459 if (*status != 0) return;
460 std::lock_guard<wpi::mutex> lock(digitalDIOMutex);
461 digitalSystem->writeFilterPeriodHdr(filterIndex, value, status);
462 if (*status == 0) {
463 digitalSystem->writeFilterPeriodMXP(filterIndex, value, status);
464 }
465}
466
467int64_t HAL_GetFilterPeriod(int32_t filterIndex, int32_t* status) {
468 initializeDigital(status);
469 if (*status != 0) return 0;
470 uint32_t hdrPeriod = 0;
471 uint32_t mxpPeriod = 0;
472 {
473 std::lock_guard<wpi::mutex> lock(digitalDIOMutex);
474 hdrPeriod = digitalSystem->readFilterPeriodHdr(filterIndex, status);
475 if (*status == 0) {
476 mxpPeriod = digitalSystem->readFilterPeriodMXP(filterIndex, status);
477 }
478 }
479 if (hdrPeriod != mxpPeriod) {
480 *status = NiFpga_Status_SoftwareFault;
481 return -1;
482 }
483 return hdrPeriod;
484}
485
486} // extern "C"