skullc-peripherals/Peripherals/Inc/peripherals_button.hpp

128 lines
2.6 KiB
C++

/*
* peripherals_button.hpp
*
* Created on: Apr 17, 2021
* Author: erki
*/
#ifndef SKULLC_PERIPHERALS_BUTTON_HPP_
#define SKULLC_PERIPHERALS_BUTTON_HPP_
#include <cstdint>
#include "peripherals_hal_concepts.hpp"
#ifdef SKULLC_WITH_CORO
#include "skullc/coro/sleep.hpp"
#include "skullc/coro/task.hpp"
#include "skullc/coro/this_coro.hpp"
#endif
namespace Peripherals
{
enum class ButtonPress : std::uint32_t
{
NOT_PRESSED,
SHORT_PRESS,
LONG_PRESS
};
template<Hal::Gpio G, Hal::StaticHal H>
class Button
{
public:
using gpio = G;
using hal = H;
gpio sw;
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), was_pressed_(sw.read())
{}
void update()
{
const bool is_pressed = sw.read();
ButtonPress new_state = ButtonPress::NOT_PRESSED;
const std::uint32_t time_held = hal::getMillis() - time_pressed_down_;
if (is_pressed && !was_pressed_)
{
time_pressed_down_ = hal::getMillis();
}
else if (!is_pressed && was_pressed_)
{
if (time_held > TIMEOUT_SHORT_PRESS && time_held <= TIMEOUT_LONG_PRESS)
{
new_state = ButtonPress::SHORT_PRESS;
}
if (state_exhausted_)
{
current_state_ = ButtonPress::NOT_PRESSED;
state_exhausted_ = false;
}
time_pressed_down_ = 0;
}
else if (is_pressed && was_pressed_)
{
if (current_state_ == ButtonPress::NOT_PRESSED && time_held > TIMEOUT_LONG_PRESS)
new_state = ButtonPress::LONG_PRESS;
}
was_pressed_ = is_pressed;
if (!state_exhausted_)
current_state_ = new_state;
}
[[nodiscard]] ButtonPress getState() const
{
if (!state_exhausted_)
{
if (current_state_ != ButtonPress::NOT_PRESSED && current_state_ != ButtonPress::SHORT_PRESS)
state_exhausted_ = true;
return current_state_;
}
else
{
return ButtonPress::NOT_PRESSED;
}
}
#ifdef SKULLC_WITH_CORO
skullc::coro::Task<ButtonPress> await_press()
{
ButtonPress result = ButtonPress::NOT_PRESSED;
while (result == ButtonPress::NOT_PRESSED)
{
update();
result = getState();
if (result == ButtonPress::NOT_PRESSED)
co_await skullc::coro::sleep(std::chrono::milliseconds(hal::getMillis()), std::chrono::milliseconds(1));
}
co_return result;
}
#endif
private:
bool was_pressed_;
std::uint32_t time_pressed_down_ = 0;
ButtonPress current_state_ = ButtonPress::NOT_PRESSED;
mutable bool state_exhausted_ = false;
};
}// namespace Peripherals
#endif /* SKULLC_PERIPHERALS_BUTTON_HPP_ */