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 org.wpilib.hardware.discrete;
006
007import org.wpilib.hardware.hal.HAL;
008import org.wpilib.hardware.hal.PWMJNI;
009import org.wpilib.hardware.hal.SimDevice;
010import org.wpilib.util.sendable.Sendable;
011import org.wpilib.util.sendable.SendableBuilder;
012import org.wpilib.util.sendable.SendableRegistry;
013
014/**
015 * Class implements the PWM generation in the FPGA.
016 *
017 * <p>The values supplied as arguments for PWM outputs range from -1.0 to 1.0. They are mapped to
018 * the microseconds to keep the pulse high, with a range of 0 (off) to 4096. Changes are immediately
019 * sent to the FPGA, and the update occurs at the next FPGA cycle (5.05ms). There is no delay.
020 */
021public class PWM implements Sendable, AutoCloseable {
022  private final int m_channel;
023
024  private int m_handle;
025
026  /**
027   * Allocate a PWM given a channel.
028   *
029   * <p>Checks channel value range and allocates the appropriate channel. The allocation is only
030   * done to help users ensure that they don't double assign channels.
031   *
032   * <p>By default, adds itself to SendableRegistry.
033   *
034   * @param channel The SmartIO channel number.
035   */
036  public PWM(final int channel) {
037    this(channel, true);
038  }
039
040  /**
041   * Allocate a PWM given a channel.
042   *
043   * @param channel The SmartIO channel number.
044   * @param registerSendable If true, adds this instance to SendableRegistry
045   */
046  @SuppressWarnings("this-escape")
047  public PWM(final int channel, final boolean registerSendable) {
048    m_channel = channel;
049
050    m_handle = PWMJNI.initializePWMPort(channel);
051
052    setDisabled();
053
054    HAL.reportUsage("IO", channel, "PWM");
055    if (registerSendable) {
056      SendableRegistry.add(this, "PWM", channel);
057    }
058  }
059
060  /** Free the resource associated with the PWM channel and set the value to 0. */
061  @Override
062  public void close() {
063    SendableRegistry.remove(this);
064    if (m_handle == 0) {
065      return;
066    }
067    setDisabled();
068    PWMJNI.freePWMPort(m_handle);
069    m_handle = 0;
070  }
071
072  /**
073   * Gets the channel number associated with the PWM Object.
074   *
075   * @return The channel number.
076   */
077  public int getChannel() {
078    return m_channel;
079  }
080
081  /**
082   * Set the PWM value directly to the hardware.
083   *
084   * <p>Write a microsecond pulse value to a PWM channel.
085   *
086   * @param microsecondPulseTime Microsecond pulse PWM value. Range 0 - 4096.
087   */
088  public void setPulseTimeMicroseconds(int microsecondPulseTime) {
089    PWMJNI.setPulseTimeMicroseconds(m_handle, microsecondPulseTime);
090  }
091
092  /**
093   * Get the PWM value directly from the hardware.
094   *
095   * <p>Read a raw value from a PWM channel.
096   *
097   * @return Microsecond pulse PWM control value. Range: 0 - 4096.
098   */
099  public int getPulseTimeMicroseconds() {
100    return PWMJNI.getPulseTimeMicroseconds(m_handle);
101  }
102
103  /** Temporarily disables the PWM output. The next set call will re-enable the output. */
104  public final void setDisabled() {
105    setPulseTimeMicroseconds(0);
106  }
107
108  /**
109   * Sets the PWM output period.
110   *
111   * @param millisecondPeriod The output period to apply to this channel, in milliseconds. Valid
112   *     values are 5ms, 10ms, and 20ms. Default is 20 ms.
113   */
114  public void setOutputPeriod(int millisecondPeriod) {
115    int scale =
116        switch (millisecondPeriod) {
117          case 5 -> 0;
118          case 10 -> 1;
119          case 20 -> 3;
120          default -> 3; // default to 20ms if invalid value is given
121        };
122
123    PWMJNI.setPWMOutputPeriod(m_handle, scale);
124  }
125
126  /**
127   * Get the underlying handle.
128   *
129   * @return Underlying PWM handle
130   */
131  public int getHandle() {
132    return m_handle;
133  }
134
135  /**
136   * Indicates this input is used by a simulated device.
137   *
138   * @param device simulated device handle
139   */
140  public void setSimDevice(SimDevice device) {
141    PWMJNI.setPWMSimDevice(m_handle, device.getNativeHandle());
142  }
143
144  @Override
145  public void initSendable(SendableBuilder builder) {
146    builder.setSmartDashboardType("PWM");
147    builder.setActuator(true);
148    builder.addDoubleProperty(
149        "Value", this::getPulseTimeMicroseconds, value -> setPulseTimeMicroseconds((int) value));
150  }
151}