001 /*----------------------------------------------------------------------------*/
002 /* Copyright (c) FIRST 2008-2012. All Rights Reserved. */
003 /* Open Source Software - may be modified and shared by FRC teams. The code */
004 /* must be accompanied by the FIRST BSD license file in the root directory of */
005 /* the project. */
006 /*----------------------------------------------------------------------------*/
007 package edu.wpi.first.wpilibj;
008
009 import com.sun.cldc.jna.Pointer;
010 import com.sun.cldc.jna.Structure;
011 import edu.wpi.first.wpilibj.communication.FRCControl;
012 import edu.wpi.first.wpilibj.parsing.IInputOutput;
013 import edu.wpi.first.wpilibj.util.BoundaryException;
014
015 /**
016 *
017 * @author dtjones
018 */
019 public class DriverStationEnhancedIO implements IInputOutput{
020
021 static class output_t extends Structure {
022
023 short digital = 0;
024 short digital_oe = 0;
025 short digital_pe = 0;
026 short[] pwm_compare = new short[4];
027 short[] pwm_period = new short[2];
028 byte[] dac = new byte[2];
029 byte leds = 0;
030 private byte enables = 0;
031 byte pwm_enable = 0; // :4
032 byte comparator_enable = 0; // :2
033 byte quad_index_enable = 0; // :2
034 // union
035 // {
036 // struct
037 // {
038 // // Bits are inverted from cypress fw because of big-endian!
039 // UINT8 pwm_enable : 4;
040 // UINT8 comparator_enable : 2;
041 // UINT8 quad_index_enable : 2;
042 // };
043 // UINT8 enables;
044 // };
045 byte fixed_digital_out = 0;
046
047 final static int size = 23;
048
049 output_t(Pointer backingMemory) {
050 useMemory(backingMemory);
051 }
052
053 public void setEnables(byte enablesByte) {
054 enables = enablesByte;
055 pwm_enable = (byte) ((enablesByte & (byte) 0xF0) >> 4);
056 comparator_enable = (byte) ((enablesByte & (byte) 0x0C) >> 2);
057 quad_index_enable = (byte) ((enablesByte & (byte) 0x03));
058 }
059
060 public byte getEnables() {
061 enables = (byte) (((pwm_enable << 4) & (byte) 0xF0) |
062 ((comparator_enable << 2) & (byte) 0x0C) |
063 ((quad_index_enable) & (byte) 0x03));
064 return enables;
065 }
066
067 public void read() {
068 digital = backingNativeMemory.getShort(0);
069 digital_oe = backingNativeMemory.getShort(2);
070 digital_pe = backingNativeMemory.getShort(4);
071 backingNativeMemory.getShorts(6, pwm_compare, 0, pwm_compare.length);
072 backingNativeMemory.getShorts(14, pwm_period, 0, pwm_period.length);
073 backingNativeMemory.getBytes(18, dac, 0, dac.length);
074 leds = backingNativeMemory.getByte(20);
075 setEnables(backingNativeMemory.getByte(21));
076 fixed_digital_out = backingNativeMemory.getByte(22);
077 }
078
079 public void write() {
080 backingNativeMemory.setShort(0, digital);
081 backingNativeMemory.setShort(2, digital_oe);
082 backingNativeMemory.setShort(4, digital_pe);
083 backingNativeMemory.setShorts(6, pwm_compare, 0, pwm_compare.length);
084 backingNativeMemory.setShorts(14, pwm_period, 0, pwm_period.length);
085 backingNativeMemory.setBytes(18, dac, 0, dac.length);
086 backingNativeMemory.setByte(20, leds);
087 backingNativeMemory.setByte(21, getEnables());
088 backingNativeMemory.setByte(22, fixed_digital_out);
089 }
090
091 public int size() {
092 return size;
093 }
094 } //data to IO (23 bytes)
095
096 // Dynamic block definitions
097 // END: Definitions from the Cypress firmware
098 static class input_t extends Structure {
099
100 byte api_version;
101 byte fw_version;
102 short[] analog = new short[8];
103 short digital;
104 short[] accel = new short[3];
105 short[] quad = new short[2];
106 byte buttons;
107 byte capsense_slider;
108 byte capsense_proximity;
109
110 final static int size = 33;
111
112 input_t(Pointer backingMemory) {
113 useMemory(backingMemory);
114 }
115
116 public void read() {
117 api_version = backingNativeMemory.getByte(0);
118 fw_version = backingNativeMemory.getByte(1);
119 backingNativeMemory.getShorts(2, analog, 0, analog.length);
120 digital = backingNativeMemory.getShort(18);
121 backingNativeMemory.getShorts(20, accel, 0, accel.length);
122 backingNativeMemory.getShorts(26, quad, 0, quad.length);
123 buttons = backingNativeMemory.getByte(30);
124 capsense_slider = backingNativeMemory.getByte(31);
125 capsense_proximity = backingNativeMemory.getByte(32);
126 }
127
128 public void write() {
129 backingNativeMemory.setByte(0, api_version);
130 backingNativeMemory.setByte(1, fw_version);
131 backingNativeMemory.setShorts(2, analog, 0, analog.length);
132 backingNativeMemory.setShort(18, digital);
133 backingNativeMemory.setShorts(20, accel, 0, accel.length);
134 backingNativeMemory.setShorts(26, quad, 0, quad.length);
135 backingNativeMemory.setByte(30, buttons);
136 backingNativeMemory.setByte(31, capsense_slider);
137 backingNativeMemory.setByte(32, capsense_proximity);
138 }
139
140 public int size() {
141 return size;
142 }
143 } //data from IO (33 bytes)
144
145 class status_block_t extends FRCControl.DynamicControlData {
146
147 byte size = 25; // Must be 25 (size remaining in the block not counting the size variable)
148 byte id = kOutputBlockID; // Must be 18
149 output_t data;
150 byte flags;
151
152 {
153 allocateMemory();
154 data = new output_t(
155 new Pointer(backingNativeMemory.address().toUWord().toPrimitive() + 2,
156 output_t.size));
157 }
158
159 public void read() {
160
161 size = backingNativeMemory.getByte(0);
162 id = backingNativeMemory.getByte(1);
163 data.read();
164 flags = backingNativeMemory.getByte(25);
165 }
166
167 public void write() {
168 backingNativeMemory.setByte(0, size);
169 backingNativeMemory.setByte(1, id);
170 data.write();
171 backingNativeMemory.setByte(25, flags);
172 }
173
174 public int size() {
175 return 26;
176 }
177
178 public void copy(status_block_t dest) {
179 write();
180 Pointer.copyBytes(backingNativeMemory, 0, dest.backingNativeMemory, 0, size());
181 dest.read();
182 }
183 }
184
185 class control_block_t extends FRCControl.DynamicControlData {
186
187 byte size = 34; // Must be 34
188 byte id = kInputBlockID; // Must be 17
189 input_t data;
190
191 {
192 allocateMemory();
193 data = new input_t(
194 new Pointer(backingNativeMemory.address().toUWord().toPrimitive() + 2,
195 input_t.size));
196 }
197
198 public void read() {
199 size = backingNativeMemory.getByte(0);
200 id = backingNativeMemory.getByte(1);
201 data.read();
202 }
203
204 public void write() {
205 backingNativeMemory.setByte(0, size);
206 backingNativeMemory.setByte(1, id);
207 data.write();
208 }
209
210 public int size() {
211 return 35;
212 }
213
214 public void copy(control_block_t dest) {
215 write();
216 Pointer.copyBytes(backingNativeMemory, 0, dest.backingNativeMemory, 0, size());
217 dest.read();
218 }
219 }
220
221 public static class EnhancedIOException extends Exception {
222
223 public EnhancedIOException(String msg) {
224 super(msg);
225 }
226 }
227 public static final double kAnalogInputResolution = ((double) ((1 << 14) - 1));
228 public static final double kAnalogInputReference = 3.3;
229 public static final double kAnalogOutputResolution = ((double) ((1 << 8) - 1));
230 public static final double kAnalogOutputReference = 4.0;
231 public static final double kAccelOffset = 8300;
232 public static final double kAccelScale = 3300.0;
233 public static final int kSupportedAPIVersion = 1;
234 control_block_t m_inputData;
235 status_block_t m_outputData;
236 final Object m_inputDataSemaphore;
237 final Object m_outputDataSemaphore;
238 boolean m_inputValid;
239 boolean m_outputValid;
240 boolean m_configChanged;
241 boolean m_requestEnhancedEnable;
242 short[] m_encoderOffsets = new short[2];
243
244 /**
245 * Digital configuration for enhanced IO
246 */
247 public static class tDigitalConfig {
248
249 /**
250 * The integer value representing this enumeration
251 */
252 public final int value;
253 static final int kUnknown_val = 0;
254 static final int kInputFloating_val = 1;
255 static final int kInputPullUp_val = 2;
256 static final int kInputPullDown_val = 3;
257 static final int kOutput_val = 4;
258 static final int kPWM_val = 5;
259 static final int kAnalogComparator_val = 6;
260 public static final tDigitalConfig kUnknown = new tDigitalConfig(kUnknown_val);
261 public static final tDigitalConfig kInputFloating = new tDigitalConfig(kInputFloating_val);
262 public static final tDigitalConfig kInputPullUp = new tDigitalConfig(kInputPullUp_val);
263 public static final tDigitalConfig kInputPullDown = new tDigitalConfig(kInputPullDown_val);
264 public static final tDigitalConfig kOutput = new tDigitalConfig((kOutput_val));
265 public static final tDigitalConfig kPWM = new tDigitalConfig((kPWM_val));
266 public static final tDigitalConfig kAnalogComparator = new tDigitalConfig((kAnalogComparator_val));
267
268 private tDigitalConfig(int value) {
269 this.value = value;
270 }
271 }
272
273 /**
274 * Accelerometer channel for enhanced IO
275 */
276 public static class tAccelChannel {
277
278 /**
279 * The integer value representing this enumeration
280 */
281 public final int value;
282 static final int kAccelX_val = 0;
283 static final int kAccelY_val = 1;
284 static final int kAccelZ_val = 2;
285 public static final tAccelChannel kAccelX = new tAccelChannel(kAccelX_val);
286 public static final tAccelChannel kAccelY = new tAccelChannel(kAccelY_val);
287 public static final tAccelChannel kAccelZ = new tAccelChannel(kAccelZ_val);
288
289 private tAccelChannel(int value) {
290 this.value = value;
291 }
292 }
293
294 /**
295 * PWM period channels for enhanced IO
296 */
297 public static class tPWMPeriodChannels {
298
299 /**
300 * The integer value representing this enumeration
301 */
302 public final int value;
303 static final int kPWMChannels1and2_val = 0;
304 static final int kPWMChannels3and4_val = 1;
305 public static final tPWMPeriodChannels kPWMChannels1and2 = new tPWMPeriodChannels(kPWMChannels1and2_val);
306 public static final tPWMPeriodChannels kPWMChannels3and4 = new tPWMPeriodChannels(kPWMChannels3and4_val);
307
308 private tPWMPeriodChannels(int value) {
309 this.value = value;
310 }
311 }
312 static final byte kInputBlockID = 17, kOutputBlockID = 18;
313 static final int kStatusValid = 0x01, kStatusConfigChanged = 0x02, kForceEnhancedMode = 0x04;
314
315 /**
316 * DriverStationEnhancedIO constructor.
317 *
318 * This is only called once when the DriverStation constructor is called.
319 */
320 DriverStationEnhancedIO() {
321 m_inputValid = false;
322 m_outputValid = false;
323 m_configChanged = false;
324 m_requestEnhancedEnable = false;
325 m_inputData = new control_block_t();
326 m_outputData = new status_block_t();
327 m_outputData.size = (byte) (m_outputData.size() - 1);
328 m_outputData.id = kOutputBlockID;
329 // Expected to be active low, so initialize inactive.
330 m_outputData.data.fixed_digital_out = 0x3;
331 m_inputDataSemaphore = new Object();
332 m_outputDataSemaphore = new Object();
333 m_encoderOffsets[0] = 0;
334 m_encoderOffsets[1] = 0;
335 }
336 status_block_t tempOutputData = new status_block_t();
337 control_block_t tempInputData = new control_block_t();
338
339 /**
340 * Called by the DriverStation class when data is available.
341 * This function will set any modified configuration / output,
342 * then read the input and configuration from the IO.
343 */
344 void updateData() {
345 int retVal;
346 synchronized (m_outputDataSemaphore) {
347 if (m_outputValid || m_configChanged || m_requestEnhancedEnable) {
348 m_outputData.flags = kStatusValid;
349 if (m_requestEnhancedEnable) {
350 // Someone called one of the get config APIs, but we are not in enhanced mode.
351 m_outputData.flags |= kForceEnhancedMode;
352 }
353 if (m_configChanged) {
354 if (!m_outputValid) {
355 // Someone called one of the set config APIs, but we are not in enhanced mode.
356 m_outputData.flags |= kForceEnhancedMode;
357 }
358 m_outputData.flags |= kStatusConfigChanged;
359 }
360 FRCControl.overrideIOConfig(m_outputData, 5);
361 }
362 retVal = FRCControl.getDynamicControlData(kOutputBlockID, tempOutputData, tempOutputData.size(), 5);
363 if (retVal == 0) {
364 if (m_outputValid) {
365 if (m_configChanged) {
366 // If our config change made the round trip then clear the flag.
367 if (isConfigEqual(tempOutputData, m_outputData)) {
368 m_configChanged = false;
369 }
370 } else {
371 // TODO: This won't work until artf1128 is fixed
372 //if (tempOutputData.flags & kStatusConfigChanged)
373 {
374 // Configuration was updated on the DS, so update our local cache.
375 mergeConfigIntoOutput(tempOutputData, m_outputData);
376 }
377 }
378 } else {
379 // Initialize the local cache.
380 mergeConfigIntoOutput(tempOutputData, m_outputData);
381 }
382 m_requestEnhancedEnable = false;
383 m_outputValid = true;
384 } else {
385 m_outputValid = false;
386 m_inputValid = false;
387 }
388 }
389
390 synchronized (m_inputDataSemaphore) {
391
392 retVal = FRCControl.getDynamicControlData(kInputBlockID, tempInputData, tempInputData.size(), 5);
393 if (retVal == 0 && tempInputData.data.api_version == kSupportedAPIVersion) {
394 tempInputData.copy(m_inputData);
395 m_inputValid = true;
396 } else {
397 m_outputValid = false;
398 m_inputValid = false;
399 }
400 }
401 }
402
403 /**
404 * Merge the config portion of the DS output block into the local cache.
405 */
406 void mergeConfigIntoOutput(status_block_t dsOutputBlock, status_block_t localCache) {
407 localCache.data.digital = (short) ((localCache.data.digital & dsOutputBlock.data.digital_oe) |
408 (dsOutputBlock.data.digital & ~dsOutputBlock.data.digital_oe));
409 localCache.data.digital_oe = dsOutputBlock.data.digital_oe;
410 localCache.data.digital_pe = dsOutputBlock.data.digital_pe;
411 localCache.data.pwm_period[0] = dsOutputBlock.data.pwm_period[0];
412 localCache.data.pwm_period[1] = dsOutputBlock.data.pwm_period[1];
413 localCache.data.setEnables(dsOutputBlock.data.getEnables());
414 }
415
416 /**
417 * Compare the config portion of the output blocks.
418 */
419 boolean isConfigEqual(status_block_t dsOutputBlock, status_block_t localCache) {
420 if (localCache.data.digital_oe != dsOutputBlock.data.digital_oe) {
421 return false;
422 }
423 if ((localCache.data.digital & ~dsOutputBlock.data.digital) !=
424 (dsOutputBlock.data.digital & ~dsOutputBlock.data.digital)) {
425 return false;
426 }
427 if (localCache.data.digital_pe != dsOutputBlock.data.digital_pe) {
428 return false;
429 }
430 if (localCache.data.pwm_period[0] != dsOutputBlock.data.pwm_period[0]) {
431 return false;
432 }
433 if (localCache.data.pwm_period[1] != dsOutputBlock.data.pwm_period[1]) {
434 return false;
435 }
436 if (localCache.data.getEnables() != dsOutputBlock.data.getEnables()) {
437 return false;
438 }
439 return true;
440 }
441
442 /**
443 * Query an accelerometer channel on the DS IO.
444 *
445 * @param channel The channel number to read.
446 * @return The current acceleration on the channel in Gs.
447 */
448 public double getAcceleration(tAccelChannel channel) throws EnhancedIOException {
449 if (!m_inputValid) {
450 throw new EnhancedIOException("Enhanced IO Missing");
451 }
452 synchronized (m_inputDataSemaphore) {
453 return (m_inputData.data.accel[channel.value] - kAccelOffset) / kAccelScale;
454 }
455 }
456
457 /**
458 * Query an analog input channel on the DS IO.
459 *
460 * @param channel The channel number to read. [1,8]
461 * @return The analog input voltage for the channel.
462 */
463 public double getAnalogIn(int channel) throws EnhancedIOException {
464 // 3.3V is the analog reference voltage
465 return getAnalogInRatio(channel) * kAnalogInputReference;
466 }
467
468 /**
469 * Query an analog input channel on the DS IO in ratiometric form.
470 *
471 * @param channel The channel number to read. [1,8]
472 * @return The analog input percentage for the channel.
473 */
474 public double getAnalogInRatio(int channel) throws EnhancedIOException {
475 BoundaryException.assertWithinBounds(channel, 1, 8);
476 if (!m_inputValid) {
477 throw new EnhancedIOException("Enhanced IO Missing");
478 }
479 synchronized (m_inputDataSemaphore) {
480 return m_inputData.data.analog[channel - 1] / kAnalogInputResolution;
481 }
482 }
483
484 /**
485 * Query the voltage currently being output.
486 *
487 * AO1 is pin 11 on the top connector (P2).
488 * AO2 is pin 12 on the top connector (P2).
489 *
490 * @param channel The analog output channel on the DS IO. [1,2]
491 * @return The voltage being output on the channel.
492 */
493 public double getAnalogOut(int channel) throws EnhancedIOException {
494 BoundaryException.assertWithinBounds(channel, 1, 2);
495 if (!m_outputValid) {
496 throw new EnhancedIOException("Enhanced IO Missing");
497 }
498
499 synchronized (m_outputDataSemaphore) {
500 int tempData = m_outputData.data.dac[channel - 1];
501 tempData = tempData < 0 ? tempData + 256 : tempData;
502 return tempData * kAnalogOutputReference / kAnalogOutputResolution;
503 }
504 }
505
506 /**
507 * Set the analog output voltage.
508 *
509 * AO1 is pin 11 on the top connector (P2).
510 * AO2 is pin 12 on the top connector (P2).
511 * AO1 is the reference voltage for the 2 analog comparators on DIO15 and DIO16.
512 *
513 * The output range is 0V to 4V, however due to the supply voltage don't expect more than about 3V.
514 * Current supply capability is only 100uA.
515 *
516 * @param channel The analog output channel on the DS IO. [1,2]
517 * @param value The voltage to output on the channel.
518 */
519 public void setAnalogOut(int channel, double value) throws EnhancedIOException {
520 BoundaryException.assertWithinBounds(channel, 1, 2);
521 if (!m_outputValid) {
522 throw new EnhancedIOException("Enhanced IO Missing");
523 }
524 if (value < 0.0) {
525 value = 0.0;
526 }
527 if (value > kAnalogOutputReference) {
528 value = kAnalogOutputReference;
529 }
530 if (value > kAnalogOutputReference) {
531 value = kAnalogOutputReference;
532 }
533
534 synchronized (m_outputDataSemaphore) {
535 m_outputData.data.dac[channel - 1] = (byte) (value / kAnalogOutputReference * kAnalogOutputResolution);
536 }
537 }
538
539 /**
540 * Get the state of a button on the IO board.
541 *
542 * Button1 is the physical button "S1".
543 * Button2 is pin 4 on the top connector (P2).
544 * Button3 is pin 6 on the top connector (P2).
545 * Button4 is pin 8 on the top connector (P2).
546 * Button5 is pin 10 on the top connector (P2).
547 * Button6 is pin 7 on the top connector (P2).
548 *
549 * Button2 through Button6 are Capacitive Sense buttons.
550 *
551 * @param channel The button channel to read. [1,6]
552 * @return The state of the selected button.
553 */
554 public boolean getButton(int channel) throws EnhancedIOException {
555 BoundaryException.assertWithinBounds(channel, 1, 6);
556 return ((getButtons() >> (channel - 1)) & 1) != 0;
557 }
558
559 /**
560 * Get the state of all the button channels.
561 *
562 * @return The state of the 6 button channels in the 6 lsb of the returned byte.
563 */
564 public byte getButtons() throws EnhancedIOException {
565 if (!m_inputValid) {
566 throw new EnhancedIOException("Enhanced IO Missing");
567 }
568 synchronized (m_inputDataSemaphore) {
569 return m_inputData.data.buttons;
570 }
571 }
572
573 /**
574 * Set the state of an LED on the IO board.
575 *
576 * @param channel The LED channel to set. [1,8]
577 * @param value True to turn the LED on.
578 */
579 public void setLED(int channel, boolean value) throws EnhancedIOException {
580 BoundaryException.assertWithinBounds(channel, 1, 8);
581 if (!m_outputValid) {
582 throw new EnhancedIOException("Enhanced IO Missing");
583 }
584 byte leds;
585 synchronized (m_outputDataSemaphore) {
586 leds = m_outputData.data.leds;
587
588 leds &= ~(1 << (channel - 1));
589 if (value) {
590 leds |= 1 << (channel - 1);
591 }
592
593 m_outputData.data.leds = leds;
594 }
595 }
596
597 /**
598 * Set the state of all 8 LEDs on the IO board.
599 *
600 * @param value The state of each LED. LED1 is lsb and LED8 is msb.
601 */
602 public void setLEDs(byte value) throws EnhancedIOException {
603 if (!m_outputValid) {
604 throw new EnhancedIOException("Enhanced IO Missing");
605 }
606 synchronized (m_outputDataSemaphore) {
607 m_outputData.data.leds = value;
608 }
609 }
610
611 /**
612 * Get the current state of a DIO channel regardless of mode.
613 *
614 * @param channel The DIO channel to read. [1,16]
615 * @return The state of the selected digital line.
616 */
617 public boolean getDigital(int channel) throws EnhancedIOException {
618 BoundaryException.assertWithinBounds(channel, 1, 16);
619 return ((getDigitals() >> (channel - 1)) & 1) != 0;
620 }
621
622 /**
623 * Get the state of all 16 DIO lines regardless of mode.
624 *
625 * @return The state of all DIO lines. DIO1 is lsb and DIO16 is msb.
626 */
627 public short getDigitals() throws EnhancedIOException {
628 if (!m_inputValid) {
629 throw new EnhancedIOException("Enhanced IO Missing");
630 }
631 synchronized (m_inputDataSemaphore) {
632 return m_inputData.data.digital;
633 }
634 }
635
636 /**
637 * Set the state of a DIO line that is configured for digital output.
638 *
639 * @param channel The DIO channel to set. [1,16]
640 * @param value The state to set the selected channel to.
641 */
642 public void setDigitalOutput(int channel, boolean value) throws EnhancedIOException {
643 BoundaryException.assertWithinBounds(channel, 1, 16);
644 if (!m_outputValid) {
645 throw new EnhancedIOException("Enhanced IO Missing");
646 }
647 short digital;
648 synchronized (m_outputDataSemaphore) {
649
650 if ((m_outputData.data.digital_oe & (1 << (channel - 1))) != 0) {
651 digital = m_outputData.data.digital;
652
653 digital &= ~(1 << (channel - 1));
654 if (value) {
655 digital |= 1 << (channel - 1);
656 }
657
658 m_outputData.data.digital = digital;
659 } else {
660 System.err.println("Line not configured for output");
661 }
662 }
663 }
664
665 /**
666 * Get the current configuration for a DIO line.
667 *
668 * This has the side effect of forcing the Driver Station to switch to Enhanced mode if it's not when called.
669 * If Enhanced mode is not enabled when this is called, it will return kUnknown.
670 *
671 * @param channel The DIO channel config to get. [1,16]
672 * @return The configured mode for the DIO line.
673 */
674 public tDigitalConfig getDigitalConfig(int channel) throws EnhancedIOException {
675 BoundaryException.assertWithinBounds(channel, 1, 16);
676 if (!m_outputValid) {
677 m_requestEnhancedEnable = true;
678 throw new EnhancedIOException("Enhanced IO Missing");
679 }
680 synchronized (m_outputDataSemaphore) {
681 if ((channel >= 1) && (channel <= 4)) {
682 if ((m_outputData.data.pwm_enable & (1 << (channel - 1))) != 0) {
683 return tDigitalConfig.kPWM;
684 }
685 }
686 if ((channel >= 15) && (channel <= 16)) {
687 if ((m_outputData.data.comparator_enable & (1 << (channel - 15))) != 0) {
688 return tDigitalConfig.kAnalogComparator;
689 }
690 }
691 if ((m_outputData.data.digital_oe & (1 << (channel - 1))) != 0) {
692 return tDigitalConfig.kOutput;
693 }
694 if ((m_outputData.data.digital_pe & (1 << (channel - 1))) == 0) {
695 return tDigitalConfig.kInputFloating;
696 }
697 if ((m_outputData.data.digital & (1 << (channel - 1))) != 0) {
698 return tDigitalConfig.kInputPullUp;
699 } else {
700 return tDigitalConfig.kInputPullDown;
701 }
702 }
703 }
704
705 /**
706 * Override the DS's configuration of a DIO line.
707 *
708 * If configured to kInputFloating, the selected DIO line will be tri-stated with no internal pull resistor.
709 *
710 * If configured to kInputPullUp, the selected DIO line will be tri-stated with a 5k-Ohm internal pull-up resistor enabled.
711 *
712 * If configured to kInputPullDown, the selected DIO line will be tri-stated with a 5k-Ohm internal pull-down resistor enabled.
713 *
714 * If configured to kOutput, the selected DIO line will actively drive to 0V or Vddio (specified by J1 and J4).
715 * DIO1 through DIO12, DIO15, and DIO16 can source 4mA and can sink 8mA.
716 * DIO12 and DIO13 can source 4mA and can sink 25mA.
717 *
718 * In addition to the common configurations, DIO1 through DIO4 can be configured to kPWM to enable PWM output.
719 *
720 * In addition to the common configurations, DIO15 and DIO16 can be configured to kAnalogComparator to enable
721 * analog comparators on those 2 DIO lines. When enabled, the lines are tri-stated and will accept analog voltages
722 * between 0V and 3.3V. If the input voltage is greater than the voltage output by AO1, the DIO will read as true,
723 * if less then false.
724 *
725 * @param channel The DIO line to configure. [1,16]
726 * @param config The mode to put the DIO line in.
727 */
728 public void setDigitalConfig(int channel, tDigitalConfig config) throws EnhancedIOException {
729 BoundaryException.assertWithinBounds(channel, 1, 16);
730 if (config == tDigitalConfig.kPWM && ((channel > 4) || (channel < 1))) {
731 throw new EnhancedIOException("PWM channels must be between 1 and 4");
732 }
733 if (config == tDigitalConfig.kAnalogComparator && ((channel < 15) || (channel > 16))) {
734 throw new EnhancedIOException("Analog comparator channels must be between 15 and 16");
735 }
736
737 synchronized (m_outputDataSemaphore) {
738 m_configChanged = true;
739
740 if ((channel >= 1) && (channel <= 4)) {
741 if (config == tDigitalConfig.kPWM) {
742 m_outputData.data.pwm_enable |= 1 << (channel - 1);
743 m_outputData.data.digital &= ~(1 << (channel - 1));
744 m_outputData.data.digital_oe |= 1 << (channel - 1);
745 m_outputData.data.digital_pe &= ~(1 << (channel - 1));
746 return;
747 } else {
748 m_outputData.data.pwm_enable &= ~(1 << (channel - 1));
749 }
750 } else if ((channel >= 15) && (channel <= 16)) {
751 if (config == tDigitalConfig.kAnalogComparator) {
752 m_outputData.data.comparator_enable |= 1 << (channel - 15);
753 m_outputData.data.digital &= ~(1 << (channel - 1));
754 m_outputData.data.digital_oe &= ~(1 << (channel - 1));
755 m_outputData.data.digital_pe &= ~(1 << (channel - 1));
756 return;
757 } else {
758 m_outputData.data.comparator_enable &= ~(1 << (channel - 15));
759 }
760 }
761 if (config == tDigitalConfig.kInputFloating) {
762 m_outputData.data.digital &= ~(1 << (channel - 1));
763 m_outputData.data.digital_oe &= ~(1 << (channel - 1));
764 m_outputData.data.digital_pe &= ~(1 << (channel - 1));
765 } else if (config == tDigitalConfig.kInputPullUp) {
766 m_outputData.data.digital |= 1 << (channel - 1);
767 m_outputData.data.digital_oe &= ~(1 << (channel - 1));
768 m_outputData.data.digital_pe |= 1 << (channel - 1);
769 } else if (config == tDigitalConfig.kInputPullDown) {
770 m_outputData.data.digital &= ~(1 << (channel - 1));
771 m_outputData.data.digital_oe &= ~(1 << (channel - 1));
772 m_outputData.data.digital_pe |= 1 << (channel - 1);
773 } else if (config == tDigitalConfig.kOutput) {
774 m_outputData.data.digital_oe |= 1 << (channel - 1);
775 m_outputData.data.digital_pe &= ~(1 << (channel - 1));
776 } else {
777 // Something went wrong.
778 }
779 }
780 }
781
782 /**
783 * Get the period of a PWM generator.
784 *
785 * This has the side effect of forcing the Driver Station to switch to Enhanced mode if it's not when called.
786 * If Enhanced mode is not enabled when this is called, it will return 0.
787 *
788 * @param channels Select the generator by specifying the two channels to which it is connected.
789 * @return The period of the PWM generator in seconds.
790 */
791 public double getPWMPeriod(tPWMPeriodChannels channels) throws EnhancedIOException {
792 if (!m_outputValid) {
793 m_requestEnhancedEnable = true;
794 throw new EnhancedIOException("Enhanced IO Missing");
795 }
796 synchronized (m_outputDataSemaphore) {
797 int tempData = m_outputData.data.pwm_period[channels.value] & 0xFFFF;
798 return tempData / 24000000.0;
799 }
800 }
801
802 /**
803 * Set the period of a PWM generator.
804 *
805 * There are 2 PWM generators on the IO board. One can generate PWM signals on DIO1 and DIO2,
806 * the other on DIO3 and DIO4. Each generator has one counter and two compare registers. As such,
807 * each pair of PWM outputs share the output period but have independent duty cycles.
808 *
809 * @param channels Select the generator by specifying the two channels to which it is connected.
810 * @param period The period of the PWM generator in seconds. [0.0,0.002731]
811 */
812 public void setPWMPeriod(tPWMPeriodChannels channels, double period) throws EnhancedIOException {
813 // Convert to ticks based on the IO board's 24MHz clock
814 double ticks = period * 24000000.0;
815 // Limit the range of the ticks... warn if too big.
816 if (ticks > 65534.0) {
817 ticks = 65534.0;
818 throw new EnhancedIOException("Enhanced IO PWM Period Out of Range");
819 } else if (ticks < 0.0) {
820 ticks = 0.0;
821 }
822 // Preserve the duty cycles.
823 double[] dutyCycles = new double[2];
824 dutyCycles[0] = getPWMOutput((channels.value << 1) + 1);
825 dutyCycles[1] = getPWMOutput((channels.value << 1) + 2);
826 synchronized (m_outputDataSemaphore) {
827 // Update the period
828 m_outputData.data.pwm_period[channels.value] = (short) ticks;
829 m_configChanged = true;
830 }
831 // Restore the duty cycles
832 setPWMOutput((channels.value << 1) + 1, dutyCycles[0]);
833 setPWMOutput((channels.value << 1) + 2, dutyCycles[1]);
834 }
835
836 /**
837 * Get the state being output on a fixed digital output.
838 *
839 * @param channel The FixedDO line to get. [1,2]
840 * @return The state of the FixedDO line.
841 */
842 public boolean getFixedDigitalOutput(int channel) throws EnhancedIOException {
843 BoundaryException.assertWithinBounds(channel, 1, 2);
844 if (!m_outputValid) {
845 throw new EnhancedIOException("Enhanced IO Missing");
846 }
847 synchronized (m_outputDataSemaphore) {
848 return ((m_outputData.data.fixed_digital_out >> (channel - 1)) & 1) != 0;
849 }
850 }
851
852 /**
853 * Set the state to output on a Fixed High Current Digital Output line.
854 *
855 * FixedDO1 is pin 5 on the top connector (P2).
856 * FixedDO2 is pin 3 on the top connector (P2).
857 *
858 * The FixedDO lines always output 0V and 3.3V regardless of J1 and J4.
859 * They can source 4mA and can sink 25mA. Because of this, they are expected to be used
860 * in an active low configuration, such as connecting to the cathode of a bright LED.
861 * Because they are expected to be active low, they default to true.
862 *
863 * @param channel The FixedDO channel to set.
864 * @param value The state to set the FixedDO.
865 */
866 public void setFixedDigitalOutput(int channel, boolean value) throws EnhancedIOException {
867 BoundaryException.assertWithinBounds(channel, 1, 2);
868 if (!m_outputValid) {
869 throw new EnhancedIOException("Enhanced IO Missing");
870 }
871 byte digital;
872 synchronized (m_outputDataSemaphore) {
873 digital = m_outputData.data.fixed_digital_out;
874
875 digital &= ~(1 << (channel - 1));
876 if (value) {
877 digital |= 1 << (channel - 1);
878 }
879
880 m_outputData.data.fixed_digital_out = digital;
881 }
882 }
883
884 /**
885 * Get the position of a quadrature encoder.
886 *
887 * There are two signed 16-bit 4X quadrature decoders on the IO board. These decoders are always monitoring
888 * the state of the lines assigned to them, but these lines do not have to be used for encoders.
889 *
890 * Encoder1 uses DIO4 for "A", DIO6 for "B", and DIO8 for "Index".
891 * Encoder2 uses DIO5 for "A", DIO7 for "B", and DIO9 for "Index".
892 *
893 * The index functionality can be enabled or disabled using SetEncoderIndexEnable().
894 *
895 * @param encoderNumber The quadrature encoder to access. [1,2]
896 * @return The current position of the quadrature encoder.
897 */
898 public short getEncoder(int encoderNumber) throws EnhancedIOException {
899 BoundaryException.assertWithinBounds(encoderNumber, 1, 2);
900 if (!m_inputValid) {
901 throw new EnhancedIOException("Enhanced IO Missing");
902 }
903 synchronized (m_inputDataSemaphore) {
904 return (short) (m_inputData.data.quad[encoderNumber - 1] - m_encoderOffsets[encoderNumber - 1]);
905 }
906 }
907
908 /**
909 * Reset the position of an encoder to 0.
910 *
911 * This simply stores an offset locally. It does not reset the hardware counter on the IO board.
912 * If you use this method with Index enabled, you may get unexpected results.
913 *
914 * @param encoderNumber The quadrature encoder to reset. [1,2]
915 */
916 public void resetEncoder(int encoderNumber) throws EnhancedIOException {
917 BoundaryException.assertWithinBounds(encoderNumber, 1, 2);
918 if (!m_inputValid) {
919 throw new EnhancedIOException("Enhanced IO Missing");
920 }
921 synchronized (m_inputDataSemaphore) {
922 m_encoderOffsets[encoderNumber - 1] = m_inputData.data.quad[encoderNumber - 1];
923 }
924 }
925
926 /**
927 * Get the current configuration of a quadrature encoder index channel.
928 *
929 * This has the side effect of forcing the Driver Station to switch to Enhanced mode if it's not when called.
930 * If Enhanced mode is not enabled when this is called, it will return false.
931 *
932 * @param encoderNumber The quadrature encoder. [1,2]
933 * @return Is the index channel of the encoder enabled.
934 */
935 public boolean getEncoderIndexEnable(int encoderNumber) throws EnhancedIOException {
936 BoundaryException.assertWithinBounds(encoderNumber, 1, 2);
937 if (!m_outputValid) {
938 m_requestEnhancedEnable = true;
939 throw new EnhancedIOException("Enhanced IO Missing");
940 }
941 synchronized (m_outputDataSemaphore) {
942 return ((m_outputData.data.quad_index_enable >> (encoderNumber - 1)) & 1) != 0;
943 }
944 }
945
946 /**
947 * Enable or disable the index channel of a quadrature encoder.
948 *
949 * The quadrature decoders on the IO board support an active-low index input.
950 *
951 * Encoder1 uses DIO8 for "Index".
952 * Encoder2 uses DIO9 for "Index".
953 *
954 * When enabled, the decoder's counter will be reset to 0 when A, B, and Index are all low.
955 *
956 * @param encoderNumber The quadrature encoder. [1,2]
957 * @param enable If true, reset the encoder in an index condition.
958 */
959 public void setEncoderIndexEnable(int encoderNumber, boolean enable) {
960 BoundaryException.assertWithinBounds(encoderNumber, 1, 2);
961 synchronized (m_outputDataSemaphore) {
962 m_outputData.data.quad_index_enable &= ~(1 << (encoderNumber - 1));
963 if (enable) {
964 m_outputData.data.quad_index_enable |= 1 << (encoderNumber - 1);
965 }
966 m_configChanged = true;
967 }
968 }
969
970 /**
971 * Get the value of the Capacitive Sense touch slider.
972 *
973 * @return Value between 0.0 (toward center of board) and 1.0 (toward edge of board). -1.0 means no touch detected.
974 */
975 public double getTouchSlider() throws EnhancedIOException {
976 if (!m_inputValid) {
977 throw new EnhancedIOException("Enhanced IO Missing");
978 }
979 synchronized (m_inputDataSemaphore) {
980 byte rawValue = m_inputData.data.capsense_slider;
981 int value = rawValue < 0 ? rawValue + 256 : rawValue;
982 return value == 255 ? -1.0 : value / 254.0;
983 }
984 }
985
986 /**
987 * Get the percent duty-cycle that the PWM generator channel is configured to output.
988 *
989 * @param channel The DIO line's PWM generator to get the duty-cycle from. [1,4]
990 * @return The percent duty-cycle being output (if the DIO line is configured for PWM). [0.0,1.0]
991 */
992 public double getPWMOutput(int channel) throws EnhancedIOException {
993 BoundaryException.assertWithinBounds(channel, 1, 4);
994 if (!m_outputValid) {
995 throw new EnhancedIOException("Enhanced IO Missing");
996 }
997 synchronized (m_outputDataSemaphore) {
998 int tempCompare = m_outputData.data.pwm_compare[channel - 1] & 0xFFFF;
999 int tempPeriod = m_outputData.data.pwm_period[(channel - 1) >> 1] & 0xFFFF;
1000 return (double) tempCompare / (double) tempPeriod;
1001 }
1002 }
1003
1004 /**
1005 * Set the percent duty-cycle to output on a PWM enabled DIO line.
1006 *
1007 * DIO1 through DIO4 have the ability to output a PWM signal. The period of the
1008 * signal can be configured in pairs using SetPWMPeriod().
1009 *
1010 * @param channel The DIO line's PWM generator to set. [1,4]
1011 * @param value The percent duty-cycle to output from the PWM generator. [0.0,1.0]
1012 */
1013 public void setPWMOutput(int channel, double value) throws EnhancedIOException {
1014 BoundaryException.assertWithinBounds(channel, 1, 4);
1015 if (!m_outputValid) {
1016 throw new EnhancedIOException("Enhanced IO Missing");
1017 }
1018 if (value > 1.0) {
1019 value = 1.0;
1020 } else if (value < 0.0) {
1021 value = 0.0;
1022 }
1023 synchronized (m_outputDataSemaphore) {
1024 m_outputData.data.pwm_compare[channel - 1] = (short) (value * (double) m_outputData.data.pwm_period[(channel - 1) >> 1]);
1025 }
1026 }
1027
1028 /**
1029 * Get the firmware version running on the IO board.
1030 *
1031 * This also has the side effect of forcing the driver station to switch to Enhanced mode if it is not.
1032 * If you plan to switch between Driver Stations with unknown IO configurations, you can call this
1033 * until it returns a non-0 version to ensure that this API is accessible before proceeding.
1034 *
1035 * @return The version of the firmware running on the IO board. 0 if the board is not attached or not in Enhanced mode.
1036 */
1037 public byte getFirmwareVersion() throws EnhancedIOException {
1038 if (!m_inputValid) {
1039 m_requestEnhancedEnable = true;
1040 throw new EnhancedIOException("Enhanced IO Missing");
1041 }
1042 synchronized (m_inputDataSemaphore) {
1043 return m_inputData.data.fw_version;
1044 }
1045 }
1046 }