Skip to content

Commit 3ce0b51

Browse files
committed
rasberrypi: IncrementalEncoder: factor out state machine
1 parent 0539b88 commit 3ce0b51

File tree

7 files changed

+130
-62
lines changed

7 files changed

+130
-62
lines changed

ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c

Lines changed: 6 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
#include <hardware/regs/pio.h>
3030
#include "common-hal/rotaryio/IncrementalEncoder.h"
31+
#include "shared-module/rotaryio/IncrementalEncoder.h"
3132
#include "bindings/rp2pio/__init__.h"
3233
#include "bindings/rp2pio/StateMachine.h"
3334

@@ -88,12 +89,10 @@ void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencode
8889
common_hal_rp2pio_statemachine_run(&self->state_machine, encoder_init, MP_ARRAY_SIZE(encoder_init));
8990

9091
// We're guaranteed by the init code that some output will be available promptly
91-
uint8_t state;
92-
common_hal_rp2pio_statemachine_readinto(&self->state_machine, &state, 1, 1);
93-
// Top two bits of self->last_state don't matter, because they'll be gone as soon as
94-
// interrupt handler is called.
95-
self->last_state = state & 3;
92+
uint8_t quiescent_state;
93+
common_hal_rp2pio_statemachine_readinto(&self->state_machine, &quiescent_state, 1, 1);
9694

95+
shared_module_softencoder_state_init(self, quiescent_state & 3);
9796
common_hal_rp2pio_statemachine_set_interrupt_handler(&self->state_machine, incrementalencoder_interrupt_handler, self, PIO_IRQ0_INTF_SM0_RXNEMPTY_BITS);
9897
}
9998

@@ -109,67 +108,13 @@ void common_hal_rotaryio_incrementalencoder_deinit(rotaryio_incrementalencoder_o
109108
common_hal_rp2pio_statemachine_deinit(&self->state_machine);
110109
}
111110

112-
mp_int_t common_hal_rotaryio_incrementalencoder_get_position(rotaryio_incrementalencoder_obj_t *self) {
113-
return self->position;
114-
}
115-
116-
void common_hal_rotaryio_incrementalencoder_set_position(rotaryio_incrementalencoder_obj_t *self,
117-
mp_int_t new_position) {
118-
self->position = new_position;
119-
}
120-
121111
STATIC void incrementalencoder_interrupt_handler(void *self_in) {
122112
rotaryio_incrementalencoder_obj_t *self = self_in;
123-
// This table also works for detent both at 11 and 00
124-
// For 11 at detent:
125-
// Turning cw: 11->01->00->10->11
126-
// Turning ccw: 11->10->00->01->11
127-
// For 00 at detent:
128-
// Turning cw: 00->10->11->10->00
129-
// Turning ccw: 00->01->11->10->00
130-
131-
// index table by state <oldA><oldB><newA><newB>
132-
#define BAD 7
133-
static const int8_t transitions[16] = {
134-
0, // 00 -> 00 no movement
135-
-1, // 00 -> 01 3/4 ccw (11 detent) or 1/4 ccw (00 at detent)
136-
+1, // 00 -> 10 3/4 cw or 1/4 cw
137-
BAD, // 00 -> 11 non-Gray-code transition
138-
+1, // 01 -> 00 2/4 or 4/4 cw
139-
0, // 01 -> 01 no movement
140-
BAD, // 01 -> 10 non-Gray-code transition
141-
-1, // 01 -> 11 4/4 or 2/4 ccw
142-
-1, // 10 -> 00 2/4 or 4/4 ccw
143-
BAD, // 10 -> 01 non-Gray-code transition
144-
0, // 10 -> 10 no movement
145-
+1, // 10 -> 11 4/4 or 2/4 cw
146-
BAD, // 11 -> 00 non-Gray-code transition
147-
+1, // 11 -> 01 1/4 or 3/4 cw
148-
-1, // 11 -> 10 1/4 or 3/4 ccw
149-
0, // 11 -> 11 no movement
150-
};
151113

152114
while (common_hal_rp2pio_statemachine_get_in_waiting(&self->state_machine)) {
153115
// Bypass all the logic of StateMachine.c:_transfer, we need something
154116
// very simple and fast for an interrupt!
155-
uint8_t new = self->state_machine.pio->rxf[self->state_machine.state_machine];
156-
157-
// Shift the old AB bits to the "old" position, and set the new AB bits.
158-
self->last_state = (self->last_state & 0x3) << 2 | (new & 0x3);
159-
160-
int8_t quarter_incr = transitions[self->last_state];
161-
if (quarter_incr == BAD) {
162-
// Missed a transition. We don't know which way we're going, so do nothing.
163-
return;
164-
}
165-
166-
self->quarter_count += quarter_incr;
167-
if (self->quarter_count >= 4) {
168-
self->position += 1;
169-
self->quarter_count = 0;
170-
} else if (self->quarter_count <= -4) {
171-
self->position -= 1;
172-
self->quarter_count = 0;
173-
}
117+
uint8_t new_state = self->state_machine.pio->rxf[self->state_machine.state_machine];
118+
shared_module_softencoder_state_update(self, new_state);
174119
}
175120
}

ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
typedef struct {
3535
mp_obj_base_t base;
3636
rp2pio_statemachine_obj_t state_machine;
37-
uint8_t last_state : 4; // <old A><old B><new A><new B>
37+
uint8_t state : 4; // <old A><old B><new A><new B>
3838
int8_t quarter_count : 4; // count intermediate transitions between detents
3939
mp_int_t position;
4040
} rotaryio_incrementalencoder_obj_t;

