From b3b03c43c8ada1e0cfb0ad30455e23df2043f9fb Mon Sep 17 00:00:00 2001 From: Austin Schuh Date: Sun, 22 Nov 2015 21:18:59 -0800 Subject: [PATCH] artf4700: Added DigitalGlitchFilter Initial Java support from Tyler Veness. Final java support done by Jerry Morrison. Change-Id: I1f85eb555f9ea4c0250c4c6729d7c51a76f5bef4 --- hal/include/HAL/Digital.hpp | 7 + hal/include/HAL/HAL.hpp | 1 + hal/lib/Athena/Digital.cpp | 91 ++++++++ .../FRC_NetworkCommunication/UsageReporting.h | 1 + wpilibc/Athena/include/Counter.h | 3 + wpilibc/Athena/include/DigitalGlitchFilter.h | 50 +++++ wpilibc/Athena/include/DigitalInput.h | 3 + wpilibc/Athena/include/Encoder.h | 2 + .../NetworkCommunication/UsageReporting.h | 1 + wpilibc/Athena/src/DigitalGlitchFilter.cpp | 198 ++++++++++++++++++ .../src/DigitalGlitchFilterTest.cpp | 60 ++++++ wpilibj/athena.gradle | 3 +- .../athena/cpp/lib/DigitalGlitchFilterJNI.cpp | 61 ++++++ .../java/edu/wpi/first/wpilibj/Counter.java | 4 +- .../first/wpilibj/DigitalGlitchFilter.java | 164 +++++++++++++++ .../FRCNetworkCommunicationsLibrary.java | 5 + .../wpilibj/hal/DigitalGlitchFilterJNI.java | 8 + .../wpilibj/DigitalGlitchFilterTest.java | 89 ++++++++ .../wpi/first/wpilibj/WpiLibJTestSuite.java | 5 +- 19 files changed, 751 insertions(+), 5 deletions(-) create mode 100644 wpilibc/Athena/include/DigitalGlitchFilter.h create mode 100644 wpilibc/Athena/src/DigitalGlitchFilter.cpp create mode 100644 wpilibcIntegrationTests/src/DigitalGlitchFilterTest.cpp create mode 100644 wpilibj/src/athena/cpp/lib/DigitalGlitchFilterJNI.cpp create mode 100644 wpilibj/src/athena/java/edu/wpi/first/wpilibj/DigitalGlitchFilter.java create mode 100644 wpilibj/src/athena/java/edu/wpi/first/wpilibj/hal/DigitalGlitchFilterJNI.java create mode 100644 wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/DigitalGlitchFilterTest.java diff --git a/hal/include/HAL/Digital.hpp b/hal/include/HAL/Digital.hpp index 25eb91be2a..fe7ee953fc 100644 --- a/hal/include/HAL/Digital.hpp +++ b/hal/include/HAL/Digital.hpp @@ -46,6 +46,13 @@ extern "C" bool isPulsing(void* digital_port_pointer, int32_t *status); bool isAnyPulsing(int32_t *status); + void setFilterSelect(void* digital_port_pointer, int filter_index, + int32_t* status); + int getFilterSelect(void* digital_port_pointer, int32_t* status); + + void setFilterPeriod(int filter_index, uint32_t value, int32_t* status); + uint32_t getFilterPeriod(int filter_index, int32_t* status); + void* initializeCounter(Mode mode, uint32_t *index, int32_t *status); void freeCounter(void* counter_pointer, int32_t *status); void setCounterAverageSize(void* counter_pointer, int32_t size, int32_t *status); diff --git a/hal/include/HAL/HAL.hpp b/hal/include/HAL/HAL.hpp index 60cad32ab7..4d08f8303c 100644 --- a/hal/include/HAL/HAL.hpp +++ b/hal/include/HAL/HAL.hpp @@ -95,6 +95,7 @@ namespace HALUsageReporting kResourceType_VictorSP, kResourceType_TalonSRX, kResourceType_CANTalonSRX, + kResourceType_DigitalGlitchFilter, }; enum tInstances diff --git a/hal/lib/Athena/Digital.cpp b/hal/lib/Athena/Digital.cpp index c99aecca37..0a088798fb 100644 --- a/hal/lib/Athena/Digital.cpp +++ b/hal/lib/Athena/Digital.cpp @@ -649,6 +649,97 @@ bool isAnyPulsing(int32_t *status) { return pulseRegister.Headers != 0 && pulseRegister.MXP != 0; } +/** + * Write the filter index from the FPGA. + * Set the filter index used to filter out short pulses. + * + * @param digital_port_pointer The digital I/O channel + * @param filter_index The filter index. Must be in the range 0 - 3, + * where 0 means "none" and 1 - 3 means filter # filter_index - 1. + */ +void setFilterSelect(void* digital_port_pointer, int filter_index, + int32_t* status) { + DigitalPort* port = (DigitalPort*)digital_port_pointer; + + std::lock_guard sync(digitalDIOMutex); + if (port->port.pin < kNumHeaders) { + digitalSystem->writeFilterSelectHdr(port->port.pin, filter_index, status); + } + else { + digitalSystem->writeFilterSelectMXP(remapMXPChannel(port->port.pin), + filter_index, status); + } +} + +/** + * Read the filter index from the FPGA. + * Get the filter index used to filter out short pulses. + * + * @param digital_port_pointer The digital I/O channel + * @return filter_index The filter index. Must be in the range 0 - 3, + * where 0 means "none" and 1 - 3 means filter # filter_index - 1. + */ +int getFilterSelect(void* digital_port_pointer, int32_t* status) { + DigitalPort* port = (DigitalPort*)digital_port_pointer; + + std::lock_guard sync(digitalDIOMutex); + if (port->port.pin < kNumHeaders) { + return digitalSystem->readFilterSelectHdr(port->port.pin, status); + } + else { + return digitalSystem->readFilterSelectMXP(remapMXPChannel(port->port.pin), + status); + } +} + +/** + * Set the filter period for the specified filter index. + * + * Set the filter period in FPGA cycles. Even though there are 2 different + * filter index domains (MXP vs HDR), ignore that distinction for now since it + * compilicates the interface. That can be changed later. + * + * @param filter_index The filter index, 0 - 2. + * @param value The number of cycles that the signal must not transition to be + * counted as a transition. + */ +void setFilterPeriod(int filter_index, uint32_t value, int32_t* status) { + std::lock_guard sync(digitalDIOMutex); + digitalSystem->writeFilterPeriodHdr(filter_index, value, status); + if (*status == 0) { + digitalSystem->writeFilterPeriodMXP(filter_index, value, status); + } +} + +/** + * Get the filter period for the specified filter index. + * + * Get the filter period in FPGA cycles. Even though there are 2 different + * filter index domains (MXP vs HDR), ignore that distinction for now since it + * compilicates the interface. Set status to NiFpga_Status_SoftwareFault if the + * filter values miss-match. + * + * @param filter_index The filter index, 0 - 2. + * @param value The number of cycles that the signal must not transition to be + * counted as a transition. + */ +uint32_t getFilterPeriod(int filter_index, int32_t* status) { + uint32_t hdr_period = 0; + uint32_t mxp_period = 0; + { + std::lock_guard sync(digitalDIOMutex); + hdr_period = digitalSystem->readFilterPeriodHdr(filter_index, status); + if (*status == 0) { + mxp_period = digitalSystem->readFilterPeriodMXP(filter_index, status); + } + } + if (hdr_period != mxp_period) { + *status = NiFpga_Status_SoftwareFault; + return -1; + } + return hdr_period; +} + struct counter_t { tCounter* counter; uint32_t index; diff --git a/hal/lib/Athena/FRC_NetworkCommunication/UsageReporting.h b/hal/lib/Athena/FRC_NetworkCommunication/UsageReporting.h index ad52da500a..fe5773ea6a 100644 --- a/hal/lib/Athena/FRC_NetworkCommunication/UsageReporting.h +++ b/hal/lib/Athena/FRC_NetworkCommunication/UsageReporting.h @@ -73,6 +73,7 @@ namespace nUsageReporting kResourceType_VictorSP, kResourceType_TalonSRX, kResourceType_CANTalonSRX, + kResourceType_DigitalGlitchFilter, } tResourceType; typedef enum diff --git a/wpilibc/Athena/include/Counter.h b/wpilibc/Athena/include/Counter.h index c5c40a91bf..01121a6b2c 100644 --- a/wpilibc/Athena/include/Counter.h +++ b/wpilibc/Athena/include/Counter.h @@ -14,6 +14,8 @@ #include +class DigitalGlitchFilter; + /** * Class for counting the number of ticks on a digital input channel. * This is a general purpose class for counting repetitive events. It can return @@ -119,4 +121,5 @@ class Counter : public SensorBase, uint32_t m_index = 0; ///< The index of this counter. std::shared_ptr m_table; + friend class DigitalGlitchFilter; }; diff --git a/wpilibc/Athena/include/DigitalGlitchFilter.h b/wpilibc/Athena/include/DigitalGlitchFilter.h new file mode 100644 index 0000000000..a6f9f90d45 --- /dev/null +++ b/wpilibc/Athena/include/DigitalGlitchFilter.h @@ -0,0 +1,50 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2015. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */ +/*----------------------------------------------------------------------------*/ +#pragma once + +#include + +#include "HAL/cpp/priority_mutex.h" +#include "DigitalSource.h" + +class Encoder; +class Counter; + +/** + * Class to enable glitch filtering on a set of digital inputs. + * This class will manage adding and removing digital inputs from a FPGA glitch + * filter. The filter lets the user configure the time that an input must remain + * high or low before it is classified as high or low. + */ +class DigitalGlitchFilter : public SensorBase { + public: + DigitalGlitchFilter(); + ~DigitalGlitchFilter(); + + void Add(DigitalSource *input); + void Add(Encoder *input); + void Add(Counter *input); + + void Remove(DigitalSource *input); + void Remove(Encoder *input); + void Remove(Counter *input); + + void SetPeriodCycles(uint32_t fpga_cycles); + void SetPeriodNanoSeconds(uint64_t nanoseconds); + + uint32_t GetPeriodCycles(); + uint64_t GetPeriodNanoSeconds(); + + private: + // Sets the filter for the input to be the requested index. A value of 0 + // disables the filter, and the filter value must be between 1 and 3, + // inclusive. + void DoAdd(DigitalSource *input, int requested_index); + + int m_channelIndex = -1; + static priority_mutex m_mutex; + static ::std::array m_filterAllocated; +}; diff --git a/wpilibc/Athena/include/DigitalInput.h b/wpilibc/Athena/include/DigitalInput.h index 17a0411395..79cfcc5ec4 100644 --- a/wpilibc/Athena/include/DigitalInput.h +++ b/wpilibc/Athena/include/DigitalInput.h @@ -12,6 +12,8 @@ #include #include +class DigitalGlitchFilter; + /** * Class to read a digital input. * This class will read digital inputs and return the current value on the @@ -45,4 +47,5 @@ class DigitalInput : public DigitalSource, public LiveWindowSendable { uint32_t m_channel; std::shared_ptr m_table; + friend class DigitalGlitchFilter; }; diff --git a/wpilibc/Athena/include/Encoder.h b/wpilibc/Athena/include/Encoder.h index 9909cf001b..a91355f388 100644 --- a/wpilibc/Athena/include/Encoder.h +++ b/wpilibc/Athena/include/Encoder.h @@ -16,6 +16,7 @@ #include class DigitalSource; +class DigitalGlitchFilter; /** * Class to read quad encoders. @@ -116,4 +117,5 @@ class Encoder : public SensorBase, int32_t m_encodingScale; // 1x, 2x, or 4x, per the encodingType std::shared_ptr m_table; + friend class DigitalGlitchFilter; }; diff --git a/wpilibc/Athena/include/NetworkCommunication/UsageReporting.h b/wpilibc/Athena/include/NetworkCommunication/UsageReporting.h index d1fdb98a89..864a589155 100644 --- a/wpilibc/Athena/include/NetworkCommunication/UsageReporting.h +++ b/wpilibc/Athena/include/NetworkCommunication/UsageReporting.h @@ -68,6 +68,7 @@ typedef enum { kResourceType_VictorSP, kResourceType_TalonSRX, kResourceType_CANTalonSRX, + kResourceType_DigitalGlitchFilter, } tResourceType; typedef enum { diff --git a/wpilibc/Athena/src/DigitalGlitchFilter.cpp b/wpilibc/Athena/src/DigitalGlitchFilter.cpp new file mode 100644 index 0000000000..25fe4b3a24 --- /dev/null +++ b/wpilibc/Athena/src/DigitalGlitchFilter.cpp @@ -0,0 +1,198 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2015. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */ +/*----------------------------------------------------------------------------*/ + +#include +#include + +#include "DigitalGlitchFilter.h" +#include "Resource.h" +#include "WPIErrors.h" +#include "Encoder.h" +#include "Counter.h" +#include "Utility.h" + +std::array DigitalGlitchFilter::m_filterAllocated = {{false, false, + false}}; +priority_mutex DigitalGlitchFilter::m_mutex; + +DigitalGlitchFilter::DigitalGlitchFilter() { + std::lock_guard sync(m_mutex); + auto index = + std::find(m_filterAllocated.begin(), m_filterAllocated.end(), false); + wpi_assert(index != m_filterAllocated.end()); + + m_channelIndex = std::distance(m_filterAllocated.begin(), index); + *index = true; +} + +DigitalGlitchFilter::~DigitalGlitchFilter() { + if (m_channelIndex >= 0) { + std::lock_guard sync(m_mutex); + m_filterAllocated[m_channelIndex] = false; + } +} + +/** + * Assigns the DigitalSource to this glitch filter. + * + * @param input The DigitalSource to add. + */ +void DigitalGlitchFilter::Add(DigitalSource *input) { + DoAdd(input, m_channelIndex + 1); +} + +void DigitalGlitchFilter::DoAdd(DigitalSource *input, int requested_index) { + // Some sources from Counters and Encoders are null. By pushing the check + // here, we catch the issue more generally. + if (input) { + int32_t status = 0; + setFilterSelect(m_digital_ports[input->GetChannelForRouting()], + requested_index, &status); + wpi_setErrorWithContext(status, getHALErrorMessage(status)); + + // Validate that we set it correctly. + int actual_index = getFilterSelect( + m_digital_ports[input->GetChannelForRouting()], &status); + wpi_assertEqual(actual_index, requested_index); + + HALReport(HALUsageReporting::kResourceType_DigitalInput, + input->GetChannelForRouting()); + } +} + +/** + * Assigns the Encoder to this glitch filter. + * + * @param input The Encoder to add. + */ +void DigitalGlitchFilter::Add(Encoder *input) { + Add(input->m_aSource.get()); + if (StatusIsFatal()) { + return; + } + Add(input->m_bSource.get()); +} + +/** + * Assigns the Counter to this glitch filter. + * + * @param input The Counter to add. + */ +void DigitalGlitchFilter::Add(Counter *input) { + Add(input->m_upSource.get()); + if (StatusIsFatal()) { + return; + } + Add(input->m_downSource.get()); +} + +/** + * Removes a digital input from this filter. + * + * Removes the DigitalSource from this glitch filter and re-assigns it to + * the default filter. + * + * @param input The DigitalSource to remove. + */ +void DigitalGlitchFilter::Remove(DigitalSource *input) { + DoAdd(input, 0); +} + +/** + * Removes an encoder from this filter. + * + * Removes the Encoder from this glitch filter and re-assigns it to + * the default filter. + * + * @param input The Encoder to remove. + */ +void DigitalGlitchFilter::Remove(Encoder *input) { + Remove(input->m_aSource.get()); + if (StatusIsFatal()) { + return; + } + Remove(input->m_bSource.get()); +} + +/** + * Removes a counter from this filter. + * + * Removes the Counter from this glitch filter and re-assigns it to + * the default filter. + * + * @param input The Counter to remove. + */ +void DigitalGlitchFilter::Remove(Counter *input) { + Remove(input->m_upSource.get()); + if (StatusIsFatal()) { + return; + } + Remove(input->m_downSource.get()); +} + +/** + * Sets the number of cycles that the input must not change state for. + * + * @param fpga_cycles The number of FPGA cycles. + */ +void DigitalGlitchFilter::SetPeriodCycles(uint32_t fpga_cycles) { + int32_t status = 0; + setFilterPeriod(m_channelIndex, fpga_cycles, &status); + wpi_setErrorWithContext(status, getHALErrorMessage(status)); + + HALReport(HALUsageReporting::kResourceType_DigitalGlitchFilter, + m_channelIndex); +} + +/** + * Sets the number of nanoseconds that the input must not change state for. + * + * @param nanoseconds The number of nanoseconds. + */ +void DigitalGlitchFilter::SetPeriodNanoSeconds(uint64_t nanoseconds) { + int32_t status = 0; + uint32_t fpga_cycles = + nanoseconds * kSystemClockTicksPerMicrosecond / 4 / 1000; + setFilterPeriod(m_channelIndex, fpga_cycles, &status); + + wpi_setErrorWithContext(status, getHALErrorMessage(status)); + + HALReport(HALUsageReporting::kResourceType_DigitalGlitchFilter, + m_channelIndex); +} + +/** + * Gets the number of cycles that the input must not change state for. + * + * @return The number of cycles. + */ +uint32_t DigitalGlitchFilter::GetPeriodCycles() { + int32_t status = 0; + uint32_t fpga_cycles = getFilterPeriod(m_channelIndex, &status); + + wpi_setErrorWithContext(status, getHALErrorMessage(status)); + + HALReport(HALUsageReporting::kResourceType_DigitalGlitchFilter, + m_channelIndex); + return fpga_cycles; +} + +/** + * Gets the number of nanoseconds that the input must not change state for. + * + * @return The number of nanoseconds. + */ +uint64_t DigitalGlitchFilter::GetPeriodNanoSeconds() { + int32_t status = 0; + uint32_t fpga_cycles = getFilterPeriod(m_channelIndex, &status); + + wpi_setErrorWithContext(status, getHALErrorMessage(status)); + + HALReport(HALUsageReporting::kResourceType_DigitalGlitchFilter, + m_channelIndex); + return static_cast(fpga_cycles) * 1000L / + static_cast(kSystemClockTicksPerMicrosecond / 4); +} diff --git a/wpilibcIntegrationTests/src/DigitalGlitchFilterTest.cpp b/wpilibcIntegrationTests/src/DigitalGlitchFilterTest.cpp new file mode 100644 index 0000000000..dfcec48070 --- /dev/null +++ b/wpilibcIntegrationTests/src/DigitalGlitchFilterTest.cpp @@ -0,0 +1,60 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2015. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include "gtest/gtest.h" + +/** + * Tests that configuring inputs to be filtered succeeds. + * + * This test actually tests everything except that the actual FPGA + * implementation works as intended. We configure the FPGA and then query it to + * make sure that the acutal configuration matches. + */ +TEST(DigitalGlitchFilterTest, BasicTest) { + DigitalInput input1(1); + DigitalInput input2(2); + DigitalInput input3(3); + DigitalInput input4(4); + Encoder encoder5(5, 6); + Counter counter7(7); + + // Check that we can make a single filter and set the period. + DigitalGlitchFilter filter1; + filter1.Add(&input1); + filter1.SetPeriodNanoSeconds(4200); + + // Check that we can make a second filter with 2 inputs. + DigitalGlitchFilter filter2; + filter2.Add(&input2); + filter2.Add(&input3); + filter2.SetPeriodNanoSeconds(97100); + + // Check that we can make a third filter with an input, an encoder, and a + // counter. + DigitalGlitchFilter filter3; + filter3.Add(&input4); + filter3.Add(&encoder5); + filter3.Add(&counter7); + filter3.SetPeriodNanoSeconds(167800); + + // Verify that the period was properly set for all 3 filters. + EXPECT_EQ(4200u, filter1.GetPeriodNanoSeconds()); + EXPECT_EQ(97100u, filter2.GetPeriodNanoSeconds()); + EXPECT_EQ(167800u, filter3.GetPeriodNanoSeconds()); + + // Clean up. + filter1.Remove(&input1); + filter2.Remove(&input2); + filter2.Remove(&input3); + filter3.Remove(&input4); + filter3.Remove(&encoder5); + filter3.Remove(&counter7); +} diff --git a/wpilibj/athena.gradle b/wpilibj/athena.gradle index d97c04ff46..5a6099d3a4 100644 --- a/wpilibj/athena.gradle +++ b/wpilibj/athena.gradle @@ -142,6 +142,7 @@ task jniHeaders { args 'edu.wpi.first.wpilibj.hal.AccelerometerJNI' args 'edu.wpi.first.wpilibj.hal.AnalogJNI' args 'edu.wpi.first.wpilibj.hal.CounterJNI' + args 'edu.wpi.first.wpilibj.hal.DigitalGlitchFilterJNI' args 'edu.wpi.first.wpilibj.hal.DIOJNI' args 'edu.wpi.first.wpilibj.hal.EncoderJNI' args 'edu.wpi.first.wpilibj.hal.I2CJNI' @@ -161,4 +162,4 @@ task jniHeaders { clean { delete generatedJNIHeaderLoc -} \ No newline at end of file +} diff --git a/wpilibj/src/athena/cpp/lib/DigitalGlitchFilterJNI.cpp b/wpilibj/src/athena/cpp/lib/DigitalGlitchFilterJNI.cpp new file mode 100644 index 0000000000..55a31b49c4 --- /dev/null +++ b/wpilibj/src/athena/cpp/lib/DigitalGlitchFilterJNI.cpp @@ -0,0 +1,61 @@ +#include +#include "HAL/HAL.hpp" +#include "HALUtil.h" + +#include "edu_wpi_first_wpilibj_hal_DigitalGlitchFilterJNI.h" + +/* + * Class: edu_wpi_first_wpilibj_hal_DigitalGlitchFilterJNI + * Method: setFilterSelect + */ +JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_hal_DigitalGlitchFilterJNI_setFilterSelect + (JNIEnv* env, jclass, jlong port_pointer, jint filter_index) +{ + int32_t status = 0; + void* digital_port_pointer = reinterpret_cast(port_pointer); + + setFilterSelect(digital_port_pointer, filter_index, &status); + CheckStatus(env, status); +} + +/* + * Class: edu_wpi_first_wpilibj_hal_DigitalGlitchFilterJNI + * Method: getFilterSelect + */ +JNIEXPORT jint JNICALL Java_edu_wpi_first_wpilibj_hal_DigitalGlitchFilterJNI_getFilterSelect + (JNIEnv *env, jclass, jlong port_pointer) +{ + int32_t status = 0; + void* digital_port_pointer = reinterpret_cast(port_pointer); + + jint result = getFilterSelect(digital_port_pointer, &status); + CheckStatus(env, status); + return result; +} + +/* + * Class: edu_wpi_first_wpilibj_hal_DigitalGlitchFilterJNI + * Method: setFilterPeriod + */ +JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_hal_DigitalGlitchFilterJNI_setFilterPeriod + (JNIEnv *env, jclass, jint filter_index, jint fpga_cycles) +{ + int32_t status = 0; + + setFilterPeriod(filter_index, fpga_cycles, &status); + CheckStatus(env, status); +} + +/* + * Class: edu_wpi_first_wpilibj_hal_DigitalGlitchFilterJNI + * Method: getFilterPeriod + */ +JNIEXPORT jint JNICALL Java_edu_wpi_first_wpilibj_hal_DigitalGlitchFilterJNI_getFilterPeriod + (JNIEnv *env, jclass, jint filter_index) +{ + int32_t status = 0; + + jint result = getFilterPeriod(filter_index, &status); + CheckStatus(env, status); + return result; +} diff --git a/wpilibj/src/athena/java/edu/wpi/first/wpilibj/Counter.java b/wpilibj/src/athena/java/edu/wpi/first/wpilibj/Counter.java index 8ba2592562..5bb0bac0a1 100644 --- a/wpilibj/src/athena/java/edu/wpi/first/wpilibj/Counter.java +++ b/wpilibj/src/athena/java/edu/wpi/first/wpilibj/Counter.java @@ -60,8 +60,8 @@ public class Counter extends SensorBase implements CounterBase, LiveWindowSendab } } - private DigitalSource m_upSource; // /< What makes the counter count up. - private DigitalSource m_downSource; // /< What makes the counter count down. + protected DigitalSource m_upSource; // /< What makes the counter count up. + protected DigitalSource m_downSource; // /< What makes the counter count down. private boolean m_allocatedUpSource; private boolean m_allocatedDownSource; private long m_counter; // /< The FPGA counter object. diff --git a/wpilibj/src/athena/java/edu/wpi/first/wpilibj/DigitalGlitchFilter.java b/wpilibj/src/athena/java/edu/wpi/first/wpilibj/DigitalGlitchFilter.java new file mode 100644 index 0000000000..582ad1f75a --- /dev/null +++ b/wpilibj/src/athena/java/edu/wpi/first/wpilibj/DigitalGlitchFilter.java @@ -0,0 +1,164 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2015. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */ +/*----------------------------------------------------------------------------*/ + +package edu.wpi.first.wpilibj; + +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import edu.wpi.first.wpilibj.DigitalSource; +import edu.wpi.first.wpilibj.Encoder; +import edu.wpi.first.wpilibj.Counter; + +import edu.wpi.first.wpilibj.hal.DigitalGlitchFilterJNI; + +/** + * Class to enable glitch filtering on a set of digital inputs. + * This class will manage adding and removing digital inputs from a FPGA glitch + * filter. The filter lets the user configure the time that an input must remain + * high or low before it is classified as high or low. + */ +public class DigitalGlitchFilter extends SensorBase { + public DigitalGlitchFilter() { + synchronized(m_mutex) { + int i = 0; + while (m_filterAllocated[i] != false && i < m_filterAllocated.length) { + i++; + } + if (i != m_filterAllocated.length) { + m_channelIndex = i; + m_filterAllocated[i] = true; + } + } + } + + public void free() { + if (m_channelIndex >= 0) { + synchronized(m_mutex) { + m_filterAllocated[m_channelIndex] = false; + } + } + } + + private static void setFilter(DigitalSource input, int channelIndex) { + if (input != null) { // Counter might have just one input + DigitalGlitchFilterJNI.setFilterSelect(input.m_port, channelIndex); + + int selected = DigitalGlitchFilterJNI.getFilterSelect(input.m_port); + if (selected != channelIndex) { + throw new IllegalStateException("DigitalGlitchFilterJNI.setFilterSelect(" + + channelIndex + ") failed -> " + selected); + } + } + } + + /** + * Assigns the DigitalSource to this glitch filter. + * + * @param input The DigitalSource to add. + */ + public void add(DigitalSource input) { + setFilter(input, m_channelIndex + 1); + } + + /** + * Assigns the Encoder to this glitch filter. + * + * @param input The Encoder to add. + */ + public void add(Encoder input) { + add(input.m_aSource); + add(input.m_bSource); + } + + /** + * Assigns the Counter to this glitch filter. + * + * @param input The Counter to add. + */ + public void add(Counter input) { + add(input.m_upSource); + add(input.m_downSource); + } + + /** + * Removes this filter from the given digital input. + * + * @param input The DigitalSource to stop filtering. + */ + public void remove(DigitalSource input) { + setFilter(input, 0); + } + + /** + * Removes this filter from the given Encoder. + * + * @param input the Encoder to stop filtering. + */ + public void remove(Encoder input) { + remove(input.m_aSource); + remove(input.m_bSource); + } + + /** + * Removes this filter from the given Counter. + * + * @param input The Counter to stop filtering. + */ + public void remove(Counter input) { + remove(input.m_upSource); + remove(input.m_downSource); + } + + /** + * Sets the number of FPGA cycles that the input must hold steady to pass + * through this glitch filter. + * + * @param fpga_cycles The number of FPGA cycles. + */ + public void setPeriodCycles(int fpga_cycles) { + DigitalGlitchFilterJNI.setFilterPeriod(m_channelIndex, fpga_cycles); + } + + /** + * Sets the number of nanoseconds that the input must hold steady to pass + * through this glitch filter. + * + * @param nanoseconds The number of nanoseconds. + */ + public void setPeriodNanoSeconds(long nanoseconds) { + int fpga_cycles = (int) (nanoseconds * kSystemClockTicksPerMicrosecond / 4 / + 1000); + setPeriodCycles(fpga_cycles); + } + + /** + * Gets the number of FPGA cycles that the input must hold steady to pass + * through this glitch filter. + * + * @return The number of cycles. + */ + public int getPeriodCycles() { + return DigitalGlitchFilterJNI.getFilterPeriod(m_channelIndex); + } + + /** + * Gets the number of nanoseconds that the input must hold steady to pass + * through this glitch filter. + * + * @return The number of nanoseconds. + */ + public long getPeriodNanoSeconds() { + int fpga_cycles = getPeriodCycles(); + + return (long) fpga_cycles * 1000L / + (long) (kSystemClockTicksPerMicrosecond / 4); + } + + private int m_channelIndex = -1; + private static final Lock m_mutex = new ReentrantLock(true); + private static final boolean[] m_filterAllocated = new boolean[3]; +}; diff --git a/wpilibj/src/athena/java/edu/wpi/first/wpilibj/communication/FRCNetworkCommunicationsLibrary.java b/wpilibj/src/athena/java/edu/wpi/first/wpilibj/communication/FRCNetworkCommunicationsLibrary.java index 871c3f8059..57de7a906c 100644 --- a/wpilibj/src/athena/java/edu/wpi/first/wpilibj/communication/FRCNetworkCommunicationsLibrary.java +++ b/wpilibj/src/athena/java/edu/wpi/first/wpilibj/communication/FRCNetworkCommunicationsLibrary.java @@ -379,6 +379,11 @@ public class FRCNetworkCommunicationsLibrary extends JNIWrapper { * src\main\include\NetworkCommunication\UsageReporting.h:62 */ public static final int kResourceType_CANTalonSRX = 52; + /** + * native declaration : + * src\main\include\NetworkCommunication\UsageReporting.h:63 + */ + public static final int kResourceType_DigitalGlitchFilter = 53; }; /** * native declaration : diff --git a/wpilibj/src/athena/java/edu/wpi/first/wpilibj/hal/DigitalGlitchFilterJNI.java b/wpilibj/src/athena/java/edu/wpi/first/wpilibj/hal/DigitalGlitchFilterJNI.java new file mode 100644 index 0000000000..52f7ecbb64 --- /dev/null +++ b/wpilibj/src/athena/java/edu/wpi/first/wpilibj/hal/DigitalGlitchFilterJNI.java @@ -0,0 +1,8 @@ +package edu.wpi.first.wpilibj.hal; + +public class DigitalGlitchFilterJNI extends JNIWrapper { + public static native void setFilterSelect(long digital_port_pointer, int filter_index); + public static native int getFilterSelect(long digital_port_pointer); + public static native void setFilterPeriod(int filter_index, int fpga_cycles); + public static native int getFilterPeriod(int filter_index); +} diff --git a/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/DigitalGlitchFilterTest.java b/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/DigitalGlitchFilterTest.java new file mode 100644 index 0000000000..85b0c601cb --- /dev/null +++ b/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/DigitalGlitchFilterTest.java @@ -0,0 +1,89 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2008-2015. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ +package edu.wpi.first.wpilibj; + +import static org.junit.Assert.assertEquals; + +import java.util.logging.Logger; + +import org.junit.Test; + +import edu.wpi.first.wpilibj.test.AbstractComsSetup; + +import edu.wpi.first.wpilibj.DigitalInput; +import edu.wpi.first.wpilibj.Encoder; +import edu.wpi.first.wpilibj.Counter; +import edu.wpi.first.wpilibj.DigitalGlitchFilter; + +/** + * Test for the DigitalGlitchFilter class. + * + * @author Austin Schuh and Jerry Morrison + */ +public class DigitalGlitchFilterTest extends AbstractComsSetup { + private static final Logger logger = Logger.getLogger( + DigitalGlitchFilterTest.class.getName()); + + protected Logger getClassLogger() { + return logger; + } + + /** + * This is a test to make sure that filters can be created and are consistent. + * This assumes that the FPGA implementation to actually implement the filter + * has been tested. It does validate that we are successfully able to set and + * get the active filters for ports and makes sure that the FPGA filter is + * changed correctly, and does the same for the period. + */ + @Test + public void testDigitalGlitchFilterBasic() { + DigitalInput input1 = new DigitalInput(1); + DigitalInput input2 = new DigitalInput(2); + DigitalInput input3 = new DigitalInput(3); + DigitalInput input4 = new DigitalInput(4); + Encoder encoder5 = new Encoder(5, 6); + Counter counter7 = new Counter(7); + + DigitalGlitchFilter filter1 = new DigitalGlitchFilter(); + filter1.add(input1); + filter1.setPeriodNanoSeconds(4200); + + DigitalGlitchFilter filter2 = new DigitalGlitchFilter(); + filter2.add(input2); + filter2.add(input3); + filter2.setPeriodNanoSeconds(97100); + + DigitalGlitchFilter filter3 = new DigitalGlitchFilter(); + filter3.add(input4); + filter3.add(encoder5); + filter3.add(counter7); + filter3.setPeriodNanoSeconds(167800); + + assertEquals(4200, filter1.getPeriodNanoSeconds()); + assertEquals(97100, filter2.getPeriodNanoSeconds()); + assertEquals(167800, filter3.getPeriodNanoSeconds()); + + filter1.remove(input1); + + filter2.remove(input2); + filter2.remove(input3); + + filter3.remove(input4); + filter3.remove(encoder5); + filter3.remove(counter7); + + input1.free(); + input2.free(); + input3.free(); + input4.free(); + encoder5.free(); + counter7.free(); + filter1.free(); + filter2.free(); + filter3.free(); + } +} diff --git a/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/WpiLibJTestSuite.java b/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/WpiLibJTestSuite.java index 592f8d311c..8183d2978f 100644 --- a/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/WpiLibJTestSuite.java +++ b/wpilibjIntegrationTests/src/main/java/edu/wpi/first/wpilibj/WpiLibJTestSuite.java @@ -20,8 +20,9 @@ import edu.wpi.first.wpilibj.test.AbstractTestSuite; @RunWith(Suite.class) @SuiteClasses({AnalogCrossConnectTest.class, AnalogPotentiometerTest.class, BuiltInAccelerometerTest.class, CANTalonTest.class, CounterTest.class, - DIOCrossConnectTest.class, EncoderTest.class, GyroTest.class, MotorEncoderTest.class, - MotorInvertingTest.class, PCMTest.class, PDPTest.class, PIDTest.class, PreferencesTest.class, + DigitalGlitchFilterTest.class, DIOCrossConnectTest.class, EncoderTest.class, + GyroTest.class, MotorEncoderTest.class, MotorInvertingTest.class, + PCMTest.class, PDPTest.class, PIDTest.class, PreferencesTest.class, RelayCrossConnectTest.class, SampleTest.class, TimerTest.class}) public class WpiLibJTestSuite extends AbstractTestSuite { }