From 63eae2baa0caf62d233c30b70445d9387f6d2c54 Mon Sep 17 00:00:00 2001 From: Doug Szumski Date: Sat, 19 May 2018 14:22:00 +0100 Subject: [PATCH] parts: add rotary encoder part Add a rotary encoder virtual part, based on the Panasonic EVEP rotary encoder. This could easily be extended to virtualise other rotary encoders. --- examples/parts/rotenc.c | 169 ++++++++++++++++++++++++++++++++++++++++ examples/parts/rotenc.h | 75 ++++++++++++++++++ 2 files changed, 244 insertions(+) create mode 100644 examples/parts/rotenc.c create mode 100644 examples/parts/rotenc.h diff --git a/examples/parts/rotenc.c b/examples/parts/rotenc.c new file mode 100644 index 0000000..4ad138e --- /dev/null +++ b/examples/parts/rotenc.c @@ -0,0 +1,169 @@ +/* + rotenc.c + + Copyright 2018 Doug Szumski + + This file is part of simavr. + + simavr is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + simavr is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with simavr. If not, see . + */ + +#include +#include +#include + +#include "sim_avr.h" +#include "rotenc.h" + +const rotenc_pins_t state_table[ROTENC_STATE_COUNT] = { + { 0, 0 }, + { 1, 0 }, + { 1, 1 }, + { 0, 1 } +}; + +static avr_cycle_count_t +rotenc_state_change( + avr_t * avr, + avr_cycle_count_t when, + void * param) +{ + rotenc_t * rotenc = (rotenc_t *) param; + + switch (rotenc->direction) { + case ROTENC_CW_CLICK: + // Advance phase forwards + if (++rotenc->phase >= ROTENC_STATE_COUNT) { + rotenc->phase = 0; + } + if (rotenc->verbose) { + printf("ROTENC: CW twist, pins A:%x, B:%x\n", + state_table[rotenc->phase].a_pin, + state_table[rotenc->phase].b_pin); + } + break; + case ROTENC_CCW_CLICK: + // Advance phase backwards + if (--rotenc->phase < 0) { + rotenc->phase = ROTENC_STATE_COUNT - 1; + } + if (rotenc->verbose) { + printf("ROTENC: CCW twist, pins: A:%x, B:%x\n", + state_table[rotenc->phase].a_pin, + state_table[rotenc->phase].b_pin); + } + break; + default: + // Invalid direction + break; + } + avr_raise_irq( + rotenc->irq + IRQ_ROTENC_OUT_A_PIN, + state_table[rotenc->phase].a_pin); + avr_raise_irq( + rotenc->irq + IRQ_ROTENC_OUT_B_PIN, + state_table[rotenc->phase].b_pin); + return 0; +} + +/* + * This function exists purely as a wrapper so that the first state change + * doesn't get cancelled when it's registered with the cycle timer. + */ +static avr_cycle_count_t +rotenc_second_state_change( + avr_t * avr, + avr_cycle_count_t when, + void * param) +{ + return rotenc_state_change(avr, when, param); +} + +static avr_cycle_count_t +rotenc_button_auto_release( + avr_t * avr, + avr_cycle_count_t when, + void * param) +{ + rotenc_t * rotenc = (rotenc_t *) param; + avr_raise_irq(rotenc->irq + IRQ_ROTENC_OUT_BUTTON_PIN, 1); + if (rotenc->verbose) { + printf("ROTENC: Button release\n"); + } + return 0; +} + +void +rotenc_button_press(rotenc_t * rotenc) +{ + // Press down + if (rotenc->verbose) { + printf("ROTENC: Button press\n"); + } + avr_raise_irq(rotenc->irq + IRQ_ROTENC_OUT_BUTTON_PIN, 0); + + // Pull up later + avr_cycle_timer_register_usec( + rotenc->avr, + ROTENC_BUTTON_DURATION_US, + rotenc_button_auto_release, + rotenc); +} + +/* + * Simulates one "click" of the rotary encoder. + */ +void +rotenc_twist( + rotenc_t * rotenc, + rotenc_twist_t direction) +{ + rotenc->direction = direction; + + // Half way to detent + avr_cycle_timer_register_usec( + rotenc->avr, + ROTENC_CLICK_DURATION_US/2, + rotenc_state_change, + rotenc); + + // Detent point, 'click' + avr_cycle_timer_register_usec( + rotenc->avr, + ROTENC_CLICK_DURATION_US, + rotenc_second_state_change, + rotenc); +} + +static const char * _rotenc_irq_names[IRQ_ROTENC_COUNT] = { + [IRQ_ROTENC_OUT_A_PIN] = ">rotenc_a_pin.out", + [IRQ_ROTENC_OUT_B_PIN] = ">rotenc_b_pin.out", + [IRQ_ROTENC_OUT_BUTTON_PIN] = ">rotenc_button_pin.out", +}; + +void +rotenc_init( + avr_t *avr, + rotenc_t * rotenc) +{ + memset(rotenc, 0, sizeof(*rotenc)); + + rotenc->irq = avr_alloc_irq( + &avr->irq_pool, + 0, + IRQ_ROTENC_COUNT, + _rotenc_irq_names); + rotenc->avr = avr; +} + diff --git a/examples/parts/rotenc.h b/examples/parts/rotenc.h new file mode 100644 index 0000000..2096a70 --- /dev/null +++ b/examples/parts/rotenc.h @@ -0,0 +1,75 @@ +/* + rotenc.h + + Copyright 2018 Doug Szumski + + This file is part of simavr. + + simavr is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + simavr is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with simavr. If not, see . + */ + +/* + This 'part' is based on a Panasonic EVEP rotary encoder with a push + button. It could easily be extended to support modelling other + rotary encoders. + */ + +#ifndef __ROTENC_H__ +#define __ROTENC_H__ + +#include "rotenc.h" + +#define ROTENC_CLICK_DURATION_US 100000UL +#define ROTENC_BUTTON_DURATION_US 100000UL +#define ROTENC_STATE_COUNT 4 + +typedef enum { + ROTENC_CW_CLICK = 0, + ROTENC_CCW_CLICK +} rotenc_twist_t; + +typedef struct rotenc_pin_state_t { + uint8_t a_pin; + uint8_t b_pin; +} rotenc_pins_t; + +enum { + IRQ_ROTENC_OUT_A_PIN = 0, + IRQ_ROTENC_OUT_B_PIN, + IRQ_ROTENC_OUT_BUTTON_PIN, + IRQ_ROTENC_COUNT +}; + +typedef struct rotenc_t { + avr_irq_t * irq; // output irq + struct avr_t * avr; + uint8_t verbose; + rotenc_twist_t direction; + int phase; // current position +} rotenc_t; + +void +rotenc_init( + struct avr_t * avr, + rotenc_t * rotenc); + +void +rotenc_twist( + rotenc_t * rotenc, + rotenc_twist_t direction); + +void +rotenc_button_press(rotenc_t * rotenc); + +#endif /* __ROTENC_H__*/ -- 2.39.5