001/*----------------------------------------------------------------------------*/
002/* Copyright (c) 2017-2018 FIRST. 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.FRCNetComm.tResourceType;
011import edu.wpi.first.wpilibj.hal.HAL;
012import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder;
013
014/**
015 * Nidec Brushless Motor.
016 */
017public class NidecBrushless extends SendableBase implements SpeedController, MotorSafety, Sendable {
018  private final MotorSafetyHelper m_safetyHelper;
019  private boolean m_isInverted = false;
020  private DigitalOutput m_dio;
021  private PWM m_pwm;
022  private volatile double m_speed = 0.0;
023  private volatile boolean m_disabled = false;
024
025  /**
026   * Constructor.
027   *
028   * @param pwmChannel The PWM channel that the Nidec Brushless controller is attached to.
029   *                   0-9 are on-board, 10-19 are on the MXP port
030   * @param dioChannel The DIO channel that the Nidec Brushless controller is attached to.
031   *                   0-9 are on-board, 10-25 are on the MXP port
032   */
033  public NidecBrushless(final int pwmChannel, final int dioChannel) {
034    m_safetyHelper = new MotorSafetyHelper(this);
035    m_safetyHelper.setExpiration(0.0);
036    m_safetyHelper.setSafetyEnabled(false);
037
038    // the dio controls the output (in PWM mode)
039    m_dio = new DigitalOutput(dioChannel);
040    addChild(m_dio);
041    m_dio.setPWMRate(15625);
042    m_dio.enablePWM(0.5);
043
044    // the pwm enables the controller
045    m_pwm = new PWM(pwmChannel);
046    addChild(m_pwm);
047
048    HAL.report(tResourceType.kResourceType_NidecBrushless, pwmChannel);
049    setName("Nidec Brushless", pwmChannel);
050  }
051
052  /**
053   * Free the resources used by this object.
054   */
055  @Override
056  public void free() {
057    super.free();
058    m_dio.free();
059    m_pwm.free();
060  }
061
062  /**
063   * Set the PWM value.
064   *
065   * <p>The PWM value is set using a range of -1.0 to 1.0, appropriately scaling the value for the
066   * FPGA.
067   *
068   * @param speed The speed value between -1.0 and 1.0 to set.
069   */
070  @Override
071  public void set(double speed) {
072    if (!m_disabled) {
073      m_speed = speed;
074      m_dio.updateDutyCycle(0.5 + 0.5 * (m_isInverted ? -speed : speed));
075      m_pwm.setRaw(0xffff);
076    }
077    m_safetyHelper.feed();
078  }
079
080  /**
081   * Get the recently set value of the PWM.
082   *
083   * @return The most recently set value for the PWM between -1.0 and 1.0.
084   */
085  @Override
086  public double get() {
087    return m_speed;
088  }
089
090  @Override
091  public void setInverted(boolean isInverted) {
092    m_isInverted = isInverted;
093  }
094
095  @Override
096  public boolean getInverted() {
097    return m_isInverted;
098  }
099
100  /**
101   * Write out the PID value as seen in the PIDOutput base object.
102   *
103   * @param output Write out the PWM value as was found in the PIDController
104   */
105  @Override
106  public void pidWrite(double output) {
107    set(output);
108  }
109
110  /**
111   * Set the safety expiration time.
112   *
113   * @param timeout The timeout (in seconds) for this motor object
114   */
115  @Override
116  public void setExpiration(double timeout) {
117    m_safetyHelper.setExpiration(timeout);
118  }
119
120  /**
121   * Return the safety expiration time.
122   *
123   * @return The expiration time value.
124   */
125  @Override
126  public double getExpiration() {
127    return m_safetyHelper.getExpiration();
128  }
129
130  /**
131   * Check if the motor is currently alive or stopped due to a timeout.
132   *
133   * @return a bool value that is true if the motor has NOT timed out and should still be running.
134   */
135  @Override
136  public boolean isAlive() {
137    return m_safetyHelper.isAlive();
138  }
139
140  /**
141   * Stop the motor. This is called by the MotorSafetyHelper object
142   * when it has a timeout for this PWM and needs to stop it from running.
143   * Calling set() will re-enable the motor.
144   */
145  @Override
146  public void stopMotor() {
147    m_dio.updateDutyCycle(0.5);
148    m_pwm.setDisabled();
149  }
150
151  /**
152   * Check if motor safety is enabled.
153   *
154   * @return True if motor safety is enforced for this object
155   */
156  @Override
157  public boolean isSafetyEnabled() {
158    return m_safetyHelper.isSafetyEnabled();
159  }
160
161  @Override
162  public void setSafetyEnabled(boolean enabled) {
163    m_safetyHelper.setSafetyEnabled(enabled);
164  }
165
166  @Override
167  public String getDescription() {
168    return "Nidec " + getChannel();
169  }
170
171  /**
172   * Disable the motor.  The enable() function must be called to re-enable
173   * the motor.
174   */
175  @Override
176  public void disable() {
177    m_disabled = true;
178    m_dio.updateDutyCycle(0.5);
179    m_pwm.setDisabled();
180  }
181
182  /**
183   * Re-enable the motor after disable() has been called.  The set()
184   * function must be called to set a new motor speed.
185   */
186  public void enable() {
187    m_disabled = false;
188  }
189
190  /**
191   * Gets the channel number associated with the object.
192   *
193   * @return The channel number.
194   */
195  public int getChannel() {
196    return m_pwm.getChannel();
197  }
198
199  @Override
200  public void initSendable(SendableBuilder builder) {
201    builder.setSmartDashboardType("Nidec Brushless");
202    builder.setSafeState(this::stopMotor);
203    builder.addDoubleProperty("Value", this::get, this::set);
204  }
205}