Skip to content

rotaryio: Add the ability to set the divisor #5468

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions locale/circuitpython.pot
Original file line number Diff line number Diff line change
Expand Up @@ -2976,6 +2976,10 @@ msgstr ""
msgid "division by zero"
msgstr ""

#: ports/espressif/common-hal/rotaryio/IncrementalEncoder.c
msgid "divisor must be 4"
msgstr ""

#: py/objdeque.c
msgid "empty"
msgstr ""
Expand Down
2 changes: 1 addition & 1 deletion ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencode
set_eic_channel_data(self->eic_channel_b, (void *)self);

self->position = 0;
self->quarter_count = 0;
self->sub_count = 0;

shared_module_softencoder_state_init(self,
((uint8_t)gpio_get_pin_level(self->pin_a) << 1) |
Expand Down
3 changes: 2 additions & 1 deletion ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ typedef struct {
uint8_t eic_channel_a;
uint8_t eic_channel_b;
uint8_t state; // <old A><old B>
int8_t quarter_count; // count intermediate transitions between detents
int8_t sub_count; // count intermediate transitions between detents
int8_t divisor; // Number of quadrature edges required per count
mp_int_t position;
} rotaryio_incrementalencoder_obj_t;

Expand Down
10 changes: 10 additions & 0 deletions ports/espressif/common-hal/rotaryio/IncrementalEncoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,13 @@ void common_hal_rotaryio_incrementalencoder_set_position(rotaryio_incrementalenc
self->position = new_position;
pcnt_counter_clear(self->unit);
}

mp_int_t common_hal_rotaryio_incrementalencoder_get_divisor(rotaryio_incrementalencoder_obj_t *self) {
return 4;
}

void common_hal_rotaryio_incrementalencoder_set_divisor(rotaryio_incrementalencoder_obj_t *self, mp_int_t divisor) {
if (divisor != 4) {
mp_raise_ValueError(translate("divisor must be 4"));
}
}
3 changes: 2 additions & 1 deletion ports/nrf/common-hal/rotaryio/IncrementalEncoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ typedef struct {
uint8_t pin_a;
uint8_t pin_b;
uint8_t state; // <old A><old B>
int8_t quarter_count; // count intermediate transitions between detents
int8_t sub_count; // count intermediate transitions between detents
int8_t divisor; // Number of quadrature edges required per count
mp_int_t position;
} rotaryio_incrementalencoder_obj_t;

Expand Down
2 changes: 1 addition & 1 deletion ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencode
}

self->position = 0;
self->quarter_count = 0;
self->sub_count = 0;

common_hal_rp2pio_statemachine_construct(&self->state_machine,
encoder, MP_ARRAY_SIZE(encoder),
Expand Down
3 changes: 2 additions & 1 deletion ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ typedef struct {
mp_obj_base_t base;
rp2pio_statemachine_obj_t state_machine;
uint8_t state; // <old A><old B>
int8_t quarter_count; // count intermediate transitions between detents
int8_t sub_count; // count intermediate transitions between detents
int8_t divisor; // Number of quadrature edges required per count
bool swapped; // Did the pins need to be swapped to be sequential?
mp_int_t position;
} rotaryio_incrementalencoder_obj_t;
39 changes: 36 additions & 3 deletions shared-bindings/rotaryio/IncrementalEncoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,14 @@
//| class IncrementalEncoder:
//| """IncrementalEncoder determines the relative rotational position based on two series of pulses."""
//|
//| def __init__(self, pin_a: microcontroller.Pin, pin_b: microcontroller.Pin) -> None:
//| def __init__(self, pin_a: microcontroller.Pin, pin_b: microcontroller.Pin, divisor: int = 4) -> None:
//| """Create an IncrementalEncoder object associated with the given pins. It tracks the positional
//| state of an incremental rotary encoder (also known as a quadrature encoder.) Position is
//| relative to the position when the object is contructed.
//|
//| :param ~microcontroller.Pin pin_a: First pin to read pulses from.
//| :param ~microcontroller.Pin pin_b: Second pin to read pulses from.
//| :param int divisor: The divisor of the quadrature signal.
//|
//| For example::
//|
Expand All @@ -61,10 +62,11 @@
//| ...
//|
STATIC mp_obj_t rotaryio_incrementalencoder_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
enum { ARG_pin_a, ARG_pin_b };
enum { ARG_pin_a, ARG_pin_b, ARG_divisor };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_pin_a, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_pin_b, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_divisor, MP_ARG_INT, { .u_int = 4 } },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
Expand All @@ -77,6 +79,7 @@ STATIC mp_obj_t rotaryio_incrementalencoder_make_new(const mp_obj_type_t *type,

common_hal_rotaryio_incrementalencoder_construct(self, pin_a, pin_b);

common_hal_rotaryio_incrementalencoder_set_divisor(self, args[ARG_divisor].u_int);
return MP_OBJ_FROM_PTR(self);
}

