blob: e230310a5bbb4cef4064055fded33c0955ab9b96 [file] [log] [blame]
Austin Schuh812d0d12021-11-04 20:16:48 -07001// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
Brian Silverman8fce7482020-01-05 13:18:21 -08004
5#include "frc/DigitalInput.h" // NOLINT(build/include_order)
6
7#include "frc/DigitalOutput.h" // NOLINT(build/include_order)
8
Austin Schuh812d0d12021-11-04 20:16:48 -07009#include <units/math.h>
10#include <units/time.h>
11
Brian Silverman8fce7482020-01-05 13:18:21 -080012#include "TestBench.h"
Austin Schuh812d0d12021-11-04 20:16:48 -070013#include "frc/AsynchronousInterrupt.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080014#include "frc/Counter.h"
Austin Schuh812d0d12021-11-04 20:16:48 -070015#include "frc/SynchronousInterrupt.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080016#include "frc/Timer.h"
17#include "gtest/gtest.h"
18
Austin Schuh812d0d12021-11-04 20:16:48 -070019#define EXPECT_NEAR_UNITS(val1, val2, eps) \
20 EXPECT_LE(units::math::abs(val1 - val2), eps)
Brian Silverman8fce7482020-01-05 13:18:21 -080021
Austin Schuh812d0d12021-11-04 20:16:48 -070022static constexpr auto kCounterTime = 1_ms;
Brian Silverman8fce7482020-01-05 13:18:21 -080023
Austin Schuh812d0d12021-11-04 20:16:48 -070024static constexpr auto kDelayTime = 100_ms;
Brian Silverman8fce7482020-01-05 13:18:21 -080025
Austin Schuh812d0d12021-11-04 20:16:48 -070026static constexpr auto kSynchronousInterruptTime = 2_s;
27static constexpr auto kSynchronousInterruptTimeTolerance = 10_ms;
Brian Silverman8fce7482020-01-05 13:18:21 -080028
29/**
30 * A fixture with a digital input and a digital output physically wired
31 * together.
32 */
33class DIOLoopTest : public testing::Test {
34 protected:
Austin Schuh812d0d12021-11-04 20:16:48 -070035 frc::DigitalInput m_input{TestBench::kLoop1InputChannel};
36 frc::DigitalOutput m_output{TestBench::kLoop1OutputChannel};
Brian Silverman8fce7482020-01-05 13:18:21 -080037
Austin Schuh812d0d12021-11-04 20:16:48 -070038 void Reset() { m_output.Set(false); }
Brian Silverman8fce7482020-01-05 13:18:21 -080039};
40
41/**
42 * Test the DigitalInput and DigitalOutput classes by setting the output and
43 * reading the input.
44 */
45TEST_F(DIOLoopTest, Loop) {
46 Reset();
47
Austin Schuh812d0d12021-11-04 20:16:48 -070048 m_output.Set(false);
49 frc::Wait(kDelayTime);
50 EXPECT_FALSE(m_input.Get()) << "The digital output was turned off, but "
51 << "the digital input is on.";
Brian Silverman8fce7482020-01-05 13:18:21 -080052
Austin Schuh812d0d12021-11-04 20:16:48 -070053 m_output.Set(true);
54 frc::Wait(kDelayTime);
55 EXPECT_TRUE(m_input.Get()) << "The digital output was turned on, but "
56 << "the digital input is off.";
Brian Silverman8fce7482020-01-05 13:18:21 -080057}
58/**
59 * Tests to see if the DIO PWM functionality works.
60 */
61TEST_F(DIOLoopTest, DIOPWM) {
62 Reset();
63
Austin Schuh812d0d12021-11-04 20:16:48 -070064 m_output.Set(false);
65 frc::Wait(kDelayTime);
66 EXPECT_FALSE(m_input.Get()) << "The digital output was turned off, but "
67 << "the digital input is on.";
Brian Silverman8fce7482020-01-05 13:18:21 -080068
69 // Set frequency to 2.0 Hz
Austin Schuh812d0d12021-11-04 20:16:48 -070070 m_output.SetPWMRate(2.0);
Brian Silverman8fce7482020-01-05 13:18:21 -080071 // Enable PWM, but leave it off
Austin Schuh812d0d12021-11-04 20:16:48 -070072 m_output.EnablePWM(0.0);
73 frc::Wait(0.5_s);
74 m_output.UpdateDutyCycle(0.5);
75 frc::SynchronousInterrupt interrupt{m_output};
76 interrupt.SetInterruptEdges(false, true);
77 frc::SynchronousInterrupt::WaitResult result =
78 interrupt.WaitForInterrupt(3_s, true);
Brian Silverman8fce7482020-01-05 13:18:21 -080079
Austin Schuh812d0d12021-11-04 20:16:48 -070080 frc::Wait(0.5_s);
81 bool firstCycle = m_input.Get();
82 frc::Wait(0.5_s);
83 bool secondCycle = m_input.Get();
84 frc::Wait(0.5_s);
85 bool thirdCycle = m_input.Get();
86 frc::Wait(0.5_s);
87 bool forthCycle = m_input.Get();
88 frc::Wait(0.5_s);
89 bool fifthCycle = m_input.Get();
90 frc::Wait(0.5_s);
91 bool sixthCycle = m_input.Get();
92 frc::Wait(0.5_s);
93 bool seventhCycle = m_input.Get();
94 m_output.DisablePWM();
95 frc::Wait(0.5_s);
96 bool firstAfterStop = m_input.Get();
97 frc::Wait(0.5_s);
98 bool secondAfterStop = m_input.Get();
Brian Silverman8fce7482020-01-05 13:18:21 -080099
Austin Schuh812d0d12021-11-04 20:16:48 -0700100 EXPECT_EQ(frc::SynchronousInterrupt::WaitResult::kFallingEdge, result)
Brian Silverman8fce7482020-01-05 13:18:21 -0800101 << "WaitForInterrupt was not falling.";
102
103 EXPECT_FALSE(firstCycle) << "Input not low after first delay";
104 EXPECT_TRUE(secondCycle) << "Input not high after second delay";
105 EXPECT_FALSE(thirdCycle) << "Input not low after third delay";
106 EXPECT_TRUE(forthCycle) << "Input not high after forth delay";
107 EXPECT_FALSE(fifthCycle) << "Input not low after fifth delay";
108 EXPECT_TRUE(sixthCycle) << "Input not high after sixth delay";
109 EXPECT_FALSE(seventhCycle) << "Input not low after seventh delay";
110 EXPECT_FALSE(firstAfterStop) << "Input not low after stopping first read";
111 EXPECT_FALSE(secondAfterStop) << "Input not low after stopping second read";
112}
113
114/**
115 * Test a fake "counter" that uses the DIO loop as an input to make sure the
116 * Counter class works
117 */
118TEST_F(DIOLoopTest, FakeCounter) {
119 Reset();
120
Austin Schuh812d0d12021-11-04 20:16:48 -0700121 frc::Counter counter{&m_input};
Brian Silverman8fce7482020-01-05 13:18:21 -0800122
123 EXPECT_EQ(0, counter.Get()) << "Counter did not initialize to 0.";
124
125 /* Count 100 ticks. The counter value should be 100 after this loop. */
Austin Schuh812d0d12021-11-04 20:16:48 -0700126 for (int32_t i = 0; i < 100; ++i) {
127 m_output.Set(true);
128 frc::Wait(kCounterTime);
129 m_output.Set(false);
130 frc::Wait(kCounterTime);
Brian Silverman8fce7482020-01-05 13:18:21 -0800131 }
132
133 EXPECT_EQ(100, counter.Get()) << "Counter did not count up to 100.";
134}
135
Brian Silverman8fce7482020-01-05 13:18:21 -0800136TEST_F(DIOLoopTest, AsynchronousInterruptWorks) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700137 Reset();
138
Brian Silverman8fce7482020-01-05 13:18:21 -0800139 int32_t param = 0;
140
Austin Schuh812d0d12021-11-04 20:16:48 -0700141 frc::AsynchronousInterrupt interrupt(m_input,
142 [&](auto a, auto b) { param = 12345; });
Brian Silverman8fce7482020-01-05 13:18:21 -0800143
Austin Schuh812d0d12021-11-04 20:16:48 -0700144 interrupt.Enable();
Brian Silverman8fce7482020-01-05 13:18:21 -0800145 // If the voltage rises
Austin Schuh812d0d12021-11-04 20:16:48 -0700146 m_output.Set(false);
147 m_output.Set(true);
Brian Silverman8fce7482020-01-05 13:18:21 -0800148
149 // Then the int32_t should be 12345
Austin Schuh812d0d12021-11-04 20:16:48 -0700150 frc::Wait(kDelayTime);
151
152 interrupt.Disable();
153
Brian Silverman8fce7482020-01-05 13:18:21 -0800154 EXPECT_EQ(12345, param) << "The interrupt did not run.";
155}
156
Brian Silverman8fce7482020-01-05 13:18:21 -0800157TEST_F(DIOLoopTest, SynchronousInterruptWorks) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700158 Reset();
Brian Silverman8fce7482020-01-05 13:18:21 -0800159
Austin Schuh812d0d12021-11-04 20:16:48 -0700160 // Given a synchronous interrupt
161 frc::SynchronousInterrupt interrupt(m_input);
162
163 std::thread thr([this]() {
164 m_output.Set(false);
165 frc::Wait(kSynchronousInterruptTime);
166 m_output.Set(true);
167 });
Brian Silverman8fce7482020-01-05 13:18:21 -0800168
169 // Then this thread should pause and resume after that number of seconds
Austin Schuh812d0d12021-11-04 20:16:48 -0700170 frc::Timer timer;
Brian Silverman8fce7482020-01-05 13:18:21 -0800171 timer.Start();
Austin Schuh812d0d12021-11-04 20:16:48 -0700172 interrupt.WaitForInterrupt(kSynchronousInterruptTime + 1_s);
173 auto time = timer.Get().value();
174 if (thr.joinable())
175 thr.join();
176 EXPECT_NEAR(kSynchronousInterruptTime.value(), time,
177 kSynchronousInterruptTimeTolerance.value());
Brian Silverman8fce7482020-01-05 13:18:21 -0800178}