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 hardware dependent values, in this case 0-2000 for the FPGA. Changes are immediately sent to 020 * the FPGA, and the update occurs at the next FPGA cycle (5.005ms). There is no delay. 021 * 022 * <p>As of revision 0.1.10 of the FPGA, the FPGA interprets the 0-2000 values as follows: - 2000 = 023 * maximum pulse width - 1999 to 1001 = linear scaling from "full forward" to "center" - 1000 = 024 * center value - 999 to 2 = linear scaling from "center" to "full reverse" - 1 = minimum pulse 025 * width (currently .5ms) - 0 = disabled (i.e. PWM output is held low) 026 */ 027public class PWM implements Sendable, AutoCloseable { 028 /** Represents the amount to multiply the minimum servo-pulse pwm period by. */ 029 public enum PeriodMultiplier { 030 /** Period Multiplier: don't skip pulses. PWM pulses occur every 5.005 ms */ 031 k1X, 032 /** Period Multiplier: skip every other pulse. PWM pulses occur every 10.010 ms */ 033 k2X, 034 /** Period Multiplier: skip three out of four pulses. PWM pulses occur every 20.020 ms */ 035 k4X 036 } 037 038 private final int m_channel; 039 040 private int m_handle; 041 042 /** 043 * Allocate a PWM given a channel. 044 * 045 * <p>Checks channel value range and allocates the appropriate channel. The allocation is only 046 * done to help users ensure that they don't double assign channels. 047 * 048 * <p>By default, adds itself to SendableRegistry and LiveWindow. 049 * 050 * @param channel The PWM channel number. 0-9 are on-board, 10-19 are on the MXP port 051 */ 052 public PWM(final int channel) { 053 this(channel, true); 054 } 055 056 /** 057 * Allocate a PWM given a channel. 058 * 059 * @param channel The PWM channel number. 0-9 are on-board, 10-19 are on the MXP port 060 * @param registerSendable If true, adds this instance to SendableRegistry and LiveWindow 061 */ 062 public PWM(final int channel, final boolean registerSendable) { 063 SensorUtil.checkPWMChannel(channel); 064 m_channel = channel; 065 066 m_handle = PWMJNI.initializePWMPort(HAL.getPort((byte) channel)); 067 068 setDisabled(); 069 070 PWMJNI.setPWMEliminateDeadband(m_handle, false); 071 072 HAL.report(tResourceType.kResourceType_PWM, channel + 1); 073 if (registerSendable) { 074 SendableRegistry.addLW(this, "PWM", channel); 075 } 076 } 077 078 /** Free the resource associated with the PWM channel and set the value to 0. */ 079 @Override 080 public void close() { 081 SendableRegistry.remove(this); 082 if (m_handle == 0) { 083 return; 084 } 085 setDisabled(); 086 PWMJNI.freePWMPort(m_handle); 087 m_handle = 0; 088 } 089 090 /** 091 * Optionally eliminate the deadband from a motor controller. 092 * 093 * @param eliminateDeadband If true, set the motor curve for the motor controller to eliminate the 094 * deadband in the middle of the range. Otherwise, keep the full range without modifying any 095 * values. 096 */ 097 public void enableDeadbandElimination(boolean eliminateDeadband) { 098 PWMJNI.setPWMEliminateDeadband(m_handle, eliminateDeadband); 099 } 100 101 /** 102 * Set the bounds on the PWM pulse widths. This sets the bounds on the PWM values for a particular 103 * type of controller. The values determine the upper and lower speeds as well as the deadband 104 * bracket. 105 * 106 * @param max The max PWM pulse width in ms 107 * @param deadbandMax The high end of the deadband range pulse width in ms 108 * @param center The center (off) pulse width in ms 109 * @param deadbandMin The low end of the deadband pulse width in ms 110 * @param min The minimum pulse width in ms 111 */ 112 public void setBounds( 113 double max, double deadbandMax, double center, double deadbandMin, double min) { 114 PWMJNI.setPWMConfig(m_handle, max, deadbandMax, center, deadbandMin, min); 115 } 116 117 /** 118 * Gets the bounds on the PWM pulse widths. This gets the bounds on the PWM values for a 119 * particular type of controller. The values determine the upper and lower speeds as well as the 120 * deadband bracket. 121 * 122 * @return The bounds on the PWM pulse widths. 123 */ 124 public PWMConfigDataResult getRawBounds() { 125 return PWMJNI.getPWMConfigRaw(m_handle); 126 } 127 128 /** 129 * Gets the channel number associated with the PWM Object. 130 * 131 * @return The channel number. 132 */ 133 public int getChannel() { 134 return m_channel; 135 } 136 137 /** 138 * Set the PWM value based on a position. 139 * 140 * <p>This is intended to be used by servos. 141 * 142 * @param pos The position to set the servo between 0.0 and 1.0. 143 * @pre SetMaxPositivePwm() called. 144 * @pre SetMinNegativePwm() called. 145 */ 146 public void setPosition(double pos) { 147 PWMJNI.setPWMPosition(m_handle, pos); 148 } 149 150 /** 151 * Get the PWM value in terms of a position. 152 * 153 * <p>This is intended to be used by servos. 154 * 155 * @return The position the servo is set to between 0.0 and 1.0. 156 * @pre SetMaxPositivePwm() called. 157 * @pre SetMinNegativePwm() called. 158 */ 159 public double getPosition() { 160 return PWMJNI.getPWMPosition(m_handle); 161 } 162 163 /** 164 * Set the PWM value based on a speed. 165 * 166 * <p>This is intended to be used by motor controllers. 167 * 168 * @param speed The speed to set the motor controller between -1.0 and 1.0. 169 * @pre SetMaxPositivePwm() called. 170 * @pre SetMinPositivePwm() called. 171 * @pre SetCenterPwm() called. 172 * @pre SetMaxNegativePwm() called. 173 * @pre SetMinNegativePwm() called. 174 */ 175 public void setSpeed(double speed) { 176 PWMJNI.setPWMSpeed(m_handle, speed); 177 } 178 179 /** 180 * Get the PWM value in terms of speed. 181 * 182 * <p>This is intended to be used by motor controllers. 183 * 184 * @return The most recently set speed between -1.0 and 1.0. 185 * @pre SetMaxPositivePwm() called. 186 * @pre SetMinPositivePwm() called. 187 * @pre SetMaxNegativePwm() called. 188 * @pre SetMinNegativePwm() called. 189 */ 190 public double getSpeed() { 191 return PWMJNI.getPWMSpeed(m_handle); 192 } 193 194 /** 195 * Set the PWM value directly to the hardware. 196 * 197 * <p>Write a raw value to a PWM channel. 198 * 199 * @param value Raw PWM value. Range 0 - 255. 200 */ 201 public void setRaw(int value) { 202 PWMJNI.setPWMRaw(m_handle, (short) value); 203 } 204 205 /** 206 * Get the PWM value directly from the hardware. 207 * 208 * <p>Read a raw value from a PWM channel. 209 * 210 * @return Raw PWM control value. Range: 0 - 255. 211 */ 212 public int getRaw() { 213 return PWMJNI.getPWMRaw(m_handle); 214 } 215 216 /** Temporarily disables the PWM output. The next set call will reenable the output. */ 217 public void setDisabled() { 218 PWMJNI.setPWMDisabled(m_handle); 219 } 220 221 /** 222 * Slow down the PWM signal for old devices. 223 * 224 * @param mult The period multiplier to apply to this channel 225 */ 226 public void setPeriodMultiplier(PeriodMultiplier mult) { 227 switch (mult) { 228 case k4X: 229 // Squelch 3 out of 4 outputs 230 PWMJNI.setPWMPeriodScale(m_handle, 3); 231 break; 232 case k2X: 233 // Squelch 1 out of 2 outputs 234 PWMJNI.setPWMPeriodScale(m_handle, 1); 235 break; 236 case k1X: 237 // Don't squelch any outputs 238 PWMJNI.setPWMPeriodScale(m_handle, 0); 239 break; 240 default: 241 // Cannot hit this, limited by PeriodMultiplier enum 242 } 243 } 244 245 public void setZeroLatch() { 246 PWMJNI.latchPWMZero(m_handle); 247 } 248 249 /** 250 * Get the underlying handle. 251 * 252 * @return Underlying PWM handle 253 */ 254 public int getHandle() { 255 return m_handle; 256 } 257 258 @Override 259 public void initSendable(SendableBuilder builder) { 260 builder.setSmartDashboardType("PWM"); 261 builder.setActuator(true); 262 builder.setSafeState(this::setDisabled); 263 builder.addDoubleProperty("Value", this::getRaw, value -> setRaw((int) value)); 264 } 265}