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 public PWM(final int channel, final boolean registerSendable) { 058 SensorUtil.checkPWMChannel(channel); 059 m_channel = channel; 060 061 m_handle = PWMJNI.initializePWMPort(HAL.getPort((byte) channel)); 062 063 setDisabled(); 064 065 PWMJNI.setPWMEliminateDeadband(m_handle, false); 066 067 HAL.report(tResourceType.kResourceType_PWM, channel + 1); 068 if (registerSendable) { 069 SendableRegistry.addLW(this, "PWM", channel); 070 } 071 } 072 073 /** Free the resource associated with the PWM channel and set the value to 0. */ 074 @Override 075 public void close() { 076 SendableRegistry.remove(this); 077 if (m_handle == 0) { 078 return; 079 } 080 setDisabled(); 081 PWMJNI.freePWMPort(m_handle); 082 m_handle = 0; 083 } 084 085 /** 086 * Optionally eliminate the deadband from a motor controller. 087 * 088 * @param eliminateDeadband If true, set the motor curve for the motor controller to eliminate the 089 * deadband in the middle of the range. Otherwise, keep the full range without modifying any 090 * values. 091 */ 092 public void enableDeadbandElimination(boolean eliminateDeadband) { 093 PWMJNI.setPWMEliminateDeadband(m_handle, eliminateDeadband); 094 } 095 096 /** 097 * Set the bounds on the PWM pulse widths. This sets the bounds on the PWM values for a particular 098 * type of controller. The values determine the upper and lower speeds as well as the deadband 099 * bracket. 100 * 101 * @param max The max PWM pulse width in us 102 * @param deadbandMax The high end of the deadband range pulse width in us 103 * @param center The center (off) pulse width in us 104 * @param deadbandMin The low end of the deadband pulse width in us 105 * @param min The minimum pulse width in us 106 */ 107 public void setBoundsMicroseconds( 108 int max, int deadbandMax, int center, int deadbandMin, int min) { 109 PWMJNI.setPWMConfigMicroseconds(m_handle, max, deadbandMax, center, deadbandMin, min); 110 } 111 112 /** 113 * Gets the bounds on the PWM pulse widths. This gets the bounds on the PWM values for a 114 * particular type of controller. The values determine the upper and lower speeds as well as the 115 * deadband bracket. 116 * 117 * @return The bounds on the PWM pulse widths. 118 */ 119 public PWMConfigDataResult getBoundsMicroseconds() { 120 return PWMJNI.getPWMConfigMicroseconds(m_handle); 121 } 122 123 /** 124 * Gets the channel number associated with the PWM Object. 125 * 126 * @return The channel number. 127 */ 128 public int getChannel() { 129 return m_channel; 130 } 131 132 /** 133 * Set the PWM value based on a position. 134 * 135 * <p>This is intended to be used by servos. 136 * 137 * @param pos The position to set the servo between 0.0 and 1.0. 138 * @pre setBoundsMicroseconds() called. 139 */ 140 public void setPosition(double pos) { 141 PWMJNI.setPWMPosition(m_handle, pos); 142 } 143 144 /** 145 * Get the PWM value in terms of a position. 146 * 147 * <p>This is intended to be used by servos. 148 * 149 * @return The position the servo is set to between 0.0 and 1.0. 150 * @pre setBoundsMicroseconds() called. 151 */ 152 public double getPosition() { 153 return PWMJNI.getPWMPosition(m_handle); 154 } 155 156 /** 157 * Set the PWM value based on a speed. 158 * 159 * <p>This is intended to be used by motor controllers. 160 * 161 * @param speed The speed to set the motor controller between -1.0 and 1.0. 162 * @pre setBoundsMicroseconds() called. 163 */ 164 public void setSpeed(double speed) { 165 PWMJNI.setPWMSpeed(m_handle, speed); 166 } 167 168 /** 169 * Get the PWM value in terms of speed. 170 * 171 * <p>This is intended to be used by motor controllers. 172 * 173 * @return The most recently set speed between -1.0 and 1.0. 174 * @pre setBoundsMicroseconds() called. 175 */ 176 public double getSpeed() { 177 return PWMJNI.getPWMSpeed(m_handle); 178 } 179 180 /** 181 * Set the PWM value directly to the hardware. 182 * 183 * <p>Write a microsecond pulse value to a PWM channel. 184 * 185 * @param microsecondPulseTime Microsecond pulse PWM value. Range 0 - 4096. 186 */ 187 public void setPulseTimeMicroseconds(int microsecondPulseTime) { 188 PWMJNI.setPulseTimeMicroseconds(m_handle, microsecondPulseTime); 189 } 190 191 /** 192 * Get the PWM value directly from the hardware. 193 * 194 * <p>Read a raw value from a PWM channel. 195 * 196 * @return Microsecond pulse PWM control value. Range: 0 - 4096. 197 */ 198 public int getPulseTimeMicroseconds() { 199 return PWMJNI.getPulseTimeMicroseconds(m_handle); 200 } 201 202 /** Temporarily disables the PWM output. The next set call will re-enable the output. */ 203 public void setDisabled() { 204 PWMJNI.setPWMDisabled(m_handle); 205 } 206 207 /** 208 * Slow down the PWM signal for old devices. 209 * 210 * @param mult The period multiplier to apply to this channel 211 */ 212 public void setPeriodMultiplier(PeriodMultiplier mult) { 213 switch (mult) { 214 case k4X: 215 // Squelch 3 out of 4 outputs 216 PWMJNI.setPWMPeriodScale(m_handle, 3); 217 break; 218 case k2X: 219 // Squelch 1 out of 2 outputs 220 PWMJNI.setPWMPeriodScale(m_handle, 1); 221 break; 222 case k1X: 223 // Don't squelch any outputs 224 PWMJNI.setPWMPeriodScale(m_handle, 0); 225 break; 226 default: 227 // Cannot hit this, limited by PeriodMultiplier enum 228 } 229 } 230 231 public void setZeroLatch() { 232 PWMJNI.latchPWMZero(m_handle); 233 } 234 235 /** Sets the PWM output to be a continous high signal while enabled. */ 236 public void setAlwaysHighMode() { 237 PWMJNI.setAlwaysHighMode(m_handle); 238 } 239 240 /** 241 * Get the underlying handle. 242 * 243 * @return Underlying PWM handle 244 */ 245 public int getHandle() { 246 return m_handle; 247 } 248 249 @Override 250 public void initSendable(SendableBuilder builder) { 251 builder.setSmartDashboardType("PWM"); 252 builder.setActuator(true); 253 builder.setSafeState(this::setDisabled); 254 builder.addDoubleProperty( 255 "Value", this::getPulseTimeMicroseconds, value -> setPulseTimeMicroseconds((int) value)); 256 builder.addDoubleProperty("Speed", this::getSpeed, this::setSpeed); 257 builder.addDoubleProperty("Position", this::getPosition, this::setPosition); 258 } 259}