From b1370d2124319e76900c6ca58bd441410b2278a0 Mon Sep 17 00:00:00 2001 From: cuu Date: Thu, 16 Dec 2021 20:41:37 +0800 Subject: [PATCH] borrwoed a lot code from https://github.com/foriequal0/devterm_keyboard in order to optimize the trackball --- Code/devterm_keyboard/debouncer.h | 53 +++++++++ Code/devterm_keyboard/debouncer.ino | 23 ++++ Code/devterm_keyboard/devterm.h | 3 + Code/devterm_keyboard/devterm_keyboard.ino | 11 +- Code/devterm_keyboard/glider.h | 29 +++++ Code/devterm_keyboard/glider.ino | 60 ++++++++++ Code/devterm_keyboard/keymaps.ino | 19 ++- Code/devterm_keyboard/keys.h | 2 +- Code/devterm_keyboard/keys.ino | 16 +-- Code/devterm_keyboard/math.h | 56 +++++++++ Code/devterm_keyboard/math.ino | 23 ++++ Code/devterm_keyboard/ratemeter.h | 38 ++++++ Code/devterm_keyboard/ratemeter.ino | 40 +++++++ Code/devterm_keyboard/state.h | 34 ++++++ Code/devterm_keyboard/state.ino | 36 ++++++ Code/devterm_keyboard/tickwaiter.h | 30 +++++ Code/devterm_keyboard/trackball.h | 53 --------- Code/devterm_keyboard/trackball.ino | 130 +++++++++++++-------- 18 files changed, 538 insertions(+), 118 deletions(-) create mode 100644 Code/devterm_keyboard/debouncer.h create mode 100644 Code/devterm_keyboard/debouncer.ino create mode 100644 Code/devterm_keyboard/glider.h create mode 100644 Code/devterm_keyboard/glider.ino create mode 100644 Code/devterm_keyboard/math.h create mode 100644 Code/devterm_keyboard/math.ino create mode 100644 Code/devterm_keyboard/ratemeter.h create mode 100644 Code/devterm_keyboard/ratemeter.ino create mode 100644 Code/devterm_keyboard/state.h create mode 100644 Code/devterm_keyboard/state.ino create mode 100644 Code/devterm_keyboard/tickwaiter.h diff --git a/Code/devterm_keyboard/debouncer.h b/Code/devterm_keyboard/debouncer.h new file mode 100644 index 0000000..dc36f17 --- /dev/null +++ b/Code/devterm_keyboard/debouncer.h @@ -0,0 +1,53 @@ +#ifndef DEBOUNCER_H +#define DEBOUNCER_H + +#include + +typedef uint8_t millis_t; + +const millis_t DEBOUNCE_MS = 5; + +/** + @brief Asymmetric debouncer +*/ +class Debouncer { + public: + Debouncer(); + void updateTime(millis_t delta); + bool sample(bool value); + private: + millis_t timeout; +}; + +template +class Timeout { + public: + Timeout() { + timeout = 0; + } + + void updateTime(millis_t delta) { + if (timeout > delta) { + timeout -= delta; + } else { + timeout = 0; + } + } + + void expire() { + timeout = 0; + } + + bool get() const { + return timeout == 0; + } + + void reset() { + timeout = millis; + } + private: + uint16_t timeout; +}; + + +#endif diff --git a/Code/devterm_keyboard/debouncer.ino b/Code/devterm_keyboard/debouncer.ino new file mode 100644 index 0000000..93cd13d --- /dev/null +++ b/Code/devterm_keyboard/debouncer.ino @@ -0,0 +1,23 @@ +#include "debouncer.h" + +Debouncer::Debouncer() + : timeout(0) +{ +} + +void Debouncer::updateTime(millis_t delta) { + if (timeout > delta) { + timeout -= delta; + } else { + timeout = 0; + } +} + +bool Debouncer::sample(bool value) { + if (value || timeout == 0) { + timeout = DEBOUNCE_MS; + return true; + } + + return false; +} diff --git a/Code/devterm_keyboard/devterm.h b/Code/devterm_keyboard/devterm.h index ce0bfdd..c9d4a46 100644 --- a/Code/devterm_keyboard/devterm.h +++ b/Code/devterm_keyboard/devterm.h @@ -2,6 +2,7 @@ #define DEVTERM_H #define KEY_LATENCY 1400 +#include "state.h" #include typedef struct key_debouncing{ @@ -32,6 +33,8 @@ class DEVTERM { //**Serial and USBCompositeSerial can not use together, otherwise the keyboard firmware uploading will be dead** //and you will need to find a way out to flash the stm32duino bootloader once again //USBSerial *_Serial;//_Serial = &Serial; + State *state; + uint32_t delta; }; #define KEYBOARD_PULL 1 // 1 for PULLUP, 0 FOR PULLDOWN diff --git a/Code/devterm_keyboard/devterm_keyboard.ino b/Code/devterm_keyboard/devterm_keyboard.ino index f5b9521..4cfe578 100644 --- a/Code/devterm_keyboard/devterm_keyboard.ino +++ b/Code/devterm_keyboard/devterm_keyboard.ino @@ -2,6 +2,7 @@ #include "keys.h" #include "trackball.h" #include "devterm.h" +#include "tickwaiter.h" #include @@ -17,6 +18,8 @@ const uint8_t reportDescription[] = { HID_MOUSE_REPORT_DESCRIPTOR() }; +static const uint32_t LOOP_INTERVAL_MS = 0; +static TickWaiter waiter; void setup() { USBComposite.setManufacturerString("ClockworkPI"); @@ -29,7 +32,8 @@ void setup() { dev_term.Consumer = new HIDConsumer(HID); dev_term.Keyboard->setAdjustForHostCapsLock(false); - + + dev_term.state = new State(); dev_term.Keyboard_state.layer = 0; dev_term.Keyboard_state.prev_layer = 0; @@ -55,8 +59,11 @@ void setup() { } void loop() { - + dev_term.delta = waiter.waitForNextTick(); + dev_term.state->tick(dev_term.delta); + trackball_task(&dev_term); + keys_task(&dev_term); //keys above keyboard keyboard_task(&dev_term); diff --git a/Code/devterm_keyboard/glider.h b/Code/devterm_keyboard/glider.h new file mode 100644 index 0000000..f4f9785 --- /dev/null +++ b/Code/devterm_keyboard/glider.h @@ -0,0 +1,29 @@ +#ifndef GLIDER_H +#define GLIDER_H + +#include + + +class Glider { +public: + Glider(); + void setDirection(int8_t); + void update(float velocity, uint16_t sustain); + void updateSpeed(float velocity); + void stop(); + + struct GlideResult { + int8_t value; + bool stopped; + }; + GlideResult glide(uint8_t delta); + +public: + int8_t direction; + float speed; + uint16_t sustain; + uint16_t release; + float error; +}; + +#endif diff --git a/Code/devterm_keyboard/glider.ino b/Code/devterm_keyboard/glider.ino new file mode 100644 index 0000000..8ecb221 --- /dev/null +++ b/Code/devterm_keyboard/glider.ino @@ -0,0 +1,60 @@ +#include + +#include "glider.h" +#include "math.h" + +Glider::Glider() +: speed(0), + sustain(0), + release(0), + error(0) +{} + +void Glider::setDirection(int8_t direction) { + if (this->direction != direction) { + stop(); + } + this->direction = direction; +} + +void Glider::update(float speed, uint16_t sustain) { + this->speed = speed; + this->sustain = sustain; + this->release = sustain; +} + +void Glider::updateSpeed(float speed) { + this->speed = speed; +} + +void Glider::stop() { + this->speed = 0; + this->sustain = 0; + this->release = 0; + this->error = 0; +} + +Glider::GlideResult Glider::glide(millis_t delta) { + const auto alreadyStopped = speed == 0; + + error += speed * delta; + int8_t distance = 0; + if (error > 0) { + distance = clamp(std::ceil(error)); + } + error -= distance; + + if (sustain > 0) { + const auto sustained = min(sustain, (uint16_t)delta); + sustain -= sustained; + } else if (release > 0) { + const auto released = min(release, (uint16_t)delta); + speed = speed * (release - released) / release; + release -= released; + } else { + speed = 0; + } + + const int8_t result = direction * distance; + return GlideResult { result, !alreadyStopped && speed == 0 }; +} diff --git a/Code/devterm_keyboard/keymaps.ino b/Code/devterm_keyboard/keymaps.ino index 8b83724..274dab0 100644 --- a/Code/devterm_keyboard/keymaps.ino +++ b/Code/devterm_keyboard/keymaps.ino @@ -58,7 +58,7 @@ enum SKEYS { _VOLUME_M, _VOLUME_P, - + _TRACKBALL_BTN, }; #define DEF_LAYER 0x00 @@ -105,7 +105,7 @@ const uint16_t keys_maps[KEYS_NUM] = {_JOYSTICK_UP,_JOYSTICK_DOWN, _JOYSTICK_LEF _JOYSTICK_RIGHT,_JOYSTICK_A,_JOYSTICK_B, \ _JOYSTICK_X,_JOYSTICK_Y,_LEFT_SHIFT_KEY,_FN_KEY,\ _LEFT_CTRL_KEY,_CMD_KEY , _LEFT_ALT, \ - _MOUSE_LEFT,_MOUSE_MID,_MOUSE_RIGHT}; + _MOUSE_LEFT,_MOUSE_MID,_MOUSE_RIGHT,_TRACKBALL_BTN}; uint8_t check_pd2(){ // if swtich 2 in back is set to on(HIGH) @@ -241,13 +241,14 @@ void keyboard_action(DEVTERM*dv,uint8_t row,uint8_t col,uint8_t mode) { void keypad_action(DEVTERM*dv,uint8_t col,uint8_t mode) { uint16_t k; - - k = keys_maps[col]; + k = keys_maps[col]; + if(k == EMP){ return; } + switch(k) { case _LEFT_SHIFT_KEY: if(mode == KEY_PRESSED) { @@ -419,7 +420,15 @@ void keypad_action(DEVTERM*dv,uint8_t col,uint8_t mode) { dv->Keyboard->release(k); } break; - + + case _TRACKBALL_BTN: + if(mode == KEY_PRESSED) { + dv->state->pressMiddleClick(); + }else { + dv->state->releaseMiddleClick(); + dv->Mouse->click(MOUSE_MIDDLE); + } + break; default:break; } diff --git a/Code/devterm_keyboard/keys.h b/Code/devterm_keyboard/keys.h index cfc7abe..93b537b 100644 --- a/Code/devterm_keyboard/keys.h +++ b/Code/devterm_keyboard/keys.h @@ -16,7 +16,7 @@ # define KEY_DEBOUNCE 5 #endif -#define KEYS_NUM 16 +#define KEYS_NUM 17 void keys_task(DEVTERM*); diff --git a/Code/devterm_keyboard/keys.ino b/Code/devterm_keyboard/keys.ino index af0b88a..a1a80f9 100644 --- a/Code/devterm_keyboard/keys.ino +++ b/Code/devterm_keyboard/keys.ino @@ -2,12 +2,12 @@ KEY_DEB keypad_debouncing; -uint8_t keys_io[ KEYS_NUM ]= {KEY1,KEY2,KEY3,KEY4,KEY5,KEY6,KEY7,KEY8,KEY9,KEY10,KEY11,KEY12,KEY13,KEY14,KEY15,KEY16}; +uint8_t keys_io[ KEYS_NUM ]= {KEY1,KEY2,KEY3,KEY4,KEY5,KEY6,KEY7,KEY8,KEY9,KEY10,KEY11,KEY12,KEY13,KEY14,KEY15,KEY16,KEY0}; /* keys state(1:on, 0:off) */ -static uint16_t keys; -static uint16_t keys_debouncing; -static uint16_t keys_prev; +static uint32_t keys; +static uint32_t keys_debouncing; +static uint32_t keys_prev; void init_keys(){ int i; @@ -19,7 +19,7 @@ void init_keys(){ } uint8_t scan_keys(){ - uint16_t data; + uint32_t data; uint8_t s; data = 0; @@ -69,9 +69,9 @@ void keys_task(DEVTERM*dv){ scan_keys(); - uint16_t _mask =1; - uint16_t _change = 0; - uint16_t _pressed = 0; + uint32_t _mask =1; + uint32_t _change = 0; + uint32_t _pressed = 0; _change = keys ^ keys_prev; diff --git a/Code/devterm_keyboard/math.h b/Code/devterm_keyboard/math.h new file mode 100644 index 0000000..8e6ccdf --- /dev/null +++ b/Code/devterm_keyboard/math.h @@ -0,0 +1,56 @@ +#ifndef MATH_H +#define MATH_H + +#include +#include +#include + +uint32_t getDelta(uint32_t prev, uint32_t now); +uint32_t getDelta(uint32_t prev, uint32_t now, uint32_t max); + +template +T sign(T value) { + if (value > 0) { + return 1; + } + if (value < 0) { + return -1; + } + return 0; +} + +template +T clamp(U value) { + if (value >= std::numeric_limits().max()) { + return std::numeric_limits().max(); + } + + if (value <= std::numeric_limits().min()) { + return std::numeric_limits().min(); + } + + return value; +} + +template +T min(T x, T y) { + if (x < y) { + return x; + } + return y; +} + +template +T max(T x, T y) { + if (x > y) { + return x; + } + return y; +} + +template +T hypot(T x, T y) { + return std::sqrt(x * x + y * y); +} + +#endif diff --git a/Code/devterm_keyboard/math.ino b/Code/devterm_keyboard/math.ino new file mode 100644 index 0000000..fe6f058 --- /dev/null +++ b/Code/devterm_keyboard/math.ino @@ -0,0 +1,23 @@ +#include + +#include "math.h" + +uint32_t getDelta(uint32_t prev, uint32_t now) { + uint32_t delta; + if (now >= prev) { + delta = now - prev; + } else { + delta = std::numeric_limits().max() - prev - now + 1; + } + return delta; +} + +uint32_t getDelta(uint32_t prev, uint32_t now, uint32_t max) { + const auto delta = getDelta(prev, now); + + if (delta < max) { + return delta; + } + + return max; +} diff --git a/Code/devterm_keyboard/ratemeter.h b/Code/devterm_keyboard/ratemeter.h new file mode 100644 index 0000000..451b189 --- /dev/null +++ b/Code/devterm_keyboard/ratemeter.h @@ -0,0 +1,38 @@ +#ifndef RATEMETER_H +#define RATEMETER_H + +#include + +#include "debouncer.h" + +class RateMeter { +public: + RateMeter(); + void onInterrupt(); + void tick(millis_t delta); + void expire(); + + uint16_t delta() const; + // Hall sensor edges per seconds. + // stopped: 0 + // really slow => ~3 + // medium => ~30 + // fast => < 300 + // max => 1000 + float rate() const; + +private: + uint32_t lastTime; + + // really Range, emperically: + // fast => < 5_000 us, + // medium => 20_000 - 40_000 us + // really slow => 250_000 us + uint32_t averageDelta; + + static const uint16_t CUTOFF_MS = 1000; + // Cut off after some seconds to prevent multiple timestamp overflow (~70 mins) + Timeout cutoff; +}; + +#endif diff --git a/Code/devterm_keyboard/ratemeter.ino b/Code/devterm_keyboard/ratemeter.ino new file mode 100644 index 0000000..f497fb2 --- /dev/null +++ b/Code/devterm_keyboard/ratemeter.ino @@ -0,0 +1,40 @@ +#include +#include + +#include "ratemeter.h" +#include "math.h" + +RateMeter::RateMeter() +: lastTime(0) +{} + +void RateMeter::onInterrupt() { + const auto now = millis(); + if (cutoff.get()) { + averageDelta = CUTOFF_MS; + } else { + const auto delta = getDelta(lastTime, now, CUTOFF_MS); + averageDelta = (averageDelta + delta) / 2; + } + lastTime = now; + cutoff.reset(); +} + +void RateMeter::tick(millis_t delta) { + cutoff.updateTime(delta); +} + +uint16_t RateMeter::delta() const { + return averageDelta; +} + +float RateMeter::rate() const { + if (cutoff.get()) { + return 0.0f; + } else if (averageDelta == 0) { + // to ensure range 0 ~ 1000.0 + return 1000.0f; + } else { + return 1000.0f / (float)averageDelta; + } +} diff --git a/Code/devterm_keyboard/state.h b/Code/devterm_keyboard/state.h new file mode 100644 index 0000000..5e26536 --- /dev/null +++ b/Code/devterm_keyboard/state.h @@ -0,0 +1,34 @@ +#ifndef STATE_H +#define STATE_H + +#include +#include +#include + +#include "debouncer.h" + +enum class TrackballMode : uint8_t { + Wheel, + Mouse, +}; + +class State +{ + public: + static const uint16_t MIDDLE_CLICK_TIMEOUT_MS = 0; + + State(); + + void tick(uint8_t delta); + + bool fn; + + void pressMiddleClick(); + bool releaseMiddleClick(); + TrackballMode moveTrackball(); + private: + bool middleClick; + Timeout middleClickTimeout; +}; + +#endif diff --git a/Code/devterm_keyboard/state.ino b/Code/devterm_keyboard/state.ino new file mode 100644 index 0000000..e2b96c9 --- /dev/null +++ b/Code/devterm_keyboard/state.ino @@ -0,0 +1,36 @@ +#include +#include +#include + +#include "state.h" + +State::State() + : fn(false), + middleClick(false) +{ +} + +void State::tick(millis_t delta) +{ + middleClickTimeout.updateTime(delta); +} + +void State::pressMiddleClick() { + middleClick = true; + middleClickTimeout.reset(); +} + +bool State::releaseMiddleClick() { + middleClick = false; + const auto timeout = middleClickTimeout.get(); + return !timeout; +} + +TrackballMode State::moveTrackball() { + middleClickTimeout.expire(); + if (middleClick) { + return TrackballMode::Wheel; + } else { + return TrackballMode::Mouse; + } +} diff --git a/Code/devterm_keyboard/tickwaiter.h b/Code/devterm_keyboard/tickwaiter.h new file mode 100644 index 0000000..9698024 --- /dev/null +++ b/Code/devterm_keyboard/tickwaiter.h @@ -0,0 +1,30 @@ +#ifndef TICKWAITER_H +#define TICKWAITER_H + +#include +#include "math.h" + +template +class TickWaiter { + public: + uint8_t waitForNextTick() { + const auto last = this->last; + const auto now = millis(); + this->last = now; + + const auto delta = getDelta(last, now, 255); + + if (delta >= TargetInterval) { + return delta; + } + + delay(TargetInterval - delta); + + const auto now2 = millis(); + return getDelta(last, now2, 255); + } + private: + uint32_t last = 0; +}; + +#endif diff --git a/Code/devterm_keyboard/trackball.h b/Code/devterm_keyboard/trackball.h index 7438ea1..cc75212 100644 --- a/Code/devterm_keyboard/trackball.h +++ b/Code/devterm_keyboard/trackball.h @@ -12,64 +12,11 @@ #define EXPONENTIAL_BASE 1.2 */ -#define BTN_PIN KEY0 #define RIGHT_PIN HO3 #define LEFT_PIN HO1 #define DOWN_PIN HO4 #define UP_PIN HO2 -typedef struct _track_speed { - uint8_t bounce_interval; - uint8_t base_move_pixels; - uint8_t exponential_bound; - double exponential_base; - -}TrackSpeed; - -class Direction { - public: - Direction(int pin1, int pin2) { - this->pins[0] = pin1; - this->pins[1] = pin2; - pinMode(this->pins[0], INPUT); - pinMode(this->pins[1], INPUT); - }; - - int read_action() { - for(int i = 0; i < 2; ++i) { - this->current_actions[i] = digitalRead(this->pins[i]); - this->current_action_times[i] = millis(); - if(this->current_actions[i] != this->last_actions[i]) { - this->last_actions[i] = this->current_actions[i]; - exponential = ( ts->exponential_bound - (this->current_action_times[i] - this->last_action_times[i])); - exponential = (exponential > 0) ? exponential : 1; - move_multiply = ts->exponential_base; - for(int i = 0; i < exponential; ++i) { - move_multiply *= ts->exponential_base; - } - this->last_action_times[i] = this->current_action_times[i]; - if(i == 0) { - return (-1) * ts->base_move_pixels * move_multiply; - } else { - return ts->base_move_pixels * move_multiply; - } - } - } - return 0; - }; - - TrackSpeed *ts; - - private: - int pins[2]; - int current_actions[2]; - int last_actions[2]; - int exponential; - double move_multiply; - unsigned long current_action_times[2]; - unsigned long last_action_times[2]; - -}; void trackball_init(DEVTERM*); void trackball_task(DEVTERM*); diff --git a/Code/devterm_keyboard/trackball.ino b/Code/devterm_keyboard/trackball.ino index 02373f7..f947040 100644 --- a/Code/devterm_keyboard/trackball.ino +++ b/Code/devterm_keyboard/trackball.ino @@ -9,71 +9,103 @@ #include + #include "trackball.h" +#include "ratemeter.h" +#include "glider.h" +#include "math.h" -int btn_state; -int btn_read_state; -unsigned long btn_current_action_time; -unsigned long btn_last_action_time; -// mouse move -int x_move, y_move; -Direction x_direction(LEFT_PIN, RIGHT_PIN); -Direction y_direction(UP_PIN, DOWN_PIN); + +enum Axis: uint8_t { + AXIS_X, + AXIS_Y, + AXIS_NUM, +}; -TrackSpeed Normal_ts; -TrackSpeed Detail_ts; -TrackSpeed *ts_ptr; +static int8_t distances[AXIS_NUM]; +static RateMeter rateMeter[AXIS_NUM]; +static Glider glider[AXIS_NUM]; -void trackball_task(DEVTERM*dv) { - - if(dv-> Keyboard_state.fn_on > 0) { - ts_ptr = &Detail_ts; - }else { - ts_ptr = &Normal_ts; +static const int8_t WHEEL_DENOM = 2; +static int8_t wheelBuffer; + +static float rateToVelocityCurve(float input) { + return std::pow(std::abs(input) / 50, 1.5); +} + +template +static void interrupt() { + distances[AXIS] += Direction; + rateMeter[AXIS].onInterrupt(); + glider[AXIS].setDirection(Direction); + + const auto rx = rateMeter[AXIS_X].rate(); + const auto ry = rateMeter[AXIS_Y].rate(); + + const auto rate = std::sqrt(rx * rx + ry * ry); + const auto ratio = rateToVelocityCurve(rate) / rate; + + const auto vx = rx * ratio; + const auto vy = ry * ratio; + + if (AXIS == AXIS_X) { + glider[AXIS_X].update(vx, rateMeter[AXIS_X].delta()); + glider[AXIS_Y].updateSpeed(vy); + } else { + glider[AXIS_X].updateSpeed(vx); + glider[AXIS_Y].update(vy, rateMeter[AXIS_Y].delta()); } - - x_direction.ts = ts_ptr; - y_direction.ts = ts_ptr; - - btn_read_state = digitalRead(BTN_PIN); - - if(btn_read_state != btn_state) { - btn_current_action_time = millis(); - if(btn_current_action_time - btn_last_action_time > ts_ptr->bounce_interval ) { - btn_state = btn_read_state; - btn_last_action_time = btn_current_action_time; - - if(btn_state == HIGH) { - dv->Mouse->release(); - } else { - dv->Mouse->press(); +} + +void trackball_task(DEVTERM*dv) { + int8_t x = 0, y = 0, w = 0; + noInterrupts(); + rateMeter[AXIS_X].tick(dv->delta); + rateMeter[AXIS_Y].tick(dv->delta); + const auto mode = dv->state->moveTrackball(); + switch(mode){ + case TrackballMode::Mouse: { + const auto rX = glider[AXIS_X].glide(dv->delta); + const auto rY = glider[AXIS_Y].glide(dv->delta); + x = rX.value; + y = rY.value; + if (rX.stopped) { + glider[AXIS_Y].stop(); } + if (rY.stopped) { + glider[AXIS_Y].stop(); + } + + break; + } + case TrackballMode::Wheel: { + wheelBuffer += distances[AXIS_Y]; + w = wheelBuffer / WHEEL_DENOM; + wheelBuffer -= w * WHEEL_DENOM; + break; } } + distances[AXIS_X] = 0; + distances[AXIS_Y] = 0; + interrupts(); - x_move = x_direction.read_action(); - y_move = y_direction.read_action(); - if(x_move != 0 || y_move != 0) { - dv->Mouse->move(x_move, y_move, 0); - } - + dv->Mouse->move(x, y, -w); + } void trackball_init(DEVTERM*){ - pinMode(BTN_PIN,INPUT); - Normal_ts.bounce_interval = 30; - Normal_ts.base_move_pixels = 5; - Normal_ts.exponential_bound = 14; - Normal_ts.exponential_base = 1.2; - - Detail_ts.bounce_interval = 100; - Detail_ts.base_move_pixels = 3; - Detail_ts.exponential_bound = 10; - Detail_ts.exponential_base = 1.2; + pinMode(LEFT_PIN, INPUT); + pinMode(UP_PIN, INPUT); + pinMode(RIGHT_PIN, INPUT); + pinMode(DOWN_PIN, INPUT); + attachInterrupt(LEFT_PIN, &interrupt, ExtIntTriggerMode::CHANGE); + attachInterrupt(RIGHT_PIN, &interrupt, ExtIntTriggerMode::CHANGE); + attachInterrupt(UP_PIN, &interrupt, ExtIntTriggerMode::CHANGE); + attachInterrupt(DOWN_PIN, &interrupt, ExtIntTriggerMode::CHANGE); }