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