Commit 63eae2baa0caf62d233c30b70445d9387f6d2c54
authorDoug Szumski <d.s.szumski@gmail.com>
Sat, 19 May 2018 13:22:00 +0000 (14:22 +0100)
committerDoug Szumski <d.s.szumski@gmail.com>
Sat, 19 May 2018 13:30:10 +0000 (14:30 +0100)
Add a rotary encoder virtual part, based on the Panasonic
EVEP rotary encoder. This could easily be extended to virtualise
other rotary encoders.

2 files changed:
examples/parts/rotenc.c [new file with mode: 0644]
examples/parts/rotenc.h [new file with mode: 0644]

diff --git a/examples/parts/rotenc.c b/examples/parts/rotenc.c
new file mode 100644 (file)
index 0000000..4ad138e
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ rotenc.c
+
+ Copyright 2018 Doug Szumski <d.s.szumski@gmail.com>
+
+ 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#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 (file)
index 0000000..2096a70
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ rotenc.h
+
+ Copyright 2018 Doug Szumski <d.s.szumski@gmail.com>
+
+ 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 <http://www.gnu.org/licenses/>.
+ */
+
+/*
+  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__*/