From 3ce0b512f8e239693aa102c0ec9ff2d3ef1bc6b9 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 8 Apr 2021 16:25:47 -0500 Subject: [PATCH] rasberrypi: IncrementalEncoder: factor out state machine --- .../common-hal/rotaryio/IncrementalEncoder.c | 67 ++------------- .../common-hal/rotaryio/IncrementalEncoder.h | 2 +- ports/raspberrypi/mpconfigport.mk | 1 + py/circuitpy_defns.mk | 1 + py/circuitpy_mpconfig.mk | 3 + shared-module/rotaryio/IncrementalEncoder.c | 84 +++++++++++++++++++ shared-module/rotaryio/IncrementalEncoder.h | 34 ++++++++ 7 files changed, 130 insertions(+), 62 deletions(-) create mode 100644 shared-module/rotaryio/IncrementalEncoder.c create mode 100644 shared-module/rotaryio/IncrementalEncoder.h diff --git a/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c b/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c index a3cf05ca65..10785c172c 100644 --- a/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c +++ b/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c @@ -28,6 +28,7 @@ #include #include "common-hal/rotaryio/IncrementalEncoder.h" +#include "shared-module/rotaryio/IncrementalEncoder.h" #include "bindings/rp2pio/__init__.h" #include "bindings/rp2pio/StateMachine.h" @@ -88,12 +89,10 @@ void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencode common_hal_rp2pio_statemachine_run(&self->state_machine, encoder_init, MP_ARRAY_SIZE(encoder_init)); // We're guaranteed by the init code that some output will be available promptly - uint8_t state; - common_hal_rp2pio_statemachine_readinto(&self->state_machine, &state, 1, 1); - // Top two bits of self->last_state don't matter, because they'll be gone as soon as - // interrupt handler is called. - self->last_state = state & 3; + uint8_t quiescent_state; + common_hal_rp2pio_statemachine_readinto(&self->state_machine, &quiescent_state, 1, 1); + shared_module_softencoder_state_init(self, quiescent_state & 3); common_hal_rp2pio_statemachine_set_interrupt_handler(&self->state_machine, incrementalencoder_interrupt_handler, self, PIO_IRQ0_INTF_SM0_RXNEMPTY_BITS); } @@ -109,67 +108,13 @@ void common_hal_rotaryio_incrementalencoder_deinit(rotaryio_incrementalencoder_o common_hal_rp2pio_statemachine_deinit(&self->state_machine); } -mp_int_t common_hal_rotaryio_incrementalencoder_get_position(rotaryio_incrementalencoder_obj_t *self) { - return self->position; -} - -void common_hal_rotaryio_incrementalencoder_set_position(rotaryio_incrementalencoder_obj_t *self, - mp_int_t new_position) { - self->position = new_position; -} - STATIC void incrementalencoder_interrupt_handler(void *self_in) { rotaryio_incrementalencoder_obj_t *self = self_in; - // This table also works for detent both at 11 and 00 - // For 11 at detent: - // Turning cw: 11->01->00->10->11 - // Turning ccw: 11->10->00->01->11 - // For 00 at detent: - // Turning cw: 00->10->11->10->00 - // Turning ccw: 00->01->11->10->00 - - // index table by state - #define BAD 7 - static const int8_t transitions[16] = { - 0, // 00 -> 00 no movement - -1, // 00 -> 01 3/4 ccw (11 detent) or 1/4 ccw (00 at detent) - +1, // 00 -> 10 3/4 cw or 1/4 cw - BAD, // 00 -> 11 non-Gray-code transition - +1, // 01 -> 00 2/4 or 4/4 cw - 0, // 01 -> 01 no movement - BAD, // 01 -> 10 non-Gray-code transition - -1, // 01 -> 11 4/4 or 2/4 ccw - -1, // 10 -> 00 2/4 or 4/4 ccw - BAD, // 10 -> 01 non-Gray-code transition - 0, // 10 -> 10 no movement - +1, // 10 -> 11 4/4 or 2/4 cw - BAD, // 11 -> 00 non-Gray-code transition - +1, // 11 -> 01 1/4 or 3/4 cw - -1, // 11 -> 10 1/4 or 3/4 ccw - 0, // 11 -> 11 no movement - }; while (common_hal_rp2pio_statemachine_get_in_waiting(&self->state_machine)) { // Bypass all the logic of StateMachine.c:_transfer, we need something // very simple and fast for an interrupt! - uint8_t new = self->state_machine.pio->rxf[self->state_machine.state_machine]; - - // Shift the old AB bits to the "old" position, and set the new AB bits. - self->last_state = (self->last_state & 0x3) << 2 | (new & 0x3); - - int8_t quarter_incr = transitions[self->last_state]; - if (quarter_incr == BAD) { - // Missed a transition. We don't know which way we're going, so do nothing. - return; - } - - self->quarter_count += quarter_incr; - if (self->quarter_count >= 4) { - self->position += 1; - self->quarter_count = 0; - } else if (self->quarter_count <= -4) { - self->position -= 1; - self->quarter_count = 0; - } + uint8_t new_state = self->state_machine.pio->rxf[self->state_machine.state_machine]; + shared_module_softencoder_state_update(self, new_state); } } diff --git a/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.h b/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.h index 83fe97d316..f794560275 100644 --- a/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.h +++ b/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.h @@ -34,7 +34,7 @@ typedef struct { mp_obj_base_t base; rp2pio_statemachine_obj_t state_machine; - uint8_t last_state : 4; // + uint8_t state : 4; // int8_t quarter_count : 4; // count intermediate transitions between detents mp_int_t position; } rotaryio_incrementalencoder_obj_t; diff --git a/ports/raspberrypi/mpconfigport.mk b/ports/raspberrypi/mpconfigport.mk index ddbd0ec63b..cece99afa9 100644 --- a/ports/raspberrypi/mpconfigport.mk +++ b/ports/raspberrypi/mpconfigport.mk @@ -26,6 +26,7 @@ CIRCUITPY_BITOPS ?= 1 CIRCUITPY_PWMIO ?= 1 CIRCUITPY_RGBMATRIX ?= 1 CIRCUITPY_ROTARYIO ?= 1 +CIRCUITPY_ROTARYIO_SOFTENCODER = 1 # Things that need to be implemented. # Use PWM interally diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index bb177c5d07..0eedfe5249 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -518,6 +518,7 @@ SRC_SHARED_MODULE_ALL = \ random/__init__.c \ rgbmatrix/RGBMatrix.c \ rgbmatrix/__init__.c \ + rotaryio/IncrementalEncoder.c \ sharpdisplay/SharpMemoryFramebuffer.c \ sharpdisplay/__init__.c \ socket/__init__.c \ diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 2a4467d493..51b590dcf3 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -271,6 +271,9 @@ CFLAGS += -DCIRCUITPY_RGBMATRIX=$(CIRCUITPY_RGBMATRIX) CIRCUITPY_ROTARYIO ?= 1 CFLAGS += -DCIRCUITPY_ROTARYIO=$(CIRCUITPY_ROTARYIO) +CIRCUITPY_ROTARYIO_SOFTENCODER ?= 0 +CFLAGS += -DCIRCUITPY_ROTARYIO_SOFTENCODER=$(CIRCUITPY_ROTARYIO_SOFTENCODER) + CIRCUITPY_RTC ?= 1 CFLAGS += -DCIRCUITPY_RTC=$(CIRCUITPY_RTC) diff --git a/shared-module/rotaryio/IncrementalEncoder.c b/shared-module/rotaryio/IncrementalEncoder.c new file mode 100644 index 0000000000..ca7c154811 --- /dev/null +++ b/shared-module/rotaryio/IncrementalEncoder.c @@ -0,0 +1,84 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Jeff Epler for Adafruit Industries + * + * 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. + */ + +#if CIRCUITPY_ROTARYIO && CIRCUITPY_ROTARYIO_SOFTENCODER +#include "shared-module/rotaryio/IncrementalEncoder.h" +#include "common-hal/rotaryio/IncrementalEncoder.h" + +void shared_module_softencoder_state_init(rotaryio_incrementalencoder_obj_t *self, uint8_t quiescent_state) { + self->state = quiescent_state; + self->quarter_count = 0; + common_hal_rotaryio_incrementalencoder_set_position(self, 0); +} + +void shared_module_softencoder_state_update(rotaryio_incrementalencoder_obj_t *self, uint8_t new_state) { + #define BAD 7 + static const int8_t transitions[16] = { + 0, // 00 -> 00 no movement + -1, // 00 -> 01 3/4 ccw (11 detent) or 1/4 ccw (00 at detent) + +1, // 00 -> 10 3/4 cw or 1/4 cw + BAD, // 00 -> 11 non-Gray-code transition + +1, // 01 -> 00 2/4 or 4/4 cw + 0, // 01 -> 01 no movement + BAD, // 01 -> 10 non-Gray-code transition + -1, // 01 -> 11 4/4 or 2/4 ccw + -1, // 10 -> 00 2/4 or 4/4 ccw + BAD, // 10 -> 01 non-Gray-code transition + 0, // 10 -> 10 no movement + +1, // 10 -> 11 4/4 or 2/4 cw + BAD, // 11 -> 00 non-Gray-code transition + +1, // 11 -> 01 1/4 or 3/4 cw + -1, // 11 -> 10 1/4 or 3/4 ccw + 0, // 11 -> 11 no movement + }; + + new_state &= 0x3; + int idx = (self->state << 2) | new_state; + self->state = new_state; + + int8_t quarter_incr = transitions[idx]; + if (quarter_incr == BAD) { + // Missed a transition. We don't know which way we're going, so do nothing. + return; + } + + if (self->quarter_count >= 4) { + self->position += 1; + self->quarter_count = 0; + } else if (self->quarter_count <= -4) { + self->position -= 1; + self->quarter_count = 0; + } +} + +mp_int_t common_hal_rotaryio_incrementalencoder_get_position(rotaryio_incrementalencoder_obj_t *self) { + return self->position; +} + +void common_hal_rotaryio_incrementalencoder_set_position(rotaryio_incrementalencoder_obj_t *self, mp_int_t position) { + self->position = position; +} +#endif diff --git a/shared-module/rotaryio/IncrementalEncoder.h b/shared-module/rotaryio/IncrementalEncoder.h new file mode 100644 index 0000000000..82a5644b47 --- /dev/null +++ b/shared-module/rotaryio/IncrementalEncoder.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Jeff Epler for Adafruit Industries + * + * 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. + */ + +#pragma once + +#include "common-hal/rotaryio/IncrementalEncoder.h" + +void shared_module_softencoder_state_init(rotaryio_incrementalencoder_obj_t *self, uint8_t quiescent_state); +void shared_module_softencoder_state_update(rotaryio_incrementalencoder_obj_t *self, uint8_t new_state); +mp_int_t common_hal_rotaryio_incrementalencoder_get_position(rotaryio_incrementalencoder_obj_t *self); +void common_hal_rotaryio_incrementalencoder_set_position(rotaryio_incrementalencoder_obj_t *self, mp_int_t position);