ports/raspberrypi/mpconfigport.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ CIRCUITPY_BITOPS ?= 1
2626
CIRCUITPY_PWMIO ?= 1
2727
CIRCUITPY_RGBMATRIX ?= 1
2828
CIRCUITPY_ROTARYIO ?= 1
29+
CIRCUITPY_ROTARYIO_SOFTENCODER = 1
2930

3031
# Things that need to be implemented.
3132
# Use PWM interally

py/circuitpy_defns.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,7 @@ SRC_SHARED_MODULE_ALL = \
518518
random/__init__.c \
519519
rgbmatrix/RGBMatrix.c \
520520
rgbmatrix/__init__.c \
521+
rotaryio/IncrementalEncoder.c \
521522
sharpdisplay/SharpMemoryFramebuffer.c \
522523
sharpdisplay/__init__.c \
523524
socket/__init__.c \

py/circuitpy_mpconfig.mk

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,9 @@ CFLAGS += -DCIRCUITPY_RGBMATRIX=$(CIRCUITPY_RGBMATRIX)
271271
CIRCUITPY_ROTARYIO ?= 1
272272
CFLAGS += -DCIRCUITPY_ROTARYIO=$(CIRCUITPY_ROTARYIO)
273273

274+
CIRCUITPY_ROTARYIO_SOFTENCODER ?= 0
275+
CFLAGS += -DCIRCUITPY_ROTARYIO_SOFTENCODER=$(CIRCUITPY_ROTARYIO_SOFTENCODER)
276+
274277
CIRCUITPY_RTC ?= 1
275278
CFLAGS += -DCIRCUITPY_RTC=$(CIRCUITPY_RTC)
276279

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2021 Jeff Epler for Adafruit Industries
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#if CIRCUITPY_ROTARYIO && CIRCUITPY_ROTARYIO_SOFTENCODER
28+
#include "shared-module/rotaryio/IncrementalEncoder.h"
29+
#include "common-hal/rotaryio/IncrementalEncoder.h"
30+
31+
void shared_module_softencoder_state_init(rotaryio_incrementalencoder_obj_t *self, uint8_t quiescent_state) {
32+
self->state = quiescent_state;
33+
self->quarter_count = 0;
34+
common_hal_rotaryio_incrementalencoder_set_position(self, 0);
35+
}
36+
37+
void shared_module_softencoder_state_update(rotaryio_incrementalencoder_obj_t *self, uint8_t new_state) {
38+
#define BAD 7
39+
static const int8_t transitions[16] = {
40+
0, // 00 -> 00 no movement
41+
-1, // 00 -> 01 3/4 ccw (11 detent) or 1/4 ccw (00 at detent)
42+
+1, // 00 -> 10 3/4 cw or 1/4 cw
43+
BAD, // 00 -> 11 non-Gray-code transition
44+
+1, // 01 -> 00 2/4 or 4/4 cw
45+
0, // 01 -> 01 no movement
46+
BAD, // 01 -> 10 non-Gray-code transition
47+
-1, // 01 -> 11 4/4 or 2/4 ccw
48+
-1, // 10 -> 00 2/4 or 4/4 ccw
49+
BAD, // 10 -> 01 non-Gray-code transition
50+
0, // 10 -> 10 no movement
51+
+1, // 10 -> 11 4/4 or 2/4 cw
52+
BAD, // 11 -> 00 non-Gray-code transition
53+
+1, // 11 -> 01 1/4 or 3/4 cw
54+
-1, // 11 -> 10 1/4 or 3/4 ccw
55+
0, // 11 -> 11 no movement
56+
};
57+
58+
new_state &= 0x3;
59+
int idx = (self->state << 2) | new_state;
60+
self->state = new_state;
61+
62+
int8_t quarter_incr = transitions[idx];
63+
if (quarter_incr == BAD) {
64+
// Missed a transition. We don't know which way we're going, so do nothing.
65+
return;
66+
}
67+
68+
if (self->quarter_count >= 4) {
69+
self->position += 1;
70+
self->quarter_count = 0;
71+
} else if (self->quarter_count <= -4) {
72+
self->position -= 1;
73+
self->quarter_count = 0;
74+
}
75+
}
76+
77+
mp_int_t common_hal_rotaryio_incrementalencoder_get_position(rotaryio_incrementalencoder_obj_t *self) {
78+
return self->position;
79+
}
80+
81+
void common_hal_rotaryio_incrementalencoder_set_position(rotaryio_incrementalencoder_obj_t *self, mp_int_t position) {
82+
self->position = position;
83+
}
84+
#endif
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2021 Jeff Epler for Adafruit Industries
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#pragma once
28+
29+
#include "common-hal/rotaryio/IncrementalEncoder.h"
30+
31+
void shared_module_softencoder_state_init(rotaryio_incrementalencoder_obj_t *self, uint8_t quiescent_state);
32+
void shared_module_softencoder_state_update(rotaryio_incrementalencoder_obj_t *self, uint8_t new_state);
33+
mp_int_t common_hal_rotaryio_incrementalencoder_get_position(rotaryio_incrementalencoder_obj_t *self);
34+
void common_hal_rotaryio_incrementalencoder_set_position(rotaryio_incrementalencoder_obj_t *self, mp_int_t position);

0 commit comments

Comments
 (0)