You've already forked M5Unit-KEYBOARD
mirror of
https://github.com/m5stack/M5Unit-KEYBOARD.git
synced 2026-05-20 11:36:42 -07:00
634 lines
18 KiB
C++
634 lines
18 KiB
C++
/*
|
|
* SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
/*!
|
|
@file unit_Keyboard.hpp
|
|
@brief Base class for units
|
|
*/
|
|
#ifndef M5_UNIT_KEYBOARD_UNIT_KEYBOARD_HPP
|
|
#define M5_UNIT_KEYBOARD_UNIT_KEYBOARD_HPP
|
|
|
|
#include <M5UnitComponent.hpp>
|
|
#include <vector>
|
|
|
|
namespace m5 {
|
|
namespace unit {
|
|
|
|
/*!
|
|
@namespace keyboard
|
|
@brief For keyboard
|
|
*/
|
|
namespace keyboard {
|
|
using key_index_t = uint8_t; //!< @brief Key index (Not character)
|
|
|
|
using key_status_bits_t = uint64_t; //!< @brief key state bits [56...63]:modifier, bits [0...55]:key bits
|
|
// clang-format off
|
|
///@name Modifier's bit [56...63]
|
|
///@{
|
|
constexpr key_status_bits_t MODIFIER_SHIFT_BIT {0x0100000000000000}; //!< Shift
|
|
constexpr key_status_bits_t MODIFIER_SYMBOL_BIT {0x0200000000000000}; //!< Symbol
|
|
constexpr key_status_bits_t MODIFIER_FUNCTION_BIT{0x0400000000000000}; //!< Function
|
|
constexpr key_status_bits_t MODIFIER_ALT_BIT {0x0800000000000000}; //!< Alt
|
|
constexpr key_status_bits_t MODIFIER_CONTROL_BIT {0x1000000000000000}; //!< Control
|
|
constexpr key_status_bits_t MODIFIER_OPTION_BIT {0x2000000000000000}; //!< Option
|
|
///@}
|
|
// clang-format on
|
|
|
|
//! @brief Gets the modifier bits from key_status_bits_t
|
|
constexpr inline key_status_bits_t modifier_bits(const key_status_bits_t kbs)
|
|
{
|
|
return kbs & (MODIFIER_SHIFT_BIT | MODIFIER_SYMBOL_BIT | MODIFIER_FUNCTION_BIT | MODIFIER_ALT_BIT |
|
|
MODIFIER_CONTROL_BIT | MODIFIER_OPTION_BIT);
|
|
}
|
|
|
|
/*!
|
|
@enum Mode
|
|
@brief Operation mode for M5Unit-KEYBOARD firmware
|
|
*/
|
|
enum class Mode : uint8_t {
|
|
/*!
|
|
Conventional behavior
|
|
@details CardKB,FacesQWERTY:Gets the released key
|
|
*/
|
|
Conventional,
|
|
/*!
|
|
M5Unit-KEYBOARD mode behavior
|
|
@details CardKB, FacesQWERTY:Gets the pressed key status
|
|
@warning M5Unit-KEYBOARD firmware must be written
|
|
*/
|
|
M5UnitUnified,
|
|
};
|
|
|
|
} // namespace keyboard
|
|
|
|
/*!
|
|
@class m5::unit::UnitKeyboard
|
|
@brief Base class of the Keyboard Unit
|
|
*/
|
|
class UnitKeyboard : public Component {
|
|
M5_UNIT_COMPONENT_HPP_BUILDER(UnitKeyboard, 0x00);
|
|
|
|
public:
|
|
// 0x5F:CardKB 0x08:FacesQWERTY
|
|
explicit UnitKeyboard(const uint8_t addr = DEFAULT_ADDRESS) : Component(addr)
|
|
{
|
|
auto ccfg = component_config();
|
|
ccfg.clock = 100 * 1000U;
|
|
component_config(ccfg);
|
|
}
|
|
virtual ~UnitKeyboard()
|
|
{
|
|
}
|
|
|
|
//! @copydoc Component::begin
|
|
virtual bool begin() override;
|
|
//! @copydoc Component::update
|
|
virtual void update(const bool force = false) override;
|
|
|
|
/*!
|
|
@brief Gets the input character
|
|
@retval != 0 Character
|
|
@retval == 0 Not input or invalid character
|
|
@note Whether the input is a released or pressed key depends on the derived class
|
|
*/
|
|
inline virtual char getchar() const
|
|
{
|
|
return released();
|
|
}
|
|
|
|
/*!
|
|
@brief Gets the released key character code if updated
|
|
@retval != 0 Released character code
|
|
@retval == 0 There is no released key
|
|
*/
|
|
inline virtual uint8_t released() const
|
|
{
|
|
return updated() ? _released_key : 0;
|
|
}
|
|
|
|
private:
|
|
uint8_t _released_key{}; // for old firmware
|
|
};
|
|
|
|
/*!
|
|
@class m5::unit::UnitKeyboardBitwise
|
|
@brief Class supporting keyboard state acquisition by key press bits
|
|
@warning To make it work, M5Unit-KEYBOARD firmware must be written to the target (CardKB, FacesQWERTY...)
|
|
*/
|
|
class UnitKeyboardBitwise : public UnitKeyboard, public PeriodicMeasurementAdapter<UnitKeyboardBitwise, uint8_t> {
|
|
M5_UNIT_COMPONENT_HPP_BUILDER(UnitKeyboardBitwise, 0x00);
|
|
|
|
public:
|
|
explicit UnitKeyboardBitwise(const uint8_t addr = DEFAULT_ADDRESS)
|
|
: UnitKeyboard(addr), _data{new m5::container::CircularBuffer<uint8_t>(1)}
|
|
{
|
|
}
|
|
virtual ~UnitKeyboardBitwise()
|
|
{
|
|
}
|
|
|
|
//! @copydoc Component::update
|
|
virtual void update(const bool force = false) override;
|
|
|
|
///@name Measurement data by periodic
|
|
///@{
|
|
/*!
|
|
@brief Gets the input character
|
|
@retval != 0 Character
|
|
@retval == 0 Not input or invalid character
|
|
*/
|
|
inline virtual char getchar() const override
|
|
{
|
|
return (_mode == keyboard::Mode::M5UnitUnified) ? pressed() : released();
|
|
}
|
|
/*!
|
|
@brief Get the oldest pressed key
|
|
@warning API valid only if using M5Unit-KEYBOARD firmware
|
|
*/
|
|
inline uint8_t pressed() const
|
|
{
|
|
return !empty() ? oldest() : 0x00;
|
|
}
|
|
/*!
|
|
@brief Get the oldest released key
|
|
@warning Disabled in all modes except Conventional mode
|
|
*/
|
|
inline virtual uint8_t released() const override
|
|
{
|
|
return (_mode == keyboard::Mode::Conventional) ? UnitKeyboard::released() : 0x00;
|
|
}
|
|
///@}
|
|
|
|
///@name Periodic measurement
|
|
///@{
|
|
/*!
|
|
@brief Start periodic measurement
|
|
@param interval Update interval time(ms)
|
|
@return True if successful
|
|
@warning If config_t::trigger_irq is set to true, arguments is ignored
|
|
*/
|
|
inline bool startPeriodicMeasurement(const uint32_t interval)
|
|
{
|
|
return PeriodicMeasurementAdapter<UnitKeyboardBitwise, uint8_t>::startPeriodicMeasurement(interval);
|
|
}
|
|
//! @brief Start periodic measurement using current settings
|
|
inline bool startPeriodicMeasurement()
|
|
{
|
|
return PeriodicMeasurementAdapter<UnitKeyboardBitwise, uint8_t>::startPeriodicMeasurement();
|
|
}
|
|
/*!
|
|
@brief Stop periodic measurement
|
|
@return True if successful
|
|
*/
|
|
inline bool stopPeriodicMeasurement()
|
|
{
|
|
return PeriodicMeasurementAdapter<UnitKeyboardBitwise, uint8_t>::stopPeriodicMeasurement();
|
|
}
|
|
///@}
|
|
|
|
///@warning API valid only if using M5Unit-KEYBOARD firmware
|
|
///@note Which bits represent what depends on the target unit
|
|
///@name Key status bits if updated
|
|
///@{
|
|
//! @brief Get the bits of the key being pressed
|
|
inline keyboard::key_status_bits_t nowBits() const
|
|
{
|
|
return _now;
|
|
}
|
|
//! @brief Get the bits of the previous key pressed
|
|
inline keyboard::key_status_bits_t previousBits() const
|
|
{
|
|
return _prev;
|
|
}
|
|
//! @brief Get the key bits at the moment they are pressed
|
|
inline keyboard::key_status_bits_t pressedBits() const
|
|
{
|
|
return _wasPressed;
|
|
}
|
|
//! @brief Get the key bits at the moment they are released
|
|
inline keyboard::key_status_bits_t releasedBits() const
|
|
{
|
|
return _wasReleased;
|
|
}
|
|
//! @brief Get the bits of the held key
|
|
inline keyboard::key_status_bits_t holdingBits() const
|
|
{
|
|
return _holding;
|
|
}
|
|
//! @brief Get the bits of the key at the moment of hold
|
|
inline keyboard::key_status_bits_t wasHoldBits() const
|
|
{
|
|
return _wasHold;
|
|
}
|
|
//! @brief Get the bits of the key that the software is repeatedly pressing
|
|
inline keyboard::key_status_bits_t repeatingBits() const
|
|
{
|
|
return _repeating;
|
|
}
|
|
//! @brief Get the bits of the modifier key being pressed
|
|
inline keyboard::key_status_bits_t modifierBits() const
|
|
{
|
|
return modifier_bits();
|
|
}
|
|
///@}
|
|
|
|
///@warning API valid only if using M5Unit-KEYBOARD firmware
|
|
///@note Some keys do not exist depending on the target Unit
|
|
///@name Modifier
|
|
///@{
|
|
//! @brief Is any modifier keys pressed?
|
|
inline bool isModifier() const
|
|
{
|
|
return modifier_bits() != 0;
|
|
}
|
|
//! @brief Is the shift key pressed?
|
|
inline bool isShift() const
|
|
{
|
|
return modifier_bits() & keyboard::MODIFIER_SHIFT_BIT;
|
|
}
|
|
//! @brief Is the symbol key pressed?
|
|
inline bool isSymbol() const
|
|
{
|
|
return modifier_bits() & keyboard::MODIFIER_SYMBOL_BIT;
|
|
}
|
|
//! @brief Is the function key pressed?
|
|
inline bool isFunction() const
|
|
{
|
|
return modifier_bits() & keyboard::MODIFIER_FUNCTION_BIT;
|
|
}
|
|
//! @brief Is the alt key pressed?
|
|
inline bool isAlt() const
|
|
{
|
|
return modifier_bits() & keyboard::MODIFIER_ALT_BIT;
|
|
}
|
|
//! @brief Is the control key pressed?
|
|
inline bool isControl() const
|
|
{
|
|
return modifier_bits() & keyboard::MODIFIER_CONTROL_BIT;
|
|
}
|
|
//! @brief Is the option key pressed?
|
|
inline bool isOption() const
|
|
{
|
|
return modifier_bits() & keyboard::MODIFIER_OPTION_BIT;
|
|
}
|
|
|
|
//! @brief Is only shift pressed among the modifier keys?
|
|
inline bool isShiftEqual() const
|
|
{
|
|
return modifier_bits() == keyboard::MODIFIER_SHIFT_BIT;
|
|
}
|
|
//! @brief Is only symbol pressed among the modifier keys?
|
|
inline bool isSymbolEqual() const
|
|
{
|
|
return modifier_bits() == keyboard::MODIFIER_SYMBOL_BIT;
|
|
}
|
|
//! @brief Is only function pressed among the modifier keys?
|
|
inline bool isFunctionEqual() const
|
|
{
|
|
return modifier_bits() == keyboard::MODIFIER_FUNCTION_BIT;
|
|
}
|
|
//! @brief Is only alt pressed among the modifier keys?
|
|
inline bool isAltEqual() const
|
|
{
|
|
return modifier_bits() == keyboard::MODIFIER_ALT_BIT;
|
|
}
|
|
//! @brief Is only control pressed among the modifier keys?
|
|
inline bool isControlEqual() const
|
|
{
|
|
return modifier_bits() == keyboard::MODIFIER_CONTROL_BIT;
|
|
}
|
|
//! @brief Is only option pressed among the modifier keys?
|
|
inline bool isOptionEqual() const
|
|
{
|
|
return modifier_bits() == keyboard::MODIFIER_OPTION_BIT;
|
|
}
|
|
///@}
|
|
|
|
///@warning API valid only if using M5Unit-KEYBOARD firmware
|
|
///@name Any key
|
|
///@{
|
|
//! @brief Is any key press?
|
|
inline bool isPressed() const
|
|
{
|
|
return _now != 0;
|
|
}
|
|
//! @brief Is all key release?
|
|
inline bool isReleased() const
|
|
{
|
|
return !isPressed();
|
|
}
|
|
//! @brief Was any key pressed?
|
|
inline bool wasPressed() const
|
|
{
|
|
return _wasPressed;
|
|
}
|
|
//! @brief Was any key released?
|
|
inline bool wasReleased() const
|
|
{
|
|
return _wasReleased;
|
|
}
|
|
//! @brief Is any key holding?
|
|
inline bool isHolding() const
|
|
{
|
|
return _holding;
|
|
}
|
|
//! @brief Was any key hold?
|
|
inline bool wasHold() const
|
|
{
|
|
return _wasHold;
|
|
}
|
|
//! @brief Is any key repeating?
|
|
inline bool isRepeating() const
|
|
{
|
|
return _repeating;
|
|
}
|
|
///@}
|
|
|
|
///@warning API valid only if using M5Unit-KEYBOARD firmware
|
|
///@name Specified key (Key index)
|
|
///@{
|
|
/*!
|
|
@brief Is the specified key pressed?
|
|
@param kidx Key index code
|
|
@return If so,true
|
|
*/
|
|
// inline
|
|
bool isPressed(const keyboard::key_index_t kidx) const
|
|
{
|
|
return _now & (1ULL << kidx);
|
|
}
|
|
/*!
|
|
@brief Is the specified key released?
|
|
@param kidx Key index code
|
|
@return If so,true
|
|
*/
|
|
inline bool isReleased(const keyboard::key_index_t kidx) const
|
|
{
|
|
return !isPressed(kidx);
|
|
}
|
|
/*!
|
|
@brief Was the specified key pressed?
|
|
@param kidx Key index code
|
|
@return If so,true
|
|
*/
|
|
inline bool wasPressed(const keyboard::key_index_t kidx) const
|
|
{
|
|
return _wasPressed & (1ULL << kidx);
|
|
}
|
|
/*!
|
|
@brief Was the specified key released?
|
|
@param kidx Key index code
|
|
@return If so,true
|
|
*/
|
|
inline bool wasReleased(const keyboard::key_index_t kidx) const
|
|
{
|
|
return _wasReleased & (1ULL << kidx);
|
|
}
|
|
/*!
|
|
@brief Is the specified key holding?
|
|
@param kidx Key index code
|
|
@return If so,true
|
|
*/
|
|
inline bool isHolding(const keyboard::key_index_t kidx) const
|
|
{
|
|
return _holding & (1ULL << kidx);
|
|
}
|
|
/*!
|
|
@brief Was the specified key hold?
|
|
@param kidx Key index code
|
|
@return If so,true
|
|
*/
|
|
inline bool wasHold(const keyboard::key_index_t kidx) const
|
|
{
|
|
return _wasHold & (1ULL << kidx);
|
|
}
|
|
/*!
|
|
@brief Is the specified key repeating?
|
|
@param kidx Key index code
|
|
@return If so,true
|
|
*/
|
|
inline bool isRepeating(const keyboard::key_index_t kidx) const
|
|
{
|
|
return _repeating & (1ULL << kidx);
|
|
}
|
|
///@}
|
|
|
|
///@warning API valid only if using M5Unit-KEYBOARD firmware.
|
|
/// For CardKB2 UART mode, character-based state queries (isPressed(ch), wasPressed(ch), etc.)
|
|
/// may return inaccurate results because press and release events can arrive in the same update
|
|
/// cycle, causing the key state to be cleared before the buffered character is read.
|
|
/// Use key-index-based queries (isPressed(kidx)) instead.
|
|
/// This limitation may be improved in a future firmware update.
|
|
///@name Specified Character
|
|
///@{
|
|
/*!
|
|
@brief Obtains the key index corresponding to the specified character
|
|
@retval != 0xFF key index
|
|
@retval == 0xFF No corresponding key index
|
|
*/
|
|
inline virtual keyboard::key_index_t toKeyIndex(const char) const
|
|
{
|
|
return 0xFF;
|
|
}
|
|
/*!
|
|
@brief Is the specified character pressed?
|
|
@param ch Character
|
|
@return If so,true
|
|
*/
|
|
inline bool isPressed(const char ch) const
|
|
{
|
|
auto kidx = toKeyIndex(ch);
|
|
return kidx != 0xFF && isPressed(kidx) && permitted_mode(to_mode_bits(ch));
|
|
}
|
|
/*!
|
|
@brief Is the specified character released?
|
|
@param ch Character
|
|
@return If so,true
|
|
*/
|
|
inline bool isReleased(const char ch) const
|
|
{
|
|
return !isPressed(ch);
|
|
}
|
|
/*!
|
|
@brief Was the specified character pressed?
|
|
@param ch Character
|
|
@return If so,true
|
|
*/
|
|
inline bool wasPressed(const char ch) const
|
|
{
|
|
auto kidx = toKeyIndex(ch);
|
|
return kidx != 0xFF && wasPressed(kidx) && permitted_mode(to_mode_bits(ch));
|
|
}
|
|
/*!
|
|
@brief Was the specified character released?
|
|
@param ch Character
|
|
@return If so,true
|
|
*/
|
|
inline bool wasReleased(const char ch) const
|
|
{
|
|
auto kidx = toKeyIndex(ch);
|
|
return kidx != 0xFF && wasReleased(kidx) && permitted_mode(to_mode_bits(ch));
|
|
}
|
|
/*!
|
|
@brief Is the specified character holding?
|
|
@param ch Character
|
|
@return If so,true
|
|
*/
|
|
inline bool isHolding(const char ch) const
|
|
{
|
|
auto kidx = toKeyIndex(ch);
|
|
return kidx != 0xFF && isHolding(kidx) && permitted_mode(to_mode_bits(ch));
|
|
}
|
|
/*!
|
|
@brief Was the specified character hold?
|
|
@param ch Character
|
|
@return If so,true
|
|
*/
|
|
inline bool wasHold(const char ch) const
|
|
{
|
|
auto kidx = toKeyIndex(ch);
|
|
return kidx != 0xFF && wasHold(kidx) && permitted_mode(to_mode_bits(ch));
|
|
}
|
|
/*!
|
|
@brief Is the specified character repeating?
|
|
@param ch Character
|
|
@return If so,true
|
|
*/
|
|
inline bool isRepeating(const char ch) const
|
|
{
|
|
auto kidx = toKeyIndex(ch);
|
|
return kidx != 0xFF && isRepeating(kidx) && permitted_mode(to_mode_bits(ch));
|
|
}
|
|
///@}
|
|
|
|
///@warning API valid only if using M5Unit-KEYBOARD firmware
|
|
///@name Firmware
|
|
///@{
|
|
/*!
|
|
@brief Gets the firmware version
|
|
@retval == 0 Conventional firmware
|
|
@retval != 0 M5UnitUnified firmware version
|
|
@warning Valid after begin
|
|
*/
|
|
inline uint8_t firmwareVersion() const
|
|
{
|
|
return _firmware_version;
|
|
}
|
|
/*!
|
|
@brief Read the firmware version
|
|
@param[out] ver Version high nibble:Major, low nibble:Minor
|
|
@return True if successful
|
|
*/
|
|
virtual bool readFirmwareVersion(uint8_t& ver);
|
|
///@}
|
|
|
|
///@warning API valid only if using M5Unit-KEYBOARD firmware
|
|
///@name Repeat/Hold threshold
|
|
///@{
|
|
//! @brief Gets the holding threshold (ms)
|
|
inline uint32_t holdingThreshold() const
|
|
{
|
|
return _holding_threshold;
|
|
}
|
|
//! @brief Gets the repeating threshold (ms)
|
|
inline uint32_t repeatingThreshold() const
|
|
{
|
|
return _repeating_threshold;
|
|
}
|
|
//! @brief Sets the holding threshold
|
|
//! @param ms Threshold in milliseconds
|
|
inline void setHoldingThreshold(const uint32_t ms)
|
|
{
|
|
_holding_threshold = ms;
|
|
}
|
|
//! @brief Sets the repeating threshold
|
|
//! @param ms Threshold in milliseconds
|
|
inline void setRepeatingThreshold(const uint32_t ms)
|
|
{
|
|
_repeating_threshold = ms;
|
|
}
|
|
///@}
|
|
|
|
///@warning API valid only if using M5Unit-KEYBOARD firmware (CardKB, FacesQWERTY).
|
|
/// CardKB2 does not support software mode switching; readMode()/writeMode() always return false.
|
|
///@name Mode
|
|
///@{
|
|
/*!
|
|
@brief Read the mode
|
|
@param[out] mode Mode
|
|
@return True if successful
|
|
*/
|
|
virtual bool readMode(keyboard::Mode& mode);
|
|
/*!
|
|
@brief Write the mode
|
|
@param mode Mode
|
|
@return True if successful
|
|
*/
|
|
virtual bool writeMode(const keyboard::Mode mode);
|
|
///@}
|
|
|
|
protected:
|
|
bool start_periodic_measurement();
|
|
bool start_periodic_measurement(const uint32_t interval);
|
|
bool stop_periodic_measurement();
|
|
|
|
bool update_new_firmware(const types::elapsed_time_t at);
|
|
void push_back(m5::container::CircularBuffer<uint8_t>* container, const uint8_t kidx, const uint8_t alt);
|
|
|
|
inline keyboard::key_status_bits_t modifier_bits() const
|
|
{
|
|
return keyboard::modifier_bits(_now);
|
|
}
|
|
|
|
inline virtual uint8_t to_mode_bits(const char) const
|
|
{
|
|
return 0x00;
|
|
}
|
|
|
|
inline virtual uint8_t scan_reg_addr() const
|
|
{
|
|
return 0x10;
|
|
}
|
|
inline virtual uint8_t mode_reg_addr() const
|
|
{
|
|
return 0x20;
|
|
}
|
|
inline virtual uint8_t firmware_version_reg_addr() const
|
|
{
|
|
return 0xFE;
|
|
}
|
|
|
|
bool permitted_mode(const uint8_t mbits) const
|
|
{
|
|
uint8_t mod8 = _now >> 56;
|
|
// mod8 bit0=Shift→mbits bit1, bit1=Sym→bit2, bit2=Fn→bit3
|
|
return mbits & (mod8 ? (mod8 << 1) : 0x01);
|
|
}
|
|
|
|
M5_UNIT_COMPONENT_PERIODIC_MEASUREMENT_ADAPTER_HPP_BUILDER(UnitKeyboardBitwise, uint8_t);
|
|
|
|
protected:
|
|
std::unique_ptr<m5::container::CircularBuffer<uint8_t>> _data{}; // was Pressed keys
|
|
keyboard::key_status_bits_t _now{}, _prev{}, _wasPressed{}, _wasReleased{}, _wasHold{}, _holding{},
|
|
_repeating{}; // key bits
|
|
std::vector<types::elapsed_time_t> _repeat_start_at{}, _hold_start_at{};
|
|
uint32_t _repeating_threshold{400}, _holding_threshold{800};
|
|
uint8_t _firmware_version{};
|
|
keyboard::Mode _mode{keyboard::Mode::Conventional};
|
|
};
|
|
|
|
namespace keyboard {
|
|
namespace command {
|
|
constexpr uint8_t CMD_SCAN_REG{0x10};
|
|
constexpr uint8_t CMD_MODE_REG{0x20};
|
|
constexpr uint8_t CMD_FIRMWARE_VERSION_REG{0xFE};
|
|
} // namespace command
|
|
|
|
} // namespace keyboard
|
|
|
|
} // namespace unit
|
|
} // namespace m5
|
|
#endif
|