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