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.HAL; 008import edu.wpi.first.hal.PWMJNI; 009import edu.wpi.first.hal.SimDevice; 010import edu.wpi.first.util.sendable.Sendable; 011import edu.wpi.first.util.sendable.SendableBuilder; 012import edu.wpi.first.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 /** Represents the output period in microseconds. */ 023 public enum OutputPeriod { 024 /** Pulse every 5ms. */ 025 k5Ms, 026 /** Pulse every 10ms. */ 027 k10Ms, 028 /** Pulse every 20ms. */ 029 k20Ms 030 } 031 032 private final int m_channel; 033 034 private int m_handle; 035 036 /** 037 * Allocate a PWM given a channel. 038 * 039 * <p>Checks channel value range and allocates the appropriate channel. The allocation is only 040 * done to help users ensure that they don't double assign channels. 041 * 042 * <p>By default, adds itself to SendableRegistry. 043 * 044 * @param channel The PWM channel number. 0-9 are on-board, 10-19 are on the MXP port 045 */ 046 public PWM(final int channel) { 047 this(channel, true); 048 } 049 050 /** 051 * Allocate a PWM given a channel. 052 * 053 * @param channel The PWM channel number. 0-9 are on-board, 10-19 are on the MXP port 054 * @param registerSendable If true, adds this instance to SendableRegistry 055 */ 056 @SuppressWarnings("this-escape") 057 public PWM(final int channel, final boolean registerSendable) { 058 SensorUtil.checkPWMChannel(channel); 059 m_channel = channel; 060 061 m_handle = PWMJNI.initializePWMPort(channel); 062 063 setDisabled(); 064 065 HAL.reportUsage("IO", channel, "PWM"); 066 if (registerSendable) { 067 SendableRegistry.add(this, "PWM", channel); 068 } 069 } 070 071 /** Free the resource associated with the PWM channel and set the value to 0. */ 072 @Override 073 public void close() { 074 SendableRegistry.remove(this); 075 if (m_handle == 0) { 076 return; 077 } 078 setDisabled(); 079 PWMJNI.freePWMPort(m_handle); 080 m_handle = 0; 081 } 082 083 /** 084 * Gets the channel number associated with the PWM Object. 085 * 086 * @return The channel number. 087 */ 088 public int getChannel() { 089 return m_channel; 090 } 091 092 /** 093 * Set the PWM value directly to the hardware. 094 * 095 * <p>Write a microsecond pulse value to a PWM channel. 096 * 097 * @param microsecondPulseTime Microsecond pulse PWM value. Range 0 - 4096. 098 */ 099 public void setPulseTimeMicroseconds(int microsecondPulseTime) { 100 PWMJNI.setPulseTimeMicroseconds(m_handle, microsecondPulseTime); 101 } 102 103 /** 104 * Get the PWM value directly from the hardware. 105 * 106 * <p>Read a raw value from a PWM channel. 107 * 108 * @return Microsecond pulse PWM control value. Range: 0 - 4096. 109 */ 110 public int getPulseTimeMicroseconds() { 111 return PWMJNI.getPulseTimeMicroseconds(m_handle); 112 } 113 114 /** Temporarily disables the PWM output. The next set call will re-enable the output. */ 115 public final void setDisabled() { 116 setPulseTimeMicroseconds(0); 117 } 118 119 /** 120 * Sets the PWM output period. 121 * 122 * @param mult The output period to apply to this channel 123 */ 124 public void setOutputPeriod(OutputPeriod mult) { 125 int scale = 126 switch (mult) { 127 case k20Ms -> 3; 128 case k10Ms -> 1; 129 case k5Ms -> 0; 130 }; 131 132 PWMJNI.setPWMOutputPeriod(m_handle, scale); 133 } 134 135 /** 136 * Get the underlying handle. 137 * 138 * @return Underlying PWM handle 139 */ 140 public int getHandle() { 141 return m_handle; 142 } 143 144 /** 145 * Indicates this input is used by a simulated device. 146 * 147 * @param device simulated device handle 148 */ 149 public void setSimDevice(SimDevice device) { 150 PWMJNI.setPWMSimDevice(m_handle, device.getNativeHandle()); 151 } 152 153 @Override 154 public void initSendable(SendableBuilder builder) { 155 builder.setSmartDashboardType("PWM"); 156 builder.setActuator(true); 157 builder.addDoubleProperty( 158 "Value", this::getPulseTimeMicroseconds, value -> setPulseTimeMicroseconds((int) value)); 159 } 160}