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 }