blob: 0d4ff5f17561190e24d1cd25638820ac9d994042 [file] [log] [blame]
Brian Silverman26e4e522015-12-17 01:56:40 -05001
2#include "HAL/Digital.hpp"
3
4#include "HAL/Port.h"
5#include "HAL/HAL.hpp"
6#include "ChipObject.h"
7#include "HAL/cpp/Resource.hpp"
8#include "HAL/cpp/priority_mutex.h"
9#include "FRC_NetworkCommunication/LoadOut.h"
10#include <stdio.h>
11#include <math.h>
12#include <mutex>
13#include "i2clib/i2c-lib.h"
14#include "spilib/spi-lib.h"
15
16static_assert(sizeof(uint32_t) <= sizeof(void *), "This file shoves uint32_ts into pointers.");
17
18static const uint32_t kExpectedLoopTiming = 40;
19static const uint32_t kDigitalPins = 26;
20static const uint32_t kPwmPins = 20;
21static const uint32_t kRelayPins = 8;
22static const uint32_t kNumHeaders = 10; // Number of non-MXP pins
23
24/**
25 * kDefaultPwmPeriod is in ms
26 *
27 * - 20ms periods (50 Hz) are the "safest" setting in that this works for all devices
28 * - 20ms periods seem to be desirable for Vex Motors
29 * - 20ms periods are the specified period for HS-322HD servos, but work reliably down
30 * to 10.0 ms; starting at about 8.5ms, the servo sometimes hums and get hot;
31 * by 5.0ms the hum is nearly continuous
32 * - 10ms periods work well for Victor 884
33 * - 5ms periods allows higher update rates for Luminary Micro Jaguar speed controllers.
34 * Due to the shipping firmware on the Jaguar, we can't run the update period less
35 * than 5.05 ms.
36 *
37 * kDefaultPwmPeriod is the 1x period (5.05 ms). In hardware, the period scaling is implemented as an
38 * output squelch to get longer periods for old devices.
39 */
40static const float kDefaultPwmPeriod = 5.05;
41/**
42 * kDefaultPwmCenter is the PWM range center in ms
43 */
44static const float kDefaultPwmCenter = 1.5;
45/**
46 * kDefaultPWMStepsDown is the number of PWM steps below the centerpoint
47 */
48static const int32_t kDefaultPwmStepsDown = 1000;
49static const int32_t kPwmDisabled = 0;
50
51struct DigitalPort {
52 Port port;
53 uint32_t PWMGeneratorID;
54};
55
56// XXX: Set these back to static once we figure out the memory clobbering issue
57// Create a mutex to protect changes to the digital output values
58priority_recursive_mutex digitalDIOMutex;
59// Create a mutex to protect changes to the relay values
60priority_recursive_mutex digitalRelayMutex;
61// Create a mutex to protect changes to the DO PWM config
62priority_recursive_mutex digitalPwmMutex;
63priority_recursive_mutex digitalI2COnBoardMutex;
64priority_recursive_mutex digitalI2CMXPMutex;
65
66tDIO* digitalSystem = NULL;
67tRelay* relaySystem = NULL;
68tPWM* pwmSystem = NULL;
69hal::Resource *DIOChannels = NULL;
70hal::Resource *DO_PWMGenerators = NULL;
71hal::Resource *PWMChannels = NULL;
72
73bool digitalSystemsInitialized = false;
74
75uint8_t i2COnboardObjCount = 0;
76uint8_t i2CMXPObjCount = 0;
77uint8_t i2COnBoardHandle = 0;
78uint8_t i2CMXPHandle = 0;
79
80int32_t m_spiCS0Handle = 0;
81int32_t m_spiCS1Handle = 0;
82int32_t m_spiCS2Handle = 0;
83int32_t m_spiCS3Handle = 0;
84int32_t m_spiMXPHandle = 0;
85priority_recursive_mutex spiOnboardSemaphore;
86priority_recursive_mutex spiMXPSemaphore;
87tSPI *spiSystem;
88
89struct SPIAccumulator {
90 void* notifier = nullptr;
91 uint32_t triggerTime;
92 uint32_t period;
93
94 int64_t value = 0;
95 uint32_t count = 0;
96 int32_t last_value = 0;
97
98 int32_t center = 0;
99 int32_t deadband = 0;
100
101 uint8_t cmd[4]; // command to send (up to 4 bytes)
102 uint32_t valid_mask;
103 uint32_t valid_value;
104 int32_t data_max; // one more than max data value
105 int32_t data_msb_mask; // data field MSB mask (for signed)
106 uint8_t data_shift; // data field shift right amount, in bits
107 uint8_t xfer_size; // SPI transfer size, in bytes (up to 4)
108 uint8_t port;
109 bool is_signed; // is data field signed?
110 bool big_endian; // is response big endian?
111};
112SPIAccumulator* spiAccumulators[5] = {nullptr, nullptr, nullptr, nullptr, nullptr};
113
114/**
115 * Initialize the digital system.
116 */
117void initializeDigital(int32_t *status) {
118 if (digitalSystemsInitialized) return;
119
120 hal::Resource::CreateResourceObject(&DIOChannels, tDIO::kNumSystems * kDigitalPins);
121 hal::Resource::CreateResourceObject(&DO_PWMGenerators, tDIO::kNumPWMDutyCycleAElements + tDIO::kNumPWMDutyCycleBElements);
122 hal::Resource::CreateResourceObject(&PWMChannels, tPWM::kNumSystems * kPwmPins);
123 digitalSystem = tDIO::create(status);
124
125 // Relay Setup
126 relaySystem = tRelay::create(status);
127
128 // Turn off all relay outputs.
129 relaySystem->writeValue_Forward(0, status);
130 relaySystem->writeValue_Reverse(0, status);
131
132 // PWM Setup
133 pwmSystem = tPWM::create(status);
134
135 // Make sure that the 9403 IONode has had a chance to initialize before continuing.
136 while(pwmSystem->readLoopTiming(status) == 0) delayTicks(1);
137
138 if (pwmSystem->readLoopTiming(status) != kExpectedLoopTiming) {
139 // TODO: char err[128];
140 // TODO: sprintf(err, "DIO LoopTiming: %d, expecting: %lu\n", digitalModules[port->module-1]->readLoopTiming(status), kExpectedLoopTiming);
141 *status = LOOP_TIMING_ERROR; // NOTE: Doesn't display the error
142 }
143
144 //Calculate the length, in ms, of one DIO loop
145 double loopTime = pwmSystem->readLoopTiming(status)/(kSystemClockTicksPerMicrosecond*1e3);
146
147 pwmSystem->writeConfig_Period((uint16_t) (kDefaultPwmPeriod/loopTime + .5), status);
148 uint16_t minHigh = (uint16_t) ((kDefaultPwmCenter-kDefaultPwmStepsDown*loopTime)/loopTime + .5);
149 pwmSystem->writeConfig_MinHigh(minHigh, status);
150// printf("MinHigh: %d\n", minHigh);
151 // Ensure that PWM output values are set to OFF
152 for (uint32_t pwm_index = 0; pwm_index < kPwmPins; pwm_index++) {
153 // Initialize port structure
154 DigitalPort digital_port;
155 digital_port.port.pin = pwm_index;
156
157 setPWM(&digital_port, kPwmDisabled, status);
158 setPWMPeriodScale(&digital_port, 3, status); // Set all to 4x by default.
159 }
160
161 digitalSystemsInitialized = true;
162}
163
164/**
165 * Create a new instance of a digital port.
166 */
167void* initializeDigitalPort(void* port_pointer, int32_t *status) {
168 initializeDigital(status);
169 Port* port = (Port*) port_pointer;
170
171 // Initialize port structure
172 DigitalPort* digital_port = new DigitalPort();
173 digital_port->port = *port;
174
175 return digital_port;
176}
177
178void freeDigitalPort(void* digital_port_pointer) {
179 DigitalPort* port = (DigitalPort*) digital_port_pointer;
180 delete port;
181}
182
183bool checkPWMChannel(void* digital_port_pointer) {
184 DigitalPort* port = (DigitalPort*) digital_port_pointer;
185 return port->port.pin < kPwmPins;
186}
187
188bool checkRelayChannel(void* digital_port_pointer) {
189 DigitalPort* port = (DigitalPort*) digital_port_pointer;
190 return port->port.pin < kRelayPins;
191}
192
193/**
194 * Check a port to make sure that it is not NULL and is a valid PWM port.
195 *
196 * Sets the status to contain the appropriate error.
197 *
198 * @return true if the port passed validation.
199 */
200static bool verifyPWMChannel(DigitalPort *port, int32_t *status) {
201 if (port == NULL) {
202 *status = NULL_PARAMETER;
203 return false;
204 } else if (!checkPWMChannel(port)) {
205 *status = PARAMETER_OUT_OF_RANGE;
206 return false;
207 } else {
208 return true;
209 }
210}
211
212/**
213 * Check a port to make sure that it is not NULL and is a valid Relay port.
214 *
215 * Sets the status to contain the appropriate error.
216 *
217 * @return true if the port passed validation.
218 */
219static bool verifyRelayChannel(DigitalPort *port, int32_t *status) {
220 if (port == NULL) {
221 *status = NULL_PARAMETER;
222 return false;
223 } else if (!checkRelayChannel(port)) {
224 *status = PARAMETER_OUT_OF_RANGE;
225 return false;
226 } else {
227 return true;
228 }
229}
230
231/**
232 * Map DIO pin numbers from their physical number (10 to 26) to their position
233 * in the bit field.
234 */
235uint32_t remapMXPChannel(uint32_t pin) {
236 return pin - 10;
237}
238
239uint32_t remapMXPPWMChannel(uint32_t pin) {
240 if(pin < 14) {
241 return pin - 10; //first block of 4 pwms (MXP 0-3)
242 } else {
243 return pin - 6; //block of PWMs after SPI
244 }
245}
246
247/**
248 * Set a PWM channel to the desired value. The values range from 0 to 255 and the period is controlled
249 * by the PWM Period and MinHigh registers.
250 *
251 * @param channel The PWM channel to set.
252 * @param value The PWM value to set.
253 */
254void setPWM(void* digital_port_pointer, unsigned short value, int32_t *status) {
255 DigitalPort* port = (DigitalPort*) digital_port_pointer;
256 if (!verifyPWMChannel(port, status)) { return; }
257
258 if(port->port.pin < tPWM::kNumHdrRegisters) {
259 pwmSystem->writeHdr(port->port.pin, value, status);
260 } else {
261 pwmSystem->writeMXP(port->port.pin - tPWM::kNumHdrRegisters, value, status);
262 }
263}
264
265/**
266 * Get a value from a PWM channel. The values range from 0 to 255.
267 *
268 * @param channel The PWM channel to read from.
269 * @return The raw PWM value.
270 */
271unsigned short getPWM(void* digital_port_pointer, int32_t *status) {
272 DigitalPort* port = (DigitalPort*) digital_port_pointer;
273 if (!verifyPWMChannel(port, status)) { return 0; }
274
275 if(port->port.pin < tPWM::kNumHdrRegisters) {
276 return pwmSystem->readHdr(port->port.pin, status);
277 } else {
278 return pwmSystem->readMXP(port->port.pin - tPWM::kNumHdrRegisters, status);
279 }
280}
281
282void latchPWMZero(void* digital_port_pointer, int32_t *status) {
283 DigitalPort* port = (DigitalPort*) digital_port_pointer;
284 if (!verifyPWMChannel(port, status)) { return; }
285
286 pwmSystem->writeZeroLatch(port->port.pin, true, status);
287 pwmSystem->writeZeroLatch(port->port.pin, false, status);
288}
289
290/**
291 * Set how how often the PWM signal is squelched, thus scaling the period.
292 *
293 * @param channel The PWM channel to configure.
294 * @param squelchMask The 2-bit mask of outputs to squelch.
295 */
296void setPWMPeriodScale(void* digital_port_pointer, uint32_t squelchMask, int32_t *status) {
297 DigitalPort* port = (DigitalPort*) digital_port_pointer;
298 if (!verifyPWMChannel(port, status)) { return; }
299
300 if(port->port.pin < tPWM::kNumPeriodScaleHdrElements) {
301 pwmSystem->writePeriodScaleHdr(port->port.pin, squelchMask, status);
302 } else {
303 pwmSystem->writePeriodScaleMXP(port->port.pin - tPWM::kNumPeriodScaleHdrElements, squelchMask, status);
304 }
305}
306
307/**
308 * Allocate a DO PWM Generator.
309 * Allocate PWM generators so that they are not accidentally reused.
310 *
311 * @return PWM Generator refnum
312 */
313void* allocatePWM(int32_t *status) {
314 return (void*)DO_PWMGenerators->Allocate("DO_PWM");
315}
316
317/**
318 * Free the resource associated with a DO PWM generator.
319 *
320 * @param pwmGenerator The pwmGen to free that was allocated with AllocateDO_PWM()
321 */
322void freePWM(void* pwmGenerator, int32_t *status) {
323 uint32_t id = (uint32_t) pwmGenerator;
324 if (id == ~0ul) return;
325 DO_PWMGenerators->Free(id);
326}
327
328/**
329 * Change the frequency of the DO PWM generator.
330 *
331 * The valid range is from 0.6 Hz to 19 kHz. The frequency resolution is logarithmic.
332 *
333 * @param rate The frequency to output all digital output PWM signals.
334 */
335void setPWMRate(double rate, int32_t *status) {
336 // Currently rounding in the log rate domain... heavy weight toward picking a higher freq.
337 // TODO: Round in the linear rate domain.
338 uint8_t pwmPeriodPower = (uint8_t)(log(1.0 / (pwmSystem->readLoopTiming(status) * 0.25E-6 * rate))/log(2.0) + 0.5);
339 digitalSystem->writePWMPeriodPower(pwmPeriodPower, status);
340}
341
342
343/**
344 * Configure the duty-cycle of the PWM generator
345 *
346 * @param pwmGenerator The generator index reserved by AllocateDO_PWM()
347 * @param dutyCycle The percent duty cycle to output [0..1].
348 */
349void setPWMDutyCycle(void* pwmGenerator, double dutyCycle, int32_t *status) {
350 uint32_t id = (uint32_t) pwmGenerator;
351 if (id == ~0ul) return;
352 if (dutyCycle > 1.0) dutyCycle = 1.0;
353 if (dutyCycle < 0.0) dutyCycle = 0.0;
354 float rawDutyCycle = 256.0 * dutyCycle;
355 if (rawDutyCycle > 255.5) rawDutyCycle = 255.5;
356 {
357 std::lock_guard<priority_recursive_mutex> sync(digitalPwmMutex);
358 uint8_t pwmPeriodPower = digitalSystem->readPWMPeriodPower(status);
359 if (pwmPeriodPower < 4) {
360 // The resolution of the duty cycle drops close to the highest frequencies.
361 rawDutyCycle = rawDutyCycle / pow(2.0, 4 - pwmPeriodPower);
362 }
363 if(id < 4)
364 digitalSystem->writePWMDutyCycleA(id, (uint8_t)rawDutyCycle, status);
365 else
366 digitalSystem->writePWMDutyCycleB(id - 4, (uint8_t)rawDutyCycle, status);
367 }
368}
369
370/**
371 * Configure which DO channel the PWM signal is output on
372 *
373 * @param pwmGenerator The generator index reserved by AllocateDO_PWM()
374 * @param channel The Digital Output channel to output on
375 */
376void setPWMOutputChannel(void* pwmGenerator, uint32_t pin, int32_t *status) {
377 uint32_t id = (uint32_t) pwmGenerator;
378 if (id > 5) return;
379 digitalSystem->writePWMOutputSelect(id, pin, status);
380}
381
382/**
383 * Set the state of a relay.
384 * Set the state of a relay output to be forward. Relays have two outputs and each is
385 * independently set to 0v or 12v.
386 */
387void setRelayForward(void* digital_port_pointer, bool on, int32_t *status) {
388 DigitalPort* port = (DigitalPort*) digital_port_pointer;
389 if (!verifyRelayChannel(port, status)) {
390 return;
391 }
392
393 {
394 std::lock_guard<priority_recursive_mutex> sync(digitalRelayMutex);
395 uint8_t forwardRelays = relaySystem->readValue_Forward(status);
396 if (on)
397 forwardRelays |= 1 << port->port.pin;
398 else
399 forwardRelays &= ~(1 << port->port.pin);
400 relaySystem->writeValue_Forward(forwardRelays, status);
401 }
402}
403
404/**
405 * Set the state of a relay.
406 * Set the state of a relay output to be reverse. Relays have two outputs and each is
407 * independently set to 0v or 12v.
408 */
409void setRelayReverse(void* digital_port_pointer, bool on, int32_t *status) {
410 DigitalPort* port = (DigitalPort*) digital_port_pointer;
411 if (!verifyRelayChannel(port, status)) {
412 return;
413 }
414
415 {
416 std::lock_guard<priority_recursive_mutex> sync(digitalRelayMutex);
417 uint8_t reverseRelays = relaySystem->readValue_Reverse(status);
418 if (on)
419 reverseRelays |= 1 << port->port.pin;
420 else
421 reverseRelays &= ~(1 << port->port.pin);
422 relaySystem->writeValue_Reverse(reverseRelays, status);
423 }
424}
425
426/**
427 * Get the current state of the forward relay channel
428 */
429bool getRelayForward(void* digital_port_pointer, int32_t *status) {
430 DigitalPort* port = (DigitalPort*) digital_port_pointer;
431 if (!verifyRelayChannel(port, status)) { return false; }
432
433 uint8_t forwardRelays = relaySystem->readValue_Forward(status);
434 return (forwardRelays & (1 << port->port.pin)) != 0;
435}
436
437/**
438 * Get the current state of the reverse relay channel
439 */
440bool getRelayReverse(void* digital_port_pointer, int32_t *status) {
441 DigitalPort* port = (DigitalPort*) digital_port_pointer;
442 if (!verifyRelayChannel(port, status)) { return false; }
443
444 uint8_t reverseRelays = relaySystem->readValue_Reverse(status);
445 return (reverseRelays & (1 << port->port.pin)) != 0;
446}
447
448/**
449 * Allocate Digital I/O channels.
450 * Allocate channels so that they are not accidently reused. Also the direction is set at the
451 * time of the allocation.
452 *
453 * @param channel The Digital I/O channel
454 * @param input If true open as input; if false open as output
455 * @return Was successfully allocated
456 */
457bool allocateDIO(void* digital_port_pointer, bool input, int32_t *status) {
458 DigitalPort* port = (DigitalPort*) digital_port_pointer;
459 char buf[64];
460 snprintf(buf, 64, "DIO %d", port->port.pin);
461 if (DIOChannels->Allocate(port->port.pin, buf) == ~0ul) {
462 *status = RESOURCE_IS_ALLOCATED;
463 return false;
464 }
465
466 {
467 std::lock_guard<priority_recursive_mutex> sync(digitalDIOMutex);
468
469 tDIO::tOutputEnable outputEnable = digitalSystem->readOutputEnable(status);
470
471 if(port->port.pin < kNumHeaders) {
472 uint32_t bitToSet = 1 << port->port.pin;
473 if (input) {
474 outputEnable.Headers = outputEnable.Headers & (~bitToSet); // clear the bit for read
475 } else {
476 outputEnable.Headers = outputEnable.Headers | bitToSet; // set the bit for write
477 }
478 } else {
479 uint32_t bitToSet = 1 << remapMXPChannel(port->port.pin);
480
481 // Disable special functions on this pin
482 short specialFunctions = digitalSystem->readEnableMXPSpecialFunction(status);
483 digitalSystem->writeEnableMXPSpecialFunction(specialFunctions & ~bitToSet, status);
484
485 if (input) {
486 outputEnable.MXP = outputEnable.MXP & (~bitToSet); // clear the bit for read
487 } else {
488 outputEnable.MXP = outputEnable.MXP | bitToSet; // set the bit for write
489 }
490 }
491
492 digitalSystem->writeOutputEnable(outputEnable, status);
493 }
494 return true;
495}
496
497bool allocatePWMChannel(void* digital_port_pointer, int32_t *status) {
498 DigitalPort* port = (DigitalPort*) digital_port_pointer;
499 if (!verifyPWMChannel(port, status)) { return false; }
500
501 char buf[64];
502 snprintf(buf, 64, "PWM %d", port->port.pin);
503 if (PWMChannels->Allocate(port->port.pin, buf) == ~0ul) {
504 *status = RESOURCE_IS_ALLOCATED;
505 return false;
506 }
507
508 if (port->port.pin > tPWM::kNumHdrRegisters-1) {
509 snprintf(buf, 64, "PWM %d and DIO %d", port->port.pin, remapMXPPWMChannel(port->port.pin) + 10);
510 if (DIOChannels->Allocate(remapMXPPWMChannel(port->port.pin) + 10, buf) == ~0ul) return false;
511 uint32_t bitToSet = 1 << remapMXPPWMChannel(port->port.pin);
512 short specialFunctions = digitalSystem->readEnableMXPSpecialFunction(status);
513 digitalSystem->writeEnableMXPSpecialFunction(specialFunctions | bitToSet, status);
514 }
515 return true;
516}
517
518void freePWMChannel(void* digital_port_pointer, int32_t *status) {
519 DigitalPort* port = (DigitalPort*) digital_port_pointer;
520 if (!port) return;
521 if (!verifyPWMChannel(port, status)) { return; }
522
523 PWMChannels->Free(port->port.pin);
524 if(port->port.pin > tPWM::kNumHdrRegisters-1) {
525 DIOChannels->Free(remapMXPPWMChannel(port->port.pin) + 10);
526 uint32_t bitToUnset = 1 << remapMXPPWMChannel(port->port.pin);
527 short specialFunctions = digitalSystem->readEnableMXPSpecialFunction(status);
528 digitalSystem->writeEnableMXPSpecialFunction(specialFunctions & ~bitToUnset, status);
529 }
530}
531
532/**
533 * Free the resource associated with a digital I/O channel.
534 *
535 * @param channel The Digital I/O channel to free
536 */
537void freeDIO(void* digital_port_pointer, int32_t *status) {
538 DigitalPort* port = (DigitalPort*) digital_port_pointer;
539 if (!port) return;
540 DIOChannels->Free(port->port.pin);
541}
542
543/**
544 * Write a digital I/O bit to the FPGA.
545 * Set a single value on a digital I/O channel.
546 *
547 * @param channel The Digital I/O channel
548 * @param value The state to set the digital channel (if it is configured as an output)
549 */
550void setDIO(void* digital_port_pointer, short value, int32_t *status) {
551 DigitalPort* port = (DigitalPort*) digital_port_pointer;
552 if (value != 0 && value != 1) {
553 if (value != 0)
554 value = 1;
555 }
556 {
557 std::lock_guard<priority_recursive_mutex> sync(digitalDIOMutex);
558 tDIO::tDO currentDIO = digitalSystem->readDO(status);
559
560 if(port->port.pin < kNumHeaders) {
561 if(value == 0) {
562 currentDIO.Headers = currentDIO.Headers & ~(1 << port->port.pin);
563 } else if (value == 1) {
564 currentDIO.Headers = currentDIO.Headers | (1 << port->port.pin);
565 }
566 } else {
567 if(value == 0) {
568 currentDIO.MXP = currentDIO.MXP & ~(1 << remapMXPChannel(port->port.pin));
569 } else if (value == 1) {
570 currentDIO.MXP = currentDIO.MXP | (1 << remapMXPChannel(port->port.pin));
571 }
572
573 uint32_t bitToSet = 1 << remapMXPChannel(port->port.pin);
574 short specialFunctions = digitalSystem->readEnableMXPSpecialFunction(status);
575 digitalSystem->writeEnableMXPSpecialFunction(specialFunctions & ~bitToSet, status);
576 }
577 digitalSystem->writeDO(currentDIO, status);
578 }
579}
580
581/**
582 * Read a digital I/O bit from the FPGA.
583 * Get a single value from a digital I/O channel.
584 *
585 * @param channel The digital I/O channel
586 * @return The state of the specified channel
587 */
588bool getDIO(void* digital_port_pointer, int32_t *status) {
589 DigitalPort* port = (DigitalPort*) digital_port_pointer;
590 tDIO::tDI currentDIO = digitalSystem->readDI(status);
591 //Shift 00000001 over channel-1 places.
592 //AND it against the currentDIO
593 //if it == 0, then return false
594 //else return true
595
596 if(port->port.pin < kNumHeaders) {
597 return ((currentDIO.Headers >> port->port.pin) & 1) != 0;
598 } else {
599 // Disable special functions
600 uint32_t bitToSet = 1 << remapMXPChannel(port->port.pin);
601 short specialFunctions = digitalSystem->readEnableMXPSpecialFunction(status);
602 digitalSystem->writeEnableMXPSpecialFunction(specialFunctions & ~bitToSet, status);
603
604 return ((currentDIO.MXP >> remapMXPChannel(port->port.pin)) & 1) != 0;
605 }
606}
607
608/**
609 * Read the direction of a the Digital I/O lines
610 * A 1 bit means output and a 0 bit means input.
611 *
612 * @param channel The digital I/O channel
613 * @return The direction of the specified channel
614 */
615bool getDIODirection(void* digital_port_pointer, int32_t *status) {
616 DigitalPort* port = (DigitalPort*) digital_port_pointer;
617 tDIO::tOutputEnable currentOutputEnable = digitalSystem->readOutputEnable(status);
618 //Shift 00000001 over port->port.pin-1 places.
619 //AND it against the currentOutputEnable
620 //if it == 0, then return false
621 //else return true
622
623 if(port->port.pin < kNumHeaders) {
624 return ((currentOutputEnable.Headers >> port->port.pin) & 1) != 0;
625 } else {
626 return ((currentOutputEnable.MXP >> remapMXPChannel(port->port.pin)) & 1) != 0;
627 }
628}
629
630/**
631 * Generate a single pulse.
632 * Write a pulse to the specified digital output channel. There can only be a single pulse going at any time.
633 *
634 * @param channel The Digital Output channel that the pulse should be output on
635 * @param pulseLength The active length of the pulse (in seconds)
636 */
637void pulse(void* digital_port_pointer, double pulseLength, int32_t *status) {
638 DigitalPort* port = (DigitalPort*) digital_port_pointer;
639 tDIO::tPulse pulse;
640
641 if(port->port.pin < kNumHeaders) {
642 pulse.Headers = 1 << port->port.pin;
643 } else {
644 pulse.MXP = 1 << remapMXPChannel(port->port.pin);
645 }
646
647 digitalSystem->writePulseLength((uint8_t)(1.0e9 * pulseLength / (pwmSystem->readLoopTiming(status) * 25)), status);
648 digitalSystem->writePulse(pulse, status);
649}
650
651/**
652 * Check a DIO line to see if it is currently generating a pulse.
653 *
654 * @return A pulse is in progress
655 */
656bool isPulsing(void* digital_port_pointer, int32_t *status) {
657 DigitalPort* port = (DigitalPort*) digital_port_pointer;
658 tDIO::tPulse pulseRegister = digitalSystem->readPulse(status);
659
660 if(port->port.pin < kNumHeaders) {
661 return (pulseRegister.Headers & (1 << port->port.pin)) != 0;
662 } else {
663 return (pulseRegister.MXP & (1 << remapMXPChannel(port->port.pin))) != 0;
664 }
665}
666
667/**
668 * Check if any DIO line is currently generating a pulse.
669 *
670 * @return A pulse on some line is in progress
671 */
672bool isAnyPulsing(int32_t *status) {
673 tDIO::tPulse pulseRegister = digitalSystem->readPulse(status);
674 return pulseRegister.Headers != 0 && pulseRegister.MXP != 0;
675}
676
677/**
678 * Write the filter index from the FPGA.
679 * Set the filter index used to filter out short pulses.
680 *
681 * @param digital_port_pointer The digital I/O channel
682 * @param filter_index The filter index. Must be in the range 0 - 3,
683 * where 0 means "none" and 1 - 3 means filter # filter_index - 1.
684 */
685void setFilterSelect(void* digital_port_pointer, int filter_index,
686 int32_t* status) {
687 DigitalPort* port = (DigitalPort*)digital_port_pointer;
688
689 std::lock_guard<priority_recursive_mutex> sync(digitalDIOMutex);
690 if (port->port.pin < kNumHeaders) {
691 digitalSystem->writeFilterSelectHdr(port->port.pin, filter_index, status);
692 }
693 else {
694 digitalSystem->writeFilterSelectMXP(remapMXPChannel(port->port.pin),
695 filter_index, status);
696 }
697}
698
699/**
700 * Read the filter index from the FPGA.
701 * Get the filter index used to filter out short pulses.
702 *
703 * @param digital_port_pointer The digital I/O channel
704 * @return filter_index The filter index. Must be in the range 0 - 3,
705 * where 0 means "none" and 1 - 3 means filter # filter_index - 1.
706 */
707int getFilterSelect(void* digital_port_pointer, int32_t* status) {
708 DigitalPort* port = (DigitalPort*)digital_port_pointer;
709
710 std::lock_guard<priority_recursive_mutex> sync(digitalDIOMutex);
711 if (port->port.pin < kNumHeaders) {
712 return digitalSystem->readFilterSelectHdr(port->port.pin, status);
713 }
714 else {
715 return digitalSystem->readFilterSelectMXP(remapMXPChannel(port->port.pin),
716 status);
717 }
718}
719
720/**
721 * Set the filter period for the specified filter index.
722 *
723 * Set the filter period in FPGA cycles. Even though there are 2 different
724 * filter index domains (MXP vs HDR), ignore that distinction for now since it
725 * compilicates the interface. That can be changed later.
726 *
727 * @param filter_index The filter index, 0 - 2.
728 * @param value The number of cycles that the signal must not transition to be
729 * counted as a transition.
730 */
731void setFilterPeriod(int filter_index, uint32_t value, int32_t* status) {
732 std::lock_guard<priority_recursive_mutex> sync(digitalDIOMutex);
733 digitalSystem->writeFilterPeriodHdr(filter_index, value, status);
734 if (*status == 0) {
735 digitalSystem->writeFilterPeriodMXP(filter_index, value, status);
736 }
737}
738
739/**
740 * Get the filter period for the specified filter index.
741 *
742 * Get the filter period in FPGA cycles. Even though there are 2 different
743 * filter index domains (MXP vs HDR), ignore that distinction for now since it
744 * compilicates the interface. Set status to NiFpga_Status_SoftwareFault if the
745 * filter values miss-match.
746 *
747 * @param filter_index The filter index, 0 - 2.
748 * @param value The number of cycles that the signal must not transition to be
749 * counted as a transition.
750 */
751uint32_t getFilterPeriod(int filter_index, int32_t* status) {
752 uint32_t hdr_period = 0;
753 uint32_t mxp_period = 0;
754 {
755 std::lock_guard<priority_recursive_mutex> sync(digitalDIOMutex);
756 hdr_period = digitalSystem->readFilterPeriodHdr(filter_index, status);
757 if (*status == 0) {
758 mxp_period = digitalSystem->readFilterPeriodMXP(filter_index, status);
759 }
760 }
761 if (hdr_period != mxp_period) {
762 *status = NiFpga_Status_SoftwareFault;
763 return -1;
764 }
765 return hdr_period;
766}
767
768struct counter_t {
769 tCounter* counter;
770 uint32_t index;
771};
772typedef struct counter_t Counter;
773
774static hal::Resource *counters = NULL;
775
776void* initializeCounter(Mode mode, uint32_t *index, int32_t *status) {
777 hal::Resource::CreateResourceObject(&counters, tCounter::kNumSystems);
778 *index = counters->Allocate("Counter");
779 if (*index == ~0ul) {
780 *status = NO_AVAILABLE_RESOURCES;
781 return NULL;
782 }
783 Counter* counter = new Counter();
784 counter->counter = tCounter::create(*index, status);
785 counter->counter->writeConfig_Mode(mode, status);
786 counter->counter->writeTimerConfig_AverageSize(1, status);
787 counter->index = *index;
788 return counter;
789}
790
791void freeCounter(void* counter_pointer, int32_t *status) {
792 Counter* counter = (Counter*) counter_pointer;
793 if (!counter) return;
794 delete counter->counter;
795 counters->Free(counter->index);
796}
797
798void setCounterAverageSize(void* counter_pointer, int32_t size, int32_t *status) {
799 Counter* counter = (Counter*) counter_pointer;
800 counter->counter->writeTimerConfig_AverageSize(size, status);
801}
802
803/**
804 * remap the digital source pin and set the module.
805 * If it's an analog trigger, determine the module from the high order routing channel
806 * else do normal digital input remapping based on pin number (MXP)
807 */
808void remapDigitalSource(bool analogTrigger, uint32_t &pin, uint8_t &module) {
809 if (analogTrigger) {
810 module = pin >> 4;
811 } else {
812 if(pin >= kNumHeaders) {
813 pin = remapMXPChannel(pin);
814 module = 1;
815 } else {
816 module = 0;
817 }
818 }
819}
820
821/**
822 * Set the source object that causes the counter to count up.
823 * Set the up counting DigitalSource.
824 */
825void setCounterUpSource(void* counter_pointer, uint32_t pin, bool analogTrigger, int32_t *status) {
826 Counter* counter = (Counter*) counter_pointer;
827
828 uint8_t module;
829
830 remapDigitalSource(analogTrigger, pin, module);
831
832 counter->counter->writeConfig_UpSource_Module(module, status);
833 counter->counter->writeConfig_UpSource_Channel(pin, status);
834 counter->counter->writeConfig_UpSource_AnalogTrigger(analogTrigger, status);
835
836 if(counter->counter->readConfig_Mode(status) == kTwoPulse ||
837 counter->counter->readConfig_Mode(status) == kExternalDirection) {
838 setCounterUpSourceEdge(counter_pointer, true, false, status);
839 }
840 counter->counter->strobeReset(status);
841}
842
843/**
844 * Set the edge sensitivity on an up counting source.
845 * Set the up source to either detect rising edges or falling edges.
846 */
847void setCounterUpSourceEdge(void* counter_pointer, bool risingEdge, bool fallingEdge, int32_t *status) {
848 Counter* counter = (Counter*) counter_pointer;
849 counter->counter->writeConfig_UpRisingEdge(risingEdge, status);
850 counter->counter->writeConfig_UpFallingEdge(fallingEdge, status);
851}
852
853/**
854 * Disable the up counting source to the counter.
855 */
856void clearCounterUpSource(void* counter_pointer, int32_t *status) {
857 Counter* counter = (Counter*) counter_pointer;
858 counter->counter->writeConfig_UpFallingEdge(false, status);
859 counter->counter->writeConfig_UpRisingEdge(false, status);
860 // Index 0 of digital is always 0.
861 counter->counter->writeConfig_UpSource_Channel(0, status);
862 counter->counter->writeConfig_UpSource_AnalogTrigger(false, status);
863}
864
865/**
866 * Set the source object that causes the counter to count down.
867 * Set the down counting DigitalSource.
868 */
869void setCounterDownSource(void* counter_pointer, uint32_t pin, bool analogTrigger, int32_t *status) {
870 Counter* counter = (Counter*) counter_pointer;
871 unsigned char mode = counter->counter->readConfig_Mode(status);
872 if (mode != kTwoPulse && mode != kExternalDirection) {
873 // TODO: wpi_setWPIErrorWithContext(ParameterOutOfRange, "Counter only supports DownSource in TwoPulse and ExternalDirection modes.");
874 *status = PARAMETER_OUT_OF_RANGE;
875 return;
876 }
877
878 uint8_t module;
879
880 remapDigitalSource(analogTrigger, pin, module);
881
882 counter->counter->writeConfig_DownSource_Module(module, status);
883 counter->counter->writeConfig_DownSource_Channel(pin, status);
884 counter->counter->writeConfig_DownSource_AnalogTrigger(analogTrigger, status);
885
886 setCounterDownSourceEdge(counter_pointer, true, false, status);
887 counter->counter->strobeReset(status);
888}
889
890/**
891 * Set the edge sensitivity on a down counting source.
892 * Set the down source to either detect rising edges or falling edges.
893 */
894void setCounterDownSourceEdge(void* counter_pointer, bool risingEdge, bool fallingEdge, int32_t *status) {
895 Counter* counter = (Counter*) counter_pointer;
896 counter->counter->writeConfig_DownRisingEdge(risingEdge, status);
897 counter->counter->writeConfig_DownFallingEdge(fallingEdge, status);
898}
899
900/**
901 * Disable the down counting source to the counter.
902 */
903void clearCounterDownSource(void* counter_pointer, int32_t *status) {
904 Counter* counter = (Counter*) counter_pointer;
905 counter->counter->writeConfig_DownFallingEdge(false, status);
906 counter->counter->writeConfig_DownRisingEdge(false, status);
907 // Index 0 of digital is always 0.
908 counter->counter->writeConfig_DownSource_Channel(0, status);
909 counter->counter->writeConfig_DownSource_AnalogTrigger(false, status);
910}
911
912/**
913 * Set standard up / down counting mode on this counter.
914 * Up and down counts are sourced independently from two inputs.
915 */
916void setCounterUpDownMode(void* counter_pointer, int32_t *status) {
917 Counter* counter = (Counter*) counter_pointer;
918 counter->counter->writeConfig_Mode(kTwoPulse, status);
919}
920
921/**
922 * Set external direction mode on this counter.
923 * Counts are sourced on the Up counter input.
924 * The Down counter input represents the direction to count.
925 */
926void setCounterExternalDirectionMode(void* counter_pointer, int32_t *status) {
927 Counter* counter = (Counter*) counter_pointer;
928 counter->counter->writeConfig_Mode(kExternalDirection, status);
929}
930
931/**
932 * Set Semi-period mode on this counter.
933 * Counts up on both rising and falling edges.
934 */
935void setCounterSemiPeriodMode(void* counter_pointer, bool highSemiPeriod, int32_t *status) {
936 Counter* counter = (Counter*) counter_pointer;
937 counter->counter->writeConfig_Mode(kSemiperiod, status);
938 counter->counter->writeConfig_UpRisingEdge(highSemiPeriod, status);
939 setCounterUpdateWhenEmpty(counter_pointer, false, status);
940}
941
942/**
943 * Configure the counter to count in up or down based on the length of the input pulse.
944 * This mode is most useful for direction sensitive gear tooth sensors.
945 * @param threshold The pulse length beyond which the counter counts the opposite direction. Units are seconds.
946 */
947void setCounterPulseLengthMode(void* counter_pointer, double threshold, int32_t *status) {
948 Counter* counter = (Counter*) counter_pointer;
949 counter->counter->writeConfig_Mode(kPulseLength, status);
950 counter->counter->writeConfig_PulseLengthThreshold((uint32_t)(threshold * 1.0e6) * kSystemClockTicksPerMicrosecond, status);
951}
952
953/**
954 * Get the Samples to Average which specifies the number of samples of the timer to
955 * average when calculating the period. Perform averaging to account for
956 * mechanical imperfections or as oversampling to increase resolution.
957 * @return SamplesToAverage The number of samples being averaged (from 1 to 127)
958 */
959int32_t getCounterSamplesToAverage(void* counter_pointer, int32_t *status) {
960 Counter* counter = (Counter*) counter_pointer;
961 return counter->counter->readTimerConfig_AverageSize(status);
962}
963
964/**
965 * Set the Samples to Average which specifies the number of samples of the timer to
966 * average when calculating the period. Perform averaging to account for
967 * mechanical imperfections or as oversampling to increase resolution.
968 * @param samplesToAverage The number of samples to average from 1 to 127.
969 */
970void setCounterSamplesToAverage(void* counter_pointer, int samplesToAverage, int32_t *status) {
971 Counter* counter = (Counter*) counter_pointer;
972 if (samplesToAverage < 1 || samplesToAverage > 127) {
973 *status = PARAMETER_OUT_OF_RANGE;
974 }
975 counter->counter->writeTimerConfig_AverageSize(samplesToAverage, status);
976}
977
978/**
979 * Reset the Counter to zero.
980 * Set the counter value to zero. This doesn't effect the running state of the counter, just sets
981 * the current value to zero.
982 */
983void resetCounter(void* counter_pointer, int32_t *status) {
984 Counter* counter = (Counter*) counter_pointer;
985 counter->counter->strobeReset(status);
986}
987
988/**
989 * Read the current counter value.
990 * Read the value at this instant. It may still be running, so it reflects the current value. Next
991 * time it is read, it might have a different value.
992 */
993int32_t getCounter(void* counter_pointer, int32_t *status) {
994 Counter* counter = (Counter*) counter_pointer;
995 int32_t value = counter->counter->readOutput_Value(status);
996 return value;
997}
998
999/*
1000 * Get the Period of the most recent count.
1001 * Returns the time interval of the most recent count. This can be used for velocity calculations
1002 * to determine shaft speed.
1003 * @returns The period of the last two pulses in units of seconds.
1004 */
1005double getCounterPeriod(void* counter_pointer, int32_t *status) {
1006 Counter* counter = (Counter*) counter_pointer;
1007 tCounter::tTimerOutput output = counter->counter->readTimerOutput(status);
1008 double period;
1009 if (output.Stalled) {
1010 // Return infinity
1011 double zero = 0.0;
1012 period = 1.0 / zero;
1013 } else {
1014 // output.Period is a fixed point number that counts by 2 (24 bits, 25 integer bits)
1015 period = (double)(output.Period << 1) / (double)output.Count;
1016 }
1017 return period * 2.5e-8; // result * timebase (currently 40ns)
1018}
1019
1020/**
1021 * Set the maximum period where the device is still considered "moving".
1022 * Sets the maximum period where the device is considered moving. This value is used to determine
1023 * the "stopped" state of the counter using the GetStopped method.
1024 * @param maxPeriod The maximum period where the counted device is considered moving in
1025 * seconds.
1026 */
1027void setCounterMaxPeriod(void* counter_pointer, double maxPeriod, int32_t *status) {
1028 Counter* counter = (Counter*) counter_pointer;
1029 counter->counter->writeTimerConfig_StallPeriod((uint32_t)(maxPeriod * 4.0e8), status);
1030}
1031
1032/**
1033 * Select whether you want to continue updating the event timer output when there are no samples captured.
1034 * The output of the event timer has a buffer of periods that are averaged and posted to
1035 * a register on the FPGA. When the timer detects that the event source has stopped
1036 * (based on the MaxPeriod) the buffer of samples to be averaged is emptied. If you
1037 * enable the update when empty, you will be notified of the stopped source and the event
1038 * time will report 0 samples. If you disable update when empty, the most recent average
1039 * will remain on the output until a new sample is acquired. You will never see 0 samples
1040 * output (except when there have been no events since an FPGA reset) and you will likely not
1041 * see the stopped bit become true (since it is updated at the end of an average and there are
1042 * no samples to average).
1043 */
1044void setCounterUpdateWhenEmpty(void* counter_pointer, bool enabled, int32_t *status) {
1045 Counter* counter = (Counter*) counter_pointer;
1046 counter->counter->writeTimerConfig_UpdateWhenEmpty(enabled, status);
1047}
1048
1049/**
1050 * Determine if the clock is stopped.
1051 * Determine if the clocked input is stopped based on the MaxPeriod value set using the
1052 * SetMaxPeriod method. If the clock exceeds the MaxPeriod, then the device (and counter) are
1053 * assumed to be stopped and it returns true.
1054 * @return Returns true if the most recent counter period exceeds the MaxPeriod value set by
1055 * SetMaxPeriod.
1056 */
1057bool getCounterStopped(void* counter_pointer, int32_t *status) {
1058 Counter* counter = (Counter*) counter_pointer;
1059 return counter->counter->readTimerOutput_Stalled(status);
1060}
1061
1062/**
1063 * The last direction the counter value changed.
1064 * @return The last direction the counter value changed.
1065 */
1066bool getCounterDirection(void* counter_pointer, int32_t *status) {
1067 Counter* counter = (Counter*) counter_pointer;
1068 bool value = counter->counter->readOutput_Direction(status);
1069 return value;
1070}
1071
1072/**
1073 * Set the Counter to return reversed sensing on the direction.
1074 * This allows counters to change the direction they are counting in the case of 1X and 2X
1075 * quadrature encoding only. Any other counter mode isn't supported.
1076 * @param reverseDirection true if the value counted should be negated.
1077 */
1078void setCounterReverseDirection(void* counter_pointer, bool reverseDirection, int32_t *status) {
1079 Counter* counter = (Counter*) counter_pointer;
1080 if (counter->counter->readConfig_Mode(status) == kExternalDirection) {
1081 if (reverseDirection)
1082 setCounterDownSourceEdge(counter_pointer, true, true, status);
1083 else
1084 setCounterDownSourceEdge(counter_pointer, false, true, status);
1085 }
1086}
1087
1088struct encoder_t {
1089 tEncoder* encoder;
1090 uint32_t index;
1091};
1092typedef struct encoder_t Encoder;
1093
1094static const double DECODING_SCALING_FACTOR = 0.25;
1095static hal::Resource *quadEncoders = NULL;
1096
1097void* initializeEncoder(uint8_t port_a_module, uint32_t port_a_pin, bool port_a_analog_trigger,
1098 uint8_t port_b_module, uint32_t port_b_pin, bool port_b_analog_trigger,
1099 bool reverseDirection, int32_t *index, int32_t *status) {
1100
1101 // Initialize encoder structure
1102 Encoder* encoder = new Encoder();
1103
1104 remapDigitalSource(port_a_analog_trigger, port_a_pin, port_a_module);
1105 remapDigitalSource(port_b_analog_trigger, port_b_pin, port_b_module);
1106
1107 hal::Resource::CreateResourceObject(&quadEncoders, tEncoder::kNumSystems);
1108 encoder->index = quadEncoders->Allocate("4X Encoder");
1109 *index = encoder->index;
1110 // TODO: if (index == ~0ul) { CloneError(quadEncoders); return; }
1111 encoder->encoder = tEncoder::create(encoder->index, status);
1112 encoder->encoder->writeConfig_ASource_Module(port_a_module, status);
1113 encoder->encoder->writeConfig_ASource_Channel(port_a_pin, status);
1114 encoder->encoder->writeConfig_ASource_AnalogTrigger(port_a_analog_trigger, status);
1115 encoder->encoder->writeConfig_BSource_Module(port_b_module, status);
1116 encoder->encoder->writeConfig_BSource_Channel(port_b_pin, status);
1117 encoder->encoder->writeConfig_BSource_AnalogTrigger(port_b_analog_trigger, status);
1118 encoder->encoder->strobeReset(status);
1119 encoder->encoder->writeConfig_Reverse(reverseDirection, status);
1120 encoder->encoder->writeTimerConfig_AverageSize(4, status);
1121
1122 return encoder;
1123}
1124
1125void freeEncoder(void* encoder_pointer, int32_t *status) {
1126 Encoder* encoder = (Encoder*) encoder_pointer;
1127 if (!encoder) return;
1128 quadEncoders->Free(encoder->index);
1129 delete encoder->encoder;
1130}
1131
1132/**
1133 * Reset the Encoder distance to zero.
1134 * Resets the current count to zero on the encoder.
1135 */
1136void resetEncoder(void* encoder_pointer, int32_t *status) {
1137 Encoder* encoder = (Encoder*) encoder_pointer;
1138 encoder->encoder->strobeReset(status);
1139}
1140
1141/**
1142 * Gets the raw value from the encoder.
1143 * The raw value is the actual count unscaled by the 1x, 2x, or 4x scale
1144 * factor.
1145 * @return Current raw count from the encoder
1146 */
1147int32_t getEncoder(void* encoder_pointer, int32_t *status) {
1148 Encoder* encoder = (Encoder*) encoder_pointer;
1149 return encoder->encoder->readOutput_Value(status);
1150}
1151
1152/**
1153 * Returns the period of the most recent pulse.
1154 * Returns the period of the most recent Encoder pulse in seconds.
1155 * This method compenstates for the decoding type.
1156 *
1157 * @deprecated Use GetRate() in favor of this method. This returns unscaled periods and GetRate() scales using value from SetDistancePerPulse().
1158 *
1159 * @return Period in seconds of the most recent pulse.
1160 */
1161double getEncoderPeriod(void* encoder_pointer, int32_t *status) {
1162 Encoder* encoder = (Encoder*) encoder_pointer;
1163 tEncoder::tTimerOutput output = encoder->encoder->readTimerOutput(status);
1164 double value;
1165 if (output.Stalled) {
1166 // Return infinity
1167 double zero = 0.0;
1168 value = 1.0 / zero;
1169 } else {
1170 // output.Period is a fixed point number that counts by 2 (24 bits, 25 integer bits)
1171 value = (double)(output.Period << 1) / (double)output.Count;
1172 }
1173 double measuredPeriod = value * 2.5e-8;
1174 return measuredPeriod / DECODING_SCALING_FACTOR;
1175}
1176
1177/**
1178 * Sets the maximum period for stopped detection.
1179 * Sets the value that represents the maximum period of the Encoder before it will assume
1180 * that the attached device is stopped. This timeout allows users to determine if the wheels or
1181 * other shaft has stopped rotating.
1182 * This method compensates for the decoding type.
1183 *
1184 * @deprecated Use SetMinRate() in favor of this method. This takes unscaled periods and SetMinRate() scales using value from SetDistancePerPulse().
1185 *
1186 * @param maxPeriod The maximum time between rising and falling edges before the FPGA will
1187 * report the device stopped. This is expressed in seconds.
1188 */
1189void setEncoderMaxPeriod(void* encoder_pointer, double maxPeriod, int32_t *status) {
1190 Encoder* encoder = (Encoder*) encoder_pointer;
1191 encoder->encoder->writeTimerConfig_StallPeriod((uint32_t)(maxPeriod * 4.0e8 * DECODING_SCALING_FACTOR), status);
1192}
1193
1194/**
1195 * Determine if the encoder is stopped.
1196 * Using the MaxPeriod value, a boolean is returned that is true if the encoder is considered
1197 * stopped and false if it is still moving. A stopped encoder is one where the most recent pulse
1198 * width exceeds the MaxPeriod.
1199 * @return True if the encoder is considered stopped.
1200 */
1201bool getEncoderStopped(void* encoder_pointer, int32_t *status) {
1202 Encoder* encoder = (Encoder*) encoder_pointer;
1203 return encoder->encoder->readTimerOutput_Stalled(status) != 0;
1204}
1205
1206/**
1207 * The last direction the encoder value changed.
1208 * @return The last direction the encoder value changed.
1209 */
1210bool getEncoderDirection(void* encoder_pointer, int32_t *status) {
1211 Encoder* encoder = (Encoder*) encoder_pointer;
1212 return encoder->encoder->readOutput_Direction(status);
1213}
1214
1215/**
1216 * Set the direction sensing for this encoder.
1217 * This sets the direction sensing on the encoder so that it could count in the correct
1218 * software direction regardless of the mounting.
1219 * @param reverseDirection true if the encoder direction should be reversed
1220 */
1221void setEncoderReverseDirection(void* encoder_pointer, bool reverseDirection, int32_t *status) {
1222 Encoder* encoder = (Encoder*) encoder_pointer;
1223 encoder->encoder->writeConfig_Reverse(reverseDirection, status);
1224}
1225
1226/**
1227 * Set the Samples to Average which specifies the number of samples of the timer to
1228 * average when calculating the period. Perform averaging to account for
1229 * mechanical imperfections or as oversampling to increase resolution.
1230 * @param samplesToAverage The number of samples to average from 1 to 127.
1231 */
1232void setEncoderSamplesToAverage(void* encoder_pointer, uint32_t samplesToAverage, int32_t *status) {
1233 Encoder* encoder = (Encoder*) encoder_pointer;
1234 if (samplesToAverage < 1 || samplesToAverage > 127) {
1235 *status = PARAMETER_OUT_OF_RANGE;
1236 }
1237 encoder->encoder->writeTimerConfig_AverageSize(samplesToAverage, status);
1238}
1239
1240/**
1241 * Get the Samples to Average which specifies the number of samples of the timer to
1242 * average when calculating the period. Perform averaging to account for
1243 * mechanical imperfections or as oversampling to increase resolution.
1244 * @return SamplesToAverage The number of samples being averaged (from 1 to 127)
1245 */
1246uint32_t getEncoderSamplesToAverage(void* encoder_pointer, int32_t *status) {
1247 Encoder* encoder = (Encoder*) encoder_pointer;
1248 return encoder->encoder->readTimerConfig_AverageSize(status);
1249}
1250
1251/**
1252 * Set an index source for an encoder, which is an input that resets the
1253 * encoder's count.
1254 */
1255void setEncoderIndexSource(void *encoder_pointer, uint32_t pin, bool analogTrigger, bool activeHigh,
1256 bool edgeSensitive, int32_t *status) {
1257 Encoder* encoder = (Encoder*) encoder_pointer;
1258 encoder->encoder->writeConfig_IndexSource_Channel((unsigned char)pin, status);
1259 encoder->encoder->writeConfig_IndexSource_Module((unsigned char)0, status);
1260 encoder->encoder->writeConfig_IndexSource_AnalogTrigger(analogTrigger, status);
1261 encoder->encoder->writeConfig_IndexActiveHigh(activeHigh, status);
1262 encoder->encoder->writeConfig_IndexEdgeSensitive(edgeSensitive, status);
1263}
1264
1265/**
1266 * Get the loop timing of the PWM system
1267 *
1268 * @return The loop time
1269 */
1270uint16_t getLoopTiming(int32_t *status) {
1271 return pwmSystem->readLoopTiming(status);
1272}
1273
1274/*
1275 * Initialize the spi port. Opens the port if necessary and saves the handle.
1276 * If opening the MXP port, also sets up the pin functions appropriately
1277 * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
1278 */
1279void spiInitialize(uint8_t port, int32_t *status) {
1280 if(spiSystem == NULL)
1281 spiSystem = tSPI::create(status);
1282 if(spiGetHandle(port) !=0 ) return;
1283 switch(port){
1284 case 0:
1285 spiSetHandle(0, spilib_open("/dev/spidev0.0"));
1286 break;
1287 case 1:
1288 spiSetHandle(1, spilib_open("/dev/spidev0.1"));
1289 break;
1290 case 2:
1291 spiSetHandle(2, spilib_open("/dev/spidev0.2"));
1292 break;
1293 case 3:
1294 spiSetHandle(3, spilib_open("/dev/spidev0.3"));
1295 break;
1296 case 4:
1297 initializeDigital(status);
1298 if(!allocateDIO(getPort(14), false, status)){printf("Failed to allocate DIO 14\n"); return;}
1299 if(!allocateDIO(getPort(15), false, status)) {printf("Failed to allocate DIO 15\n"); return;}
1300 if(!allocateDIO(getPort(16), true, status)) {printf("Failed to allocate DIO 16\n"); return;}
1301 if(!allocateDIO(getPort(17), false, status)) {printf("Failed to allocate DIO 17\n"); return;}
1302 digitalSystem->writeEnableMXPSpecialFunction(digitalSystem->readEnableMXPSpecialFunction(status)|0x00F0, status);
1303 spiSetHandle(4, spilib_open("/dev/spidev1.0"));
1304 break;
1305 default:
1306 break;
1307 }
1308 return;
1309}
1310
1311/**
1312 * Generic transaction.
1313 *
1314 * This is a lower-level interface to the spi hardware giving you more control over each transaction.
1315 *
1316 * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
1317 * @param dataToSend Buffer of data to send as part of the transaction.
1318 * @param dataReceived Buffer to read data into.
1319 * @param size Number of bytes to transfer. [0..7]
1320 * @return Number of bytes transferred, -1 for error
1321 */
1322int32_t spiTransaction(uint8_t port, uint8_t *dataToSend, uint8_t *dataReceived, uint8_t size)
1323{
1324 std::lock_guard<priority_recursive_mutex> sync(spiGetSemaphore(port));
1325 return spilib_writeread(spiGetHandle(port), (const char*) dataToSend, (char*) dataReceived, (int32_t) size);
1326}
1327
1328/**
1329 * Execute a write transaction with the device.
1330 *
1331 * Write to a device and wait until the transaction is complete.
1332 *
1333 * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
1334 * @param datToSend The data to write to the register on the device.
1335 * @param sendSize The number of bytes to be written
1336 * @return The number of bytes written. -1 for an error
1337 */
1338int32_t spiWrite(uint8_t port, uint8_t* dataToSend, uint8_t sendSize)
1339{
1340 std::lock_guard<priority_recursive_mutex> sync(spiGetSemaphore(port));
1341 return spilib_write(spiGetHandle(port), (const char*) dataToSend, (int32_t) sendSize);
1342}
1343
1344/**
1345 * Execute a read from the device.
1346 *
1347 * This methdod does not write any data out to the device
1348 * Most spi devices will require a register address to be written before
1349 * they begin returning data
1350 *
1351 * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
1352 * @param buffer A pointer to the array of bytes to store the data read from the device.
1353 * @param count The number of bytes to read in the transaction. [1..7]
1354 * @return Number of bytes read. -1 for error.
1355 */
1356int32_t spiRead(uint8_t port, uint8_t *buffer, uint8_t count)
1357{
1358 std::lock_guard<priority_recursive_mutex> sync(spiGetSemaphore(port));
1359 return spilib_read(spiGetHandle(port), (char*) buffer, (int32_t) count);
1360}
1361
1362/**
1363 * Close the SPI port
1364 *
1365 * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
1366 */
1367void spiClose(uint8_t port) {
1368 std::lock_guard<priority_recursive_mutex> sync(spiGetSemaphore(port));
1369 if (spiAccumulators[port]) {
1370 int32_t status = 0;
1371 spiFreeAccumulator(port, &status);
1372 }
1373 spilib_close(spiGetHandle(port));
1374 spiSetHandle(port, 0);
1375 return;
1376}
1377
1378/**
1379 * Set the clock speed for the SPI bus.
1380 *
1381 * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
1382 * @param speed The speed in Hz (0-1MHz)
1383 */
1384void spiSetSpeed(uint8_t port, uint32_t speed) {
1385 std::lock_guard<priority_recursive_mutex> sync(spiGetSemaphore(port));
1386 spilib_setspeed(spiGetHandle(port), speed);
1387}
1388
1389/**
1390 * Set the SPI options
1391 *
1392 * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
1393 * @param msb_first True to write the MSB first, False for LSB first
1394 * @param sample_on_trailing True to sample on the trailing edge, False to sample on the leading edge
1395 * @param clk_idle_high True to set the clock to active low, False to set the clock active high
1396 */
1397void spiSetOpts(uint8_t port, int msb_first, int sample_on_trailing, int clk_idle_high) {
1398 std::lock_guard<priority_recursive_mutex> sync(spiGetSemaphore(port));
1399 spilib_setopts(spiGetHandle(port), msb_first, sample_on_trailing, clk_idle_high);
1400}
1401
1402/**
1403 * Set the CS Active high for a SPI port
1404 *
1405 * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
1406 */
1407void spiSetChipSelectActiveHigh(uint8_t port, int32_t *status){
1408 std::lock_guard<priority_recursive_mutex> sync(spiGetSemaphore(port));
1409 if(port < 4)
1410 {
1411 spiSystem->writeChipSelectActiveHigh_Hdr(spiSystem->readChipSelectActiveHigh_Hdr(status) | (1<<port), status);
1412 }
1413 else
1414 {
1415 spiSystem->writeChipSelectActiveHigh_MXP(1, status);
1416 }
1417}
1418
1419/**
1420 * Set the CS Active low for a SPI port
1421 *
1422 * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
1423 */
1424void spiSetChipSelectActiveLow(uint8_t port, int32_t *status){
1425 std::lock_guard<priority_recursive_mutex> sync(spiGetSemaphore(port));
1426 if(port < 4)
1427 {
1428 spiSystem->writeChipSelectActiveHigh_Hdr(spiSystem->readChipSelectActiveHigh_Hdr(status) & ~(1<<port), status);
1429 }
1430 else
1431 {
1432 spiSystem->writeChipSelectActiveHigh_MXP(0, status);
1433 }
1434}
1435
1436/**
1437 * Get the stored handle for a SPI port
1438 *
1439 * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
1440 * @return The stored handle for the SPI port. 0 represents no stored handle.
1441 */
1442int32_t spiGetHandle(uint8_t port){
1443 std::lock_guard<priority_recursive_mutex> sync(spiGetSemaphore(port));
1444 switch(port){
1445 case 0:
1446 return m_spiCS0Handle;
1447 case 1:
1448 return m_spiCS1Handle;
1449 case 2:
1450 return m_spiCS2Handle;
1451 case 3:
1452 return m_spiCS3Handle;
1453 case 4:
1454 return m_spiMXPHandle;
1455 default:
1456 return 0;
1457 }
1458}
1459
1460/**
1461 * Set the stored handle for a SPI port
1462 *
1463 * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP.
1464 * @param handle The value of the handle for the port.
1465 */
1466void spiSetHandle(uint8_t port, int32_t handle){
1467 std::lock_guard<priority_recursive_mutex> sync(spiGetSemaphore(port));
1468 switch(port){
1469 case 0:
1470 m_spiCS0Handle = handle;
1471 break;
1472 case 1:
1473 m_spiCS1Handle = handle;
1474 break;
1475 case 2:
1476 m_spiCS2Handle = handle;
1477 break;
1478 case 3:
1479 m_spiCS3Handle = handle;
1480 break;
1481 case 4:
1482 m_spiMXPHandle = handle;
1483 break;
1484 default:
1485 break;
1486 }
1487}
1488
1489/**
1490 * Get the semaphore for a SPI port
1491 *
1492 * @param port The number of the port to use. 0-3 for Onboard CS0-CS2, 4 for MXP
1493 * @return The semaphore for the SPI port.
1494 */
1495priority_recursive_mutex& spiGetSemaphore(uint8_t port) {
1496 if(port < 4)
1497 return spiOnboardSemaphore;
1498 else
1499 return spiMXPSemaphore;
1500}
1501
1502static void spiAccumulatorProcess(uint32_t currentTime, void *param) {
1503 SPIAccumulator* accum = (SPIAccumulator*)param;
1504
1505 // perform SPI transaction
1506 uint8_t resp_b[4];
1507 std::lock_guard<priority_recursive_mutex> sync(spiGetSemaphore(accum->port));
1508 spilib_writeread(spiGetHandle(accum->port), (const char*) accum->cmd, (char*) resp_b, (int32_t) accum->xfer_size);
1509
1510 // convert from bytes
1511 uint32_t resp = 0;
1512 if (accum->big_endian) {
1513 for (int i=0; i < accum->xfer_size; ++i) {
1514 resp <<= 8;
1515 resp |= resp_b[i] & 0xff;
1516 }
1517 } else {
1518 for (int i = accum->xfer_size - 1; i >= 0; --i) {
1519 resp <<= 8;
1520 resp |= resp_b[i] & 0xff;
1521 }
1522 }
1523
1524 // process response
1525 if ((resp & accum->valid_mask) == accum->valid_value) {
1526 // valid sensor data; extract data field
1527 int32_t data = (int32_t)(resp >> accum->data_shift);
1528 data &= accum->data_max - 1;
1529 // 2s complement conversion if signed MSB is set
1530 if (accum->is_signed && (data & accum->data_msb_mask) != 0)
1531 data -= accum->data_max;
1532 // center offset
1533 data -= accum->center;
1534 // only accumulate if outside deadband
1535 if (data < -accum->deadband || data > accum->deadband)
1536 accum->value += data;
1537 ++accum->count;
1538 accum->last_value = data;
1539 } else {
1540 // no data from the sensor; just clear the last value
1541 accum->last_value = 0;
1542 }
1543
1544 // reschedule timer
1545 accum->triggerTime += accum->period;
1546 // handle timer slip
1547 if (accum->triggerTime < currentTime)
1548 accum->triggerTime = currentTime + accum->period;
1549 int32_t status = 0;
1550 updateNotifierAlarm(accum->notifier, accum->triggerTime, &status);
1551}
1552
1553/**
1554 * Initialize a SPI accumulator.
1555 *
1556 * @param port SPI port
1557 * @param period Time between reads, in us
1558 * @param cmd SPI command to send to request data
1559 * @param xfer_size SPI transfer size, in bytes
1560 * @param valid_mask Mask to apply to received data for validity checking
1561 * @param valid_data After valid_mask is applied, required matching value for
1562 * validity checking
1563 * @param data_shift Bit shift to apply to received data to get actual data
1564 * value
1565 * @param data_size Size (in bits) of data field
1566 * @param is_signed Is data field signed?
1567 * @param big_endian Is device big endian?
1568 */
1569void spiInitAccumulator(uint8_t port, uint32_t period, uint32_t cmd,
1570 uint8_t xfer_size, uint32_t valid_mask, uint32_t valid_value,
1571 uint8_t data_shift, uint8_t data_size, bool is_signed,
1572 bool big_endian, int32_t *status) {
1573 std::lock_guard<priority_recursive_mutex> sync(spiGetSemaphore(port));
1574 if (port > 4) return;
1575 if (!spiAccumulators[port])
1576 spiAccumulators[port] = new SPIAccumulator();
1577 SPIAccumulator* accum = spiAccumulators[port];
1578 if (big_endian) {
1579 for (int i = xfer_size - 1; i >= 0; --i) {
1580 accum->cmd[i] = cmd & 0xff;
1581 cmd >>= 8;
1582 }
1583 } else {
1584 accum->cmd[0] = cmd & 0xff; cmd >>= 8;
1585 accum->cmd[1] = cmd & 0xff; cmd >>= 8;
1586 accum->cmd[2] = cmd & 0xff; cmd >>= 8;
1587 accum->cmd[3] = cmd & 0xff;
1588 }
1589 accum->period = period;
1590 accum->xfer_size = xfer_size;
1591 accum->valid_mask = valid_mask;
1592 accum->valid_value = valid_value;
1593 accum->data_shift = data_shift;
1594 accum->data_max = (1 << data_size);
1595 accum->data_msb_mask = (1 << (data_size - 1));
1596 accum->is_signed = is_signed;
1597 accum->big_endian = big_endian;
1598 if (!accum->notifier) {
1599 accum->notifier = initializeNotifier(spiAccumulatorProcess, accum, status);
1600 accum->triggerTime = getFPGATime(status) + period;
1601 if (*status != 0) return;
1602 updateNotifierAlarm(accum->notifier, accum->triggerTime, status);
1603 }
1604}
1605
1606/**
1607 * Frees a SPI accumulator.
1608 */
1609void spiFreeAccumulator(uint8_t port, int32_t *status) {
1610 std::lock_guard<priority_recursive_mutex> sync(spiGetSemaphore(port));
1611 SPIAccumulator* accum = spiAccumulators[port];
1612 if (!accum) {
1613 *status = NULL_PARAMETER;
1614 return;
1615 }
1616 cleanNotifier(accum->notifier, status);
1617 delete accum;
1618 spiAccumulators[port] = nullptr;
1619}
1620
1621/**
1622 * Resets the accumulator to zero.
1623 */
1624void spiResetAccumulator(uint8_t port, int32_t *status) {
1625 std::lock_guard<priority_recursive_mutex> sync(spiGetSemaphore(port));
1626 SPIAccumulator* accum = spiAccumulators[port];
1627 if (!accum) {
1628 *status = NULL_PARAMETER;
1629 return;
1630 }
1631 accum->value = 0;
1632 accum->count = 0;
1633 accum->last_value = 0;
1634}
1635
1636/**
1637 * Set the center value of the accumulator.
1638 *
1639 * The center value is subtracted from each value before it is added to the accumulator. This
1640 * is used for the center value of devices like gyros and accelerometers to make integration work
1641 * and to take the device offset into account when integrating.
1642 */
1643void spiSetAccumulatorCenter(uint8_t port, int32_t center, int32_t *status) {
1644 std::lock_guard<priority_recursive_mutex> sync(spiGetSemaphore(port));
1645 SPIAccumulator* accum = spiAccumulators[port];
1646 if (!accum) {
1647 *status = NULL_PARAMETER;
1648 return;
1649 }
1650 accum->center = center;
1651}
1652
1653/**
1654 * Set the accumulator's deadband.
1655 */
1656void spiSetAccumulatorDeadband(uint8_t port, int32_t deadband, int32_t *status) {
1657 std::lock_guard<priority_recursive_mutex> sync(spiGetSemaphore(port));
1658 SPIAccumulator* accum = spiAccumulators[port];
1659 if (!accum) {
1660 *status = NULL_PARAMETER;
1661 return;
1662 }
1663 accum->deadband = deadband;
1664}
1665
1666/**
1667 * Read the last value read by the accumulator engine.
1668 */
1669int32_t spiGetAccumulatorLastValue(uint8_t port, int32_t *status) {
1670 std::lock_guard<priority_recursive_mutex> sync(spiGetSemaphore(port));
1671 SPIAccumulator* accum = spiAccumulators[port];
1672 if (!accum) {
1673 *status = NULL_PARAMETER;
1674 return 0;
1675 }
1676 return accum->last_value;
1677}
1678
1679/**
1680 * Read the accumulated value.
1681 *
1682 * @return The 64-bit value accumulated since the last Reset().
1683 */
1684int64_t spiGetAccumulatorValue(uint8_t port, int32_t *status) {
1685 std::lock_guard<priority_recursive_mutex> sync(spiGetSemaphore(port));
1686 SPIAccumulator* accum = spiAccumulators[port];
1687 if (!accum) {
1688 *status = NULL_PARAMETER;
1689 return 0;
1690 }
1691 return accum->value;
1692}
1693
1694/**
1695 * Read the number of accumulated values.
1696 *
1697 * Read the count of the accumulated values since the accumulator was last Reset().
1698 *
1699 * @return The number of times samples from the channel were accumulated.
1700 */
1701uint32_t spiGetAccumulatorCount(uint8_t port, int32_t *status) {
1702 std::lock_guard<priority_recursive_mutex> sync(spiGetSemaphore(port));
1703 SPIAccumulator* accum = spiAccumulators[port];
1704 if (!accum) {
1705 *status = NULL_PARAMETER;
1706 return 0;
1707 }
1708 return accum->count;
1709}
1710
1711/**
1712 * Read the average of the accumulated value.
1713 *
1714 * @return The accumulated average value (value / count).
1715 */
1716double spiGetAccumulatorAverage(uint8_t port, int32_t *status) {
1717 int64_t value;
1718 uint32_t count;
1719 spiGetAccumulatorOutput(port, &value, &count, status);
1720 if (count == 0) return 0.0;
1721 return ((double)value) / count;
1722}
1723
1724/**
1725 * Read the accumulated value and the number of accumulated values atomically.
1726 *
1727 * This function reads the value and count atomically.
1728 * This can be used for averaging.
1729 *
1730 * @param value Pointer to the 64-bit accumulated output.
1731 * @param count Pointer to the number of accumulation cycles.
1732 */
1733void spiGetAccumulatorOutput(uint8_t port, int64_t *value, uint32_t *count,
1734 int32_t *status) {
1735 std::lock_guard<priority_recursive_mutex> sync(spiGetSemaphore(port));
1736 SPIAccumulator* accum = spiAccumulators[port];
1737 if (!accum) {
1738 *status = NULL_PARAMETER;
1739 *value = 0;
1740 *count = 0;
1741 return;
1742 }
1743 *value = accum->value;
1744 *count = accum->count;
1745}
1746
1747/*
1748 * Initialize the I2C port. Opens the port if necessary and saves the handle.
1749 * If opening the MXP port, also sets up the pin functions appropriately
1750 * @param port The port to open, 0 for the on-board, 1 for the MXP.
1751 */
1752void i2CInitialize(uint8_t port, int32_t *status) {
1753 initializeDigital(status);
1754
1755 if(port > 1)
1756 {
1757 //Set port out of range error here
1758 return;
1759 }
1760
1761 priority_recursive_mutex &lock = port == 0 ? digitalI2COnBoardMutex : digitalI2CMXPMutex;
1762 {
1763 std::lock_guard<priority_recursive_mutex> sync(lock);
1764 if(port == 0) {
1765 i2COnboardObjCount++;
1766 if (i2COnBoardHandle > 0) return;
1767 i2COnBoardHandle = i2clib_open("/dev/i2c-2");
1768 } else if(port == 1) {
1769 i2CMXPObjCount++;
1770 if (i2CMXPHandle > 0) return;
1771 if(!allocateDIO(getPort(24), false, status)) return;
1772 if(!allocateDIO(getPort(25), false, status)) return;
1773 digitalSystem->writeEnableMXPSpecialFunction(digitalSystem->readEnableMXPSpecialFunction(status)|0xC000, status);
1774 i2CMXPHandle = i2clib_open("/dev/i2c-1");
1775 }
1776 return;
1777 }
1778}
1779
1780/**
1781 * Generic transaction.
1782 *
1783 * This is a lower-level interface to the I2C hardware giving you more control over each transaction.
1784 *
1785 * @param dataToSend Buffer of data to send as part of the transaction.
1786 * @param sendSize Number of bytes to send as part of the transaction.
1787 * @param dataReceived Buffer to read data into.
1788 * @param receiveSize Number of bytes to read from the device.
1789 * @return Transfer Aborted... false for success, true for aborted.
1790 */
1791int32_t i2CTransaction(uint8_t port, uint8_t deviceAddress, uint8_t *dataToSend, uint8_t sendSize, uint8_t *dataReceived, uint8_t receiveSize)
1792{
1793 if(port > 1) {
1794 //Set port out of range error here
1795 return -1;
1796 }
1797
1798 int32_t handle = port == 0 ? i2COnBoardHandle:i2CMXPHandle;
1799 priority_recursive_mutex &lock = port == 0 ? digitalI2COnBoardMutex : digitalI2CMXPMutex;
1800
1801 {
1802 std::lock_guard<priority_recursive_mutex> sync(lock);
1803 return i2clib_writeread(handle, deviceAddress, (const char*) dataToSend, (int32_t) sendSize, (char*) dataReceived, (int32_t) receiveSize);
1804 }
1805}
1806
1807/**
1808 * Execute a write transaction with the device.
1809 *
1810 * Write a single byte to a register on a device and wait until the
1811 * transaction is complete.
1812 *
1813 * @param registerAddress The address of the register on the device to be written.
1814 * @param data The byte to write to the register on the device.
1815 * @return Transfer Aborted... false for success, true for aborted.
1816 */
1817int32_t i2CWrite(uint8_t port, uint8_t deviceAddress, uint8_t* dataToSend, uint8_t sendSize)
1818{
1819 if(port > 1) {
1820 //Set port out of range error here
1821 return -1;
1822 }
1823
1824 int32_t handle = port == 0 ? i2COnBoardHandle:i2CMXPHandle;
1825 priority_recursive_mutex &lock = port == 0 ? digitalI2COnBoardMutex : digitalI2CMXPMutex;
1826 {
1827 std::lock_guard<priority_recursive_mutex> sync(lock);
1828 return i2clib_write(handle, deviceAddress, (const char*) dataToSend, (int32_t) sendSize);
1829 }
1830}
1831
1832/**
1833 * Execute a read transaction with the device.
1834 *
1835 * Read bytes from a device.
1836 * Most I2C devices will auto-increment the register pointer internally allowing
1837 * you to read consecutive registers on a device in a single transaction.
1838 *
1839 * @param registerAddress The register to read first in the transaction.
1840 * @param count The number of bytes to read in the transaction.
1841 * @param buffer A pointer to the array of bytes to store the data read from the device.
1842 * @return Transfer Aborted... false for success, true for aborted.
1843 */
1844int32_t i2CRead(uint8_t port, uint8_t deviceAddress, uint8_t *buffer, uint8_t count)
1845{
1846 if(port > 1) {
1847 //Set port out of range error here
1848 return -1;
1849 }
1850
1851 int32_t handle = port == 0 ? i2COnBoardHandle:i2CMXPHandle;
1852 priority_recursive_mutex &lock = port == 0 ? digitalI2COnBoardMutex : digitalI2CMXPMutex;
1853 {
1854 std::lock_guard<priority_recursive_mutex> sync(lock);
1855 return i2clib_read(handle, deviceAddress, (char*) buffer, (int32_t) count);
1856 }
1857
1858}
1859
1860void i2CClose(uint8_t port) {
1861 if(port > 1) {
1862 //Set port out of range error here
1863 return;
1864 }
1865 priority_recursive_mutex &lock = port == 0 ? digitalI2COnBoardMutex : digitalI2CMXPMutex;
1866 {
1867 std::lock_guard<priority_recursive_mutex> sync(lock);
1868 if((port == 0 ? i2COnboardObjCount--:i2CMXPObjCount--) == 0) {
1869 int32_t handle = port == 0 ? i2COnBoardHandle:i2CMXPHandle;
1870 i2clib_close(handle);
1871 }
1872 }
1873 return;
1874}