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 \
|
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 \
|
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 \
|
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 \
|
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
|
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
|
//buttonDown = 3
|
||||||
//buttonBack = 4
|
//buttonBack = 4
|
||||||
//buttonInsert = 5
|
//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;
|
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)
|
void IEC_Bus::ReadBrowseMode(void)
|
||||||
{
|
{
|
||||||
gplev0 = read32(ARM_GPIO_GPLEV0);
|
gplev0 = read32(ARM_GPIO_GPLEV0);
|
||||||
|
|
||||||
int index;
|
if (IEC_Bus::rotaryEncoderEnable == true)
|
||||||
for (index = 0; index < buttonCount; ++index)
|
|
||||||
{
|
{
|
||||||
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);
|
bool ATNIn = (gplev0 & PIGPIO_MASK_IN_ATN) == (invertIECInputs ? PIGPIO_MASK_IN_ATN : 0);
|
||||||
|
|
|
@ -26,6 +26,9 @@
|
||||||
#include "rpi-gpio.h"
|
#include "rpi-gpio.h"
|
||||||
#include "rpiHardware.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_DEBOUNCE_THRESHOLD 20000
|
||||||
#define INPUT_BUTTON_REPEAT_THRESHOLD 460000
|
#define INPUT_BUTTON_REPEAT_THRESHOLD 460000
|
||||||
|
|
||||||
|
@ -308,6 +311,13 @@ public:
|
||||||
}
|
}
|
||||||
RPI_GpioBase->GPPUD = 0;
|
RPI_GpioBase->GPPUD = 0;
|
||||||
RPI_GpioBase->GPPUDCLK0 = 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()
|
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 ReadBrowseMode(void);
|
||||||
static void ReadEmulationMode1541(void);
|
static void ReadEmulationMode1541(void);
|
||||||
static void ReadButtonsEmulationMode(void);
|
static void ReadButtonsEmulationMode(void);
|
||||||
|
@ -544,6 +595,12 @@ public:
|
||||||
ignoreReset = value;
|
ignoreReset = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//ROTARY: Added for rotary encoder support - 09/05/2019 by Geo...
|
||||||
|
static inline void SetRotaryEncoderEnable(bool value)
|
||||||
|
{
|
||||||
|
rotaryEncoderEnable = value;
|
||||||
|
}
|
||||||
|
|
||||||
// CA1 input ATN
|
// CA1 input ATN
|
||||||
// If CA1 is ever set to output
|
// If CA1 is ever set to output
|
||||||
// - CA1 will start to drive pb7
|
// - CA1 will start to drive pb7
|
||||||
|
@ -607,5 +664,10 @@ private:
|
||||||
static u32 inputRepeatThreshold[5];
|
static u32 inputRepeatThreshold[5];
|
||||||
static u32 inputRepeat[5];
|
static u32 inputRepeat[5];
|
||||||
static u32 inputRepeatPrev[5];
|
static u32 inputRepeatPrev[5];
|
||||||
|
|
||||||
|
//ROTARY: Added for rotary encoder support - 09/05/2019 by Geo...
|
||||||
|
static RotaryEncoder rotaryEncoder;
|
||||||
|
static bool rotaryEncoderEnable;
|
||||||
|
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1763,6 +1763,9 @@ extern "C"
|
||||||
IEC_Bus::SetInvertIECOutputs(options.InvertIECOutputs());
|
IEC_Bus::SetInvertIECOutputs(options.InvertIECOutputs());
|
||||||
IEC_Bus::SetIgnoreReset(options.IgnoreReset());
|
IEC_Bus::SetIgnoreReset(options.IgnoreReset());
|
||||||
|
|
||||||
|
//ROTARY: Added for rotary encoder support - 09/05/2019 by Geo...
|
||||||
|
IEC_Bus::SetRotaryEncoderEnable(options.RotaryEncoderEnable());
|
||||||
|
|
||||||
if (!options.SoundOnGPIO())
|
if (!options.SoundOnGPIO())
|
||||||
{
|
{
|
||||||
dmaSound = (u32*)malloc(Sample_bin_size * 4);
|
dmaSound = (u32*)malloc(Sample_bin_size * 4);
|
||||||
|
|
|
@ -156,6 +156,7 @@ Options::Options(void)
|
||||||
, buttonDown(3)
|
, buttonDown(3)
|
||||||
, buttonBack(4)
|
, buttonBack(4)
|
||||||
, buttonInsert(5)
|
, buttonInsert(5)
|
||||||
|
, rotaryEncoderEnable(0) //ROTARY:
|
||||||
|
|
||||||
{
|
{
|
||||||
autoMountImageName[0] = 0;
|
autoMountImageName[0] = 0;
|
||||||
|
@ -245,6 +246,7 @@ void Options::Process(char* buffer)
|
||||||
ELSE_CHECK_DECIMAL_OPTION(buttonDown)
|
ELSE_CHECK_DECIMAL_OPTION(buttonDown)
|
||||||
ELSE_CHECK_DECIMAL_OPTION(buttonBack)
|
ELSE_CHECK_DECIMAL_OPTION(buttonBack)
|
||||||
ELSE_CHECK_DECIMAL_OPTION(buttonInsert)
|
ELSE_CHECK_DECIMAL_OPTION(buttonInsert)
|
||||||
|
ELSE_CHECK_DECIMAL_OPTION(rotaryEncoderEnable) //ROTARY:
|
||||||
else if ((strcasecmp(pOption, "AutoBaseName") == 0))
|
else if ((strcasecmp(pOption, "AutoBaseName") == 0))
|
||||||
{
|
{
|
||||||
strncpy(autoBaseName, pValue, 255);
|
strncpy(autoBaseName, pValue, 255);
|
||||||
|
|
|
@ -100,6 +100,9 @@ public:
|
||||||
inline unsigned int GetButtonBack() const { return buttonBack - 1; }
|
inline unsigned int GetButtonBack() const { return buttonBack - 1; }
|
||||||
inline unsigned int GetButtonInsert() const { return buttonInsert - 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.
|
// 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?
|
// Perhaps we should use some keyboard modifier to the the other screen?
|
||||||
inline unsigned int KeyboardBrowseLCDScreen() const { return keyboardBrowseLCDScreen; }
|
inline unsigned int KeyboardBrowseLCDScreen() const { return keyboardBrowseLCDScreen; }
|
||||||
|
@ -175,5 +178,9 @@ private:
|
||||||
char ROMName1581[256];
|
char ROMName1581[256];
|
||||||
|
|
||||||
char newDiskType[32];
|
char newDiskType[32];
|
||||||
|
|
||||||
|
//ROTARY: Added for rotary encoder support - 09/05/2019 by Geo...
|
||||||
|
unsigned int rotaryEncoderEnable;
|
||||||
|
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue