From 50e9b7b8e071fa13c1ff2031bf7723baaf6c9886 Mon Sep 17 00:00:00 2001 From: probonopd Date: Fri, 15 Jul 2022 19:30:51 +0200 Subject: [PATCH] Configurable GPIO buttons (#274) * Default behavior changed: Click to enter menus, long-press to leave menus, double-click to go to the home screen (configurable) * Buttons can have click, double click and long press events * Changed default config, added new options and renamed EncoderPinSwitch to ButtonPinShortcut Co-authored-by: Kevin Co-authored-by: Stephen Brown --- .gitignore | 4 + src/Makefile | 2 +- src/config.cpp | 87 +++++++++- src/config.h | 38 +++- src/mididevice.cpp | 2 +- src/minidexed.ini | 20 ++- src/uibuttons.cpp | 392 ++++++++++++++++++++++++++++++++++++++++++ src/uibuttons.h | 154 +++++++++++++++++ src/userinterface.cpp | 101 +++++++++-- src/userinterface.h | 5 + 10 files changed, 777 insertions(+), 28 deletions(-) create mode 100644 src/uibuttons.cpp create mode 100644 src/uibuttons.h diff --git a/.gitignore b/.gitignore index 62c5120..6303c46 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,7 @@ MiniDexed* sdcard *.zip *.img + +# Editor related files +*.swp +*.swo diff --git a/src/Makefile b/src/Makefile index 3ea9c78..d379b88 100644 --- a/src/Makefile +++ b/src/Makefile @@ -9,7 +9,7 @@ CMSIS_DIR = ../CMSIS_5/CMSIS OBJS = main.o kernel.o minidexed.o config.o userinterface.o uimenu.o \ mididevice.o midikeyboard.o serialmididevice.o pckeyboard.o \ sysexfileloader.o performanceconfig.o perftimer.o \ - effect_compressor.o effect_platervbstereo.o + effect_compressor.o effect_platervbstereo.o uibuttons.o OPTIMIZE = -O3 diff --git a/src/config.cpp b/src/config.cpp index 3433b73..e808459 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -81,10 +81,25 @@ void CConfig::Load (void) m_nLCDPinData7 = m_Properties.GetNumber ("LCDPinData7", 25); m_nLCDI2CAddress = m_Properties.GetNumber ("LCDI2CAddress", 0); + m_nButtonPinPrev = m_Properties.GetNumber ("ButtonPinPrev", 0); + m_nButtonPinNext = m_Properties.GetNumber ("ButtonPinNext", 0); + m_nButtonPinBack = m_Properties.GetNumber ("ButtonPinBack", 11); + m_nButtonPinSelect = m_Properties.GetNumber ("ButtonPinSelect", 11); + m_nButtonPinHome = m_Properties.GetNumber ("ButtonPinHome", 11); + m_nButtonPinShortcut = m_Properties.GetNumber ("ButtonPinShortcut", 11); + + m_ButtonActionPrev = m_Properties.GetString ("ButtonActionPrev", ""); + m_ButtonActionNext = m_Properties.GetString ("ButtonActionNext", ""); + m_ButtonActionBack = m_Properties.GetString ("ButtonActionBack", "doubleclick"); + m_ButtonActionSelect = m_Properties.GetString ("ButtonActionSelect", "click"); + m_ButtonActionHome = m_Properties.GetString ("ButtonActionHome", "longpress"); + + m_nDoubleClickTimeout = m_Properties.GetNumber ("DoubleClickTimeout", 400); + m_nLongPressTimeout = m_Properties.GetNumber ("LongPressTimeout", 600); + m_bEncoderEnabled = m_Properties.GetNumber ("EncoderEnabled", 0) != 0; m_nEncoderPinClock = m_Properties.GetNumber ("EncoderPinClock", 10); m_nEncoderPinData = m_Properties.GetNumber ("EncoderPinData", 9); - m_nEncoderPinSwitch = m_Properties.GetNumber ("EncoderPinSwitch", 11); m_bMIDIDumpEnabled = m_Properties.GetNumber ("MIDIDumpEnabled", 0) != 0; m_bProfileEnabled = m_Properties.GetNumber ("ProfileEnabled", 0) != 0; @@ -180,6 +195,71 @@ unsigned CConfig::GetLCDI2CAddress (void) const return m_nLCDI2CAddress; } +unsigned CConfig::GetButtonPinPrev (void) const +{ + return m_nButtonPinPrev; +} + +unsigned CConfig::GetButtonPinNext (void) const +{ + return m_nButtonPinNext; +} + +unsigned CConfig::GetButtonPinBack (void) const +{ + return m_nButtonPinBack; +} + +unsigned CConfig::GetButtonPinSelect (void) const +{ + return m_nButtonPinSelect; +} + +unsigned CConfig::GetButtonPinHome (void) const +{ + return m_nButtonPinHome; +} + +unsigned CConfig::GetButtonPinShortcut (void) const +{ + return m_nButtonPinShortcut; +} + +const char *CConfig::GetButtonActionPrev (void) const +{ + return m_ButtonActionPrev.c_str(); +} + +const char *CConfig::GetButtonActionNext (void) const +{ + return m_ButtonActionNext.c_str(); +} + +const char *CConfig::GetButtonActionBack (void) const +{ + return m_ButtonActionBack.c_str(); +} + +const char *CConfig::GetButtonActionSelect (void) const +{ + return m_ButtonActionSelect.c_str(); +} + +const char *CConfig::GetButtonActionHome (void) const +{ + return m_ButtonActionHome.c_str(); +} + +unsigned CConfig::GetDoubleClickTimeout (void) const +{ + return m_nDoubleClickTimeout; +} + +unsigned CConfig::GetLongPressTimeout (void) const +{ + return m_nLongPressTimeout; +} + bool CConfig::GetEncoderEnabled (void) const { return m_bEncoderEnabled; @@ -195,11 +275,6 @@ unsigned CConfig::GetEncoderPinData (void) const return m_nEncoderPinData; } -unsigned CConfig::GetEncoderPinSwitch (void) const -{ - return m_nEncoderPinSwitch; -} - bool CConfig::GetMIDIDumpEnabled (void) const { return m_bMIDIDumpEnabled; diff --git a/src/config.h b/src/config.h index cf51755..53c8e2e 100644 --- a/src/config.h +++ b/src/config.h @@ -87,13 +87,32 @@ public: unsigned GetLCDPinData6 (void) const; unsigned GetLCDPinData7 (void) const; unsigned GetLCDI2CAddress (void) const; + + // GPIO Button Navigation + // GPIO pin numbers are chip numbers, not header positions + unsigned GetButtonPinPrev (void) const; + unsigned GetButtonPinNext (void) const; + unsigned GetButtonPinBack (void) const; + unsigned GetButtonPinSelect (void) const; + unsigned GetButtonPinHome (void) const; + unsigned GetButtonPinShortcut (void) const; + + // Action type for buttons: "click", "doubleclick", "longpress", "" + const char *GetButtonActionPrev (void) const; + const char *GetButtonActionNext (void) const; + const char *GetButtonActionBack (void) const; + const char *GetButtonActionSelect (void) const; + const char *GetButtonActionHome (void) const; + + // Timeouts for button events in milliseconds + unsigned GetDoubleClickTimeout (void) const; + unsigned GetLongPressTimeout (void) const; // KY-040 Rotary Encoder // GPIO pin numbers are chip numbers, not header positions bool GetEncoderEnabled (void) const; unsigned GetEncoderPinClock (void) const; unsigned GetEncoderPinData (void) const; - unsigned GetEncoderPinSwitch (void) const; // Debug bool GetMIDIDumpEnabled (void) const; @@ -122,11 +141,26 @@ private: unsigned m_nLCDPinData6; unsigned m_nLCDPinData7; unsigned m_nLCDI2CAddress; + + unsigned m_nButtonPinPrev; + unsigned m_nButtonPinNext; + unsigned m_nButtonPinBack; + unsigned m_nButtonPinSelect; + unsigned m_nButtonPinHome; + unsigned m_nButtonPinShortcut; + + std::string m_ButtonActionPrev; + std::string m_ButtonActionNext; + std::string m_ButtonActionBack; + std::string m_ButtonActionSelect; + std::string m_ButtonActionHome; + + unsigned m_nDoubleClickTimeout; + unsigned m_nLongPressTimeout; bool m_bEncoderEnabled; unsigned m_nEncoderPinClock; unsigned m_nEncoderPinData; - unsigned m_nEncoderPinSwitch; bool m_bMIDIDumpEnabled; bool m_bProfileEnabled; diff --git a/src/mididevice.cpp b/src/mididevice.cpp index 4e15cf1..ec89fe8 100644 --- a/src/mididevice.cpp +++ b/src/mididevice.cpp @@ -470,6 +470,6 @@ void CMIDIDevice::SendSystemExclusiveVoice(uint8_t nVoice, const unsigned nCable for(Iterator = s_DeviceMap.begin(); Iterator != s_DeviceMap.end(); ++Iterator) { Iterator->second->Send (voicedump, sizeof(voicedump)*sizeof(uint8_t)); - LOGDBG("Send SYSEX voice dump %u to \"%s\"",nVoice,Iterator->first.c_str()); + // LOGDBG("Send SYSEX voice dump %u to \"%s\"",nVoice,Iterator->first.c_str()); } } diff --git a/src/minidexed.ini b/src/minidexed.ini index c3724a8..6eeb07d 100644 --- a/src/minidexed.ini +++ b/src/minidexed.ini @@ -27,11 +27,29 @@ LCDPinData6=24 LCDPinData7=25 LCDI2CAddress=0x00 +# GPIO Button Navigation +# Any buttons set to 0 will be ignored +ButtonPinPrev=0 +ButtonActionPrev= +ButtonPinNext=0 +ButtonActionNext= +ButtonPinBack=11 +ButtonActionBack=longpress +ButtonPinSelect=11 +ButtonActionSelect=click +ButtonPinHome=11 +ButtonActionHome=doubleclick +ButtonPinShortcut=11 +# (Shortcut doesn't have an action) + +# Timeouts in milliseconds for double click and long press +DoubleClickTimeout=400 +LongPressTimeout=400 + # KY-040 Rotary Encoder EncoderEnabled=1 EncoderPinClock=10 EncoderPinData=9 -EncoderPinSwitch=11 # Debug MIDIDumpEnabled=0 diff --git a/src/uibuttons.cpp b/src/uibuttons.cpp new file mode 100644 index 0000000..7635b18 --- /dev/null +++ b/src/uibuttons.cpp @@ -0,0 +1,392 @@ +// +// uibuttons.cpp +// +// MiniDexed - Dexed FM synthesizer for bare metal Raspberry Pi +// Copyright (C) 2022 The MiniDexed Team +// +// Original author of this class: +// R. Stange +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +#include "uibuttons.h" +#include +#include +#include +#include + +LOGMODULE ("uibuttons"); + +CUIButton::CUIButton (void) +: m_pinNumber (0), + m_pin (0), + m_lastValue (1), + m_timer (0), + m_debounceTimer (0), + m_numClicks (0), + m_clickEvent(BtnEventNone), + m_doubleClickEvent(BtnEventNone), + m_longPressEvent(BtnEventNone), + m_doubleClickTimeout(0), + m_longPressTimeout(0) +{ +} + +CUIButton::~CUIButton (void) +{ + if (m_pin) + { + delete m_pin; + } +} + +void CUIButton::reset (void) +{ + m_timer = m_longPressTimeout; + m_numClicks = 0; +} + +boolean CUIButton::Initialize (unsigned pinNumber, unsigned doubleClickTimeout, unsigned longPressTimeout) +{ + assert (!m_pin); + assert(longPressTimeout >= doubleClickTimeout); + + m_pinNumber = pinNumber; + m_doubleClickTimeout = doubleClickTimeout; + m_longPressTimeout = longPressTimeout; + + // Initialise timing values + m_timer = m_longPressTimeout; + m_debounceTimer = DEBOUNCE_TIME; + + if (m_pinNumber != 0) + { + m_pin = new CGPIOPin (m_pinNumber, GPIOModeInputPullUp); + } + return TRUE; +} + +void CUIButton::setClickEvent(BtnEvent clickEvent) +{ + m_clickEvent = clickEvent; +} + +void CUIButton::setDoubleClickEvent(BtnEvent doubleClickEvent) +{ + m_doubleClickEvent = doubleClickEvent; +} + +void CUIButton::setLongPressEvent(BtnEvent longPressEvent) +{ + m_longPressEvent = longPressEvent; +} + +unsigned CUIButton::getPinNumber(void) +{ + return m_pinNumber; +} + +CUIButton::BtnTrigger CUIButton::ReadTrigger (void) +{ + if (!m_pin) + { + // Always return "not pressed" if not configured + return BtnTriggerNone; + } + + unsigned value = m_pin->Read(); + + if (m_timer < m_longPressTimeout) { + m_timer++; + + if (m_timer == m_doubleClickTimeout && m_lastValue == 1 && m_numClicks == 1) { + // The user has clicked and released the button once within the + // timeout - this must be a single click + reset(); + return BtnTriggerClick; + } + if (m_timer == m_longPressTimeout) { + if (m_lastValue == 0 && m_numClicks == 1) { + // Single long press + reset(); + return BtnTriggerLongPress; + } + else { + // Just reset it - we've run out of possible interactions + reset(); + } + } + } + + // Debounce here - we don't need to do anything if the debounce timer is active + if (m_debounceTimer < DEBOUNCE_TIME) { + m_debounceTimer++; + return BtnTriggerNone; + } + + // Buttons in PULL UP mode are "active low" + if (value == 0) + { + if (m_lastValue == 0) { + // 0 -> 0 : Button is pressed, was already pressed + } + else { + // 1 -> 0 : Button was not pressed but is now pressed + m_lastValue = 0; + m_debounceTimer = 0; + + if (m_numClicks == 0) { + // No clicks recorded - start a new timer + m_timer = 0; + } + if (m_numClicks < 2) { + m_numClicks++; + } + } + } + else + { + if (m_lastValue == 1) { + // 1 -> 1 : Button is not pressed, was already not pressed + } + else { + // 0 -> 1 : Button was pressed but is now not pressed (it was released) + m_lastValue = 1; + m_debounceTimer = 0; + + if (m_numClicks == 1 && + (m_doubleClickEvent == BtnEventNone || + m_timer >= m_doubleClickTimeout && m_timer < m_longPressTimeout) + ) { + // Either the user released the button when there is no double + // click mapped + // OR: + // The user released the button after the double click + // timeout, but before the long press timeout + reset(); + return BtnTriggerClick; + } + else if (m_numClicks == 2) { + // This is the second release in a short period of time + reset(); + return BtnTriggerDoubleClick; + } + } + } + + return BtnTriggerNone; +} + +CUIButton::BtnEvent CUIButton::Read (void) { + BtnTrigger trigger = ReadTrigger(); + + if (trigger == BtnTriggerClick) { + return m_clickEvent; + } + else if (trigger == BtnTriggerDoubleClick) { + return m_doubleClickEvent; + } + else if (trigger == BtnTriggerLongPress) { + return m_longPressEvent; + } + + assert (trigger == BtnTriggerNone); + + return BtnEventNone; +} + +CUIButton::BtnTrigger CUIButton::triggerTypeFromString(const char* triggerString) +{ + if (strcmp(triggerString, "") == 0 || strcmp(triggerString, "none") == 0) { + return BtnTriggerNone; + } + else if (strcmp(triggerString, "click") == 0) { + return BtnTriggerClick; + } + else if (strcmp(triggerString, "doubleclick") == 0) { + return BtnTriggerDoubleClick; + } + else if (strcmp(triggerString, "longpress") == 0) { + return BtnTriggerLongPress; + } + + LOGERR("Invalid action: %s", triggerString); + + return BtnTriggerNone; +} + + +CUIButtons::CUIButtons ( + unsigned prevPin, const char *prevAction, + unsigned nextPin, const char *nextAction, + unsigned backPin, const char *backAction, + unsigned selectPin, const char *selectAction, + unsigned homePin, const char *homeAction, + unsigned doubleClickTimeout, unsigned longPressTimeout +) +: m_doubleClickTimeout(doubleClickTimeout), + m_longPressTimeout(longPressTimeout), + m_prevPin(prevPin), + m_prevAction(CUIButton::triggerTypeFromString(prevAction)), + m_nextPin(nextPin), + m_nextAction(CUIButton::triggerTypeFromString(nextAction)), + m_backPin(backPin), + m_backAction(CUIButton::triggerTypeFromString(backAction)), + m_selectPin(selectPin), + m_selectAction(CUIButton::triggerTypeFromString(selectAction)), + m_homePin(homePin), + m_homeAction(CUIButton::triggerTypeFromString(homeAction)), + m_eventHandler (0), + m_lastTick (0) +{ +} + +CUIButtons::~CUIButtons (void) +{ +} + +boolean CUIButtons::Initialize (void) +{ + // First sanity check and convert the timeouts: + // Internally values are in tenths of a millisecond, but config values + // are in milliseconds + unsigned doubleClickTimeout = m_doubleClickTimeout * 10; + unsigned longPressTimeout = m_longPressTimeout * 10; + + if (longPressTimeout < doubleClickTimeout) { + // This is invalid - long press must be longest timeout + LOGERR("LongPressTimeout (%u) should not be shorter than DoubleClickTimeout (%u)", + m_longPressTimeout, + m_doubleClickTimeout); + + // Just make long press as long as double click + longPressTimeout = doubleClickTimeout; + } + + // Each button can be assigned up to 3 actions: click, doubleclick and + // longpress. We may not initialise all of the buttons + unsigned pins[MAX_BUTTONS] = { + m_prevPin, m_nextPin, m_backPin, m_selectPin, m_homePin + }; + CUIButton::BtnTrigger triggers[MAX_BUTTONS] = { + m_prevAction, m_nextAction, m_backAction, m_selectAction, m_homeAction + }; + CUIButton::BtnEvent events[MAX_BUTTONS] = { + CUIButton::BtnEventPrev, + CUIButton::BtnEventNext, + CUIButton::BtnEventBack, + CUIButton::BtnEventSelect, + CUIButton::BtnEventHome + }; + + for (unsigned i=0; i +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +#ifndef _uibuttons_h +#define _uibuttons_h + +#include +#include +#include "config.h" + +#define BUTTONS_UPDATE_NUM_TICKS 100 +#define DEBOUNCE_TIME 100 +#define MAX_BUTTONS 5 + +class CUIButtons; + +class CUIButton +{ +public: + enum BtnTrigger + { + BtnTriggerNone = 0, + BtnTriggerClick = 1, + BtnTriggerDoubleClick = 2, + BtnTriggerLongPress = 3 + }; + + enum BtnEvent + { + BtnEventNone = 0, + BtnEventPrev = 1, + BtnEventNext = 2, + BtnEventBack = 3, + BtnEventSelect = 4, + BtnEventHome = 5, + BtnEventUnknown = 6 + }; + + CUIButton (void); + ~CUIButton (void); + + void reset (void); + boolean Initialize (unsigned pinNumber, unsigned doubleClickTimeout, unsigned longPressTimeout); + + void setClickEvent(BtnEvent clickEvent); + void setDoubleClickEvent(BtnEvent doubleClickEvent); + void setLongPressEvent(BtnEvent longPressEvent); + + unsigned getPinNumber(void); + + BtnTrigger ReadTrigger (void); + BtnEvent Read (void); + + static BtnTrigger triggerTypeFromString(const char* triggerString); + +private: + // Pin number + unsigned m_pinNumber; + // GPIO pin + CGPIOPin *m_pin; + // The value of the pin at the end of the last loop + unsigned m_lastValue; + // Set to 0 on press, increment each read, use to trigger events + uint16_t m_timer; + // Debounce timer + uint16_t m_debounceTimer; + // Number of clicks recorded since last timer reset + uint8_t m_numClicks; + // Event to fire on click + BtnEvent m_clickEvent; + // Event to fire on double click + BtnEvent m_doubleClickEvent; + // Event to fire on long press + BtnEvent m_longPressEvent; + + // Timeout for double click in tenths of a millisecond + unsigned m_doubleClickTimeout; + // Timeout for long press in tenths of a millisecond + unsigned m_longPressTimeout; +}; + +class CUIButtons +{ +public: + typedef void BtnEventHandler (CUIButton::BtnEvent Event, void *param); + +public: + CUIButtons ( + unsigned prevPin, const char *prevAction, + unsigned nextPin, const char *nextAction, + unsigned backPin, const char *backAction, + unsigned selectPin, const char *selectAction, + unsigned homePin, const char *homeAction, + unsigned doubleClickTimeout, unsigned longPressTimeout + ); + ~CUIButtons (void); + + boolean Initialize (void); + + void RegisterEventHandler (BtnEventHandler *handler, void *param = 0); + + void Update (void); + + void ResetButton (unsigned pinNumber); + +private: + // Array of 5 buttons + CUIButton m_buttons[MAX_BUTTONS]; + + // Timeout for double click in tenths of a millisecond + unsigned m_doubleClickTimeout; + // Timeout for long press in tenths of a millisecond + unsigned m_longPressTimeout; + + // Configuration for buttons + unsigned m_prevPin; + CUIButton::BtnTrigger m_prevAction; + unsigned m_nextPin; + CUIButton::BtnTrigger m_nextAction; + unsigned m_backPin; + CUIButton::BtnTrigger m_backAction; + unsigned m_selectPin; + CUIButton::BtnTrigger m_selectAction; + unsigned m_homePin; + CUIButton::BtnTrigger m_homeAction; + + BtnEventHandler *m_eventHandler; + void *m_eventParam; + + unsigned m_lastTick; + + void bindButton(unsigned pinNumber, CUIButton::BtnTrigger trigger, CUIButton::BtnEvent event); +}; + +#endif diff --git a/src/userinterface.cpp b/src/userinterface.cpp index c7c746f..799b386 100644 --- a/src/userinterface.cpp +++ b/src/userinterface.cpp @@ -34,6 +34,7 @@ CUserInterface::CUserInterface (CMiniDexed *pMiniDexed, CGPIOManager *pGPIOManag m_pConfig (pConfig), m_pLCD (0), m_pLCDBuffered (0), + m_pUIButtons (0), m_pRotaryEncoder (0), m_bSwitchPressed (false), m_Menu (this, pMiniDexed) @@ -43,6 +44,7 @@ CUserInterface::CUserInterface (CMiniDexed *pMiniDexed, CGPIOManager *pGPIOManag CUserInterface::~CUserInterface (void) { delete m_pRotaryEncoder; + delete m_pUIButtons; delete m_pLCDBuffered; delete m_pLCD; } @@ -85,11 +87,34 @@ bool CUserInterface::Initialize (void) LOGDBG ("LCD initialized"); } + m_pUIButtons = new CUIButtons ( m_pConfig->GetButtonPinPrev (), + m_pConfig->GetButtonActionPrev (), + m_pConfig->GetButtonPinNext (), + m_pConfig->GetButtonActionNext (), + m_pConfig->GetButtonPinBack (), + m_pConfig->GetButtonActionBack (), + m_pConfig->GetButtonPinSelect (), + m_pConfig->GetButtonActionSelect (), + m_pConfig->GetButtonPinHome (), + m_pConfig->GetButtonActionHome (), + m_pConfig->GetDoubleClickTimeout (), + m_pConfig->GetLongPressTimeout () ); + assert (m_pUIButtons); + + if (!m_pUIButtons->Initialize ()) + { + return false; + } + + m_pUIButtons->RegisterEventHandler (UIButtonsEventStub, this); + + LOGDBG ("Button User Interface initialized"); + if (m_pConfig->GetEncoderEnabled ()) { m_pRotaryEncoder = new CKY040 (m_pConfig->GetEncoderPinClock (), m_pConfig->GetEncoderPinData (), - m_pConfig->GetEncoderPinSwitch (), + m_pConfig->GetButtonPinShortcut (), m_pGPIOManager); assert (m_pRotaryEncoder); @@ -114,6 +139,10 @@ void CUserInterface::Process (void) { m_pLCDBuffered->Update (); } + if (m_pUIButtons) + { + m_pUIButtons->Update(); + } } void CUserInterface::ParameterChanged (void) @@ -197,25 +226,26 @@ void CUserInterface::EncoderEventHandler (CKY040::TEvent Event) break; case CKY040::EventClockwise: - m_Menu.EventHandler (m_bSwitchPressed ? CUIMenu::MenuEventPressAndStepUp - : CUIMenu::MenuEventStepUp); - break; - - case CKY040::EventCounterclockwise: - m_Menu.EventHandler (m_bSwitchPressed ? CUIMenu::MenuEventPressAndStepDown - : CUIMenu::MenuEventStepDown); - break; - - case CKY040::EventSwitchClick: - m_Menu.EventHandler (CUIMenu::MenuEventBack); - break; + if (m_bSwitchPressed) { + // We must reset the encoder switch button to prevent events from being + // triggered after the encoder is rotated + m_pUIButtons->ResetButton(m_pConfig->GetButtonPinShortcut()); + m_Menu.EventHandler(CUIMenu::MenuEventPressAndStepUp); - case CKY040::EventSwitchDoubleClick: - m_Menu.EventHandler (CUIMenu::MenuEventSelect); + } + else { + m_Menu.EventHandler(CUIMenu::MenuEventStepUp); + } break; - case CKY040::EventSwitchTripleClick: - m_Menu.EventHandler (CUIMenu::MenuEventHome); + case CKY040::EventCounterclockwise: + if (m_bSwitchPressed) { + m_pUIButtons->ResetButton(m_pConfig->GetButtonPinShortcut()); + m_Menu.EventHandler(CUIMenu::MenuEventPressAndStepDown); + } + else { + m_Menu.EventHandler(CUIMenu::MenuEventStepDown); + } break; case CKY040::EventSwitchHold: @@ -239,3 +269,40 @@ void CUserInterface::EncoderEventStub (CKY040::TEvent Event, void *pParam) pThis->EncoderEventHandler (Event); } + +void CUserInterface::UIButtonsEventHandler (CUIButton::BtnEvent Event) +{ + switch (Event) + { + case CUIButton::BtnEventPrev: + m_Menu.EventHandler (CUIMenu::MenuEventStepDown); + break; + + case CUIButton::BtnEventNext: + m_Menu.EventHandler (CUIMenu::MenuEventStepUp); + break; + + case CUIButton::BtnEventBack: + m_Menu.EventHandler (CUIMenu::MenuEventBack); + break; + + case CUIButton::BtnEventSelect: + m_Menu.EventHandler (CUIMenu::MenuEventSelect); + break; + + case CUIButton::BtnEventHome: + m_Menu.EventHandler (CUIMenu::MenuEventHome); + break; + + default: + break; + } +} + +void CUserInterface::UIButtonsEventStub (CUIButton::BtnEvent Event, void *pParam) +{ + CUserInterface *pThis = static_cast (pParam); + assert (pThis != 0); + + pThis->UIButtonsEventHandler (Event); +} diff --git a/src/userinterface.h b/src/userinterface.h index 726abbd..202872a 100644 --- a/src/userinterface.h +++ b/src/userinterface.h @@ -22,6 +22,7 @@ #include "config.h" #include "uimenu.h" +#include "uibuttons.h" #include #include #include @@ -55,6 +56,8 @@ private: void EncoderEventHandler (CKY040::TEvent Event); static void EncoderEventStub (CKY040::TEvent Event, void *pParam); + void UIButtonsEventHandler (CUIButton::BtnEvent Event); + static void UIButtonsEventStub (CUIButton::BtnEvent Event, void *pParam); private: CMiniDexed *m_pMiniDexed; @@ -64,6 +67,8 @@ private: CHD44780Device *m_pLCD; CWriteBufferDevice *m_pLCDBuffered; + + CUIButtons *m_pUIButtons; CKY040 *m_pRotaryEncoder; bool m_bSwitchPressed;