001// Copyright (c) FIRST and other WPILib contributors. 002// Open Source Software; you can modify and/or share it under the terms of 003// the WPILib BSD license file in the root directory of this project. 004 005package edu.wpi.first.wpilibj; 006 007import edu.wpi.first.hal.FRCNetComm.tResourceType; 008import edu.wpi.first.hal.HAL; 009import edu.wpi.first.hal.PWMConfigDataResult; 010import edu.wpi.first.hal.PWMJNI; 011import edu.wpi.first.util.sendable.Sendable; 012import edu.wpi.first.util.sendable.SendableBuilder; 013import edu.wpi.first.util.sendable.SendableRegistry; 014 015/** 016 * Class implements the PWM generation in the FPGA. 017 * 018 * <p>The values supplied as arguments for PWM outputs range from -1.0 to 1.0. They are mapped to 019 * the microseconds to keep the pulse high, with a range of 0 (off) to 4096. Changes are immediately 020 * sent to the FPGA, and the update occurs at the next FPGA cycle (5.05ms). There is no delay. 021 */ 022public class PWM implements Sendable, AutoCloseable { 023 /** Represents the amount to multiply the minimum servo-pulse pwm period by. */ 024 public enum PeriodMultiplier { 025 /** Period Multiplier: don't skip pulses. PWM pulses occur every 5.05 ms */ 026 k1X, 027 /** Period Multiplier: skip every other pulse. PWM pulses occur every 10.10 ms */ 028 k2X, 029 /** Period Multiplier: skip three out of four pulses. PWM pulses occur every 20.20 ms */ 030 k4X 031 } 032 033 private final int m_channel; 034 035 private int m_handle; 036 037 /** 038 * Allocate a PWM given a channel. 039 * 040 * <p>Checks channel value range and allocates the appropriate channel. The allocation is only 041 * done to help users ensure that they don't double assign channels. 042 * 043 * <p>By default, adds itself to SendableRegistry and LiveWindow. 044 * 045 * @param channel The PWM channel number. 0-9 are on-board, 10-19 are on the MXP port 046 */ 047 public PWM(final int channel) { 048 this(channel, true); 049 } 050 051 /** 052 * Allocate a PWM given a channel. 053 * 054 * @param channel The PWM channel number. 0-9 are on-board, 10-19 are on the MXP port 055 * @param registerSendable If true, adds this instance to SendableRegistry and LiveWindow 056 */ 057 @SuppressWarnings("this-escape") 058 public PWM(final int channel, final boolean registerSendable) { 059 SensorUtil.checkPWMChannel(channel); 060 m_channel = channel; 061 062 m_handle = PWMJNI.initializePWMPort(HAL.getPort((byte) channel)); 063 064 setDisabled(); 065 066 PWMJNI.setPWMEliminateDeadband(m_handle, false); 067 068 HAL.report(tResourceType.kResourceType_PWM, channel + 1); 069 if (registerSendable) { 070 SendableRegistry.addLW(this, "PWM", channel); 071 } 072 } 073 074 /** Free the resource associated with the PWM channel and set the value to 0. */ 075 @Override 076 public void close() { 077 SendableRegistry.remove(this); 078 if (m_handle == 0) { 079 return; 080 } 081 setDisabled(); 082 PWMJNI.freePWMPort(m_handle); 083 m_handle = 0; 084 } 085 086 /** 087 * Optionally eliminate the deadband from a motor controller. 088 * 089 * @param eliminateDeadband If true, set the motor curve for the motor controller to eliminate the 090 * deadband in the middle of the range. Otherwise, keep the full range without modifying any 091 * values. 092 */ 093 public void enableDeadbandElimination(boolean eliminateDeadband) { 094 PWMJNI.setPWMEliminateDeadband(m_handle, eliminateDeadband); 095 } 096 097 /** 098 * Set the bounds on the PWM pulse widths. This sets the bounds on the PWM values for a particular 099 * type of controller. The values determine the upper and lower speeds as well as the deadband 100 * bracket. 101 * 102 * @param max The max PWM pulse width in us 103 * @param deadbandMax The high end of the deadband range pulse width in us 104 * @param center The center (off) pulse width in us 105 * @param deadbandMin The low end of the deadband pulse width in us 106 * @param min The minimum pulse width in us 107 */ 108 public void setBoundsMicroseconds( 109 int max, int deadbandMax, int center, int deadbandMin, int min) { 110 PWMJNI.setPWMConfigMicroseconds(m_handle, max, deadbandMax, center, deadbandMin, min); 111 } 112 113 /** 114 * Gets the bounds on the PWM pulse widths. This gets the bounds on the PWM values for a 115 * particular type of controller. The values determine the upper and lower speeds as well as the 116 * deadband bracket. 117 * 118 * @return The bounds on the PWM pulse widths. 119 */ 120 public PWMConfigDataResult getBoundsMicroseconds() { 121 return PWMJNI.getPWMConfigMicroseconds(m_handle); 122 } 123 124 /** 125 * Gets the channel number associated with the PWM Object. 126 * 127 * @return The channel number. 128 */ 129 public int getChannel() { 130 return m_channel; 131 } 132 133 /** 134 * Set the PWM value based on a position. 135 * 136 * <p>This is intended to be used by servos. 137 * 138 * @param pos The position to set the servo between 0.0 and 1.0. 139 * @pre setBoundsMicroseconds() called. 140 */ 141 public void setPosition(double pos) { 142 PWMJNI.setPWMPosition(m_handle, pos); 143 } 144 145 /** 146 * Get the PWM value in terms of a position. 147 * 148 * <p>This is intended to be used by servos. 149 * 150 * @return The position the servo is set to between 0.0 and 1.0. 151 * @pre setBoundsMicroseconds() called. 152 */ 153 public double getPosition() { 154 return PWMJNI.getPWMPosition(m_handle); 155 } 156 157 /** 158 * Set the PWM value based on a speed. 159 * 160 * <p>This is intended to be used by motor controllers. 161 * 162 * @param speed The speed to set the motor controller between -1.0 and 1.0. 163 * @pre setBoundsMicroseconds() called. 164 */ 165 public void setSpeed(double speed) { 166 PWMJNI.setPWMSpeed(m_handle, speed); 167 } 168 169 /** 170 * Get the PWM value in terms of speed. 171 * 172 * <p>This is intended to be used by motor controllers. 173 * 174 * @return The most recently set speed between -1.0 and 1.0. 175 * @pre setBoundsMicroseconds() called. 176 */ 177 public double getSpeed() { 178 return PWMJNI.getPWMSpeed(m_handle); 179 } 180 181 /** 182 * Set the PWM value directly to the hardware. 183 * 184 * <p>Write a microsecond pulse value to a PWM channel. 185 * 186 * @param microsecondPulseTime Microsecond pulse PWM value. Range 0 - 4096. 187 */ 188 public void setPulseTimeMicroseconds(int microsecondPulseTime) { 189 PWMJNI.setPulseTimeMicroseconds(m_handle, microsecondPulseTime); 190 } 191 192 /** 193 * Get the PWM value directly from the hardware. 194 * 195 * <p>Read a raw value from a PWM channel. 196 * 197 * @return Microsecond pulse PWM control value. Range: 0 - 4096. 198 */ 199 public int getPulseTimeMicroseconds() { 200 return PWMJNI.getPulseTimeMicroseconds(m_handle); 201 } 202 203 /** Temporarily disables the PWM output. The next set call will re-enable the output. */ 204 public final void setDisabled() { 205 PWMJNI.setPWMDisabled(m_handle); 206 } 207 208 /** 209 * Slow down the PWM signal for old devices. 210 * 211 * @param mult The period multiplier to apply to this channel 212 */ 213 public void setPeriodMultiplier(PeriodMultiplier mult) { 214 switch (mult) { 215 case k4X: 216 // Squelch 3 out of 4 outputs 217 PWMJNI.setPWMPeriodScale(m_handle, 3); 218 break; 219 case k2X: 220 // Squelch 1 out of 2 outputs 221 PWMJNI.setPWMPeriodScale(m_handle, 1); 222 break; 223 case k1X: 224 // Don't squelch any outputs 225 PWMJNI.setPWMPeriodScale(m_handle, 0); 226 break; 227 default: 228 // Cannot hit this, limited by PeriodMultiplier enum 229 } 230 } 231 232 /** Latches PWM to zero. */ 233 public void setZeroLatch() { 234 PWMJNI.latchPWMZero(m_handle); 235 } 236 237 /** Sets the PWM output to be a continuous high signal while enabled. */ 238 public void setAlwaysHighMode() { 239 PWMJNI.setAlwaysHighMode(m_handle); 240 } 241 242 /** 243 * Get the underlying handle. 244 * 245 * @return Underlying PWM handle 246 */ 247 public int getHandle() { 248 return m_handle; 249 } 250 251 @Override 252 public void initSendable(SendableBuilder builder) { 253 builder.setSmartDashboardType("PWM"); 254 builder.setActuator(true); 255 builder.setSafeState(this::setDisabled); 256 builder.addDoubleProperty( 257 "Value", this::getPulseTimeMicroseconds, value -> setPulseTimeMicroseconds((int) value)); 258 builder.addDoubleProperty("Speed", this::getSpeed, this::setSpeed); 259 builder.addDoubleProperty("Position", this::getPosition, this::setPosition); 260 } 261}