001/*----------------------------------------------------------------------------*/ 002/* Copyright (c) FIRST 2008-2017. 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 edu.wpi.first.wpilibj.hal.EncoderJNI; 011import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType; 012import edu.wpi.first.wpilibj.hal.HAL; 013import edu.wpi.first.wpilibj.livewindow.LiveWindow; 014import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable; 015import edu.wpi.first.wpilibj.tables.ITable; 016import edu.wpi.first.wpilibj.util.AllocationException; 017 018/** 019 * Class to read quadrature encoders. Quadrature encoders are devices that count shaft rotation and 020 * can sense direction. The output of the Encoder class is an integer that can count either up or 021 * down, and can go negative for reverse direction counting. When creating Encoders, a direction 022 * can be supplied that inverts the sense of the output to make code more readable if the encoder is 023 * mounted such that forward movement generates negative values. Quadrature encoders have two 024 * digital outputs, an A Channel and a B Channel, that are out of phase with each other for 025 * direction sensing. 026 * 027 * <p>All encoders will immediately start counting - reset() them if you need them to be zeroed 028 * before use. 029 */ 030public class Encoder extends SensorBase implements CounterBase, PIDSource, LiveWindowSendable { 031 032 public enum IndexingType { 033 kResetWhileHigh(0), kResetWhileLow(1), kResetOnFallingEdge(2), kResetOnRisingEdge(3); 034 035 @SuppressWarnings("MemberName") 036 public final int value; 037 038 IndexingType(int value) { 039 this.value = value; 040 } 041 } 042 043 /** 044 * The a source. 045 */ 046 @SuppressWarnings("MemberName") 047 protected DigitalSource m_aSource; // the A phase of the quad encoder 048 /** 049 * The b source. 050 */ 051 @SuppressWarnings("MemberName") 052 protected DigitalSource m_bSource; // the B phase of the quad encoder 053 /** 054 * The index source. 055 */ 056 protected DigitalSource m_indexSource = null; // Index on some encoders 057 private boolean m_allocatedA; 058 private boolean m_allocatedB; 059 private boolean m_allocatedI; 060 private PIDSourceType m_pidSource; 061 062 private int m_encoder; // the HAL encoder object 063 064 065 /** 066 * Common initialization code for Encoders. This code allocates resources for Encoders and is 067 * common to all constructors. 068 * 069 * <p>The encoder will start counting immediately. 070 * 071 * @param reverseDirection If true, counts down instead of up (this is all relative) 072 */ 073 private void initEncoder(boolean reverseDirection, final EncodingType type) { 074 m_encoder = EncoderJNI.initializeEncoder(m_aSource.getPortHandleForRouting(), 075 m_aSource.getAnalogTriggerTypeForRouting(), m_bSource.getPortHandleForRouting(), 076 m_bSource.getAnalogTriggerTypeForRouting(), reverseDirection, type.value); 077 078 m_pidSource = PIDSourceType.kDisplacement; 079 080 HAL.report(tResourceType.kResourceType_Encoder, getFPGAIndex(), type.value); 081 LiveWindow.addSensor("Encoder", m_aSource.getChannel(), this); 082 } 083 084 /** 085 * Encoder constructor. Construct a Encoder given a and b channels. 086 * 087 * <p>The encoder will start counting immediately. 088 * 089 * @param channelA The a channel DIO channel. 0-9 are on-board, 10-25 are on the MXP port 090 * @param channelB The b channel DIO channel. 0-9 are on-board, 10-25 are on the MXP port 091 * @param reverseDirection represents the orientation of the encoder and inverts the output values 092 * if necessary so forward represents positive values. 093 */ 094 public Encoder(final int channelA, final int channelB, boolean reverseDirection) { 095 m_allocatedA = true; 096 m_allocatedB = true; 097 m_allocatedI = false; 098 m_aSource = new DigitalInput(channelA); 099 m_bSource = new DigitalInput(channelB); 100 initEncoder(reverseDirection, EncodingType.k4X); 101 } 102 103 /** 104 * Encoder constructor. Construct a Encoder given a and b channels. 105 * 106 * <p>The encoder will start counting immediately. 107 * 108 * @param channelA The a channel digital input channel. 109 * @param channelB The b channel digital input channel. 110 */ 111 public Encoder(final int channelA, final int channelB) { 112 this(channelA, channelB, false); 113 } 114 115 /** 116 * Encoder constructor. Construct a Encoder given a and b channels. 117 * 118 * <p>The encoder will start counting immediately. 119 * 120 * @param channelA The a channel digital input channel. 121 * @param channelB The b channel digital input channel. 122 * @param reverseDirection represents the orientation of the encoder and inverts the output values 123 * if necessary so forward represents positive values. 124 * @param encodingType either k1X, k2X, or k4X to indicate 1X, 2X or 4X decoding. If 4X is 125 * selected, then an encoder FPGA object is used and the returned counts 126 * will be 4x the encoder spec'd value since all rising and falling edges 127 * are counted. If 1X or 2X are selected then a m_counter object will be 128 * used and the returned value will either exactly match the spec'd count 129 * or be double (2x) the spec'd count. 130 */ 131 public Encoder(final int channelA, final int channelB, boolean reverseDirection, 132 final EncodingType encodingType) { 133 m_allocatedA = true; 134 m_allocatedB = true; 135 m_allocatedI = false; 136 if (encodingType == null) { 137 throw new NullPointerException("Given encoding type was null"); 138 } 139 m_aSource = new DigitalInput(channelA); 140 m_bSource = new DigitalInput(channelB); 141 initEncoder(reverseDirection, encodingType); 142 } 143 144 /** 145 * Encoder constructor. Construct a Encoder given a and b channels. Using an index pulse forces 4x 146 * encoding 147 * 148 * <p>The encoder will start counting immediately. 149 * 150 * @param channelA The a channel digital input channel. 151 * @param channelB The b channel digital input channel. 152 * @param indexChannel The index channel digital input channel. 153 * @param reverseDirection represents the orientation of the encoder and inverts the output values 154 * if necessary so forward represents positive values. 155 */ 156 public Encoder(final int channelA, final int channelB, final int indexChannel, 157 boolean reverseDirection) { 158 m_allocatedA = true; 159 m_allocatedB = true; 160 m_allocatedI = true; 161 m_aSource = new DigitalInput(channelA); 162 m_bSource = new DigitalInput(channelB); 163 m_indexSource = new DigitalInput(indexChannel); 164 initEncoder(reverseDirection, EncodingType.k4X); 165 setIndexSource(m_indexSource); 166 } 167 168 /** 169 * Encoder constructor. Construct a Encoder given a and b channels. Using an index pulse forces 4x 170 * encoding 171 * 172 * <p>The encoder will start counting immediately. 173 * 174 * @param channelA The a channel digital input channel. 175 * @param channelB The b channel digital input channel. 176 * @param indexChannel The index channel digital input channel. 177 */ 178 public Encoder(final int channelA, final int channelB, final int indexChannel) { 179 this(channelA, channelB, indexChannel, false); 180 } 181 182 /** 183 * Encoder constructor. Construct a Encoder given a and b channels as digital inputs. This is used 184 * in the case where the digital inputs are shared. The Encoder class will not allocate the 185 * digital inputs and assume that they already are counted. 186 * 187 * <p>The encoder will start counting immediately. 188 * 189 * @param sourceA The source that should be used for the a channel. 190 * @param sourceB the source that should be used for the b channel. 191 * @param reverseDirection represents the orientation of the encoder and inverts the output values 192 * if necessary so forward represents positive values. 193 */ 194 public Encoder(DigitalSource sourceA, DigitalSource sourceB, boolean reverseDirection) { 195 m_allocatedA = false; 196 m_allocatedB = false; 197 m_allocatedI = false; 198 if (sourceA == null) { 199 throw new NullPointerException("Digital Source A was null"); 200 } 201 m_aSource = sourceA; 202 if (sourceB == null) { 203 throw new NullPointerException("Digital Source B was null"); 204 } 205 m_bSource = sourceB; 206 initEncoder(reverseDirection, EncodingType.k4X); 207 } 208 209 /** 210 * Encoder constructor. Construct a Encoder given a and b channels as digital inputs. This is used 211 * in the case where the digital inputs are shared. The Encoder class will not allocate the 212 * digital inputs and assume that they already are counted. 213 * 214 * <p>The encoder will start counting immediately. 215 * 216 * @param sourceA The source that should be used for the a channel. 217 * @param sourceB the source that should be used for the b channel. 218 */ 219 public Encoder(DigitalSource sourceA, DigitalSource sourceB) { 220 this(sourceA, sourceB, false); 221 } 222 223 /** 224 * Encoder constructor. Construct a Encoder given a and b channels as digital inputs. This is used 225 * in the case where the digital inputs are shared. The Encoder class will not allocate the 226 * digital inputs and assume that they already are counted. 227 * 228 * <p>The encoder will start counting immediately. 229 * 230 * @param sourceA The source that should be used for the a channel. 231 * @param sourceB the source that should be used for the b channel. 232 * @param reverseDirection represents the orientation of the encoder and inverts the output values 233 * if necessary so forward represents positive values. 234 * @param encodingType either k1X, k2X, or k4X to indicate 1X, 2X or 4X decoding. If 4X is 235 * selected, then an encoder FPGA object is used and the returned counts 236 * will be 4x the encoder spec'd value since all rising and falling edges 237 * are counted. If 1X or 2X are selected then a m_counter object will be 238 * used and the returned value will either exactly match the spec'd count 239 * or be double (2x) the spec'd count. 240 */ 241 public Encoder(DigitalSource sourceA, DigitalSource sourceB, boolean reverseDirection, 242 final EncodingType encodingType) { 243 m_allocatedA = false; 244 m_allocatedB = false; 245 m_allocatedI = false; 246 if (encodingType == null) { 247 throw new NullPointerException("Given encoding type was null"); 248 } 249 if (sourceA == null) { 250 throw new NullPointerException("Digital Source A was null"); 251 } 252 m_aSource = sourceA; 253 if (sourceB == null) { 254 throw new NullPointerException("Digital Source B was null"); 255 } 256 m_aSource = sourceA; 257 m_bSource = sourceB; 258 initEncoder(reverseDirection, encodingType); 259 } 260 261 /** 262 * Encoder constructor. Construct a Encoder given a, b and index channels as digital inputs. This 263 * is used in the case where the digital inputs are shared. The Encoder class will not allocate 264 * the digital inputs and assume that they already are counted. 265 * 266 * <p>The encoder will start counting immediately. 267 * 268 * @param sourceA The source that should be used for the a channel. 269 * @param sourceB the source that should be used for the b channel. 270 * @param indexSource the source that should be used for the index channel. 271 * @param reverseDirection represents the orientation of the encoder and inverts the output values 272 * if necessary so forward represents positive values. 273 */ 274 public Encoder(DigitalSource sourceA, DigitalSource sourceB, DigitalSource indexSource, 275 boolean reverseDirection) { 276 m_allocatedA = false; 277 m_allocatedB = false; 278 m_allocatedI = false; 279 if (sourceB == null) { 280 throw new NullPointerException("Digital Source A was null"); 281 } 282 m_aSource = sourceA; 283 if (sourceB == null) { 284 throw new NullPointerException("Digital Source B was null"); 285 } 286 m_aSource = sourceA; 287 m_bSource = sourceB; 288 m_indexSource = indexSource; 289 initEncoder(reverseDirection, EncodingType.k4X); 290 setIndexSource(indexSource); 291 } 292 293 /** 294 * Encoder constructor. Construct a Encoder given a, b and index channels as digital inputs. This 295 * is used in the case where the digital inputs are shared. The Encoder class will not allocate 296 * the digital inputs and assume that they already are counted. 297 * 298 * <p>The encoder will start counting immediately. 299 * 300 * @param sourceA The source that should be used for the a channel. 301 * @param sourceB the source that should be used for the b channel. 302 * @param indexSource the source that should be used for the index channel. 303 */ 304 public Encoder(DigitalSource sourceA, DigitalSource sourceB, DigitalSource indexSource) { 305 this(sourceA, sourceB, indexSource, false); 306 } 307 308 /** 309 * @return The Encoder's FPGA index. 310 */ 311 @SuppressWarnings("AbbreviationAsWordInName") 312 public int getFPGAIndex() { 313 return EncoderJNI.getEncoderFPGAIndex(m_encoder); 314 } 315 316 /** 317 * Used to divide raw edge counts down to spec'd counts. 318 * 319 * @return The encoding scale factor 1x, 2x, or 4x, per the requested encoding type. 320 */ 321 public int getEncodingScale() { 322 return EncoderJNI.getEncoderEncodingScale(m_encoder); 323 } 324 325 /** 326 * Free the resources used by this object. 327 */ 328 public void free() { 329 if (m_aSource != null && m_allocatedA) { 330 m_aSource.free(); 331 m_allocatedA = false; 332 } 333 if (m_bSource != null && m_allocatedB) { 334 m_bSource.free(); 335 m_allocatedB = false; 336 } 337 if (m_indexSource != null && m_allocatedI) { 338 m_indexSource.free(); 339 m_allocatedI = false; 340 } 341 342 m_aSource = null; 343 m_bSource = null; 344 m_indexSource = null; 345 EncoderJNI.freeEncoder(m_encoder); 346 m_encoder = 0; 347 } 348 349 /** 350 * Gets the raw value from the encoder. The raw value is the actual count unscaled by the 1x, 2x, 351 * or 4x scale factor. 352 * 353 * @return Current raw count from the encoder 354 */ 355 public int getRaw() { 356 return EncoderJNI.getEncoderRaw(m_encoder); 357 } 358 359 /** 360 * Gets the current count. Returns the current count on the Encoder. This method compensates for 361 * the decoding type. 362 * 363 * @return Current count from the Encoder adjusted for the 1x, 2x, or 4x scale factor. 364 */ 365 public int get() { 366 return EncoderJNI.getEncoder(m_encoder); 367 } 368 369 /** 370 * Reset the Encoder distance to zero. Resets the current count to zero on the encoder. 371 */ 372 public void reset() { 373 EncoderJNI.resetEncoder(m_encoder); 374 } 375 376 /** 377 * Returns the period of the most recent pulse. Returns the period of the most recent Encoder 378 * pulse in seconds. This method compensates for the decoding type. 379 * 380 * <p><b>Warning:</b> This returns unscaled periods and getRate() scales using value from 381 * setDistancePerPulse(). 382 * 383 * @return Period in seconds of the most recent pulse. 384 * @deprecated Use getRate() in favor of this method. 385 */ 386 @Deprecated 387 public double getPeriod() { 388 return EncoderJNI.getEncoderPeriod(m_encoder); 389 } 390 391 /** 392 * Sets the maximum period for stopped detection. Sets the value that represents the maximum 393 * period of the Encoder before it will assume that the attached device is stopped. This timeout 394 * allows users to determine if the wheels or other shaft has stopped rotating. This method 395 * compensates for the decoding type. 396 * 397 * @param maxPeriod The maximum time between rising and falling edges before the FPGA will report 398 * the device stopped. This is expressed in seconds. 399 */ 400 public void setMaxPeriod(double maxPeriod) { 401 EncoderJNI.setEncoderMaxPeriod(m_encoder, maxPeriod); 402 } 403 404 /** 405 * Determine if the encoder is stopped. Using the MaxPeriod value, a boolean is returned that is 406 * true if the encoder is considered stopped and false if it is still moving. A stopped encoder is 407 * one where the most recent pulse width exceeds the MaxPeriod. 408 * 409 * @return True if the encoder is considered stopped. 410 */ 411 public boolean getStopped() { 412 return EncoderJNI.getEncoderStopped(m_encoder); 413 } 414 415 /** 416 * The last direction the encoder value changed. 417 * 418 * @return The last direction the encoder value changed. 419 */ 420 public boolean getDirection() { 421 return EncoderJNI.getEncoderDirection(m_encoder); 422 } 423 424 /** 425 * Get the distance the robot has driven since the last reset as scaled by the value from {@link 426 * #setDistancePerPulse(double)}. 427 * 428 * @return The distance driven since the last reset 429 */ 430 public double getDistance() { 431 return EncoderJNI.getEncoderDistance(m_encoder); 432 } 433 434 /** 435 * Get the current rate of the encoder. Units are distance per second as scaled by the value from 436 * setDistancePerPulse(). 437 * 438 * @return The current rate of the encoder. 439 */ 440 public double getRate() { 441 return EncoderJNI.getEncoderRate(m_encoder); 442 } 443 444 /** 445 * Set the minimum rate of the device before the hardware reports it stopped. 446 * 447 * @param minRate The minimum rate. The units are in distance per second as scaled by the value 448 * from setDistancePerPulse(). 449 */ 450 public void setMinRate(double minRate) { 451 EncoderJNI.setEncoderMinRate(m_encoder, minRate); 452 } 453 454 /** 455 * Set the distance per pulse for this encoder. This sets the multiplier used to determine the 456 * distance driven based on the count value from the encoder. Do not include the decoding type in 457 * this scale. The library already compensates for the decoding type. Set this value based on the 458 * encoder's rated Pulses per Revolution and factor in gearing reductions following the encoder 459 * shaft. This distance can be in any units you like, linear or angular. 460 * 461 * @param distancePerPulse The scale factor that will be used to convert pulses to useful units. 462 */ 463 public void setDistancePerPulse(double distancePerPulse) { 464 EncoderJNI.setEncoderDistancePerPulse(m_encoder, distancePerPulse); 465 } 466 467 /** 468 * Set the direction sensing for this encoder. This sets the direction sensing on the encoder so 469 * that it could count in the correct software direction regardless of the mounting. 470 * 471 * @param reverseDirection true if the encoder direction should be reversed 472 */ 473 public void setReverseDirection(boolean reverseDirection) { 474 EncoderJNI.setEncoderReverseDirection(m_encoder, reverseDirection); 475 } 476 477 /** 478 * Set the Samples to Average which specifies the number of samples of the timer to average when 479 * calculating the period. Perform averaging to account for mechanical imperfections or as 480 * oversampling to increase resolution. 481 * 482 * @param samplesToAverage The number of samples to average from 1 to 127. 483 */ 484 public void setSamplesToAverage(int samplesToAverage) { 485 EncoderJNI.setEncoderSamplesToAverage(m_encoder, samplesToAverage); 486 } 487 488 /** 489 * Get the Samples to Average which specifies the number of samples of the timer to average when 490 * calculating the period. Perform averaging to account for mechanical imperfections or as 491 * oversampling to increase resolution. 492 * 493 * @return SamplesToAverage The number of samples being averaged (from 1 to 127) 494 */ 495 public int getSamplesToAverage() { 496 return EncoderJNI.getEncoderSamplesToAverage(m_encoder); 497 } 498 499 /** 500 * Set which parameter of the encoder you are using as a process control variable. The encoder 501 * class supports the rate and distance parameters. 502 * 503 * @param pidSource An enum to select the parameter. 504 */ 505 public void setPIDSourceType(PIDSourceType pidSource) { 506 m_pidSource = pidSource; 507 } 508 509 @Override 510 public PIDSourceType getPIDSourceType() { 511 return m_pidSource; 512 } 513 514 /** 515 * Implement the PIDSource interface. 516 * 517 * @return The current value of the selected source parameter. 518 */ 519 public double pidGet() { 520 switch (m_pidSource) { 521 case kDisplacement: 522 return getDistance(); 523 case kRate: 524 return getRate(); 525 default: 526 return 0.0; 527 } 528 } 529 530 /** 531 * Set the index source for the encoder. When this source is activated, the encoder count 532 * automatically resets. 533 * 534 * @param channel A DIO channel to set as the encoder index 535 */ 536 public void setIndexSource(int channel) { 537 setIndexSource(channel, IndexingType.kResetOnRisingEdge); 538 } 539 540 /** 541 * Set the index source for the encoder. When this source is activated, the encoder count 542 * automatically resets. 543 * 544 * @param source A digital source to set as the encoder index 545 */ 546 public void setIndexSource(DigitalSource source) { 547 setIndexSource(source, IndexingType.kResetOnRisingEdge); 548 } 549 550 /** 551 * Set the index source for the encoder. When this source rises, the encoder count automatically 552 * resets. 553 * 554 * @param channel A DIO channel to set as the encoder index 555 * @param type The state that will cause the encoder to reset 556 */ 557 public void setIndexSource(int channel, IndexingType type) { 558 if (m_allocatedI) { 559 throw new AllocationException("Digital Input for Indexing already allocated"); 560 } 561 m_indexSource = new DigitalInput(channel); 562 m_allocatedI = true; 563 setIndexSource(m_indexSource, type); 564 } 565 566 /** 567 * Set the index source for the encoder. When this source rises, the encoder count automatically 568 * resets. 569 * 570 * @param source A digital source to set as the encoder index 571 * @param type The state that will cause the encoder to reset 572 */ 573 public void setIndexSource(DigitalSource source, IndexingType type) { 574 EncoderJNI.setEncoderIndexSource(m_encoder, source.getPortHandleForRouting(), 575 source.getAnalogTriggerTypeForRouting(), type.value); 576 } 577 578 /** 579 * Live Window code, only does anything if live window is activated. 580 */ 581 public String getSmartDashboardType() { 582 if (EncoderJNI.getEncoderEncodingType(m_encoder) == EncodingType.k4X.value) { 583 return "Quadrature Encoder"; 584 } 585 return "Encoder"; 586 } 587 588 private ITable m_table; 589 590 @Override 591 public void initTable(ITable subtable) { 592 m_table = subtable; 593 updateTable(); 594 } 595 596 @Override 597 public ITable getTable() { 598 return m_table; 599 } 600 601 @Override 602 public void updateTable() { 603 if (m_table != null) { 604 m_table.putNumber("Speed", getRate()); 605 m_table.putNumber("Distance", getDistance()); 606 m_table.putNumber("Distance per Tick", EncoderJNI.getEncoderDistancePerPulse(m_encoder)); 607 } 608 } 609 610 @Override 611 public void startLiveWindowMode() { 612 } 613 614 @Override 615 public void stopLiveWindowMode() { 616 } 617}