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.DigitalGlitchFilterJNI; 008import edu.wpi.first.hal.FRCNetComm.tResourceType; 009import edu.wpi.first.hal.HAL; 010import edu.wpi.first.hal.util.AllocationException; 011import edu.wpi.first.util.sendable.Sendable; 012import edu.wpi.first.util.sendable.SendableBuilder; 013import edu.wpi.first.util.sendable.SendableRegistry; 014import java.util.concurrent.locks.Lock; 015import java.util.concurrent.locks.ReentrantLock; 016 017/** 018 * Class to enable glitch filtering on a set of digital inputs. This class will manage adding and 019 * removing digital inputs from an FPGA glitch filter. The filter lets the user configure the time 020 * that an input must remain high or low before it is classified as high or low. 021 */ 022public class DigitalGlitchFilter implements Sendable, AutoCloseable { 023 /** Configures the Digital Glitch Filter to its default settings. */ 024 @SuppressWarnings("this-escape") 025 public DigitalGlitchFilter() { 026 m_channelIndex = allocateFilterIndex(); 027 if (m_channelIndex < 0) { 028 throw new AllocationException("No filters available to allocate."); 029 } 030 HAL.report(tResourceType.kResourceType_DigitalGlitchFilter, m_channelIndex + 1, 0); 031 SendableRegistry.addLW(this, "DigitalGlitchFilter", m_channelIndex); 032 } 033 034 @Override 035 public void close() { 036 SendableRegistry.remove(this); 037 if (m_channelIndex >= 0) { 038 m_mutex.lock(); 039 try { 040 m_filterAllocated[m_channelIndex] = false; 041 m_channelIndex = -1; 042 } finally { 043 m_mutex.unlock(); 044 } 045 } 046 } 047 048 /** 049 * Allocates the next available filter index, or -1 if there are no filters available. 050 * 051 * @return the filter index 052 */ 053 private static int allocateFilterIndex() { 054 m_mutex.lock(); 055 try { 056 for (int index = 0; index < m_filterAllocated.length; index++) { 057 if (!m_filterAllocated[index]) { 058 m_filterAllocated[index] = true; 059 return index; 060 } 061 } 062 } finally { 063 m_mutex.unlock(); 064 } 065 return -1; 066 } 067 068 private static void setFilter(DigitalSource input, int channelIndex) { 069 if (input != null) { // Counter might have just one input 070 // analog triggers are not supported for DigitalGlitchFilters 071 if (input.isAnalogTrigger()) { 072 throw new IllegalStateException("Analog Triggers not supported for DigitalGlitchFilters"); 073 } 074 DigitalGlitchFilterJNI.setFilterSelect(input.getPortHandleForRouting(), channelIndex); 075 076 int selected = DigitalGlitchFilterJNI.getFilterSelect(input.getPortHandleForRouting()); 077 if (selected != channelIndex) { 078 throw new IllegalStateException( 079 "DigitalGlitchFilterJNI.setFilterSelect(" + channelIndex + ") failed -> " + selected); 080 } 081 } 082 } 083 084 /** 085 * Assigns the DigitalSource to this glitch filter. 086 * 087 * @param input The DigitalSource to add. 088 */ 089 public void add(DigitalSource input) { 090 setFilter(input, m_channelIndex + 1); 091 } 092 093 /** 094 * Assigns the Encoder to this glitch filter. 095 * 096 * @param input The Encoder to add. 097 */ 098 public void add(Encoder input) { 099 add(input.m_aSource); 100 add(input.m_bSource); 101 } 102 103 /** 104 * Assigns the Counter to this glitch filter. 105 * 106 * @param input The Counter to add. 107 */ 108 public void add(Counter input) { 109 add(input.m_upSource); 110 add(input.m_downSource); 111 } 112 113 /** 114 * Removes this filter from the given digital input. 115 * 116 * @param input The DigitalSource to stop filtering. 117 */ 118 public void remove(DigitalSource input) { 119 setFilter(input, 0); 120 } 121 122 /** 123 * Removes this filter from the given Encoder. 124 * 125 * @param input the Encoder to stop filtering. 126 */ 127 public void remove(Encoder input) { 128 remove(input.m_aSource); 129 remove(input.m_bSource); 130 } 131 132 /** 133 * Removes this filter from the given Counter. 134 * 135 * @param input The Counter to stop filtering. 136 */ 137 public void remove(Counter input) { 138 remove(input.m_upSource); 139 remove(input.m_downSource); 140 } 141 142 /** 143 * Sets the number of FPGA cycles that the input must hold steady to pass through this glitch 144 * filter. 145 * 146 * @param fpgaCycles The number of FPGA cycles. 147 */ 148 public void setPeriodCycles(int fpgaCycles) { 149 DigitalGlitchFilterJNI.setFilterPeriod(m_channelIndex, fpgaCycles); 150 } 151 152 /** 153 * Sets the number of nanoseconds that the input must hold steady to pass through this glitch 154 * filter. 155 * 156 * @param nanoseconds The number of nanoseconds. 157 */ 158 public void setPeriodNanoSeconds(long nanoseconds) { 159 int fpgaCycles = (int) (nanoseconds * SensorUtil.kSystemClockTicksPerMicrosecond / 4 / 1000); 160 setPeriodCycles(fpgaCycles); 161 } 162 163 /** 164 * Gets the number of FPGA cycles that the input must hold steady to pass through this glitch 165 * filter. 166 * 167 * @return The number of cycles. 168 */ 169 public int getPeriodCycles() { 170 return DigitalGlitchFilterJNI.getFilterPeriod(m_channelIndex); 171 } 172 173 /** 174 * Gets the number of nanoseconds that the input must hold steady to pass through this glitch 175 * filter. 176 * 177 * @return The number of nanoseconds. 178 */ 179 public long getPeriodNanoSeconds() { 180 int fpgaCycles = getPeriodCycles(); 181 182 return fpgaCycles * 1000L / (SensorUtil.kSystemClockTicksPerMicrosecond / 4); 183 } 184 185 @Override 186 public void initSendable(SendableBuilder builder) {} 187 188 private int m_channelIndex; 189 private static final Lock m_mutex = new ReentrantLock(true); 190 private static final boolean[] m_filterAllocated = new boolean[3]; 191}