mirror of
https://github.com/akuker/RASCSI.git
synced 2024-12-21 08:29:59 +00:00
8bit encoder detection + debounced button detection, numpy removal (#710)
* debounce interrupt events for rotary events and button presses with 400 microseconds by default. * Massive improvements for the encoder detection. Removed numpy as a dependency. * Debounce button presses which started to jump around. * formatting cleanup.
This commit is contained in:
parent
1df7cdb1f3
commit
0297fe856a
@ -992,8 +992,8 @@ function installRaScsiCtrlBoard() {
|
||||
updateRaScsiGit
|
||||
|
||||
sudo apt-get update && sudo apt-get install libjpeg-dev libpng-dev libopenjp2-7-dev i2c-tools raspi-config -y </dev/null
|
||||
# install numpy via apt to avoid compilation
|
||||
sudo apt-get install python3-numpy python3-cbor2 -y </dev/null
|
||||
# install python packages through apt that need compilation
|
||||
sudo apt-get install python3-cbor2 -y </dev/null
|
||||
|
||||
# enable i2c
|
||||
if [[ $(grep -c "^dtparam=i2c_arm=on" /boot/config.txt) -ge 1 ]]; then
|
||||
|
@ -47,6 +47,7 @@ the raspberry pi model and enable transitions only for the following pi models:
|
||||
The model detection can be overriden by adding a --transitions parameter to start.sh.
|
||||
|
||||
## Credits
|
||||
|
||||
### DejaVuSansMono-Bold.ttf
|
||||
* Source: https://dejavu-fonts.github.io
|
||||
* Distributed under DejaVu Fonts Lience (see DejaVu Fonts License.txt for full text)
|
||||
|
@ -7,5 +7,4 @@ RPi.GPIO==0.7.0
|
||||
protobuf==3.19.3
|
||||
unidecode==1.3.2
|
||||
smbus==1.1.post2
|
||||
# installed via apt to avoid lengthy compilation
|
||||
#numpy==1.21.5
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
"""Module providing the interface to the RaSCSI Control Board hardware"""
|
||||
# noinspection PyUnresolvedReferences
|
||||
import logging
|
||||
import RPi.GPIO as GPIO
|
||||
import numpy
|
||||
import smbus
|
||||
import time
|
||||
|
||||
import RPi.GPIO as GPIO
|
||||
import smbus
|
||||
from ctrlboard_hw import pca9554multiplexer
|
||||
from ctrlboard_hw.hardware_button import HardwareButton
|
||||
from ctrlboard_hw.ctrlboard_hw_constants import CtrlBoardHardwareConstants
|
||||
@ -16,9 +16,10 @@ from observable import Observable
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
class CtrlBoardHardware(Observable):
|
||||
"""Class implements the RaSCSI Control Board hardware and provides an interface to it."""
|
||||
def __init__(self, display_i2c_address, pca9554_i2c_address):
|
||||
def __init__(self, display_i2c_address, pca9554_i2c_address, debounce_ms=200):
|
||||
self.display_i2c_address = display_i2c_address
|
||||
self.pca9554_i2c_address = pca9554_i2c_address
|
||||
self.debounce_ms = debounce_ms
|
||||
self.rascsi_controlboard_detected = self.detect_rascsi_controlboard()
|
||||
log = logging.getLogger(__name__)
|
||||
log.info("RaSCSI Control Board detected: %s", str(self.rascsi_controlboard_detected))
|
||||
@ -53,7 +54,7 @@ class CtrlBoardHardware(Observable):
|
||||
self.pca_driver.write_configuration_register_port(CtrlBoardHardwareConstants.
|
||||
PCA9554_PIN_LED_2,
|
||||
PCA9554Multiplexer.PIN_ENABLED_AS_OUTPUT)
|
||||
self.input_register_buffer = numpy.uint32(0)
|
||||
self.input_register_buffer = 0
|
||||
|
||||
# pylint: disable=no-member
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
@ -97,6 +98,7 @@ class CtrlBoardHardware(Observable):
|
||||
self.rotary = Encoder(self.rotary_a, self.rotary_b)
|
||||
self.rotary.pos_prev = 0
|
||||
self.rotary.name = CtrlBoardHardwareConstants.ROTARY
|
||||
self.rot_prev_state = 0b11
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
# pylint: disable=unused-argument
|
||||
@ -113,11 +115,18 @@ class CtrlBoardHardware(Observable):
|
||||
|
||||
value = button.state_interrupt
|
||||
|
||||
# ignore button press if debounce time is not reached
|
||||
if button.last_press is not None:
|
||||
elapsed = (time.time_ns() - button.last_press)/1000000
|
||||
if elapsed < self.debounce_ms:
|
||||
return
|
||||
|
||||
if value != button.state and value is False:
|
||||
button.state = False
|
||||
self.notify(button)
|
||||
button.state = True
|
||||
button.state_interrupt = True
|
||||
button.last_press = time.time_ns()
|
||||
|
||||
def check_rotary_encoder(self, rotary):
|
||||
"""Checks whether the rotary state has changed."""
|
||||
@ -150,19 +159,20 @@ class CtrlBoardHardware(Observable):
|
||||
|
||||
def process_events(self):
|
||||
"""Non-blocking event processor for hardware events (button presses etc.)"""
|
||||
input_register_buffer_length = int(len(format(self.input_register_buffer, 'b'))/8)
|
||||
if input_register_buffer_length <= 1:
|
||||
return
|
||||
|
||||
input_register_buffer = self.input_register_buffer
|
||||
self.input_register_buffer = 0
|
||||
|
||||
input_register_buffer_length = int(len(format(input_register_buffer, 'b'))/8)
|
||||
if input_register_buffer_length < 1:
|
||||
return
|
||||
|
||||
for i in range(0, input_register_buffer_length):
|
||||
shiftval = (input_register_buffer_length-1-i)*8
|
||||
input_register = (input_register_buffer >> shiftval) & 0b11111111
|
||||
|
||||
rot_a = self.button_value(input_register, 0)
|
||||
rot_b = self.button_value(input_register, 1)
|
||||
|
||||
button_rotary = self.button_value(input_register, 5)
|
||||
button_1 = self.button_value(input_register, 2)
|
||||
button_2 = self.button_value(input_register, 3)
|
||||
@ -176,19 +186,21 @@ class CtrlBoardHardware(Observable):
|
||||
if button_rotary == 0:
|
||||
self.rotary_button.state_interrupt = bool(button_rotary)
|
||||
|
||||
rot_tmp_state = 0b11
|
||||
if rot_a == 0:
|
||||
self.rotary.enc_a.state_interrupt = bool(rot_a)
|
||||
|
||||
rot_tmp_state &= 0b10
|
||||
if rot_b == 0:
|
||||
rot_tmp_state &= 0b01
|
||||
|
||||
if rot_tmp_state != self.rot_prev_state:
|
||||
self.rot_prev_state = rot_tmp_state
|
||||
self.rotary.enc_a.state_interrupt = bool(rot_a)
|
||||
self.rotary.enc_b.state_interrupt = bool(rot_b)
|
||||
self.check_rotary_encoder(self.rotary)
|
||||
|
||||
self.check_button_press(self.rotary_button)
|
||||
self.check_button_press(self.button1)
|
||||
self.check_button_press(self.button2)
|
||||
self.check_rotary_encoder(self.rotary)
|
||||
|
||||
self.rotary.state = 0b11
|
||||
self.input_register_buffer = 0
|
||||
|
||||
@staticmethod
|
||||
def detect_i2c_devices(_bus):
|
||||
|
@ -10,57 +10,36 @@ class Encoder:
|
||||
self.enc_a = enc_a
|
||||
self.enc_b = enc_b
|
||||
self.pos = 0
|
||||
self.state = 0b0011
|
||||
self.state = 0b00000000
|
||||
self.direction = 0
|
||||
|
||||
def update(self):
|
||||
"""Updates the internal attributes wrt. to the encoder position and direction."""
|
||||
self.update2()
|
||||
|
||||
def update2(self):
|
||||
"""Primary method for detecting the direction"""
|
||||
value_enc_a = self.enc_a.state_interrupt
|
||||
value_enc_b = self.enc_b.state_interrupt
|
||||
|
||||
self.direction = 0
|
||||
state = self.state & 0b0011
|
||||
state = self.state & 0b00111111
|
||||
|
||||
if value_enc_a:
|
||||
state |= 0b0100
|
||||
state |= 0b01000000
|
||||
if value_enc_b:
|
||||
state |= 0b1000
|
||||
state |= 0b10000000
|
||||
|
||||
if state == 0b1011:
|
||||
# clockwise pattern detection
|
||||
if (state == 0b11010010 or state == 0b11001000 or state == 0b11011000 or
|
||||
state == 0b11010001 or state == 0b11011011 or state == 0b11100000 or
|
||||
state == 0b11001011):
|
||||
self.pos += 1
|
||||
self.direction = 1
|
||||
|
||||
if state == 0b0111:
|
||||
self.state = 0b00000000
|
||||
return
|
||||
# counter-clockwise pattern detection
|
||||
elif (state == 0b11000100 or state == 0b11100100 or state == 0b11100001 or
|
||||
state == 0b11000111 or state == 0b11100111):
|
||||
self.pos -= 1
|
||||
self.direction = -1
|
||||
self.state = 0b00000000
|
||||
return
|
||||
|
||||
self.state = state >> 2
|
||||
|
||||
self.enc_a.state_interrupt = True
|
||||
self.enc_b.state_interrupt = True
|
||||
|
||||
def update1(self):
|
||||
"""Secondary, less well working method to detect the direction"""
|
||||
if self.enc_a.state_interrupt is True and self.enc_b.state_interrupt is True:
|
||||
return
|
||||
|
||||
if self.enc_a.state_interrupt is False and self.enc_b.state_interrupt is False:
|
||||
self.enc_a.state_interrupt = True
|
||||
self.enc_b.state_interrupt = True
|
||||
return
|
||||
|
||||
self.direction = 0
|
||||
|
||||
if self.enc_a.state_interrupt is False:
|
||||
self.pos += 1
|
||||
self.direction = 1
|
||||
elif self.enc_a.state_interrupt is True:
|
||||
self.pos -= 1
|
||||
self.direction = -1
|
||||
|
||||
self.enc_a.state_interrupt = True
|
||||
self.enc_b.state_interrupt = True
|
||||
|
@ -11,6 +11,7 @@ class HardwareButton:
|
||||
self.state = True
|
||||
self.state_interrupt = True
|
||||
self.name = "n/a"
|
||||
self.last_press = None
|
||||
|
||||
def read(self):
|
||||
"""Reads the configured port of the i2c multiplexer"""
|
||||
|
Loading…
Reference in New Issue
Block a user