001/*----------------------------------------------------------------------------*/ 002/* Copyright (c) FIRST 2008-2014. 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 008package edu.wpi.first.wpilibj; 009 010import java.nio.ByteBuffer; 011import java.nio.ByteOrder; 012 013import edu.wpi.first.wpilibj.can.CANExceptionFactory; 014import edu.wpi.first.wpilibj.can.CANJNI; 015import edu.wpi.first.wpilibj.can.CANMessageNotFoundException; 016import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable; 017import edu.wpi.first.wpilibj.tables.ITable; 018import edu.wpi.first.wpilibj.tables.ITableListener; 019import edu.wpi.first.wpilibj.util.AllocationException; 020import edu.wpi.first.wpilibj.util.CheckedAllocationException; 021 022/** 023 * Texas Instruments Jaguar Speed Controller as a CAN device. 024 * @author Thomas Clark 025 */ 026public class CANJaguar implements MotorSafety, PIDOutput, SpeedController, LiveWindowSendable { 027 028 public static final int kMaxMessageDataSize = 8; 029 030 // The internal PID control loop in the Jaguar runs at 1kHz. 031 public static final int kControllerRate = 1000; 032 public static final double kApproxBusVoltage = 12.0; 033 034 private MotorSafetyHelper m_safetyHelper; 035 private static final Resource allocated = new Resource(63); 036 037 private static final int kFullMessageIDMask = CANJNI.CAN_MSGID_API_M | CANJNI.CAN_MSGID_MFR_M | CANJNI.CAN_MSGID_DTYPE_M; 038 private static final int kSendMessagePeriod = 20; 039 040 // Control Mode tags 041 private static class EncoderTag {}; 042 /** Sets an encoder as the speed reference only. <br> Passed as the "tag" when setting the control mode.*/ 043 public final static EncoderTag kEncoder = new EncoderTag(); 044 045 private static class QuadEncoderTag {}; 046 /** Sets a quadrature encoder as the position and speed reference. <br> Passed as the "tag" when setting the control mode.*/ 047 public final static QuadEncoderTag kQuadEncoder = new QuadEncoderTag(); 048 049 private static class PotentiometerTag {}; 050 /** Sets a potentiometer as the position reference only. <br> Passed as the "tag" when setting the control mode. */ 051 public final static PotentiometerTag kPotentiometer = new PotentiometerTag(); 052 053 054 /** 055 * Mode determines how the Jaguar is controlled, used internally. 056 */ 057 public enum ControlMode { 058 PercentVbus, Current, Speed, Position, Voltage; 059 } 060 061 public static final int kCurrentFault = 1; 062 public static final int kTemperatureFault = 2; 063 public static final int kBusVoltageFault = 4; 064 public static final int kGateDriverFault = 8; 065 066 /** 067 * Limit switch masks 068 */ 069 public static final int kForwardLimit = 1; 070 public static final int kReverseLimit = 2; 071 072 /** 073 * Determines how the Jaguar behaves when sending a zero signal. 074 */ 075 public enum NeutralMode { 076 /** Use the NeutralMode that is set by the jumper wire on the CAN device */ 077 Jumper((byte)0), 078 /** Stop the motor's rotation by applying a force. */ 079 Brake((byte)1), 080 /** Do not attempt to stop the motor. Instead allow it to coast to a stop without applying resistance. */ 081 Coast((byte)2); 082 083 public byte value; 084 085 public static NeutralMode valueOf(byte value) { 086 for(NeutralMode mode : values()) { 087 if(mode.value == value) { 088 return mode; 089 } 090 } 091 092 return null; 093 } 094 095 private NeutralMode(byte value) { 096 this.value = value; 097 } 098 } 099 100 101 /** 102 * Determines which sensor to use for position reference. 103 * Limit switches will always be used to limit the rotation. This can not be disabled. 104 */ 105 public enum LimitMode { 106 /** 107 * Disables the soft position limits and only uses the limit switches to limit rotation. 108 * @see CANJaguar#getForwardLimitOK() 109 * @see CANJaguar#getReverseLimitOK() 110 */ 111 SwitchInputsOnly((byte)0), 112 /** 113 * Enables the soft position limits on the Jaguar. 114 * These will be used in addition to the limit switches. This does not disable the behavior 115 * of the limit switch input. 116 * @see CANJaguar#configSoftPositionLimits(double, double) 117 */ 118 SoftPositionLimits((byte)1); 119 120 public byte value; 121 122 public static LimitMode valueOf(byte value) { 123 for(LimitMode mode : values()) { 124 if(mode.value == value) { 125 return mode; 126 } 127 } 128 129 return null; 130 } 131 132 private LimitMode(byte value) { 133 this.value = value; 134 } 135 } 136 137 /** 138 * Constructor for the CANJaguar device.<br> 139 * By default the device is configured in Percent mode. 140 * The control mode can be changed by calling one of the control modes listed below. 141 * 142 * @param deviceNumber The address of the Jaguar on the CAN bus. 143 * @see CANJaguar#setCurrentMode(double, double, double) 144 * @see CANJaguar#setCurrentMode(PotentiometerTag, double, double, double) 145 * @see CANJaguar#setCurrentMode(EncoderTag, int, double, double, double) 146 * @see CANJaguar#setCurrentMode(QuadEncoderTag, int, double, double, double) 147 * @see CANJaguar#setPercentMode() 148 * @see CANJaguar#setPercentMode(PotentiometerTag) 149 * @see CANJaguar#setPercentMode(EncoderTag, int) 150 * @see CANJaguar#setPercentMode(QuadEncoderTag, int) 151 * @see CANJaguar#setPositionMode(PotentiometerTag, double, double, double) 152 * @see CANJaguar#setPositionMode(QuadEncoderTag, int, double, double, double) 153 * @see CANJaguar#setSpeedMode(EncoderTag, int, double, double, double) 154 * @see CANJaguar#setSpeedMode(QuadEncoderTag, int, double, double, double) 155 * @see CANJaguar#setVoltageMode() 156 * @see CANJaguar#setVoltageMode(PotentiometerTag) 157 * @see CANJaguar#setVoltageMode(EncoderTag, int) 158 * @see CANJaguar#setVoltageMode(QuadEncoderTag, int) 159 */ 160 public CANJaguar(int deviceNumber) { 161 try { 162 allocated.allocate(deviceNumber-1); 163 } catch (CheckedAllocationException e1) { 164 throw new AllocationException( 165 "CANJaguar device " + e1.getMessage() + "(increment index by one)"); 166 } 167 168 m_deviceNumber = (byte)deviceNumber; 169 m_controlMode = ControlMode.PercentVbus; 170 171 m_safetyHelper = new MotorSafetyHelper(this); 172 173 boolean receivedFirmwareVersion = false; 174 byte[] data = new byte[8]; 175 176 // Request firmware and hardware version only once 177 requestMessage(CANJNI.CAN_IS_FRAME_REMOTE | CANJNI.CAN_MSGID_API_FIRMVER); 178 requestMessage(CANJNI.LM_API_HWVER); 179 180 // Wait until we've gotten all of the status data at least once. 181 for(int i = 0; i < kReceiveStatusAttempts; i++) 182 { 183 Timer.delay(0.001); 184 185 setupPeriodicStatus(); 186 updatePeriodicStatus(); 187 188 if(!receivedFirmwareVersion) { 189 try { 190 getMessage(CANJNI.CAN_MSGID_API_FIRMVER, CANJNI.CAN_MSGID_FULL_M, data); 191 m_firmwareVersion = unpackINT32(data); 192 receivedFirmwareVersion = true; 193 } catch(CANMessageNotFoundException e) {} 194 } 195 196 if(m_receivedStatusMessage0 && 197 m_receivedStatusMessage1 && 198 m_receivedStatusMessage2 && 199 receivedFirmwareVersion) { 200 break; 201 } 202 } 203 204 if(!m_receivedStatusMessage0 || 205 !m_receivedStatusMessage1 || 206 !m_receivedStatusMessage2 || 207 !receivedFirmwareVersion) { 208 /* Free the resource */ 209 free(); 210 throw new CANMessageNotFoundException(); 211 } 212 213 try { 214 getMessage(CANJNI.LM_API_HWVER, CANJNI.CAN_MSGID_FULL_M, data); 215 m_hardwareVersion = data[0]; 216 } catch(CANMessageNotFoundException e) { 217 // Not all Jaguar firmware reports a hardware version. 218 m_hardwareVersion = 0; 219 } 220 221 // 3330 was the first shipping RDK firmware version for the Jaguar 222 if (m_firmwareVersion >= 3330 || m_firmwareVersion < 108) 223 { 224 if (m_firmwareVersion < 3330) 225 { 226 DriverStation.reportError("Jag " + m_deviceNumber + " firmware " + m_firmwareVersion + " is too old (must be at least version 108 of the FIRST approved firmware)", false); 227 } 228 else 229 { 230 DriverStation.reportError("Jag" + m_deviceNumber + " firmware " + m_firmwareVersion + " is not FIRST approved (must be at least version 108 of the FIRST approved firmware)", false); 231 } 232 return; 233 } 234 } 235 236 /** 237 * Cancel periodic messages to the Jaguar, effectively disabling it. 238 * No other methods should be called after this is called. 239 */ 240 public void free() { 241 allocated.free(m_deviceNumber-1); 242 m_safetyHelper = null; 243 244 ByteBuffer status = ByteBuffer.allocateDirect(4); 245 status.order(ByteOrder.LITTLE_ENDIAN); 246 status.asIntBuffer().put(0, 0); 247 248 int messageID ; 249 250 // Disable periodic setpoints 251 switch(m_controlMode) { 252 case PercentVbus: 253 messageID = m_deviceNumber | CANJNI.LM_API_VOLT_T_SET; 254 break; 255 256 case Speed: 257 messageID = m_deviceNumber | CANJNI.LM_API_SPD_T_SET; 258 break; 259 260 case Position: 261 messageID = m_deviceNumber | CANJNI.LM_API_POS_T_SET; 262 break; 263 264 case Current: 265 messageID = m_deviceNumber | CANJNI.LM_API_ICTRL_T_SET; 266 break; 267 268 case Voltage: 269 messageID = m_deviceNumber | CANJNI.LM_API_VCOMP_T_SET; 270 break; 271 272 default: 273 return; 274 } 275 276 CANJNI.FRCNetworkCommunicationCANSessionMuxSendMessage(messageID, null, 277 CANJNI.CAN_SEND_PERIOD_STOP_REPEATING, status.asIntBuffer()); 278 279 configMaxOutputVoltage(kApproxBusVoltage); 280 } 281 282 /** 283 * @return The CAN ID passed in the constructor 284 */ 285 int getDeviceNumber() 286 { 287 return m_deviceNumber; 288 } 289 290 /** 291 * Get the recently set outputValue set point. 292 * 293 * The scale and the units depend on the mode the Jaguar is in.<br> 294 * In percentVbus mode, the outputValue is from -1.0 to 1.0 (same as PWM Jaguar).<br> 295 * In voltage mode, the outputValue is in volts.<br> 296 * In current mode, the outputValue is in amps.<br> 297 * In speed mode, the outputValue is in rotations/minute.<br> 298 * In position mode, the outputValue is in rotations.<br> 299 * 300 * @return The most recently set outputValue set point. 301 */ 302 @Override 303 public double get() { 304 return m_value; 305 } 306 307 /** 308 * Sets the output set-point value. 309 * 310 * The scale and the units depend on the mode the Jaguar is in.<br> 311 * In percentVbus Mode, the outputValue is from -1.0 to 1.0 (same as PWM Jaguar).<br> 312 * In voltage Mode, the outputValue is in volts. <br> 313 * In current Mode, the outputValue is in amps.<br> 314 * In speed mode, the outputValue is in rotations/minute.<br> 315 * In position Mode, the outputValue is in rotations. 316 * 317 * @param outputValue The set-point to sent to the motor controller. 318 * @param syncGroup The update group to add this set() to, pending UpdateSyncGroup(). If 0, update immediately. 319 */ 320 @Override 321 public void set(double outputValue, byte syncGroup) { 322 int messageID; 323 byte[] data = new byte[8]; 324 byte dataSize; 325 326 if(m_controlEnabled) { 327 switch(m_controlMode) { 328 case PercentVbus: 329 messageID = CANJNI.LM_API_VOLT_T_SET; 330 dataSize = packPercentage(data, outputValue); 331 break; 332 333 case Speed: 334 messageID = CANJNI.LM_API_SPD_T_SET; 335 dataSize = packFXP16_16(data, outputValue); 336 break; 337 338 case Position: 339 messageID = CANJNI.LM_API_POS_T_SET; 340 dataSize = packFXP16_16(data, outputValue); 341 break; 342 343 case Current: 344 messageID = CANJNI.LM_API_ICTRL_T_SET; 345 dataSize = packFXP8_8(data, outputValue); 346 break; 347 348 349 case Voltage: 350 messageID = CANJNI.LM_API_VCOMP_T_SET; 351 dataSize = packFXP8_8(data, outputValue); 352 break; 353 354 default: 355 return; 356 } 357 358 if(syncGroup != 0) { 359 data[dataSize++] = syncGroup; 360 } 361 362 sendMessage(messageID, data, dataSize, kSendMessagePeriod); 363 364 if(m_safetyHelper != null) m_safetyHelper.feed(); 365 } 366 367 m_value = outputValue; 368 369 verify(); 370 } 371 372 /** 373 * Sets the output set-point value. 374 * 375 * The scale and the units depend on the mode the Jaguar is in.<br> 376 * In percentVbus mode, the outputValue is from -1.0 to 1.0 (same as PWM Jaguar).<br> 377 * In voltage mode, the outputValue is in volts. <br> 378 * In current mode, the outputValue is in amps. <br> 379 * In speed mode, the outputValue is in rotations/minute.<br> 380 * In position mode, the outputValue is in rotations. 381 * 382 * @param value 383 * The set-point to sent to the motor controller. 384 */ 385 @Override 386 public void set(double value) { 387 set(value, (byte)0); 388 } 389 390 /** 391 * Check all unverified params and make sure they're equal to their local 392 * cached versions. If a value isn't available, it gets requested. If a value 393 * doesn't match up, it gets set again. 394 */ 395 protected void verify() { 396 byte[] data = new byte[8]; 397 398 // If the Jaguar lost power, everything should be considered unverified 399 try { 400 getMessage(CANJNI.LM_API_STATUS_POWER, CANJNI.CAN_MSGID_FULL_M, data); 401 boolean powerCycled = data[0] != 0; 402 403 if(powerCycled) { 404 // Clear the power cycled bit 405 data[0] = 1; 406 sendMessage(CANJNI.LM_API_STATUS_POWER, data, 1); 407 408 // Mark everything as unverified 409 m_controlModeVerified = false; 410 m_speedRefVerified = false; 411 m_posRefVerified = false; 412 m_neutralModeVerified = false; 413 m_encoderCodesPerRevVerified = false; 414 m_potentiometerTurnsVerified = false; 415 m_forwardLimitVerified = false; 416 m_reverseLimitVerified = false; 417 m_limitModeVerified = false; 418 m_maxOutputVoltageVerified = false; 419 m_faultTimeVerified = false; 420 421 if(m_controlMode == ControlMode.PercentVbus || m_controlMode == ControlMode.Voltage) { 422 m_voltageRampRateVerified = false; 423 } 424 else { 425 m_pVerified = false; 426 m_iVerified = false; 427 m_dVerified = false; 428 } 429 430 // Verify periodic status messages again 431 m_receivedStatusMessage0 = false; 432 m_receivedStatusMessage1 = false; 433 m_receivedStatusMessage2 = false; 434 435 // Remove any old values from netcomms. Otherwise, parameters 436 // are incorrectly marked as verified based on stale messages. 437 int[] messages = new int[] { 438 CANJNI.LM_API_SPD_REF, CANJNI.LM_API_POS_REF, 439 CANJNI.LM_API_SPD_PC, CANJNI.LM_API_POS_PC, 440 CANJNI.LM_API_ICTRL_PC, CANJNI.LM_API_SPD_IC, 441 CANJNI.LM_API_POS_IC, CANJNI.LM_API_ICTRL_IC, 442 CANJNI.LM_API_SPD_DC, CANJNI.LM_API_POS_DC, 443 CANJNI.LM_API_ICTRL_DC, CANJNI.LM_API_CFG_ENC_LINES, 444 CANJNI.LM_API_CFG_POT_TURNS, CANJNI.LM_API_CFG_BRAKE_COAST, 445 CANJNI.LM_API_CFG_LIMIT_MODE, CANJNI.LM_API_CFG_LIMIT_REV, 446 CANJNI.LM_API_CFG_MAX_VOUT, CANJNI.LM_API_VOLT_SET_RAMP, 447 CANJNI.LM_API_VCOMP_COMP_RAMP, CANJNI.LM_API_CFG_FAULT_TIME, 448 CANJNI.LM_API_CFG_LIMIT_FWD 449 }; 450 451 for(int message : messages) { 452 try { 453 getMessage(message, CANJNI.CAN_MSGID_FULL_M, data); 454 } catch (CANMessageNotFoundException e) {} 455 } 456 } 457 } catch(CANMessageNotFoundException e) { 458 requestMessage(CANJNI.LM_API_STATUS_POWER); 459 } 460 461 // Verify that any recently set parameters are correct 462 if(!m_controlModeVerified && m_controlEnabled) { 463 try { 464 getMessage(CANJNI.LM_API_STATUS_CMODE, CANJNI.CAN_MSGID_FULL_M, data); 465 466 ControlMode mode = ControlMode.values()[data[0]]; 467 468 if(m_controlMode == mode) { 469 m_controlModeVerified = true; 470 } else { 471 // Enable control again to resend the control mode 472 enableControl(); 473 } 474 } catch(CANMessageNotFoundException e) { 475 // Verification is needed but not available - request it again. 476 requestMessage(CANJNI.LM_API_STATUS_CMODE); 477 } 478 } 479 480 if(!m_speedRefVerified) { 481 try { 482 getMessage(CANJNI.LM_API_SPD_REF, CANJNI.CAN_MSGID_FULL_M, data); 483 484 int speedRef = data[0]; 485 486 if(m_speedReference == speedRef) { 487 m_speedRefVerified = true; 488 } else { 489 // It's wrong - set it again 490 setSpeedReference(m_speedReference); 491 } 492 } catch(CANMessageNotFoundException e) { 493 // Verification is needed but not available - request it again. 494 requestMessage(CANJNI.LM_API_SPD_REF); 495 } 496 } 497 498 if(!m_posRefVerified) { 499 try { 500 getMessage(CANJNI.LM_API_POS_REF, CANJNI.CAN_MSGID_FULL_M, data); 501 502 int posRef = data[0]; 503 504 if(m_positionReference == posRef) { 505 m_posRefVerified = true; 506 } else { 507 // It's wrong - set it again 508 setPositionReference(m_positionReference); 509 } 510 } catch(CANMessageNotFoundException e) { 511 // Verification is needed but not available - request it again. 512 requestMessage(CANJNI.LM_API_POS_REF); 513 } 514 } 515 516 if(!m_pVerified) { 517 int message = 0; 518 519 switch(m_controlMode) { 520 case Speed: 521 message = CANJNI.LM_API_SPD_PC; 522 break; 523 524 case Position: 525 message = CANJNI.LM_API_POS_PC; 526 break; 527 528 case Current: 529 message = CANJNI.LM_API_ICTRL_PC; 530 break; 531 532 default: 533 break; 534 } 535 536 try { 537 getMessage(message, CANJNI.CAN_MSGID_FULL_M, data); 538 539 double p = unpackFXP16_16(data); 540 541 if(FXP16_EQ(m_p, p)) { 542 m_pVerified = true; 543 } else { 544 // It's wrong - set it again 545 setP(m_p); 546 } 547 } catch(CANMessageNotFoundException e) { 548 // Verification is needed but not available - request it again. 549 requestMessage(message); 550 } 551 } 552 553 if(!m_iVerified) { 554 int message = 0; 555 556 switch(m_controlMode) { 557 case Speed: 558 message = CANJNI.LM_API_SPD_IC; 559 break; 560 561 case Position: 562 message = CANJNI.LM_API_POS_IC; 563 break; 564 565 case Current: 566 message = CANJNI.LM_API_ICTRL_IC; 567 break; 568 569 default: 570 break; 571 } 572 573 try { 574 getMessage(message, CANJNI.CAN_MSGID_FULL_M, data); 575 576 double i = unpackFXP16_16(data); 577 578 if(FXP16_EQ(m_i, i)) { 579 m_iVerified = true; 580 } else { 581 // It's wrong - set it again 582 setI(m_i); 583 } 584 } catch(CANMessageNotFoundException e) { 585 // Verification is needed but not available - request it again. 586 requestMessage(message); 587 } 588 } 589 590 if(!m_dVerified) { 591 int message = 0; 592 593 switch(m_controlMode) { 594 case Speed: 595 message = CANJNI.LM_API_SPD_DC; 596 break; 597 598 case Position: 599 message = CANJNI.LM_API_POS_DC; 600 break; 601 602 case Current: 603 message = CANJNI.LM_API_ICTRL_DC; 604 break; 605 606 default: 607 break; 608 } 609 610 try { 611 getMessage(message, CANJNI.CAN_MSGID_FULL_M, data); 612 613 double d = unpackFXP16_16(data); 614 615 if(FXP16_EQ(m_d, d)) { 616 m_dVerified = true; 617 } else { 618 // It's wrong - set it again 619 setD(m_d); 620 } 621 } catch(CANMessageNotFoundException e) { 622 // Verification is needed but not available - request it again. 623 requestMessage(message); 624 } 625 } 626 627 if(!m_neutralModeVerified) { 628 try { 629 getMessage(CANJNI.LM_API_CFG_BRAKE_COAST, CANJNI.CAN_MSGID_FULL_M, data); 630 631 NeutralMode mode = NeutralMode.valueOf(data[0]); 632 633 if(mode == m_neutralMode) { 634 m_neutralModeVerified = true; 635 } else { 636 //It's wrong - set it again 637 configNeutralMode(m_neutralMode); 638 } 639 } catch(CANMessageNotFoundException e) { 640 // Verification is needed but not available - request it again. 641 requestMessage(CANJNI.LM_API_CFG_BRAKE_COAST); 642 } 643 } 644 645 if(!m_encoderCodesPerRevVerified) { 646 try { 647 getMessage(CANJNI.LM_API_CFG_ENC_LINES, CANJNI.CAN_MSGID_FULL_M, data); 648 649 short codes = unpackINT16(data); 650 651 if(codes == m_encoderCodesPerRev) { 652 m_encoderCodesPerRevVerified = true; 653 } else { 654 //It's wrong - set it again 655 configEncoderCodesPerRev(m_encoderCodesPerRev); 656 } 657 } catch(CANMessageNotFoundException e) { 658 // Verification is needed but not available - request it again. 659 requestMessage(CANJNI.LM_API_CFG_ENC_LINES); 660 } 661 } 662 663 if(!m_potentiometerTurnsVerified) { 664 try { 665 getMessage(CANJNI.LM_API_CFG_POT_TURNS, CANJNI.CAN_MSGID_FULL_M, data); 666 667 short turns = unpackINT16(data); 668 669 if(turns == m_potentiometerTurns) { 670 m_potentiometerTurnsVerified = true; 671 } else { 672 //It's wrong - set it again 673 configPotentiometerTurns(m_potentiometerTurns); 674 } 675 } catch(CANMessageNotFoundException e) { 676 // Verification is needed but not available - request it again. 677 requestMessage(CANJNI.LM_API_CFG_POT_TURNS); 678 } 679 } 680 681 if(!m_limitModeVerified) { 682 try { 683 getMessage(CANJNI.LM_API_CFG_LIMIT_MODE, CANJNI.CAN_MSGID_FULL_M, data); 684 685 LimitMode mode = LimitMode.valueOf(data[0]); 686 687 if(mode == m_limitMode) { 688 m_limitModeVerified = true; 689 } else { 690 //It's wrong - set it again 691 configLimitMode(m_limitMode); 692 } 693 } catch(CANMessageNotFoundException e) { 694 // Verification is needed but not available - request it again. 695 requestMessage(CANJNI.LM_API_CFG_LIMIT_MODE); 696 } 697 } 698 699 if(!m_forwardLimitVerified) { 700 try { 701 getMessage(CANJNI.LM_API_CFG_LIMIT_FWD, CANJNI.CAN_MSGID_FULL_M, data); 702 703 double limit = unpackFXP16_16(data); 704 705 if(FXP16_EQ(limit, m_forwardLimit)) { 706 m_forwardLimitVerified = true; 707 } else { 708 //It's wrong - set it again 709 configForwardLimit(m_forwardLimit); 710 } 711 } catch(CANMessageNotFoundException e) { 712 // Verification is needed but not available - request it again. 713 requestMessage(CANJNI.LM_API_CFG_LIMIT_FWD); 714 } 715 } 716 717 if(!m_reverseLimitVerified) { 718 try { 719 getMessage(CANJNI.LM_API_CFG_LIMIT_REV, CANJNI.CAN_MSGID_FULL_M, data); 720 721 double limit = unpackFXP16_16(data); 722 723 if(FXP16_EQ(limit, m_reverseLimit)) { 724 m_reverseLimitVerified = true; 725 } else { 726 //It's wrong - set it again 727 configReverseLimit(m_reverseLimit); 728 } 729 } catch(CANMessageNotFoundException e) { 730 // Verification is needed but not available - request it again. 731 requestMessage(CANJNI.LM_API_CFG_LIMIT_REV); 732 } 733 } 734 735 if(!m_maxOutputVoltageVerified) { 736 try { 737 getMessage(CANJNI.LM_API_CFG_MAX_VOUT, CANJNI.CAN_MSGID_FULL_M, data); 738 739 double voltage = unpackFXP8_8(data); 740 741 // The returned max output voltage is sometimes slightly higher 742 // or lower than what was sent. This should not trigger 743 // resending the message. 744 if(Math.abs(voltage - m_maxOutputVoltage) < 0.1) { 745 m_maxOutputVoltageVerified = true; 746 } else { 747 // It's wrong - set it again 748 configMaxOutputVoltage(m_maxOutputVoltage); 749 } 750 751 } catch(CANMessageNotFoundException e) { 752 // Verification is needed but not available - request it again. 753 requestMessage(CANJNI.LM_API_CFG_MAX_VOUT); 754 } 755 } 756 757 if(!m_voltageRampRateVerified) { 758 if(m_controlMode == ControlMode.PercentVbus) { 759 try { 760 getMessage(CANJNI.LM_API_VOLT_SET_RAMP, CANJNI.CAN_MSGID_FULL_M, data); 761 762 double rate = unpackPercentage(data); 763 764 if(FXP16_EQ(rate, m_voltageRampRate)) { 765 m_voltageRampRateVerified = true; 766 } else { 767 // It's wrong - set it again 768 setVoltageRampRate(m_voltageRampRate); 769 } 770 771 } catch(CANMessageNotFoundException e) { 772 // Verification is needed but not available - request it again. 773 requestMessage(CANJNI.LM_API_VOLT_SET_RAMP); 774 } 775 } 776 } else if(m_controlMode == ControlMode.Voltage) { 777 try { 778 getMessage(CANJNI.LM_API_VCOMP_COMP_RAMP, CANJNI.CAN_MSGID_FULL_M, data); 779 780 double rate = unpackFXP8_8(data); 781 782 if(FXP8_EQ(rate, m_voltageRampRate)) { 783 m_voltageRampRateVerified = true; 784 } else { 785 // It's wrong - set it again 786 setVoltageRampRate(m_voltageRampRate); 787 } 788 789 } catch(CANMessageNotFoundException e) { 790 // Verification is needed but not available - request it again. 791 requestMessage(CANJNI.LM_API_VCOMP_COMP_RAMP); 792 } 793 } 794 795 if(!m_faultTimeVerified) { 796 try { 797 getMessage(CANJNI.LM_API_CFG_FAULT_TIME, CANJNI.CAN_MSGID_FULL_M, data); 798 799 int faultTime = unpackINT16(data); 800 801 if((int)(m_faultTime * 1000.0) == faultTime) { 802 m_faultTimeVerified = true; 803 } else { 804 //It's wrong - set it again 805 configFaultTime(m_faultTime); 806 } 807 } catch(CANMessageNotFoundException e) { 808 // Verification is needed but not available - request it again. 809 requestMessage(CANJNI.LM_API_CFG_FAULT_TIME); 810 } 811 } 812 813 if(!m_receivedStatusMessage0 || 814 !m_receivedStatusMessage1 || 815 !m_receivedStatusMessage2) { 816 // If the periodic status messages haven't been verified as received, 817 // request periodic status messages again and attempt to unpack any 818 // available ones. 819 setupPeriodicStatus(); 820 getTemperature(); 821 getPosition(); 822 getFaults(); 823 } 824 } 825 826 /** 827 * Common interface for disabling a motor. 828 * 829 * @deprecated Call {@link #disableControl()} instead. 830 */ 831 @Deprecated 832 @Override 833 public void disable() { 834 disableControl(); 835 } 836 837 // PIDOutput interface 838 @Override 839 public void pidWrite(double output) { 840 if (m_controlMode == ControlMode.PercentVbus) { 841 set(output); 842 } else { 843 throw new IllegalStateException("PID only supported in PercentVbus mode"); 844 } 845 } 846 847 /** 848 * Set the reference source device for speed controller mode. 849 * 850 * Choose encoder as the source of speed feedback when in speed control mode. 851 * 852 * @param reference Specify a speed reference. 853 */ 854 private void setSpeedReference(int reference) { 855 sendMessage(CANJNI.LM_API_SPD_REF, new byte[] { (byte)reference }, 1); 856 857 m_speedReference = reference; 858 m_speedRefVerified = false; 859 } 860 861 /** 862 * Set the reference source device for position controller mode. 863 * 864 * Choose between using and encoder and using a potentiometer 865 * as the source of position feedback when in position control mode. 866 * 867 * @param reference Specify a position reference. 868 */ 869 private void setPositionReference(int reference) { 870 sendMessage(CANJNI.LM_API_POS_REF, new byte[] { (byte)reference }, 1); 871 872 m_positionReference = reference; 873 m_posRefVerified = false; 874 } 875 876 /** 877 * Set the P constant for the closed loop modes. 878 * 879 * @param p The proportional gain of the Jaguar's PID controller. 880 */ 881 public void setP(double p) { 882 byte[] data = new byte[8]; 883 byte dataSize = packFXP16_16(data, p); 884 885 switch(m_controlMode) { 886 case Speed: 887 sendMessage(CANJNI.LM_API_SPD_PC, data, dataSize); 888 break; 889 890 case Position: 891 sendMessage(CANJNI.LM_API_POS_PC, data, dataSize); 892 break; 893 894 case Current: 895 sendMessage(CANJNI.LM_API_ICTRL_PC, data, dataSize); 896 break; 897 898 default: 899 throw new IllegalStateException("PID constants only apply in Speed, Position, and Current mode"); 900 } 901 902 m_p = p; 903 m_pVerified = false; 904 } 905 906 /** 907 * Set the I constant for the closed loop modes. 908 * 909 * @param i The integral gain of the Jaguar's PID controller. 910 */ 911 public void setI(double i) { 912 byte[] data = new byte[8]; 913 byte dataSize = packFXP16_16(data, i); 914 915 switch(m_controlMode) { 916 case Speed: 917 sendMessage(CANJNI.LM_API_SPD_IC, data, dataSize); 918 break; 919 920 case Position: 921 sendMessage(CANJNI.LM_API_POS_IC, data, dataSize); 922 break; 923 924 case Current: 925 sendMessage(CANJNI.LM_API_ICTRL_IC, data, dataSize); 926 break; 927 928 default: 929 throw new IllegalStateException("PID constants only apply in Speed, Position, and Current mode"); 930 } 931 932 m_i = i; 933 m_iVerified = false; 934 } 935 936 /** 937 * Set the D constant for the closed loop modes. 938 * 939 * @param d The derivative gain of the Jaguar's PID controller. 940 */ 941 public void setD(double d) { 942 byte[] data = new byte[8]; 943 byte dataSize = packFXP16_16(data, d); 944 945 switch(m_controlMode) { 946 case Speed: 947 sendMessage(CANJNI.LM_API_SPD_DC, data, dataSize); 948 break; 949 950 case Position: 951 sendMessage(CANJNI.LM_API_POS_DC, data, dataSize); 952 break; 953 954 case Current: 955 sendMessage(CANJNI.LM_API_ICTRL_DC, data, dataSize); 956 break; 957 958 default: 959 throw new IllegalStateException("PID constants only apply in Speed, Position, and Current mode"); 960 } 961 962 m_d = d; 963 m_dVerified = false; 964 } 965 966 /** 967 * Set the P, I, and D constants for the closed loop modes. 968 * 969 * @param p The proportional gain of the Jaguar's PID controller. 970 * @param i The integral gain of the Jaguar's PID controller. 971 * @param d The differential gain of the Jaguar's PID controller. 972 */ 973 public void setPID(double p, double i, double d) { 974 setP(p); 975 setI(i); 976 setD(d); 977 } 978 979 /** 980 * Get the Proportional gain of the controller. 981 * 982 * @return The proportional gain. 983 */ 984 public double getP() { 985 if(m_controlMode.equals(ControlMode.PercentVbus) || m_controlMode.equals(ControlMode.Voltage)){ 986 throw new IllegalStateException("PID does not apply in Percent or Voltage control modes"); 987 } 988 return m_p; 989 } 990 991 /** 992 * Get the Integral gain of the controller. 993 * 994 * @return The integral gain. 995 */ 996 public double getI() { 997 if(m_controlMode.equals(ControlMode.PercentVbus) || m_controlMode.equals(ControlMode.Voltage)){ 998 throw new IllegalStateException("PID does not apply in Percent or Voltage control modes"); 999 } 1000 return m_i; 1001 } 1002 1003 /** 1004 * Get the Derivative gain of the controller. 1005 * 1006 * @return The derivative gain. 1007 */ 1008 public double getD() { 1009 if(m_controlMode.equals(ControlMode.PercentVbus) || m_controlMode.equals(ControlMode.Voltage)){ 1010 throw new IllegalStateException("PID does not apply in Percent or Voltage control modes"); 1011 } 1012 return m_d; 1013 } 1014 1015 /** 1016 * Enable the closed loop controller. 1017 * 1018 * Start actually controlling the output based on the feedback. 1019 * If starting a position controller with an encoder reference, 1020 * use the encoderInitialPosition parameter to initialize the 1021 * encoder state. 1022 * 1023 * @param encoderInitialPosition Encoder position to set if position with encoder reference. Ignored otherwise. 1024 */ 1025 public void enableControl(double encoderInitialPosition) { 1026 switch(m_controlMode) { 1027 case PercentVbus: 1028 sendMessage(CANJNI.LM_API_VOLT_T_EN, new byte[0], 0); 1029 break; 1030 1031 case Speed: 1032 sendMessage(CANJNI.LM_API_SPD_T_EN, new byte[0], 0); 1033 break; 1034 1035 case Position: 1036 byte[] data = new byte[8]; 1037 int dataSize = packFXP16_16(data, encoderInitialPosition); 1038 sendMessage(CANJNI.LM_API_POS_T_EN, data, dataSize); 1039 break; 1040 1041 case Current: 1042 sendMessage(CANJNI.LM_API_ICTRL_T_EN, new byte[0], 0); 1043 break; 1044 1045 case Voltage: 1046 sendMessage(CANJNI.LM_API_VCOMP_T_EN, new byte[0], 0); 1047 break; 1048 } 1049 1050 m_controlEnabled = true; 1051 } 1052 1053 /** 1054 * Enable the closed loop controller. 1055 * 1056 * Start actually controlling the output based on the feedback. 1057 * This is the same as calling <code>CANJaguar.enableControl(double encoderInitialPosition)</code> 1058 * with <code>encoderInitialPosition</code> set to <code>0.0</code> 1059 */ 1060 public void enableControl() { 1061 enableControl(0.0); 1062 } 1063 1064 /** 1065 * Disable the closed loop controller. 1066 * 1067 * Stop driving the output based on the feedback. 1068 */ 1069 public void disableControl() { 1070 // Disable all control modes. 1071 sendMessage(CANJNI.LM_API_VOLT_DIS, new byte[0], 0); 1072 sendMessage(CANJNI.LM_API_SPD_DIS, new byte[0], 0); 1073 sendMessage(CANJNI.LM_API_POS_DIS, new byte[0], 0); 1074 sendMessage(CANJNI.LM_API_ICTRL_DIS, new byte[0], 0); 1075 sendMessage(CANJNI.LM_API_VCOMP_DIS, new byte[0], 0); 1076 1077 // Stop all periodic setpoints 1078 sendMessage(CANJNI.LM_API_VOLT_T_SET, new byte[0], 0, CANJNI.CAN_SEND_PERIOD_STOP_REPEATING); 1079 sendMessage(CANJNI.LM_API_SPD_T_SET, new byte[0], 0, CANJNI.CAN_SEND_PERIOD_STOP_REPEATING); 1080 sendMessage(CANJNI.LM_API_POS_T_SET, new byte[0], 0, CANJNI.CAN_SEND_PERIOD_STOP_REPEATING); 1081 sendMessage(CANJNI.LM_API_ICTRL_T_SET, new byte[0], 0, CANJNI.CAN_SEND_PERIOD_STOP_REPEATING); 1082 sendMessage(CANJNI.LM_API_VCOMP_T_SET, new byte[0], 0, CANJNI.CAN_SEND_PERIOD_STOP_REPEATING); 1083 1084 m_controlEnabled = false; 1085 } 1086 1087 /** 1088 * Enable controlling the motor voltage as a percentage of the bus voltage 1089 * without any position or speed feedback.<br> 1090 * After calling this you must call {@link CANJaguar#enableControl()} or {@link CANJaguar#enableControl(double)} to enable the device. 1091 */ 1092 public void setPercentMode() 1093 { 1094 changeControlMode(ControlMode.PercentVbus); 1095 setPositionReference(CANJNI.LM_REF_NONE); 1096 setSpeedReference(CANJNI.LM_REF_NONE); 1097 } 1098 1099 /** 1100 * Enable controlling the motor voltage as a percentage of the bus voltage, 1101 * and enable speed sensing from a non-quadrature encoder.<br> 1102 * After calling this you must call {@link CANJaguar#enableControl()} or {@link CANJaguar#enableControl(double)} to enable the device. 1103 * 1104 * @param tag The constant {@link CANJaguar#kEncoder} 1105 * @param codesPerRev The counts per revolution on the encoder 1106 */ 1107 public void setPercentMode(EncoderTag tag, int codesPerRev) 1108 { 1109 changeControlMode(ControlMode.PercentVbus); 1110 setPositionReference(CANJNI.LM_REF_NONE); 1111 setSpeedReference(CANJNI.LM_REF_ENCODER); 1112 configEncoderCodesPerRev(codesPerRev); 1113 } 1114 1115 /** 1116 * Enable controlling the motor voltage as a percentage of the bus voltage, 1117 * and enable position and speed sensing from a quadrature encoder.<br> 1118 * After calling this you must call {@link CANJaguar#enableControl()} or {@link CANJaguar#enableControl(double)} to enable the device. 1119 * 1120 * @param tag The constant {@link CANJaguar#kQuadEncoder} 1121 * @param codesPerRev The counts per revolution on the encoder 1122 */ 1123 public void setPercentMode(QuadEncoderTag tag, int codesPerRev) 1124 { 1125 changeControlMode(ControlMode.PercentVbus); 1126 setPositionReference(CANJNI.LM_REF_ENCODER); 1127 setSpeedReference(CANJNI.LM_REF_QUAD_ENCODER); 1128 configEncoderCodesPerRev(codesPerRev); 1129 } 1130 1131 /** 1132 * Enable controlling the motor voltage as a percentage of the bus voltage, 1133 * and enable position sensing from a potentiometer and no speed feedback.<br> 1134 * After calling this you must call {@link CANJaguar#enableControl()} or {@link CANJaguar#enableControl(double)} to enable the device. 1135 * 1136 * @param tag The constant {@link CANJaguar#kPotentiometer} 1137 */ 1138 public void setPercentMode(PotentiometerTag tag) 1139 { 1140 changeControlMode(ControlMode.PercentVbus); 1141 setPositionReference(CANJNI.LM_REF_POT); 1142 setSpeedReference(CANJNI.LM_REF_NONE); 1143 configPotentiometerTurns(1); 1144 } 1145 1146 /** 1147 * Enable controlling the motor current with a PID loop.<br> 1148 * After calling this you must call {@link CANJaguar#enableControl()} or {@link CANJaguar#enableControl(double)} to enable the device. 1149 * 1150 * @param p The proportional gain of the Jaguar's PID controller. 1151 * @param i The integral gain of the Jaguar's PID controller. 1152 * @param d The differential gain of the Jaguar's PID controller. 1153 */ 1154 public void setCurrentMode(double p, double i, double d) 1155 { 1156 changeControlMode(ControlMode.Current); 1157 setPositionReference(CANJNI.LM_REF_NONE); 1158 setSpeedReference(CANJNI.LM_REF_NONE); 1159 setPID(p, i, d); 1160 } 1161 1162 /** 1163 * Enable controlling the motor current with a PID loop, and enable speed 1164 * sensing from a non-quadrature encoder.<br> 1165 * After calling this you must call {@link CANJaguar#enableControl()} or {@link CANJaguar#enableControl(double)} to enable the device. 1166 * 1167 * @param tag The constant {@link CANJaguar#kEncoder} 1168 * @param p The proportional gain of the Jaguar's PID controller. 1169 * @param i The integral gain of the Jaguar's PID controller. 1170 * @param d The differential gain of the Jaguar's PID controller. 1171 */ 1172 public void setCurrentMode(EncoderTag tag, int codesPerRev, double p, double i, double d) 1173 { 1174 changeControlMode(ControlMode.Current); 1175 setPositionReference(CANJNI.LM_REF_NONE); 1176 setSpeedReference(CANJNI.LM_REF_NONE); 1177 configEncoderCodesPerRev(codesPerRev); 1178 setPID(p, i, d); 1179 } 1180 1181 /** 1182 * Enable controlling the motor current with a PID loop, and enable speed and 1183 * position sensing from a quadrature encoder.<br> 1184 * After calling this you must call {@link CANJaguar#enableControl()} or {@link CANJaguar#enableControl(double)} to enable the device. 1185 * 1186 * @param tag The constant {@link CANJaguar#kQuadEncoder} 1187 * @param p The proportional gain of the Jaguar's PID controller. 1188 * @param i The integral gain of the Jaguar's PID controller. 1189 * @param d The differential gain of the Jaguar's PID controller. 1190 */ 1191 public void setCurrentMode(QuadEncoderTag tag, int codesPerRev, double p, double i, double d) 1192 { 1193 changeControlMode(ControlMode.Current); 1194 setPositionReference(CANJNI.LM_REF_ENCODER); 1195 setSpeedReference(CANJNI.LM_REF_QUAD_ENCODER); 1196 configEncoderCodesPerRev(codesPerRev); 1197 setPID(p, i, d); 1198 } 1199 1200 /** 1201 * Enable controlling the motor current with a PID loop, and enable position 1202 * sensing from a potentiometer.<br> 1203 * After calling this you must call {@link CANJaguar#enableControl()} or {@link CANJaguar#enableControl(double)} to enable the device. 1204 * 1205 * @param tag The constant {@link CANJaguar#kPotentiometer} 1206 * @param p The proportional gain of the Jaguar's PID controller. 1207 * @param i The integral gain of the Jaguar's PID controller. 1208 * @param d The differential gain of the Jaguar's PID controller. 1209 */ 1210 public void setCurrentMode(PotentiometerTag tag, double p, double i, double d) 1211 { 1212 changeControlMode(ControlMode.Current); 1213 setPositionReference(CANJNI.LM_REF_POT); 1214 setSpeedReference(CANJNI.LM_REF_NONE); 1215 configPotentiometerTurns(1); 1216 setPID(p, i, d); 1217 } 1218 1219 /** 1220 * Enable controlling the speed with a feedback loop from a non-quadrature 1221 * encoder.<br> 1222 * After calling this you must call {@link CANJaguar#enableControl()} or {@link CANJaguar#enableControl(double)} to enable the device. 1223 * 1224 * @param tag The constant {@link CANJaguar#kEncoder} 1225 * @param codesPerRev The counts per revolution on the encoder 1226 * @param p The proportional gain of the Jaguar's PID controller. 1227 * @param i The integral gain of the Jaguar's PID controller. 1228 * @param d The differential gain of the Jaguar's PID controller. 1229 */ 1230 public void setSpeedMode(EncoderTag tag, int codesPerRev, double p, double i, double d) 1231 { 1232 changeControlMode(ControlMode.Speed); 1233 setPositionReference(CANJNI.LM_REF_NONE); 1234 setSpeedReference(CANJNI.LM_REF_ENCODER); 1235 configEncoderCodesPerRev(codesPerRev); 1236 setPID(p, i, d); 1237 } 1238 1239 /** 1240 * Enable controlling the speed with a feedback loop from a quadrature encoder.<br> 1241 * After calling this you must call {@link CANJaguar#enableControl()} or {@link CANJaguar#enableControl(double)} to enable the device. 1242 * 1243 * @param tag The constant {@link CANJaguar#kQuadEncoder} 1244 * @param codesPerRev The counts per revolution on the encoder 1245 * @param p The proportional gain of the Jaguar's PID controller. 1246 * @param i The integral gain of the Jaguar's PID controller. 1247 * @param d The differential gain of the Jaguar's PID controller. 1248 */ 1249 public void setSpeedMode(QuadEncoderTag tag, int codesPerRev, double p, double i, double d) 1250 { 1251 changeControlMode(ControlMode.Speed); 1252 setPositionReference(CANJNI.LM_REF_ENCODER); 1253 setSpeedReference(CANJNI.LM_REF_QUAD_ENCODER); 1254 configEncoderCodesPerRev(codesPerRev); 1255 setPID(p, i, d); 1256 } 1257 1258 /** 1259 * Enable controlling the position with a feedback loop using an encoder.<br> 1260 * After calling this you must call {@link CANJaguar#enableControl()} or {@link CANJaguar#enableControl(double)} to enable the device. 1261 * 1262 * @param tag The constant {@link CANJaguar#kQuadEncoder} 1263 * @param codesPerRev The counts per revolution on the encoder 1264 * @param p The proportional gain of the Jaguar's PID controller. 1265 * @param i The integral gain of the Jaguar's PID controller. 1266 * @param d The differential gain of the Jaguar's PID controller. 1267 * 1268 */ 1269 public void setPositionMode(QuadEncoderTag tag, int codesPerRev, double p, double i, double d) 1270 { 1271 changeControlMode(ControlMode.Position); 1272 setPositionReference(CANJNI.LM_REF_ENCODER); 1273 configEncoderCodesPerRev(codesPerRev); 1274 setPID(p, i, d); 1275 } 1276 1277 /** 1278 * Enable controlling the position with a feedback loop using a potentiometer.<br> 1279 * After calling this you must call {@link CANJaguar#enableControl()} or {@link CANJaguar#enableControl(double)} to enable the device. 1280 * 1281 * @param tag The constant {@link CANJaguar#kPotentiometer} 1282 * @param p The proportional gain of the Jaguar's PID controller. 1283 * @param i The integral gain of the Jaguar's PID controller. 1284 * @param d The differential gain of the Jaguar's PID controller. 1285 */ 1286 public void setPositionMode(PotentiometerTag tag, double p, double i, double d) 1287 { 1288 changeControlMode(ControlMode.Position); 1289 setPositionReference(CANJNI.LM_REF_POT); 1290 configPotentiometerTurns(1); 1291 setPID(p, i, d); 1292 } 1293 1294 /** 1295 * Enable controlling the motor voltage without any position or speed feedback.<br> 1296 * After calling this you must call {@link CANJaguar#enableControl()} or {@link CANJaguar#enableControl(double)} to enable the device. 1297 */ 1298 public void setVoltageMode() 1299 { 1300 changeControlMode(ControlMode.Voltage); 1301 setPositionReference(CANJNI.LM_REF_NONE); 1302 setSpeedReference(CANJNI.LM_REF_NONE); 1303 } 1304 1305 /** 1306 * Enable controlling the motor voltage with speed feedback from a 1307 * non-quadrature encoder and no position feedback.<br> 1308 * After calling this you must call {@link CANJaguar#enableControl()} or {@link CANJaguar#enableControl(double)} to enable the device. 1309 * 1310 * @param tag The constant {@link CANJaguar#kEncoder} 1311 * @param codesPerRev The counts per revolution on the encoder 1312 */ 1313 public void setVoltageMode(EncoderTag tag, int codesPerRev) 1314 { 1315 changeControlMode(ControlMode.Voltage); 1316 setPositionReference(CANJNI.LM_REF_NONE); 1317 setSpeedReference(CANJNI.LM_REF_ENCODER); 1318 configEncoderCodesPerRev(codesPerRev); 1319 } 1320 1321 /** 1322 * Enable controlling the motor voltage with position and speed feedback from a 1323 * quadrature encoder.<br> 1324 * After calling this you must call {@link CANJaguar#enableControl()} or {@link CANJaguar#enableControl(double)} to enable the device. 1325 * 1326 * @param tag The constant {@link CANJaguar#kQuadEncoder} 1327 * @param codesPerRev The counts per revolution on the encoder 1328 */ 1329 public void setVoltageMode(QuadEncoderTag tag, int codesPerRev) 1330 { 1331 changeControlMode(ControlMode.Voltage); 1332 setPositionReference(CANJNI.LM_REF_ENCODER); 1333 setSpeedReference(CANJNI.LM_REF_QUAD_ENCODER); 1334 configEncoderCodesPerRev(codesPerRev); 1335 } 1336 1337 /** 1338 * Enable controlling the motor voltage with position feedback from a 1339 * potentiometer and no speed feedback. 1340 * 1341 * @param tag The constant {@link CANJaguar#kPotentiometer} 1342 */ 1343 public void setVoltageMode(PotentiometerTag tag) 1344 { 1345 changeControlMode(ControlMode.Voltage); 1346 setPositionReference(CANJNI.LM_REF_POT); 1347 setSpeedReference(CANJNI.LM_REF_NONE); 1348 configPotentiometerTurns(1); 1349 } 1350 1351 /** 1352 * Used internally. In order to set the control mode see the methods listed below. 1353 * 1354 * Change the control mode of this Jaguar object. 1355 * 1356 * After changing modes, configure any PID constants or other settings needed 1357 * and then EnableControl() to actually change the mode on the Jaguar. 1358 * 1359 * @param controlMode The new mode. 1360 * 1361 * @see CANJaguar#setCurrentMode(double, double, double) 1362 * @see CANJaguar#setCurrentMode(PotentiometerTag, double, double, double) 1363 * @see CANJaguar#setCurrentMode(EncoderTag, int, double, double, double) 1364 * @see CANJaguar#setCurrentMode(QuadEncoderTag, int, double, double, double) 1365 * @see CANJaguar#setPercentMode() 1366 * @see CANJaguar#setPercentMode(PotentiometerTag) 1367 * @see CANJaguar#setPercentMode(EncoderTag, int) 1368 * @see CANJaguar#setPercentMode(QuadEncoderTag, int) 1369 * @see CANJaguar#setPositionMode(PotentiometerTag, double, double, double) 1370 * @see CANJaguar#setPositionMode(QuadEncoderTag, int, double, double, double) 1371 * @see CANJaguar#setSpeedMode(EncoderTag, int, double, double, double) 1372 * @see CANJaguar#setSpeedMode(QuadEncoderTag, int, double, double, double) 1373 * @see CANJaguar#setVoltageMode() 1374 * @see CANJaguar#setVoltageMode(PotentiometerTag) 1375 * @see CANJaguar#setVoltageMode(EncoderTag, int) 1376 * @see CANJaguar#setVoltageMode(QuadEncoderTag, int) 1377 */ 1378 private void changeControlMode(ControlMode controlMode) { 1379 // Disable the previous mode 1380 disableControl(); 1381 1382 // Update the local mode 1383 m_controlMode = controlMode; 1384 m_controlModeVerified = false; 1385 } 1386 1387 /** 1388 * Get the active control mode from the Jaguar. 1389 * 1390 * Ask the Jagaur what mode it is in. 1391 * 1392 * @return ControlMode that the Jag is in. 1393 */ 1394 public ControlMode getControlMode() { 1395 return m_controlMode; 1396 } 1397 1398 /** 1399 * Get the voltage at the battery input terminals of the Jaguar. 1400 * 1401 * @return The bus voltage in Volts. 1402 */ 1403 public double getBusVoltage() { 1404 updatePeriodicStatus(); 1405 1406 return m_busVoltage; 1407 } 1408 1409 /** 1410 * Get the voltage being output from the motor terminals of the Jaguar. 1411 * 1412 * @return The output voltage in Volts. 1413 */ 1414 public double getOutputVoltage() { 1415 updatePeriodicStatus(); 1416 1417 return m_outputVoltage; 1418 } 1419 1420 /** 1421 * Get the current through the motor terminals of the Jaguar. 1422 * 1423 * @return The output current in Amps. 1424 */ 1425 public double getOutputCurrent() { 1426 updatePeriodicStatus(); 1427 1428 return m_outputCurrent; 1429 } 1430 1431 /** 1432 * Get the internal temperature of the Jaguar. 1433 * 1434 * @return The temperature of the Jaguar in degrees Celsius. 1435 */ 1436 public double getTemperature() { 1437 updatePeriodicStatus(); 1438 1439 return m_temperature; 1440 } 1441 1442 /** 1443 * Get the position of the encoder or potentiometer. 1444 * 1445 * @return The position of the motor in rotations based on the configured feedback. 1446 * @see CANJaguar#configPotentiometerTurns(int) 1447 * @see CANJaguar#configEncoderCodesPerRev(int) 1448 */ 1449 public double getPosition() { 1450 updatePeriodicStatus(); 1451 1452 return m_position; 1453 } 1454 1455 /** 1456 * Get the speed of the encoder. 1457 * 1458 * @return The speed of the motor in RPM based on the configured feedback. 1459 */ 1460 public double getSpeed() { 1461 updatePeriodicStatus(); 1462 1463 return m_speed; 1464 } 1465 1466 /** 1467 * Get the status of the forward limit switch. 1468 * 1469 * @return true if the motor is allowed to turn in the forward direction. 1470 */ 1471 public boolean getForwardLimitOK() { 1472 updatePeriodicStatus(); 1473 1474 return (m_limits & kForwardLimit) != 0; 1475 } 1476 1477 /** 1478 * Get the status of the reverse limit switch. 1479 * 1480 * @return true if the motor is allowed to turn in the reverse direction. 1481 */ 1482 public boolean getReverseLimitOK() { 1483 updatePeriodicStatus(); 1484 1485 return (m_limits & kReverseLimit) != 0; 1486 } 1487 1488 /** 1489 * Get the status of any faults the Jaguar has detected. 1490 * 1491 * @return A bit-mask of faults defined by the "Faults" constants. 1492 * @see #kCurrentFault 1493 * @see #kBusVoltageFault 1494 * @see #kTemperatureFault 1495 * @see #kGateDriverFault 1496 */ 1497 public short getFaults() { 1498 updatePeriodicStatus(); 1499 1500 return m_faults; 1501 } 1502 1503 /** 1504 * set the maximum voltage change rate. 1505 * 1506 * When in PercentVbus or Voltage output mode, the rate at which the voltage changes can 1507 * be limited to reduce current spikes. set this to 0.0 to disable rate limiting. 1508 * 1509 * @param rampRate The maximum rate of voltage change in Percent Voltage mode in V/s. 1510 */ 1511 public void setVoltageRampRate(double rampRate) { 1512 byte[] data = new byte[8]; 1513 int dataSize; 1514 int message; 1515 1516 switch(m_controlMode) { 1517 case PercentVbus: 1518 dataSize = packPercentage(data, rampRate / (m_maxOutputVoltage * kControllerRate)); 1519 message = CANJNI.LM_API_VOLT_SET_RAMP; 1520 break; 1521 case Voltage: 1522 dataSize = packFXP8_8(data, rampRate / kControllerRate); 1523 message = CANJNI.LM_API_VCOMP_COMP_RAMP; 1524 break; 1525 default: 1526 throw new IllegalStateException("Voltage ramp rate only applies in Percentage and Voltage modes"); 1527 } 1528 1529 sendMessage(message, data, dataSize); 1530 } 1531 1532 /** 1533 * Get the version of the firmware running on the Jaguar. 1534 * 1535 * @return The firmware version. 0 if the device did not respond. 1536 */ 1537 public int getFirmwareVersion() { 1538 return m_firmwareVersion; 1539 } 1540 1541 /** 1542 * Get the version of the Jaguar hardware. 1543 * 1544 * @return The hardware version. 1: Jaguar, 2: Black Jaguar 1545 */ 1546 public byte getHardwareVersion() { 1547 return m_hardwareVersion; 1548 } 1549 1550 /** 1551 * Configure what the controller does to the H-Bridge when neutral (not driving the output). 1552 * 1553 * This allows you to override the jumper configuration for brake or coast. 1554 * 1555 * @param mode Select to use the jumper setting or to override it to coast or brake. 1556 */ 1557 public void configNeutralMode(NeutralMode mode) { 1558 sendMessage(CANJNI.LM_API_CFG_BRAKE_COAST, new byte[] { mode.value }, 1); 1559 1560 m_neutralMode = mode; 1561 m_neutralModeVerified = false; 1562 } 1563 1564 /** 1565 * Configure how many codes per revolution are generated by your encoder. 1566 * 1567 * @param codesPerRev The number of counts per revolution in 1X mode. 1568 */ 1569 public void configEncoderCodesPerRev(int codesPerRev) { 1570 byte[] data = new byte[8]; 1571 1572 int dataSize = packINT16(data, (short)codesPerRev); 1573 sendMessage(CANJNI.LM_API_CFG_ENC_LINES, data, dataSize); 1574 1575 m_encoderCodesPerRev = (short)codesPerRev; 1576 m_encoderCodesPerRevVerified = false; 1577 } 1578 1579 /** 1580 * Configure the number of turns on the potentiometer. 1581 * 1582 * There is no special support for continuous turn potentiometers. 1583 * Only integer numbers of turns are supported. 1584 * 1585 * @param turns The number of turns of the potentiometer 1586 */ 1587 public void configPotentiometerTurns(int turns) { 1588 byte[] data = new byte[8]; 1589 1590 int dataSize = packINT16(data, (short)turns); 1591 sendMessage(CANJNI.LM_API_CFG_POT_TURNS, data, dataSize); 1592 1593 m_potentiometerTurns = (short)turns; 1594 m_potentiometerTurnsVerified = false; 1595 } 1596 1597 /** 1598 * Configure Soft Position Limits when in Position Controller mode.<br> 1599 * 1600 * When controlling position, you can add additional limits on top of the limit switch inputs 1601 * that are based on the position feedback. If the position limit is reached or the 1602 * switch is opened, that direction will be disabled. 1603 * 1604 * @param forwardLimitPosition The position that, if exceeded, will disable the forward direction. 1605 * @param reverseLimitPosition The position that, if exceeded, will disable the reverse direction. 1606 */ 1607 public void configSoftPositionLimits(double forwardLimitPosition, double reverseLimitPosition) { 1608 configLimitMode(LimitMode.SoftPositionLimits); 1609 configForwardLimit(forwardLimitPosition); 1610 configReverseLimit(reverseLimitPosition); 1611 } 1612 1613 /** 1614 * Disable Soft Position Limits if previously enabled.<br> 1615 * 1616 * Soft Position Limits are disabled by default. 1617 */ 1618 public void disableSoftPositionLimits() { 1619 configLimitMode(LimitMode.SwitchInputsOnly); 1620 } 1621 1622 /** 1623 * Set the limit mode for position control mode.<br> 1624 * 1625 * Use {@link #configSoftPositionLimits(double, double)} or {@link #disableSoftPositionLimits()} to set this 1626 * automatically. 1627 * @param mode The {@link LimitMode} to use to limit the rotation of the device. 1628 * @see LimitMode#SwitchInputsOnly 1629 * @see LimitMode#SoftPositionLimits 1630 */ 1631 public void configLimitMode(LimitMode mode) { 1632 sendMessage(CANJNI.LM_API_CFG_LIMIT_MODE, new byte[] { mode.value }, 1); 1633 } 1634 1635 /** 1636 * Set the position that, if exceeded, will disable the forward direction. 1637 * 1638 * Use {@link #configSoftPositionLimits(double, double)} to set this and the {@link LimitMode} automatically. 1639 * @param forwardLimitPosition The position that, if exceeded, will disable the forward direction. 1640 */ 1641 public void configForwardLimit(double forwardLimitPosition) { 1642 byte[] data = new byte[8]; 1643 1644 int dataSize = packFXP16_16(data, forwardLimitPosition); 1645 data[dataSize++] = 1; 1646 sendMessage(CANJNI.LM_API_CFG_LIMIT_FWD, data, dataSize); 1647 1648 m_forwardLimit = forwardLimitPosition; 1649 m_forwardLimitVerified = false; 1650 } 1651 1652 /** 1653 * Set the position that, if exceeded, will disable the reverse direction. 1654 * 1655 * Use {@link #configSoftPositionLimits(double, double)} to set this and the {@link LimitMode} automatically. 1656 * @param reverseLimitPosition The position that, if exceeded, will disable the reverse direction. 1657 */ 1658 public void configReverseLimit(double reverseLimitPosition) { 1659 byte[] data = new byte[8]; 1660 1661 int dataSize = packFXP16_16(data, reverseLimitPosition); 1662 data[dataSize++] = 1; 1663 sendMessage(CANJNI.LM_API_CFG_LIMIT_REV, data, dataSize); 1664 1665 m_reverseLimit = reverseLimitPosition; 1666 m_reverseLimitVerified = false; 1667 } 1668 1669 /** 1670 * Configure the maximum voltage that the Jaguar will ever output. 1671 * 1672 * This can be used to limit the maximum output voltage in all modes so that 1673 * motors which cannot withstand full bus voltage can be used safely. 1674 * 1675 * @param voltage The maximum voltage output by the Jaguar. 1676 */ 1677 public void configMaxOutputVoltage(double voltage) { 1678 byte[] data = new byte[8]; 1679 1680 int dataSize = packFXP8_8(data, voltage); 1681 sendMessage(CANJNI.LM_API_CFG_MAX_VOUT, data, dataSize); 1682 1683 m_maxOutputVoltage = voltage; 1684 m_maxOutputVoltageVerified = false; 1685 } 1686 1687 /** 1688 * Configure how long the Jaguar waits in the case of a fault before resuming operation. 1689 * 1690 * Faults include over temerature, over current, and bus under voltage. 1691 * The default is 3.0 seconds, but can be reduced to as low as 0.5 seconds. 1692 * 1693 * @param faultTime The time to wait before resuming operation, in seconds. 1694 */ 1695 public void configFaultTime(float faultTime) { 1696 byte[] data = new byte[8]; 1697 1698 if(faultTime < 0.5f) faultTime = 0.5f; 1699 else if(faultTime > 3.0f) faultTime = 3.0f; 1700 1701 int dataSize = packINT16(data, (short)(faultTime * 1000.0)); 1702 sendMessage(CANJNI.LM_API_CFG_FAULT_TIME, data, dataSize); 1703 1704 m_faultTime = faultTime; 1705 m_faultTimeVerified = false; 1706 } 1707 1708 byte m_deviceNumber; 1709 double m_value = 0.0f; 1710 1711 // Parameters/configuration 1712 ControlMode m_controlMode; 1713 int m_speedReference = CANJNI.LM_REF_NONE; 1714 int m_positionReference = CANJNI.LM_REF_NONE; 1715 double m_p = 0.0; 1716 double m_i = 0.0; 1717 double m_d = 0.0; 1718 NeutralMode m_neutralMode = NeutralMode.Jumper; 1719 short m_encoderCodesPerRev = 0; 1720 short m_potentiometerTurns = 0; 1721 LimitMode m_limitMode = LimitMode.SwitchInputsOnly; 1722 double m_forwardLimit = 0.0; 1723 double m_reverseLimit = 0.0; 1724 double m_maxOutputVoltage = kApproxBusVoltage; 1725 double m_voltageRampRate = 0.0; 1726 float m_faultTime = 0.0f; 1727 1728 // Which parameters have been verified since they were last set? 1729 boolean m_controlModeVerified = true; 1730 boolean m_speedRefVerified = true; 1731 boolean m_posRefVerified = true; 1732 boolean m_pVerified = true; 1733 boolean m_iVerified = true; 1734 boolean m_dVerified = true; 1735 boolean m_neutralModeVerified = true; 1736 boolean m_encoderCodesPerRevVerified = true; 1737 boolean m_potentiometerTurnsVerified = true; 1738 boolean m_forwardLimitVerified = true; 1739 boolean m_reverseLimitVerified = true; 1740 boolean m_limitModeVerified = true; 1741 boolean m_maxOutputVoltageVerified = true; 1742 boolean m_voltageRampRateVerified = true; 1743 boolean m_faultTimeVerified = true; 1744 1745 // Status data 1746 double m_busVoltage = 0.0f; 1747 double m_outputVoltage = 0.0f; 1748 double m_outputCurrent = 0.0f; 1749 double m_temperature = 0.0f; 1750 double m_position = 0.0; 1751 double m_speed = 0.0; 1752 byte m_limits = (byte)0; 1753 short m_faults = (short)0; 1754 int m_firmwareVersion = 0; 1755 byte m_hardwareVersion = (byte)0; 1756 1757 // Which periodic status messages have we received at least once? 1758 boolean m_receivedStatusMessage0 = false; 1759 boolean m_receivedStatusMessage1 = false; 1760 boolean m_receivedStatusMessage2 = false; 1761 1762 static final int kReceiveStatusAttempts = 50; 1763 1764 boolean m_controlEnabled = true; 1765 1766 static void sendMessageHelper(int messageID, byte[] data, int dataSize, int period) throws CANMessageNotFoundException { 1767 final int[] kTrustedMessages = { 1768 CANJNI.LM_API_VOLT_T_EN, CANJNI.LM_API_VOLT_T_SET, CANJNI.LM_API_SPD_T_EN, CANJNI.LM_API_SPD_T_SET, 1769 CANJNI.LM_API_VCOMP_T_EN, CANJNI.LM_API_VCOMP_T_SET, CANJNI.LM_API_POS_T_EN, CANJNI.LM_API_POS_T_SET, 1770 CANJNI.LM_API_ICTRL_T_EN, CANJNI.LM_API_ICTRL_T_SET 1771 }; 1772 1773 ByteBuffer status = ByteBuffer.allocateDirect(4); 1774 status.order(ByteOrder.LITTLE_ENDIAN); 1775 status.asIntBuffer().put(0, 0); 1776 1777 for(byte i = 0; i < kTrustedMessages.length; i++) { 1778 if((kFullMessageIDMask & messageID) == kTrustedMessages[i]) 1779 { 1780 // Make sure the data will still fit after adjusting for the token. 1781 if (dataSize > kMaxMessageDataSize - 2) { 1782 throw new RuntimeException("CAN message has too much data."); 1783 } 1784 1785 ByteBuffer trustedBuffer = ByteBuffer.allocateDirect(dataSize+2); 1786 trustedBuffer.put(0, (byte)0); 1787 trustedBuffer.put(1, (byte)0); 1788 1789 for(byte j = 0; j < dataSize; j++) { 1790 trustedBuffer.put(j+2, data[j]); 1791 } 1792 1793 CANJNI.FRCNetworkCommunicationCANSessionMuxSendMessage(messageID, trustedBuffer, period, status.asIntBuffer()); 1794 int statusCode = status.asIntBuffer().get(0); 1795 if(statusCode < 0) { 1796 CANExceptionFactory.checkStatus(statusCode, messageID); 1797 } 1798 1799 return; 1800 } 1801 } 1802 1803 // Use a null pointer for the data buffer if the given array is null 1804 ByteBuffer buffer; 1805 if(data != null) { 1806 buffer = ByteBuffer.allocateDirect(dataSize); 1807 for(byte i = 0; i < dataSize; i++) { 1808 buffer.put(i, data[i]); 1809 } 1810 } else { 1811 buffer = null; 1812 } 1813 1814 CANJNI.FRCNetworkCommunicationCANSessionMuxSendMessage(messageID, buffer, period, status.asIntBuffer()); 1815 1816 int statusCode = status.asIntBuffer().get(0); 1817 if(statusCode < 0) { 1818 CANExceptionFactory.checkStatus(statusCode, messageID); 1819 } 1820 } 1821 1822 /** 1823 * Send a message to the Jaguar. 1824 * 1825 * @param messageID The messageID to be used on the CAN bus (device number 1826 * is added internally) 1827 * @param data The up to 8 bytes of data to be sent with the message 1828 * @param dataSize Specify how much of the data in "data" to send 1829 * @param period If positive, tell Network Communications to send the 1830 * message every "period" milliseconds. 1831 */ 1832 protected void sendMessage(int messageID, byte[] data, int dataSize, int period) { 1833 sendMessageHelper(messageID | m_deviceNumber, data, dataSize, period); 1834 } 1835 1836 /** 1837 * Send a message to the Jaguar, non-periodically 1838 * 1839 * @param messageID The messageID to be used on the CAN bus (device number 1840 * is added internally) 1841 * @param data The up to 8 bytes of data to be sent with the message 1842 * @param dataSize Specify how much of the data in "data" to send 1843 */ 1844 protected void sendMessage(int messageID, byte[] data, int dataSize) { 1845 sendMessage(messageID, data, dataSize, CANJNI.CAN_SEND_PERIOD_NO_REPEAT); 1846 } 1847 1848 /** 1849 * Request a message from the Jaguar, but don't wait for it to arrive. 1850 * 1851 * @param messageID The message to request 1852 * @param period If positive, tell Network Communications to request the 1853 * message every "period" milliseconds. 1854 */ 1855 protected void requestMessage(int messageID, int period) { 1856 sendMessageHelper(messageID | m_deviceNumber, null, 0, period); 1857 } 1858 1859 /** 1860 * Request a message from the Jaguar, but don't wait for it to arrive. 1861 * 1862 * @param messageID The message to request 1863 */ 1864 protected void requestMessage(int messageID) { 1865 requestMessage(messageID, CANJNI.CAN_SEND_PERIOD_NO_REPEAT); 1866 } 1867 1868 /** 1869 * Get a previously requested message. 1870 * 1871 * Jaguar always generates a message with the same message ID when replying. 1872 * 1873 * @param messageID The messageID to read from the CAN bus (device number is added internally) 1874 * @param data The up to 8 bytes of data that was received with the message 1875 * 1876 * @throws CANMessageNotFoundException if there's not new message available 1877 */ 1878 protected void getMessage(int messageID, int messageMask, byte[] data) throws CANMessageNotFoundException { 1879 messageID |= m_deviceNumber; 1880 messageID &= CANJNI.CAN_MSGID_FULL_M; 1881 1882 ByteBuffer targetedMessageID = ByteBuffer.allocateDirect(4); 1883 targetedMessageID.order(ByteOrder.LITTLE_ENDIAN); 1884 targetedMessageID.asIntBuffer().put(0, messageID); 1885 1886 ByteBuffer timeStamp = ByteBuffer.allocateDirect(4); 1887 1888 ByteBuffer status = ByteBuffer.allocateDirect(4); 1889 status.order(ByteOrder.LITTLE_ENDIAN); 1890 status.asIntBuffer().put(0, 0); 1891 1892 // Get the data. 1893 ByteBuffer dataBuffer = CANJNI.FRCNetworkCommunicationCANSessionMuxReceiveMessage( 1894 targetedMessageID.asIntBuffer(), 1895 messageMask, 1896 timeStamp, 1897 status.asIntBuffer()); 1898 1899 if(data != null) { 1900 for(int i = 0; i < dataBuffer.capacity(); i++) { 1901 data[i] = dataBuffer.get(i); 1902 } 1903 } 1904 1905 int statusCode = status.asIntBuffer().get(0); 1906 if(statusCode < 0) { 1907 CANExceptionFactory.checkStatus(statusCode, messageID); 1908 } 1909 } 1910 1911 /** 1912 * Enables periodic status updates from the Jaguar 1913 */ 1914 protected void setupPeriodicStatus() { 1915 byte[] data = new byte[8]; 1916 int dataSize; 1917 1918 // Message 0 returns bus voltage, output voltage, output current, and 1919 // temperature. 1920 final byte[] kMessage0Data = new byte[] { 1921 CANJNI.LM_PSTAT_VOLTBUS_B0, CANJNI.LM_PSTAT_VOLTBUS_B1, 1922 CANJNI.LM_PSTAT_VOLTOUT_B0, CANJNI.LM_PSTAT_VOLTOUT_B1, 1923 CANJNI.LM_PSTAT_CURRENT_B0, CANJNI.LM_PSTAT_CURRENT_B1, 1924 CANJNI.LM_PSTAT_TEMP_B0, CANJNI.LM_PSTAT_TEMP_B1 1925 }; 1926 1927 // Message 1 returns position and speed 1928 final byte[] kMessage1Data = new byte[] { 1929 CANJNI.LM_PSTAT_POS_B0, CANJNI.LM_PSTAT_POS_B1, CANJNI.LM_PSTAT_POS_B2, CANJNI.LM_PSTAT_POS_B3, 1930 CANJNI.LM_PSTAT_SPD_B0, CANJNI.LM_PSTAT_SPD_B1, CANJNI.LM_PSTAT_SPD_B2, CANJNI.LM_PSTAT_SPD_B3 1931 }; 1932 1933 // Message 2 returns limits and faults 1934 final byte[] kMessage2Data = new byte[] { 1935 CANJNI.LM_PSTAT_LIMIT_CLR, 1936 CANJNI.LM_PSTAT_FAULT, 1937 CANJNI.LM_PSTAT_END, 1938 (byte)0, 1939 (byte)0, 1940 (byte)0, 1941 (byte)0, 1942 (byte)0, 1943 }; 1944 1945 dataSize = packINT16(data, (short)(kSendMessagePeriod)); 1946 sendMessage(CANJNI.LM_API_PSTAT_PER_EN_S0, data, dataSize); 1947 sendMessage(CANJNI.LM_API_PSTAT_PER_EN_S1, data, dataSize); 1948 sendMessage(CANJNI.LM_API_PSTAT_PER_EN_S2, data, dataSize); 1949 1950 dataSize = 8; 1951 sendMessage(CANJNI.LM_API_PSTAT_CFG_S0, kMessage0Data, dataSize); 1952 sendMessage(CANJNI.LM_API_PSTAT_CFG_S1, kMessage1Data, dataSize); 1953 sendMessage(CANJNI.LM_API_PSTAT_CFG_S2, kMessage2Data, dataSize); 1954 } 1955 1956 /** 1957 * Check for new periodic status updates and unpack them into local variables. 1958 */ 1959 protected void updatePeriodicStatus() { 1960 byte[] data = new byte[8]; 1961 int dataSize; 1962 1963 // Check if a new bus voltage/output voltage/current/temperature message 1964 // has arrived and unpack the values into the cached member variables 1965 try { 1966 getMessage(CANJNI.LM_API_PSTAT_DATA_S0, CANJNI.CAN_MSGID_FULL_M, data); 1967 1968 m_busVoltage = unpackFXP8_8(new byte[] { data[0], data[1] }); 1969 m_outputVoltage = unpackPercentage(new byte[] { data[2], data[3] }) * m_busVoltage; 1970 m_outputCurrent = unpackFXP8_8(new byte[] { data[4], data[5] }); 1971 m_temperature = unpackFXP8_8(new byte[] { data[6], data[7] }); 1972 1973 m_receivedStatusMessage0 = true; 1974 } catch(CANMessageNotFoundException e) {} 1975 1976 // Check if a new position/speed message has arrived and do the same 1977 try { 1978 getMessage(CANJNI.LM_API_PSTAT_DATA_S1, CANJNI.CAN_MSGID_FULL_M, data); 1979 1980 m_position = unpackFXP16_16(new byte[] { data[0], data[1], data[2], data[3] }); 1981 m_speed = unpackFXP16_16(new byte[] { data[4], data[5], data[6], data[7] }); 1982 1983 m_receivedStatusMessage1 = true; 1984 } catch(CANMessageNotFoundException e) {} 1985 1986 // Check if a new limits/faults message has arrived and do the same 1987 try { 1988 getMessage(CANJNI.LM_API_PSTAT_DATA_S2, CANJNI.CAN_MSGID_FULL_M, data); 1989 m_limits = data[0]; 1990 m_faults = data[1]; 1991 1992 m_receivedStatusMessage2 = true; 1993 } catch(CANMessageNotFoundException e) {} 1994 } 1995 1996 /** 1997 * Update all the motors that have pending sets in the syncGroup. 1998 * 1999 * @param syncGroup A bitmask of groups to generate synchronous output. 2000 */ 2001 public static void updateSyncGroup(byte syncGroup) { 2002 byte[] data = new byte[8]; 2003 2004 data[0] = syncGroup; 2005 2006 sendMessageHelper(CANJNI.CAN_MSGID_API_SYNC, data, 1, CANJNI.CAN_SEND_PERIOD_NO_REPEAT); 2007 } 2008 2009 /* we are on ARM-LE now, not Freescale so no need to swap */ 2010 private final static void swap16(int x, byte[] buffer) { 2011 buffer[0] = (byte)(x & 0xff); 2012 buffer[1] = (byte)((x>>8) & 0xff); 2013 } 2014 2015 private final static void swap32(int x, byte[] buffer) { 2016 buffer[0] = (byte)(x & 0xff); 2017 buffer[1] = (byte)((x>>8) & 0xff); 2018 buffer[2] = (byte)((x>>16) & 0xff); 2019 buffer[3] = (byte)((x>>24) & 0xff); 2020 } 2021 2022 private static final byte packPercentage(byte[] buffer, double value) { 2023 if(value < -1.0) value = -1.0; 2024 if(value > 1.0) value = 1.0; 2025 short intValue = (short) (value * 32767.0); 2026 swap16(intValue, buffer); 2027 return 2; 2028 } 2029 2030 private static final byte packFXP8_8(byte[] buffer, double value) { 2031 short intValue = (short) (value * 256.0); 2032 swap16(intValue, buffer); 2033 return 2; 2034 } 2035 2036 private static final byte packFXP16_16(byte[] buffer, double value) { 2037 int intValue = (int) (value * 65536.0); 2038 swap32(intValue, buffer); 2039 return 4; 2040 } 2041 2042 private static final byte packINT16(byte[] buffer, short value) { 2043 swap16(value, buffer); 2044 return 2; 2045 } 2046 2047 private static final byte packINT32(byte[] buffer, int value) { 2048 swap32(value, buffer); 2049 return 4; 2050 } 2051 2052 /** 2053 * Unpack 16-bit data from a buffer in little-endian byte order 2054 * @param buffer The buffer to unpack from 2055 * @param offset The offset into he buffer to unpack 2056 * @return The data that was unpacked 2057 */ 2058 private static final short unpack16(byte[] buffer, int offset) { 2059 return (short) ((buffer[offset] & 0xFF) | (short) ((buffer[offset + 1] << 8)) & 0xFF00); 2060 } 2061 2062 /** 2063 * Unpack 32-bit data from a buffer in little-endian byte order 2064 * @param buffer The buffer to unpack from 2065 * @param offset The offset into he buffer to unpack 2066 * @return The data that was unpacked 2067 */ 2068 private static final int unpack32(byte[] buffer, int offset) { 2069 return (buffer[offset] & 0xFF) | ((buffer[offset + 1] << 8) & 0xFF00) | 2070 ((buffer[offset + 2] << 16) & 0xFF0000) | ((buffer[offset + 3] << 24) & 0xFF000000); 2071 } 2072 2073 private static final double unpackPercentage(byte[] buffer) { 2074 return unpack16(buffer,0) / 32767.0; 2075 } 2076 2077 private static final double unpackFXP8_8(byte[] buffer) { 2078 return unpack16(buffer,0) / 256.0; 2079 } 2080 2081 private static final double unpackFXP16_16(byte[] buffer) { 2082 return unpack32(buffer,0) / 65536.0; 2083 } 2084 2085 private static final short unpackINT16(byte[] buffer) { 2086 return unpack16(buffer,0); 2087 } 2088 2089 private static final int unpackINT32(byte[] buffer) { 2090 return unpack32(buffer,0); 2091 } 2092 2093 /* Compare floats for equality as fixed point numbers */ 2094 public boolean FXP8_EQ(double a, double b) { 2095 return (int)(a * 256.0) == (int)(b * 256.0); 2096 } 2097 2098 /* Compare floats for equality as fixed point numbers */ 2099 public boolean FXP16_EQ(double a, double b) { 2100 return (int)(a * 65536.0) == (int)(b * 65536.0); 2101 } 2102 2103 @Override 2104 public void setExpiration(double timeout) { 2105 m_safetyHelper.setExpiration(timeout); 2106 } 2107 2108 @Override 2109 public double getExpiration() { 2110 return m_safetyHelper.getExpiration(); 2111 } 2112 2113 @Override 2114 public boolean isAlive() { 2115 return m_safetyHelper.isAlive(); 2116 } 2117 2118 @Override 2119 public boolean isSafetyEnabled() { 2120 return m_safetyHelper.isSafetyEnabled(); 2121 } 2122 2123 @Override 2124 public void setSafetyEnabled(boolean enabled) { 2125 m_safetyHelper.setSafetyEnabled(enabled); 2126 } 2127 2128 @Override 2129 public String getDescription() { 2130 return "CANJaguar ID "+m_deviceNumber; 2131 } 2132 2133 public int getDeviceID() { 2134 return (int)m_deviceNumber; 2135 } 2136 2137 /** 2138 * Common interface for stopping a motor. 2139 * 2140 * @deprecated Use disableControl instead. 2141 */ 2142 @Override 2143 @Deprecated 2144 public void stopMotor() { 2145 disableControl(); 2146 } 2147 2148 /* 2149 * Live Window code, only does anything if live window is activated. 2150 */ 2151 @Override 2152 public String getSmartDashboardType() { 2153 return "Speed Controller"; 2154 } 2155 private ITable m_table = null; 2156 private ITableListener m_table_listener = null; 2157 2158 /** 2159 * {@inheritDoc} 2160 */ 2161 @Override 2162 public void initTable(ITable subtable) { 2163 m_table = subtable; 2164 updateTable(); 2165 } 2166 2167 /** 2168 * {@inheritDoc} 2169 */ 2170 @Override 2171 public void updateTable() { 2172 if (m_table != null) { 2173 m_table.putNumber("Value", get()); 2174 } 2175 } 2176 2177 /** 2178 * {@inheritDoc} 2179 */ 2180 @Override 2181 public ITable getTable() { 2182 return m_table; 2183 } 2184 2185 /** 2186 * {@inheritDoc} 2187 */ 2188 @Override 2189 public void startLiveWindowMode() { 2190 set(0); // Stop for safety 2191 m_table_listener = new ITableListener() { 2192 @Override 2193 public void valueChanged(ITable itable, String key, Object value, boolean bln) { 2194 set(((Double) value).doubleValue()); 2195 } 2196 }; 2197 m_table.addTableListener("Value", m_table_listener, true); 2198 } 2199 2200 /** 2201 * {@inheritDoc} 2202 */ 2203 @Override 2204 public void stopLiveWindowMode() { 2205 set(0); // Stop for safety 2206 // TODO: Broken, should only remove the listener from "Value" only. 2207 m_table.removeTableListener(m_table_listener); 2208 } 2209}