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}