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.system.SensorUtil; 011import org.wpilib.util.sendable.Sendable; 012import org.wpilib.util.sendable.SendableBuilder; 013import org.wpilib.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 output period in microseconds. */ 024 public enum OutputPeriod { 025 /** Pulse every 5ms. */ 026 k5Ms, 027 /** Pulse every 10ms. */ 028 k10Ms, 029 /** Pulse every 20ms. */ 030 k20Ms 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. 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 056 */ 057 @SuppressWarnings("this-escape") 058 public PWM(final int channel, final boolean registerSendable) { 059 SensorUtil.checkPWMChannel(channel); 060 m_channel = channel; 061 062 m_handle = PWMJNI.initializePWMPort(channel); 063 064 setDisabled(); 065 066 HAL.reportUsage("IO", channel, "PWM"); 067 if (registerSendable) { 068 SendableRegistry.add(this, "PWM", channel); 069 } 070 } 071 072 /** Free the resource associated with the PWM channel and set the value to 0. */ 073 @Override 074 public void close() { 075 SendableRegistry.remove(this); 076 if (m_handle == 0) { 077 return; 078 } 079 setDisabled(); 080 PWMJNI.freePWMPort(m_handle); 081 m_handle = 0; 082 } 083 084 /** 085 * Gets the channel number associated with the PWM Object. 086 * 087 * @return The channel number. 088 */ 089 public int getChannel() { 090 return m_channel; 091 } 092 093 /** 094 * Set the PWM value directly to the hardware. 095 * 096 * <p>Write a microsecond pulse value to a PWM channel. 097 * 098 * @param microsecondPulseTime Microsecond pulse PWM value. Range 0 - 4096. 099 */ 100 public void setPulseTimeMicroseconds(int microsecondPulseTime) { 101 PWMJNI.setPulseTimeMicroseconds(m_handle, microsecondPulseTime); 102 } 103 104 /** 105 * Get the PWM value directly from the hardware. 106 * 107 * <p>Read a raw value from a PWM channel. 108 * 109 * @return Microsecond pulse PWM control value. Range: 0 - 4096. 110 */ 111 public int getPulseTimeMicroseconds() { 112 return PWMJNI.getPulseTimeMicroseconds(m_handle); 113 } 114 115 /** Temporarily disables the PWM output. The next set call will re-enable the output. */ 116 public final void setDisabled() { 117 setPulseTimeMicroseconds(0); 118 } 119 120 /** 121 * Sets the PWM output period. 122 * 123 * @param mult The output period to apply to this channel 124 */ 125 public void setOutputPeriod(OutputPeriod mult) { 126 int scale = 127 switch (mult) { 128 case k20Ms -> 3; 129 case k10Ms -> 1; 130 case k5Ms -> 0; 131 }; 132 133 PWMJNI.setPWMOutputPeriod(m_handle, scale); 134 } 135 136 /** 137 * Get the underlying handle. 138 * 139 * @return Underlying PWM handle 140 */ 141 public int getHandle() { 142 return m_handle; 143 } 144 145 /** 146 * Indicates this input is used by a simulated device. 147 * 148 * @param device simulated device handle 149 */ 150 public void setSimDevice(SimDevice device) { 151 PWMJNI.setPWMSimDevice(m_handle, device.getNativeHandle()); 152 } 153 154 @Override 155 public void initSendable(SendableBuilder builder) { 156 builder.setSmartDashboardType("PWM"); 157 builder.setActuator(true); 158 builder.addDoubleProperty( 159 "Value", this::getPulseTimeMicroseconds, value -> setPulseTimeMicroseconds((int) value)); 160 } 161}