diff --git a/hal/include/HAL/Digital.hpp b/hal/include/HAL/Digital.hpp index 8c92f4e51a..002bb16f27 100644 --- a/hal/include/HAL/Digital.hpp +++ b/hal/include/HAL/Digital.hpp @@ -86,6 +86,8 @@ extern "C" void setEncoderSamplesToAverage(void* encoder_pointer, uint32_t samplesToAverage, int32_t *status); uint32_t getEncoderSamplesToAverage(void* encoder_pointer, int32_t *status); + void setEncoderIndexSource(void *encoder_pointer, uint32_t pin, bool analogTrigger, bool activeHigh, + bool edgeSensitive, int32_t *status); uint16_t getLoopTiming(int32_t *status); diff --git a/hal/lib/Athena/Digital.cpp b/hal/lib/Athena/Digital.cpp index 38f040ab07..6df962b441 100644 --- a/hal/lib/Athena/Digital.cpp +++ b/hal/lib/Athena/Digital.cpp @@ -1078,6 +1078,20 @@ uint32_t getEncoderSamplesToAverage(void* encoder_pointer, int32_t *status) { return encoder->encoder->readTimerConfig_AverageSize(status); } +/** + * Set an index source for an encoder, which is an input that resets the + * encoder's count. + */ +void setEncoderIndexSource(void *encoder_pointer, uint32_t pin, bool analogTrigger, bool activeHigh, + bool edgeSensitive, int32_t *status) { + Encoder* encoder = (Encoder*) encoder_pointer; + encoder->encoder->writeConfig_IndexSource_Channel((unsigned char)pin, status); + encoder->encoder->writeConfig_IndexSource_Module((unsigned char)0, status); + encoder->encoder->writeConfig_IndexSource_AnalogTrigger(analogTrigger, status); + encoder->encoder->writeConfig_IndexActiveHigh(activeHigh, status); + encoder->encoder->writeConfig_IndexEdgeSensitive(edgeSensitive, status); +} + /** * Get the loop timing of the PWM system * diff --git a/wpilibc/wpilibC++Devices/include/Encoder.h b/wpilibc/wpilibC++Devices/include/Encoder.h index 4166c0735a..b4c4ac207e 100644 --- a/wpilibc/wpilibC++Devices/include/Encoder.h +++ b/wpilibc/wpilibC++Devices/include/Encoder.h @@ -29,6 +29,7 @@ class DigitalSource; class Encoder : public SensorBase, public CounterBase, public PIDSource, public LiveWindowSendable { public: + enum IndexingType { kResetWhileHigh, kResetWhileLow, kResetOnFallingEdge, kResetOnRisingEdge }; Encoder(uint32_t aChannel, uint32_t bChannel, bool reverseDirection = false, EncodingType encodingType = k4X); @@ -57,6 +58,10 @@ public: void SetPIDSourceParameter(PIDSourceParameter pidSource); double PIDGet(); + void SetIndexSource(uint32_t channel, IndexingType type = kResetOnRisingEdge); + void SetIndexSource(DigitalSource *source, IndexingType type = kResetOnRisingEdge); + void SetIndexSource(DigitalSource &source, IndexingType type = kResetOnRisingEdge); + void UpdateTable(); void StartLiveWindowMode(); void StopLiveWindowMode(); diff --git a/wpilibc/wpilibC++Devices/src/Encoder.cpp b/wpilibc/wpilibC++Devices/src/Encoder.cpp index 567af9a319..0ff2296b28 100644 --- a/wpilibc/wpilibC++Devices/src/Encoder.cpp +++ b/wpilibc/wpilibC++Devices/src/Encoder.cpp @@ -507,6 +507,47 @@ double Encoder::PIDGet() } } +/** + * Set the index source for the encoder. When this source is activated, the encoder count automatically resets. + * + * @param channel A DIO channel to set as the encoder index + * @param type The state that will cause the encoder to reset + */ +void Encoder::SetIndexSource(uint32_t channel, Encoder::IndexingType type) { + int32_t status = 0; + bool activeHigh = (type == kResetWhileHigh) || (type == kResetOnRisingEdge); + bool edgeSensitive = (type == kResetOnFallingEdge) || (type == kResetOnRisingEdge); + + setEncoderIndexSource(m_encoder, channel, false, activeHigh, edgeSensitive, &status); + wpi_setGlobalErrorWithContext(status, getHALErrorMessage(status)); +} + +/** + * Set the index source for the encoder. When this source is activated, the encoder count automatically resets. + * + * @param channel A digital source to set as the encoder index + * @param type The state that will cause the encoder to reset + */ +void Encoder::SetIndexSource(DigitalSource *source, Encoder::IndexingType type) { + int32_t status = 0; + bool activeHigh = (type == kResetWhileHigh) || (type == kResetOnRisingEdge); + bool edgeSensitive = (type == kResetOnFallingEdge) || (type == kResetOnRisingEdge); + + setEncoderIndexSource(m_encoder, source->GetChannelForRouting(), source->GetAnalogTriggerForRouting(), activeHigh, + edgeSensitive, &status); + wpi_setGlobalErrorWithContext(status, getHALErrorMessage(status)); +} + +/** + * Set the index source for the encoder. When this source is activated, the encoder count automatically resets. + * + * @param channel A digital source to set as the encoder index + * @param type The state that will cause the encoder to reset + */ +void Encoder::SetIndexSource(DigitalSource &source, Encoder::IndexingType type) { + SetIndexSource(&source, type); +} + void Encoder::UpdateTable() { if (m_table != NULL) { m_table->PutNumber("Speed", GetRate()); diff --git a/wpilibc/wpilibC++IntegrationTests/src/FakeEncoderTest.cpp b/wpilibc/wpilibC++IntegrationTests/src/FakeEncoderTest.cpp index d6c3c4d6a9..2143482c12 100644 --- a/wpilibc/wpilibC++IntegrationTests/src/FakeEncoderTest.cpp +++ b/wpilibc/wpilibC++IntegrationTests/src/FakeEncoderTest.cpp @@ -9,24 +9,62 @@ #include "gtest/gtest.h" #include "TestBench.h" -static const double kDelayTime = 0.01; +static const double kDelayTime = 0.001; class FakeEncoderTest : public testing::Test { protected: - Encoder *m_encoder; DigitalOutput *m_outputA; DigitalOutput *m_outputB; + AnalogOutput *m_indexOutput; + + Encoder *m_encoder; + AnalogTrigger *m_indexAnalogTrigger; + AnalogTriggerOutput *m_indexAnalogTriggerOutput; virtual void SetUp() { m_outputA = new DigitalOutput(TestBench::kLoop2OutputChannel); m_outputB = new DigitalOutput(TestBench::kLoop1OutputChannel); + m_indexOutput = new AnalogOutput(TestBench::kAnalogOutputChannel); + m_encoder = new Encoder(TestBench::kLoop1InputChannel, TestBench::kLoop2InputChannel); + m_indexAnalogTrigger = new AnalogTrigger(TestBench::kFakeAnalogOutputChannel); + m_indexAnalogTrigger->SetLimitsVoltage(2.0, 3.0); + m_indexAnalogTriggerOutput = m_indexAnalogTrigger->CreateOutput(AnalogTriggerType::kState); } virtual void TearDown() { - delete m_encoder; delete m_outputA; delete m_outputB; + delete m_indexOutput; + delete m_encoder; + delete m_indexAnalogTrigger; + delete m_indexAnalogTriggerOutput; + } + + /** + * Output pulses to the encoder's input channels to simulate a change of 100 ticks + */ + void Simulate100QuadratureTicks() { + for(int i = 0; i < 100; i++) { + m_outputA->Set(true); + Wait(kDelayTime); + m_outputB->Set(true); + Wait(kDelayTime); + m_outputA->Set(false); + Wait(kDelayTime); + m_outputB->Set(false); + Wait(kDelayTime); + } + } + + void SetIndexHigh() { + m_indexOutput->SetVoltage(5.0); + Wait(kDelayTime); + } + + void SetIndexLow() { + m_indexOutput->SetVoltage(0.0); + Wait(kDelayTime); } }; @@ -42,22 +80,69 @@ TEST_F(FakeEncoderTest, TestDefaultState) { * Test the encoder by setting the digital outputs and reading the value. */ TEST_F(FakeEncoderTest, TestCountUp) { - m_outputA->Set(false); - m_outputB->Set(false); - m_encoder->Reset(); - - //Sets the outputs such that the encoder moves in the positive direction - for(int i = 0; i < 100; i++) { - m_outputA->Set(true); - Wait(kDelayTime); - m_outputB->Set(true); - Wait(kDelayTime); - m_outputA->Set(false); - Wait(kDelayTime); - m_outputB->Set(false); - Wait(kDelayTime); - } + Simulate100QuadratureTicks(); EXPECT_FLOAT_EQ(100.0f, m_encoder->Get()) << "Encoder did not count to 100."; } + +/** + * Test that the encoder can stay reset while the index source is high + */ +TEST_F(FakeEncoderTest, TestResetWhileHigh) { + m_encoder->SetIndexSource(m_indexAnalogTriggerOutput, Encoder::IndexingType::kResetWhileHigh); + + SetIndexLow(); + Simulate100QuadratureTicks(); + SetIndexHigh(); + EXPECT_EQ(0, m_encoder->Get()); + + Simulate100QuadratureTicks(); + EXPECT_EQ(0, m_encoder->Get()); +} + +/** + * Test that the encoder can reset when the index source goes from low to high + */ +TEST_F(FakeEncoderTest, TestResetOnRisingEdge) { + m_encoder->SetIndexSource(m_indexAnalogTriggerOutput, Encoder::IndexingType::kResetOnRisingEdge); + + SetIndexLow(); + Simulate100QuadratureTicks(); + SetIndexHigh(); + EXPECT_EQ(0, m_encoder->Get()); + + Simulate100QuadratureTicks(); + EXPECT_EQ(100, m_encoder->Get()); +} + +/** + * Test that the encoder can stay reset while the index source is low + */ +TEST_F(FakeEncoderTest, TestResetWhileLow) { + m_encoder->SetIndexSource(m_indexAnalogTriggerOutput, Encoder::IndexingType::kResetWhileLow); + + SetIndexHigh(); + Simulate100QuadratureTicks(); + SetIndexLow(); + EXPECT_EQ(0, m_encoder->Get()); + + Simulate100QuadratureTicks(); + EXPECT_EQ(0, m_encoder->Get()); +} + +/** + * Test that the encoder can reset when the index source goes from high to low + */ +TEST_F(FakeEncoderTest, TestResetOnFallingEdge) { + m_encoder->SetIndexSource(m_indexAnalogTriggerOutput, Encoder::IndexingType::kResetOnFallingEdge); + + SetIndexHigh(); + Simulate100QuadratureTicks(); + SetIndexLow(); + EXPECT_EQ(0, m_encoder->Get()); + + Simulate100QuadratureTicks(); + EXPECT_EQ(100, m_encoder->Get()); +} +