Expand Down Expand Up @@ -116,9 +119,38 @@ STATIC mp_obj_t rotaryio_incrementalencoder_obj___exit__(size_t n_args, const mp
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(rotaryio_incrementalencoder___exit___obj, 4, 4, rotaryio_incrementalencoder_obj___exit__);


//| divisor: int
//| """The divisor of the quadrature signal. Use 1 for encoders without
//| detents, or encoders with 4 detents per cycle. Use 2 for encoders with 2
//| detents per cycle. Use 4 for encoders with 1 detent per cycle."""
//|
STATIC mp_obj_t rotaryio_incrementalencoder_obj_get_divisor(mp_obj_t self_in) {
rotaryio_incrementalencoder_obj_t *self = MP_OBJ_TO_PTR(self_in);
check_for_deinit(self);

return mp_obj_new_int(common_hal_rotaryio_incrementalencoder_get_divisor(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(rotaryio_incrementalencoder_get_divisor_obj, rotaryio_incrementalencoder_obj_get_divisor);

STATIC mp_obj_t rotaryio_incrementalencoder_obj_set_divisor(mp_obj_t self_in, mp_obj_t new_divisor) {
rotaryio_incrementalencoder_obj_t *self = MP_OBJ_TO_PTR(self_in);
check_for_deinit(self);

common_hal_rotaryio_incrementalencoder_set_divisor(self, mp_obj_get_int(new_divisor));
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_2(rotaryio_incrementalencoder_set_divisor_obj, rotaryio_incrementalencoder_obj_set_divisor);

const mp_obj_property_t rotaryio_incrementalencoder_divisor_obj = {
.base.type = &mp_type_property,
.proxy = {(mp_obj_t)&rotaryio_incrementalencoder_get_divisor_obj,
(mp_obj_t)&rotaryio_incrementalencoder_set_divisor_obj,
MP_ROM_NONE},
};

//| position: int
//| """The current position in terms of pulses. The number of pulses per rotation is defined by the
//| specific hardware."""
//| specific hardware and by the divisor."""
//|
STATIC mp_obj_t rotaryio_incrementalencoder_obj_get_position(mp_obj_t self_in) {
rotaryio_incrementalencoder_obj_t *self = MP_OBJ_TO_PTR(self_in);
Expand Down Expand Up @@ -150,6 +182,7 @@ STATIC const mp_rom_map_elem_t rotaryio_incrementalencoder_locals_dict_table[] =
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) },
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&rotaryio_incrementalencoder___exit___obj) },
{ MP_ROM_QSTR(MP_QSTR_position), MP_ROM_PTR(&rotaryio_incrementalencoder_position_obj) },
{ MP_ROM_QSTR(MP_QSTR_divisor), MP_ROM_PTR(&rotaryio_incrementalencoder_divisor_obj) },
};
STATIC MP_DEFINE_CONST_DICT(rotaryio_incrementalencoder_locals_dict, rotaryio_incrementalencoder_locals_dict_table);

Expand Down
3 changes: 3 additions & 0 deletions shared-bindings/rotaryio/IncrementalEncoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,8 @@ extern bool common_hal_rotaryio_incrementalencoder_deinited(rotaryio_incremental
extern mp_int_t common_hal_rotaryio_incrementalencoder_get_position(rotaryio_incrementalencoder_obj_t *self);
extern void common_hal_rotaryio_incrementalencoder_set_position(rotaryio_incrementalencoder_obj_t *self,
mp_int_t new_position);
extern mp_int_t common_hal_rotaryio_incrementalencoder_get_divisor(rotaryio_incrementalencoder_obj_t *self);
extern void common_hal_rotaryio_incrementalencoder_set_divisor(rotaryio_incrementalencoder_obj_t *self,
mp_int_t new_divisor);

#endif // MICROPY_INCLUDED_SHARED_BINDINGS_ROTARYIO_INCREMENTALENCODER_H
35 changes: 19 additions & 16 deletions shared-module/rotaryio/IncrementalEncoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,26 +30,25 @@

void shared_module_softencoder_state_init(rotaryio_incrementalencoder_obj_t *self, uint8_t quiescent_state) {
self->state = quiescent_state;
self->quarter_count = 0;
self->sub_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
0, // 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
0, // 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 -> 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
0, // 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
Expand All @@ -59,20 +58,16 @@ void shared_module_softencoder_state_update(rotaryio_incrementalencoder_obj_t *s
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;
}
int8_t sub_incr = transitions[idx];

self->quarter_count += quarter_incr;
self->sub_count += sub_incr;

if (self->quarter_count >= 4) {
if (self->sub_count >= self->divisor) {
self->position += 1;
self->quarter_count = 0;
} else if (self->quarter_count <= -4) {
self->sub_count = 0;
} else if (self->sub_count <= -self->divisor) {
self->position -= 1;
self->quarter_count = 0;
self->sub_count = 0;
}
}

Expand All @@ -83,4 +78,12 @@ mp_int_t common_hal_rotaryio_incrementalencoder_get_position(rotaryio_incrementa
void common_hal_rotaryio_incrementalencoder_set_position(rotaryio_incrementalencoder_obj_t *self, mp_int_t position) {
self->position = position;
}

mp_int_t common_hal_rotaryio_incrementalencoder_get_divisor(rotaryio_incrementalencoder_obj_t *self) {
return self->divisor;
}

void common_hal_rotaryio_incrementalencoder_set_divisor(rotaryio_incrementalencoder_obj_t *self, mp_int_t divisor) {
self->divisor = divisor;
}
#endif