Added support for KY-040 rotary encoder to replace browse up/down/select buttons; all code
changes were annotated with //ROTARY: for easy review;
This commit is contained in:
parent
c71fe6e1dc
commit
b409732ddd
9 changed files with 687 additions and 4 deletions
2
Makefile
2
Makefile
|
@ -1,6 +1,6 @@
|
|||
OBJS = armc-start.o armc-cstartup.o armc-cstubs.o armc-cppstubs.o \
|
||||
exception.o main.o rpi-aux.o rpi-i2c.o rpi-mailbox-interface.o rpi-mailbox.o \
|
||||
rpi-gpio.o rpi-interrupts.o cache.o ff.o interrupt.o Keyboard.o performance.o \
|
||||
rpi-gpio.o rpi-interrupts.o dmRotary.o cache.o ff.o interrupt.o Keyboard.o performance.o \
|
||||
Drive.o Pi1541.o DiskImage.o iec_bus.o iec_commands.o m6502.o m6522.o \
|
||||
gcr.o prot.o lz.o emmc.o diskio.o options.o Screen.o SSD1306.o ScreenLCD.o \
|
||||
Timer.o FileBrowser.o DiskCaddy.o ROMs.o InputMappings.o xga_font_data.o m8520.o wd177x.o Pi1581.o SpinLock.o
|
||||
|
|
21
options.txt
21
options.txt
|
@ -107,3 +107,24 @@ GraphIEC = 1
|
|||
//buttonDown = 3
|
||||
//buttonBack = 4
|
||||
//buttonInsert = 5
|
||||
|
||||
//ROTARY:
|
||||
//
|
||||
// KY-040 Rotary Encoder Support
|
||||
//
|
||||
// If you would like to use a KY-040 Rotary Encoder for browse menu up/down
|
||||
// and select, you can enable it here. Connect as follows:
|
||||
//
|
||||
// GPIO 22 - Menu up - Encoder pin A (CLK)
|
||||
// GPIO 23 - Menu down - Encoder pin B (DT)
|
||||
// GPIO 27 - Enter/Select - Encoder pushbutton (SW)
|
||||
//
|
||||
// ** Using an encoder is incompatible with the button remapping. You must
|
||||
// use the default values of Enter=1, Up=2, Down=3, Back=4 and Insert=5.
|
||||
//
|
||||
// ** This has only been tested using a Raspberry Pi 3. Earlier models may
|
||||
// or may not work as expected!
|
||||
//
|
||||
// Please see dmRotary.h for full implementation details.
|
||||
//
|
||||
//RotaryEncoderEnable = 1
|
||||
|
|
285
src/dmRotary.cpp
Normal file
285
src/dmRotary.cpp
Normal file
|
@ -0,0 +1,285 @@
|
|||
/*
|
||||
* dmRotary.cpp - A simple decoder to use a KY-040 rotary encoder for browse
|
||||
* naviation in Pi1541.
|
||||
*
|
||||
* Copyright © 2019 devMash.com
|
||||
*
|
||||
* https://devMash.com
|
||||
* https://github.com/devMashHub
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
* software and associated documentation files (the “Software”), to deal in the Software
|
||||
* without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
* to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "dmRotary.h"
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Initialize
|
||||
//
|
||||
void RotaryEncoder::Initialize(rpi_gpio_pin_t clockGpioPin, rpi_gpio_pin_t dataGpioPin, rpi_gpio_pin_t switchGpioPin)
|
||||
{
|
||||
|
||||
#ifdef DM_ROTARY_DEBUG
|
||||
|
||||
char message[1024] = "";
|
||||
|
||||
WriteToMiniUart("##########\r\n\r\n");
|
||||
|
||||
WriteToMiniUart("RotaryEncoder::Initialize()\r\n");
|
||||
|
||||
sprintf(message, " clockGpioPin = %d dataGpioPin = %d switchGpioPin = %d\r\n\r\n", clockGpioPin, dataGpioPin, switchGpioPin);
|
||||
WriteToMiniUart(message);
|
||||
|
||||
#endif
|
||||
|
||||
//Store specified pins for polling method
|
||||
_clockPin.SetGpioPin(clockGpioPin);
|
||||
_dataPin.SetGpioPin(dataGpioPin);
|
||||
_switchPin.SetGpioPin(switchGpioPin);
|
||||
|
||||
//Set pins for input
|
||||
RPI_SetGpioInput(clockGpioPin);
|
||||
RPI_SetGpioInput(dataGpioPin);
|
||||
RPI_SetGpioInput(switchGpioPin);
|
||||
|
||||
//Enable pull-ups
|
||||
unsigned controlSignal = 2;
|
||||
unsigned gpioPinMask = _clockPin.GetGpioPinMask() | _dataPin.GetGpioPinMask() | _switchPin.GetGpioPinMask();
|
||||
SetGpioPullUpDown(controlSignal, gpioPinMask);
|
||||
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Poll
|
||||
//
|
||||
rotary_result_t RotaryEncoder::Poll()
|
||||
{
|
||||
|
||||
//Read physical pin levels (GPLEV0 is pins 0 to 31)
|
||||
unsigned gplev0 = read32(ARM_GPIO_GPLEV0);
|
||||
|
||||
return Poll(gplev0);
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Poll
|
||||
//
|
||||
rotary_result_t RotaryEncoder::Poll(unsigned gplev0)
|
||||
{
|
||||
|
||||
rotary_result_t result = NoChange;
|
||||
|
||||
#ifdef DM_ROTARY_DEBUG
|
||||
|
||||
char message[1024] = "";
|
||||
|
||||
#endif
|
||||
|
||||
//Decode switch
|
||||
if (result == NoChange)
|
||||
{
|
||||
|
||||
//Debounce switch and determine state
|
||||
_switchPin.Update((gplev0 & _switchPin.GetGpioPinMask()) == 0);
|
||||
bool switchState = _switchPin.GetState();
|
||||
|
||||
//Detect switch state change
|
||||
if (switchState != _currentSwitchState)
|
||||
{
|
||||
|
||||
//Determine result
|
||||
result = switchState ? ButtonDown : ButtonUp;
|
||||
|
||||
//Update switch state
|
||||
_currentSwitchState = switchState;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Decode rotation
|
||||
if (result == NoChange)
|
||||
{
|
||||
|
||||
//Debounce clock and determine state
|
||||
_clockPin.Update((gplev0 & _clockPin.GetGpioPinMask()) == 0);
|
||||
bool clockState = _clockPin.GetState();
|
||||
|
||||
//Debounce data and determine state
|
||||
_dataPin.Update((gplev0 & _dataPin.GetGpioPinMask()) == 0);
|
||||
bool dataState = _dataPin.GetState();
|
||||
|
||||
//Detect rotary state change
|
||||
int rotaryState = (clockState << 1) | dataState;
|
||||
if (rotaryState != _currentRotaryState)
|
||||
{
|
||||
|
||||
//Update rotary sequence
|
||||
_currentRotarySequence = (_currentRotarySequence << 2) | rotaryState;
|
||||
|
||||
if (rotaryState == 0)
|
||||
{
|
||||
|
||||
switch (_currentRotarySequence)
|
||||
{
|
||||
|
||||
//Detect positive (clockwise) rotation
|
||||
//
|
||||
// 0xb4 - 00 10 11 01 00 - Received and decoded perfect sequence
|
||||
// 0x2c - 00 10 11 00 - Missed data but decoded unique sequence
|
||||
// 0x34 - 00 11 01 00 - Missed data but decoded unique sequence
|
||||
// 0xb8 - 00 10 11 10 00 - Invalid sequence, using best guess
|
||||
//
|
||||
case 0xb4:
|
||||
case 0x2c:
|
||||
case 0x34:
|
||||
case 0xb8:
|
||||
result = RotatePositive;
|
||||
break;
|
||||
|
||||
//Detect negative (counter-clockwise) rotation
|
||||
//
|
||||
// 0x78 - 00 01 11 10 00 - Received and decoded perfect sequence
|
||||
// 0x1c - 00 01 11 00 - Missed data but decoded unique sequence
|
||||
// 0x38 - 00 11 10 00 - Missed data but decoded unique sequence
|
||||
// 0x74 - 00 01 11 01 00 - Invalid sequence, using best guess
|
||||
//
|
||||
case 0x78:
|
||||
case 0x1c:
|
||||
case 0x38:
|
||||
case 0x74:
|
||||
result = RotateNegative;
|
||||
break;
|
||||
|
||||
#ifdef DM_ROTARY_DEBUG
|
||||
|
||||
//Unable to decode sequence
|
||||
//
|
||||
// 0x0c - 00 11 00 - No way to determine rotation direction
|
||||
//
|
||||
default:
|
||||
if (_currentRotarySequence != 0x0c)
|
||||
{
|
||||
sprintf(message, "decode failed: %x\r\n", _currentRotarySequence);
|
||||
WriteToMiniUart(message);
|
||||
}
|
||||
break;
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
//Clear rotary sequence
|
||||
_currentRotarySequence = 0;
|
||||
|
||||
}
|
||||
|
||||
//Update rotary state
|
||||
_currentRotaryState = rotaryState;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#ifdef DM_ROTARY_DEBUG
|
||||
|
||||
switch (result)
|
||||
{
|
||||
|
||||
case NoChange:
|
||||
break;
|
||||
|
||||
case ButtonDown:
|
||||
WriteToMiniUart("Button Down\r\n");
|
||||
break;
|
||||
|
||||
case ButtonUp:
|
||||
WriteToMiniUart("Button Up\r\n");
|
||||
break;
|
||||
|
||||
case RotatePositive:
|
||||
WriteToMiniUart("Clockwise\r\n");
|
||||
break;
|
||||
|
||||
case RotateNegative:
|
||||
WriteToMiniUart("Counter-Clockwise\r\n");
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// SetGpioPullUpDown
|
||||
//
|
||||
void RotaryEncoder::SetGpioPullUpDown(unsigned controlSignal, unsigned gpioPinMask)
|
||||
{
|
||||
|
||||
volatile int i;
|
||||
int delayCycles = 150;
|
||||
|
||||
// Write to GPPUD to set the required control signal
|
||||
//
|
||||
// 01 = Enable Pull-Down 00 = Off (disable pull-up/down)
|
||||
// 10 = Enable Pull-Up 11 = Reserved
|
||||
//
|
||||
write32(ARM_GPIO_GPPUD, controlSignal);
|
||||
|
||||
// Delay cycles (to provide the required set-up time for the control signal)
|
||||
for (i = 0; i < delayCycles; i++) { }
|
||||
|
||||
// Write to GPPUDCLK0/1 to clock the control signal into the GPIO pads
|
||||
//
|
||||
// Note: Only the pads which receive a clock will be modified, all others will
|
||||
// retain their previous state.
|
||||
//
|
||||
write32(ARM_GPIO_GPPUDCLK0, gpioPinMask);
|
||||
|
||||
// Delay cycles (to provide the required hold time for the control signal)
|
||||
for (i = 0; i < delayCycles; i++) { }
|
||||
|
||||
// Write to GPPUD to remove the control signal
|
||||
write32(ARM_GPIO_GPPUD, 0);
|
||||
|
||||
//Write to GPPUDCLK0/1 to remove the clock
|
||||
write32(ARM_GPIO_GPPUDCLK0, 0);
|
||||
|
||||
}
|
||||
|
||||
|
||||
#ifdef DM_ROTARY_DEBUG
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// WriteToMiniUart
|
||||
//
|
||||
void RotaryEncoder::WriteToMiniUart(char* pMessage)
|
||||
{
|
||||
while(*pMessage)
|
||||
{
|
||||
RPI_AuxMiniUartWrite(*pMessage++);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
250
src/dmRotary.h
Normal file
250
src/dmRotary.h
Normal file
|
@ -0,0 +1,250 @@
|
|||
/*
|
||||
* dmRrotary.h - A simple decoder to use a KY-040 rotary encoder for browse
|
||||
* naviation in Pi1541.
|
||||
*
|
||||
* Copyright © 2019 devMash.com
|
||||
*
|
||||
* https://devMash.com
|
||||
* https://github.com/devMashHub
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
* software and associated documentation files (the “Software”), to deal in the Software
|
||||
* without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
* to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DM_ROTARY_H
|
||||
#define DM_ROTARY_H
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "rpi-aux.h"
|
||||
#include "rpiHardware.h"
|
||||
}
|
||||
|
||||
//Enable debugging messages on the mini uart
|
||||
//#define DM_ROTARY_DEBUG
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// rotary_result_t
|
||||
//
|
||||
typedef enum {
|
||||
NoChange = 0,
|
||||
ButtonDown = 1,
|
||||
ButtonUp = 2,
|
||||
RotatePositive = 3, // clockwise rotation
|
||||
RotateNegative = 4 // counter-clockwise rotation
|
||||
} rotary_result_t;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// RotaryPin
|
||||
//
|
||||
// NOTES:
|
||||
//
|
||||
// This helper class represents a single rotary encoder pin to encapsulate
|
||||
// debouncing and state engine logic.
|
||||
//
|
||||
class RotaryPin
|
||||
{
|
||||
|
||||
private:
|
||||
|
||||
rpi_gpio_pin_t _gpioPin = RPI_GPIO0;
|
||||
|
||||
int _count = 0;
|
||||
int _threshold = 256; // I just like powers of two
|
||||
|
||||
bool _currentState = false;
|
||||
|
||||
public:
|
||||
|
||||
rpi_gpio_pin_t GetGpioPin() const { return _gpioPin; }
|
||||
void SetGpioPin(rpi_gpio_pin_t value) { _gpioPin = value; }
|
||||
|
||||
unsigned GetGpioPinMask() { return (1 << _gpioPin); }
|
||||
|
||||
bool GetState() const { return _currentState; }
|
||||
|
||||
void Update(bool state)
|
||||
{
|
||||
|
||||
_count += state ? 1 : -1;
|
||||
|
||||
bool newState = _currentState;
|
||||
|
||||
if (_count <= 0)
|
||||
{
|
||||
_count = 0;
|
||||
newState = false;
|
||||
}
|
||||
else if (_count >= _threshold)
|
||||
{
|
||||
_count = _threshold;
|
||||
newState = true;
|
||||
}
|
||||
|
||||
_currentState = newState;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// RotaryEncoder
|
||||
//
|
||||
// NOTES:
|
||||
//
|
||||
// The KY-040 rotary encoder has the following pins:
|
||||
//
|
||||
// CLK - Encoder pin A
|
||||
// DT - Encoder pin B
|
||||
// SW - Pushbutton switch
|
||||
// VCC - Supply voltage
|
||||
// GND - Ground
|
||||
//
|
||||
//
|
||||
// Decoding the KY-040
|
||||
//
|
||||
// Determining which encoder pin changes state first is how the direction
|
||||
// of rotation is determined. If CLK changes state first, the encoder
|
||||
// is rotating in a positive (clockwise) direction. If DT changes state
|
||||
// first, the encoder is rotating in a negative (counter-clockwise) direction.
|
||||
//
|
||||
// The entire rotation sequence can be represented as a predictable bit
|
||||
// pattern by mapping the CLK state in the higher bit and mapping the DT
|
||||
// state in the lower bit:
|
||||
//
|
||||
// Positive rotation - 00 -> 10 -> 11 -> 01 -> 00 (0xb4)
|
||||
// Negative rotation - 00 -> 01 -> 11 -> 10 -> 00 (0x78)
|
||||
//
|
||||
// To decode, we simply monitor the CLK and DT and watch for one of these
|
||||
// two patterns.
|
||||
//
|
||||
// However, in the real world, some pin transitions can be missed due to
|
||||
// lag during the polling interval or contact bounce - resulting in less
|
||||
// than perfect decode sequences. To combat against this, the code will
|
||||
// also accept several permutations that are 'close enough':
|
||||
//
|
||||
// Positive (clockwise) rotation:
|
||||
//
|
||||
// 0xb4 - 00 10 11 01 00 - Received and decoded perfect sequence
|
||||
// 0x2c - 00 10 11 00 - Missed data but decoded unique sequence
|
||||
// 0x34 - 00 11 01 00 - Missed data but decoded unique sequence
|
||||
// 0xb8 - 00 10 11 10 00 - Invalid but usually denotes positive
|
||||
//
|
||||
// Detect negative (counter-clockwise) rotation:
|
||||
//
|
||||
// 0x78 - 00 01 11 10 00 - Received and decoded perfect sequence
|
||||
// 0x1c - 00 01 11 00 - Missed data but decoded unique sequence
|
||||
// 0x38 - 00 11 10 00 - Missed data but decoded unique sequence
|
||||
// 0x74 - 00 01 11 01 00 - Invalid but usually denotes negative
|
||||
//
|
||||
//
|
||||
// Wiring the KY-040
|
||||
//
|
||||
// The GPIO pins used for the rotary encoder are specified when initializing
|
||||
// the class. However, it is probably a good idea to reuse the same pins as
|
||||
// the original Pi1541 pushbuttons:
|
||||
//
|
||||
// GPIO 22 - Menu up - Encoder pin A (CLK)
|
||||
// GPIO 23 - Menu down - Encoder pin B (DT)
|
||||
// GPIO 27 - Enter/Select - Encoder pushbutton (SW)
|
||||
//
|
||||
//
|
||||
// USAGE:
|
||||
//
|
||||
// *** Please Note! I have only tried this with a Raspberry Pi 3. I have no
|
||||
// reason to believe this wouldn't also work on an earlier model, but your
|
||||
// results may vary!
|
||||
//
|
||||
// To use the RotaryEncoder, instance the class and initialize with the desired
|
||||
// GPIO pins like this:
|
||||
//
|
||||
// //Initialize using CLK on GPIO22, DT on GPIO32 and SW on GPIO27
|
||||
// RotaryEncoder rotaryEncoder;
|
||||
// rotaryEncoder.Initialize(RPI_GPIO22, RPI_GPIO23, RPI_GPIO27);
|
||||
//
|
||||
// Monitor the encoder by calling the Poll() method during your main processing
|
||||
// loop. The polling logic is constrained by only evaluating GPLEV0, which
|
||||
// restricts usable pins to GPIO00 through GPIO31. An overloaded version of
|
||||
// Poll() exists to accept a value representing GPLEV0 without having to
|
||||
// perform a re-read (providing a small optimization when the current value of
|
||||
// GPLEV0 is already available).
|
||||
//
|
||||
// // Read GPLEV0 and decode the rotary state
|
||||
// rotary_result_t result = rotaryEncoder.Poll();
|
||||
//
|
||||
// or
|
||||
//
|
||||
// // Read GPLEV0 locally and decode the rotary state
|
||||
// unsigned gplev0 = read32(ARM_GPIO_GPLEV0);
|
||||
// { some other logic here }
|
||||
// rotary_result_t result = rotaryEncoder.Poll(gplev0);
|
||||
//
|
||||
// Note, the Poll() logic depends on frequent polling of the encoder. Calling
|
||||
// Poll() as often as possible/permissable will yield better decode results.
|
||||
//
|
||||
// Any event detected by the polling logic will be returned as the result
|
||||
// from the polling method as a rotary_result_t. The controlling logic can
|
||||
// then take whatever action is appropriate based on the result.
|
||||
//
|
||||
//
|
||||
// HISTORY:
|
||||
//
|
||||
// 09/03/2019 - Initial implementation and notes
|
||||
// 09/04/2019 - Integration into Pi1541 with shim logic to simulate 'original style' button presses
|
||||
// 09/05/2019 - Code cleanup, improved documentation, options.txt logic, dynamic button indexes
|
||||
//
|
||||
class RotaryEncoder {
|
||||
|
||||
private:
|
||||
|
||||
// Switch data
|
||||
|
||||
RotaryPin _switchPin;
|
||||
|
||||
bool _currentSwitchState = false;
|
||||
|
||||
// Rotation data
|
||||
|
||||
RotaryPin _clockPin;
|
||||
RotaryPin _dataPin;
|
||||
|
||||
int _currentRotaryState = 0;
|
||||
int _currentRotarySequence = 0;
|
||||
|
||||
// Private methods
|
||||
|
||||
void SetGpioPullUpDown(unsigned controlSignal, unsigned gpioPinMask);
|
||||
|
||||
#ifdef DM_ROTARY_DEBUG
|
||||
void WriteToMiniUart(char* pMessage);
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
// Public methods
|
||||
|
||||
void Initialize(rpi_gpio_pin_t clkGpioPin, rpi_gpio_pin_t dtGpioPin, rpi_gpio_pin_t swGpioPin);
|
||||
|
||||
rotary_result_t Poll();
|
||||
rotary_result_t Poll(unsigned gplev0);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -73,15 +73,68 @@ u32 IEC_Bus::emulationModeCheckButtonIndex = 0;
|
|||
|
||||
unsigned IEC_Bus::gplev0;
|
||||
|
||||
//ROTARY: Added for rotary encoder support - 09/05/2019 by Geo...
|
||||
RotaryEncoder IEC_Bus::rotaryEncoder;
|
||||
bool IEC_Bus::rotaryEncoderEnable;
|
||||
|
||||
//ROTARY: Modified for rotary encoder support - 09/05/2019 by Geo...
|
||||
void IEC_Bus::ReadBrowseMode(void)
|
||||
{
|
||||
gplev0 = read32(ARM_GPIO_GPLEV0);
|
||||
|
||||
int index;
|
||||
for (index = 0; index < buttonCount; ++index)
|
||||
if (IEC_Bus::rotaryEncoderEnable == true)
|
||||
{
|
||||
UpdateButton(index, gplev0);
|
||||
|
||||
int indexEnter = 0;
|
||||
int indexUp = 1;
|
||||
int indexDown = 2;
|
||||
int indexBack = 3;
|
||||
int indexInsert = 4;
|
||||
|
||||
//Poll the rotary encoder
|
||||
//
|
||||
// Note: If the rotary encoder returns any value other than 'NoChange' an
|
||||
// event has been detected. We force the button state of the original
|
||||
// input button registers to reflect the desired action, and allow the
|
||||
// original processing logic to do it's work.
|
||||
//
|
||||
rotary_result_t rotaryResult = IEC_Bus::rotaryEncoder.Poll(gplev0);
|
||||
switch (rotaryResult)
|
||||
{
|
||||
|
||||
case ButtonDown:
|
||||
SetButtonState(indexEnter, true);
|
||||
break;
|
||||
|
||||
case RotateNegative:
|
||||
SetButtonState(indexUp, true);
|
||||
break;
|
||||
|
||||
case RotatePositive:
|
||||
SetButtonState(indexDown, true);
|
||||
break;
|
||||
|
||||
default:
|
||||
SetButtonState(indexEnter, false);
|
||||
SetButtonState(indexUp, false);
|
||||
SetButtonState(indexDown, false);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
UpdateButton(indexBack, gplev0);
|
||||
UpdateButton(indexInsert, gplev0);
|
||||
|
||||
}
|
||||
else // Unmolested original logic
|
||||
{
|
||||
|
||||
int index;
|
||||
for (index = 0; index < buttonCount; ++index)
|
||||
{
|
||||
UpdateButton(index, gplev0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool ATNIn = (gplev0 & PIGPIO_MASK_IN_ATN) == (invertIECInputs ? PIGPIO_MASK_IN_ATN : 0);
|
||||
|
|
|
@ -26,6 +26,9 @@
|
|||
#include "rpi-gpio.h"
|
||||
#include "rpiHardware.h"
|
||||
|
||||
//ROTARY: Added for rotary encoder support - 09/05/2019 by Geo...
|
||||
#include "dmRotary.h"
|
||||
|
||||
#define INPUT_BUTTON_DEBOUNCE_THRESHOLD 20000
|
||||
#define INPUT_BUTTON_REPEAT_THRESHOLD 460000
|
||||
|
||||
|
@ -308,6 +311,13 @@ public:
|
|||
}
|
||||
RPI_GpioBase->GPPUD = 0;
|
||||
RPI_GpioBase->GPPUDCLK0 = 0;
|
||||
|
||||
//ROTARY: Added for rotary encoder support - 09/05/2019 by Geo...
|
||||
if (IEC_Bus::rotaryEncoderEnable == true)
|
||||
{
|
||||
IEC_Bus::rotaryEncoder.Initialize(RPI_GPIO22, RPI_GPIO23, RPI_GPIO27);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static inline void LetSRQBePulledHigh()
|
||||
|
@ -349,6 +359,47 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
//ROTARY: Added for rotary encoder support - 09/05/2019 by Geo...
|
||||
//
|
||||
// Note: This method serves as a shim to allow the rotary encoder
|
||||
// logic to set a specific input button state (fooling the
|
||||
// original logic into thinking a button was pressed or
|
||||
// released).
|
||||
//
|
||||
static inline void SetButtonState(int index, bool state)
|
||||
{
|
||||
|
||||
InputButtonPrev[index] = InputButton[index];
|
||||
inputRepeatPrev[index] = inputRepeat[index];
|
||||
|
||||
if (state == true)
|
||||
{
|
||||
|
||||
InputButton[index] = true;
|
||||
validInputCount[index] = INPUT_BUTTON_DEBOUNCE_THRESHOLD;
|
||||
inputRepeatThreshold[index] = INPUT_BUTTON_DEBOUNCE_THRESHOLD + INPUT_BUTTON_REPEAT_THRESHOLD;
|
||||
inputRepeat[index]++;
|
||||
|
||||
validInputCount[index] = inputRepeatThreshold[index];
|
||||
inputRepeat[index]++;
|
||||
inputRepeatThreshold[index] += INPUT_BUTTON_REPEAT_THRESHOLD / inputRepeat[index];
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
InputButton[index] = false;
|
||||
validInputCount[index] = 0;
|
||||
inputRepeatThreshold[index] = INPUT_BUTTON_REPEAT_THRESHOLD;
|
||||
inputRepeat[index] = 0;
|
||||
inputRepeatPrev[index] = 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void ReadBrowseMode(void);
|
||||
static void ReadEmulationMode1541(void);
|
||||
static void ReadButtonsEmulationMode(void);
|
||||
|
@ -544,6 +595,12 @@ public:
|
|||
ignoreReset = value;
|
||||
}
|
||||
|
||||
//ROTARY: Added for rotary encoder support - 09/05/2019 by Geo...
|
||||
static inline void SetRotaryEncoderEnable(bool value)
|
||||
{
|
||||
rotaryEncoderEnable = value;
|
||||
}
|
||||
|
||||
// CA1 input ATN
|
||||
// If CA1 is ever set to output
|
||||
// - CA1 will start to drive pb7
|
||||
|
@ -607,5 +664,10 @@ private:
|
|||
static u32 inputRepeatThreshold[5];
|
||||
static u32 inputRepeat[5];
|
||||
static u32 inputRepeatPrev[5];
|
||||
|
||||
//ROTARY: Added for rotary encoder support - 09/05/2019 by Geo...
|
||||
static RotaryEncoder rotaryEncoder;
|
||||
static bool rotaryEncoderEnable;
|
||||
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -1763,6 +1763,9 @@ extern "C"
|
|||
IEC_Bus::SetInvertIECOutputs(options.InvertIECOutputs());
|
||||
IEC_Bus::SetIgnoreReset(options.IgnoreReset());
|
||||
|
||||
//ROTARY: Added for rotary encoder support - 09/05/2019 by Geo...
|
||||
IEC_Bus::SetRotaryEncoderEnable(options.RotaryEncoderEnable());
|
||||
|
||||
if (!options.SoundOnGPIO())
|
||||
{
|
||||
dmaSound = (u32*)malloc(Sample_bin_size * 4);
|
||||
|
|
|
@ -156,6 +156,7 @@ Options::Options(void)
|
|||
, buttonDown(3)
|
||||
, buttonBack(4)
|
||||
, buttonInsert(5)
|
||||
, rotaryEncoderEnable(0) //ROTARY:
|
||||
|
||||
{
|
||||
autoMountImageName[0] = 0;
|
||||
|
@ -245,6 +246,7 @@ void Options::Process(char* buffer)
|
|||
ELSE_CHECK_DECIMAL_OPTION(buttonDown)
|
||||
ELSE_CHECK_DECIMAL_OPTION(buttonBack)
|
||||
ELSE_CHECK_DECIMAL_OPTION(buttonInsert)
|
||||
ELSE_CHECK_DECIMAL_OPTION(rotaryEncoderEnable) //ROTARY:
|
||||
else if ((strcasecmp(pOption, "AutoBaseName") == 0))
|
||||
{
|
||||
strncpy(autoBaseName, pValue, 255);
|
||||
|
|
|
@ -100,6 +100,9 @@ public:
|
|||
inline unsigned int GetButtonBack() const { return buttonBack - 1; }
|
||||
inline unsigned int GetButtonInsert() const { return buttonInsert - 1; }
|
||||
|
||||
//ROTARY: Added for rotary encoder support - 09/05/2019 by Geo...
|
||||
inline unsigned int RotaryEncoderEnable() const { return rotaryEncoderEnable; }
|
||||
|
||||
// Page up and down will jump a different amount based on the maximum number rows displayed.
|
||||
// Perhaps we should use some keyboard modifier to the the other screen?
|
||||
inline unsigned int KeyboardBrowseLCDScreen() const { return keyboardBrowseLCDScreen; }
|
||||
|
@ -175,5 +178,9 @@ private:
|
|||
char ROMName1581[256];
|
||||
|
||||
char newDiskType[32];
|
||||
|
||||
//ROTARY: Added for rotary encoder support - 09/05/2019 by Geo...
|
||||
unsigned int rotaryEncoderEnable;
|
||||
|
||||
};
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue