rasberrypi: IncrementalEncoder: factor out state machine

This commit is contained in:
Jeff Epler 2021-04-08 16:25:47 -05:00
parent 0539b88a6b
commit 3ce0b512f8
7 changed files with 130 additions and 62 deletions

View File

@ -28,6 +28,7 @@
#include <hardware/regs/pio.h>
#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 <oldA><oldB><newA><newB>
#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);
}
}

View File

@ -34,7 +34,7 @@
typedef struct {
mp_obj_base_t base;
rp2pio_statemachine_obj_t state_machine;
uint8_t last_state : 4; // <old A><old B><new A><new B>
uint8_t state : 4; // <old A><old B><new A><new B>
int8_t quarter_count : 4; // count intermediate transitions between detents
mp_int_t position;
} rotaryio_incrementalencoder_obj_t;

View File

@ -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

View File

@ -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 \

View File

@ -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)

View File

@ -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

View File

@ -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);