blob: 10b64d595f61723a0fa2c41a204e88edd5bb28b7 [file] [log] [blame]
Brian Silverman41cdd3e2019-01-19 19:48:58 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) 2014-2018 FIRST. All Rights Reserved. */
3/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
8#include "frc/DigitalInput.h" // NOLINT(build/include_order)
9
10#include "frc/DigitalOutput.h" // NOLINT(build/include_order)
11
12#include "TestBench.h"
13#include "frc/Counter.h"
14#include "frc/InterruptableSensorBase.h"
15#include "frc/Timer.h"
16#include "gtest/gtest.h"
17
18using namespace frc;
19
20static const double kCounterTime = 0.001;
21
22static const double kDelayTime = 0.1;
23
24static const double kSynchronousInterruptTime = 2.0;
25static const double kSynchronousInterruptTimeTolerance = 0.01;
26
27/**
28 * A fixture with a digital input and a digital output physically wired
29 * together.
30 */
31class DIOLoopTest : public testing::Test {
32 protected:
33 DigitalInput* m_input;
34 DigitalOutput* m_output;
35
36 void SetUp() override {
37 m_input = new DigitalInput(TestBench::kLoop1InputChannel);
38 m_output = new DigitalOutput(TestBench::kLoop1OutputChannel);
39 }
40
41 void TearDown() override {
42 delete m_input;
43 delete m_output;
44 }
45
46 void Reset() { m_output->Set(false); }
47};
48
49/**
50 * Test the DigitalInput and DigitalOutput classes by setting the output and
51 * reading the input.
52 */
53TEST_F(DIOLoopTest, Loop) {
54 Reset();
55
56 m_output->Set(false);
57 Wait(kDelayTime);
58 EXPECT_FALSE(m_input->Get()) << "The digital output was turned off, but "
59 << "the digital input is on.";
60
61 m_output->Set(true);
62 Wait(kDelayTime);
63 EXPECT_TRUE(m_input->Get()) << "The digital output was turned on, but "
64 << "the digital input is off.";
65}
66/**
67 * Tests to see if the DIO PWM functionality works.
68 */
69TEST_F(DIOLoopTest, DIOPWM) {
70 Reset();
71
72 m_output->Set(false);
73 Wait(kDelayTime);
74 EXPECT_FALSE(m_input->Get()) << "The digital output was turned off, but "
75 << "the digital input is on.";
76
77 // Set frequency to 2.0 Hz
78 m_output->SetPWMRate(2.0);
79 // Enable PWM, but leave it off
80 m_output->EnablePWM(0.0);
81 Wait(0.5);
82 m_output->UpdateDutyCycle(0.5);
83 m_input->RequestInterrupts();
84 m_input->SetUpSourceEdge(false, true);
85 InterruptableSensorBase::WaitResult result =
86 m_input->WaitForInterrupt(3.0, true);
87
88 Wait(0.5);
89 bool firstCycle = m_input->Get();
90 Wait(0.5);
91 bool secondCycle = m_input->Get();
92 Wait(0.5);
93 bool thirdCycle = m_input->Get();
94 Wait(0.5);
95 bool forthCycle = m_input->Get();
96 Wait(0.5);
97 bool fifthCycle = m_input->Get();
98 Wait(0.5);
99 bool sixthCycle = m_input->Get();
100 Wait(0.5);
101 bool seventhCycle = m_input->Get();
102 m_output->DisablePWM();
103 Wait(0.5);
104 bool firstAfterStop = m_input->Get();
105 Wait(0.5);
106 bool secondAfterStop = m_input->Get();
107
108 EXPECT_EQ(InterruptableSensorBase::WaitResult::kFallingEdge, result)
109 << "WaitForInterrupt was not falling.";
110
111 EXPECT_FALSE(firstCycle) << "Input not low after first delay";
112 EXPECT_TRUE(secondCycle) << "Input not high after second delay";
113 EXPECT_FALSE(thirdCycle) << "Input not low after third delay";
114 EXPECT_TRUE(forthCycle) << "Input not high after forth delay";
115 EXPECT_FALSE(fifthCycle) << "Input not low after fifth delay";
116 EXPECT_TRUE(sixthCycle) << "Input not high after sixth delay";
117 EXPECT_FALSE(seventhCycle) << "Input not low after seventh delay";
118 EXPECT_FALSE(firstAfterStop) << "Input not low after stopping first read";
119 EXPECT_FALSE(secondAfterStop) << "Input not low after stopping second read";
120}
121
122/**
123 * Test a fake "counter" that uses the DIO loop as an input to make sure the
124 * Counter class works
125 */
126TEST_F(DIOLoopTest, FakeCounter) {
127 Reset();
128
129 Counter counter(m_input);
130
131 EXPECT_EQ(0, counter.Get()) << "Counter did not initialize to 0.";
132
133 /* Count 100 ticks. The counter value should be 100 after this loop. */
134 for (int32_t i = 0; i < 100; i++) {
135 m_output->Set(true);
136 Wait(kCounterTime);
137 m_output->Set(false);
138 Wait(kCounterTime);
139 }
140
141 EXPECT_EQ(100, counter.Get()) << "Counter did not count up to 100.";
142}
143
144static void InterruptHandler(uint32_t interruptAssertedMask, void* param) {
145 *reinterpret_cast<int32_t*>(param) = 12345;
146}
147
148TEST_F(DIOLoopTest, AsynchronousInterruptWorks) {
149 int32_t param = 0;
150
151 // Given an interrupt handler that sets an int32_t to 12345
152 m_input->RequestInterrupts(InterruptHandler, &param);
153 m_input->EnableInterrupts();
154
155 // If the voltage rises
156 m_output->Set(false);
157 m_output->Set(true);
158 m_input->CancelInterrupts();
159
160 // Then the int32_t should be 12345
161 Wait(kDelayTime);
162 EXPECT_EQ(12345, param) << "The interrupt did not run.";
163}
164
165static void* InterruptTriggerer(void* data) {
166 DigitalOutput* output = static_cast<DigitalOutput*>(data);
167 output->Set(false);
168 Wait(kSynchronousInterruptTime);
169 output->Set(true);
170 return nullptr;
171}
172
173TEST_F(DIOLoopTest, SynchronousInterruptWorks) {
174 // Given a synchronous interrupt
175 m_input->RequestInterrupts();
176
177 // If we have another thread trigger the interrupt in a few seconds
178 pthread_t interruptTriggererLoop;
179 pthread_create(&interruptTriggererLoop, nullptr, InterruptTriggerer,
180 m_output);
181
182 // Then this thread should pause and resume after that number of seconds
183 Timer timer;
184 timer.Start();
185 m_input->WaitForInterrupt(kSynchronousInterruptTime + 1.0);
186 EXPECT_NEAR(kSynchronousInterruptTime, timer.Get(),
187 kSynchronousInterruptTimeTolerance);
188}