diff --git a/CMakeLists.txt b/CMakeLists.txt index 521963c..1f5c83f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,10 +20,7 @@ option(WITH_HAL "Enable the compiling and deployment of the HAL dependent sectio include(skullc-install) -if(WITH_HAL) - add_subdirectory(Peripherals) -endif() - +add_subdirectory(Peripherals) add_subdirectory(Utility) add_subdirectory(Messaging) diff --git a/Peripherals/Inc/peripherals_button.hpp b/Peripherals/Inc/peripherals_button.hpp new file mode 100644 index 0000000..fe2207f --- /dev/null +++ b/Peripherals/Inc/peripherals_button.hpp @@ -0,0 +1,76 @@ +/* + * peripherals_button.hpp + * + * Created on: Apr 17, 2021 + * Author: erki + */ + +#ifndef PERIPHERALS_BUTTON_HPP_ +#define PERIPHERALS_BUTTON_HPP_ + +#include + +namespace Peripherals +{ + +enum class ButtonPress : std::uint32_t +{ + NOT_PRESSED, + SHORT_PRESS, + LONG_PRESS +}; + +template +class Button +{ +public: + using gpio = G; + using hal = H; + + static constexpr std::uint32_t TIMEOUT_SHORT_PRESS = 50; + static constexpr std::uint32_t TIMEOUT_LONG_PRESS = 500; + + Button() = delete; + explicit Button(const gpio& sw) + : _sw(sw) + {} + + void update() + { + const bool is_pressed = _sw.Read(); + ButtonPress new_state = ButtonPress::NOT_PRESSED; + + if (is_pressed && !_was_pressed) + { + _time_pressed_down = hal::GetMillis(); + } else if (!is_pressed && _was_pressed) + { + const std::uint32_t time_held = hal::GetMillis() - _time_pressed_down; + if (time_held > TIMEOUT_LONG_PRESS) + new_state = ButtonPress::LONG_PRESS; + else if (time_held > TIMEOUT_SHORT_PRESS) + new_state = ButtonPress::SHORT_PRESS; + + _time_pressed_down = 0; + } + + _was_pressed = is_pressed; + _current_state = new_state; + } + + [[nodiscard]] ButtonPress getState() const + { + return _current_state; + } + +private: + gpio _sw; + + bool _was_pressed = false; + std::uint32_t _time_pressed_down = 0; + ButtonPress _current_state = ButtonPress::NOT_PRESSED; +}; + +}// namespace Peripherals + +#endif /* PERIPHERALS_BUTTON_HPP_ */ diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 41d3356..1a472f1 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -13,12 +13,14 @@ add_executable(tests main.cpp ringbuffer.cpp packet.cpp - parser.cpp) + parser.cpp + button.cpp) target_link_libraries(tests PUBLIC skullc::utility skullc::messaging + skullc::peripherals Catch2::Catch2 ) diff --git a/Tests/button.cpp b/Tests/button.cpp new file mode 100644 index 0000000..ff30e7f --- /dev/null +++ b/Tests/button.cpp @@ -0,0 +1,126 @@ +// +// Created by erki on 17.04.21. +// + +#include + +#include "peripherals_button.hpp" + +namespace +{ + +struct HAL +{ + HAL() + { + millis = 1; + } + + ~HAL() + { + millis = 1; + } + + static std::uint32_t millis; + + static std::uint32_t GetMillis() + { + return millis; + } +}; + +std::uint32_t HAL::millis = 1; + +struct Gpio +{ + static bool set; + + Gpio() + { + set = false; + } + + bool Read() + { + return set; + } +}; + +bool Gpio::set = false; + +}// namespace + +using Button = Peripherals::Button; + +TEST_CASE("Button reads no press as NOT_PRESSED.", "[peripherals],[button]") +{ + Gpio g; + HAL hal; + + Button button{g}; + + button.update(); + + HAL::millis = Button::TIMEOUT_LONG_PRESS + 10; + + button.update(); + + REQUIRE(button.getState() == Peripherals::ButtonPress::NOT_PRESSED); +} + +TEST_CASE("Button reads presses properly.", "[peripherals],[button]") +{ + Gpio g; + HAL hal; + + Button button{g}; + + Gpio::set = true; + button.update(); + + SECTION("Too short of a press is debounced.") + { + HAL::millis = Button::TIMEOUT_SHORT_PRESS - 1; + Gpio::set = false; + + button.update(); + + REQUIRE(button.getState() == Peripherals::ButtonPress::NOT_PRESSED); + } + + SECTION("Short press is identified properly.") + { + HAL::millis = Button::TIMEOUT_SHORT_PRESS + 2; + Gpio::set = false; + + button.update(); + + REQUIRE(button.getState() == Peripherals::ButtonPress::SHORT_PRESS); + + SECTION("Next state is NOT_PRESSED.") + { + HAL::millis += 1; + button.update(); + + REQUIRE(button.getState() == Peripherals::ButtonPress::NOT_PRESSED); + } + } + + SECTION("Long press is identified properly.") + { + HAL::millis = Button::TIMEOUT_LONG_PRESS + 2; + Gpio::set = false; + + button.update(); + + REQUIRE(button.getState() == Peripherals::ButtonPress::LONG_PRESS); + + SECTION("Next state is NOT_PRESSED.") + { + HAL::millis += 1; + button.update(); + + REQUIRE(button.getState() == Peripherals::ButtonPress::NOT_PRESSED); + } + } +}