mirror of
https://github.com/clockworkpi/DevTerm.git
synced 2025-12-13 10:48:49 +01:00
borrwoed a lot code from https://github.com/foriequal0/devterm_keyboard in order to optimize the trackball
This commit is contained in:
parent
1a1be01dbb
commit
b1370d2124
53
Code/devterm_keyboard/debouncer.h
Normal file
53
Code/devterm_keyboard/debouncer.h
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#ifndef DEBOUNCER_H
|
||||||
|
#define DEBOUNCER_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
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<typename T, T millis>
|
||||||
|
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
|
||||||
23
Code/devterm_keyboard/debouncer.ino
Normal file
23
Code/devterm_keyboard/debouncer.ino
Normal file
@ -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;
|
||||||
|
}
|
||||||
@ -2,6 +2,7 @@
|
|||||||
#define DEVTERM_H
|
#define DEVTERM_H
|
||||||
|
|
||||||
#define KEY_LATENCY 1400
|
#define KEY_LATENCY 1400
|
||||||
|
#include "state.h"
|
||||||
|
|
||||||
#include <USBComposite.h>
|
#include <USBComposite.h>
|
||||||
typedef struct key_debouncing{
|
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**
|
//**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
|
//and you will need to find a way out to flash the stm32duino bootloader once again
|
||||||
//USBSerial *_Serial;//_Serial = &Serial;
|
//USBSerial *_Serial;//_Serial = &Serial;
|
||||||
|
State *state;
|
||||||
|
uint32_t delta;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define KEYBOARD_PULL 1 // 1 for PULLUP, 0 FOR PULLDOWN
|
#define KEYBOARD_PULL 1 // 1 for PULLUP, 0 FOR PULLDOWN
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
#include "keys.h"
|
#include "keys.h"
|
||||||
#include "trackball.h"
|
#include "trackball.h"
|
||||||
#include "devterm.h"
|
#include "devterm.h"
|
||||||
|
#include "tickwaiter.h"
|
||||||
|
|
||||||
#include <USBComposite.h>
|
#include <USBComposite.h>
|
||||||
|
|
||||||
@ -17,6 +18,8 @@ const uint8_t reportDescription[] = {
|
|||||||
HID_MOUSE_REPORT_DESCRIPTOR()
|
HID_MOUSE_REPORT_DESCRIPTOR()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const uint32_t LOOP_INTERVAL_MS = 0;
|
||||||
|
static TickWaiter<LOOP_INTERVAL_MS> waiter;
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
USBComposite.setManufacturerString("ClockworkPI");
|
USBComposite.setManufacturerString("ClockworkPI");
|
||||||
@ -30,6 +33,7 @@ void setup() {
|
|||||||
|
|
||||||
dev_term.Keyboard->setAdjustForHostCapsLock(false);
|
dev_term.Keyboard->setAdjustForHostCapsLock(false);
|
||||||
|
|
||||||
|
dev_term.state = new State();
|
||||||
|
|
||||||
dev_term.Keyboard_state.layer = 0;
|
dev_term.Keyboard_state.layer = 0;
|
||||||
dev_term.Keyboard_state.prev_layer = 0;
|
dev_term.Keyboard_state.prev_layer = 0;
|
||||||
@ -55,8 +59,11 @@ void setup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
|
dev_term.delta = waiter.waitForNextTick();
|
||||||
|
dev_term.state->tick(dev_term.delta);
|
||||||
|
|
||||||
trackball_task(&dev_term);
|
trackball_task(&dev_term);
|
||||||
|
|
||||||
keys_task(&dev_term); //keys above keyboard
|
keys_task(&dev_term); //keys above keyboard
|
||||||
keyboard_task(&dev_term);
|
keyboard_task(&dev_term);
|
||||||
|
|
||||||
|
|||||||
29
Code/devterm_keyboard/glider.h
Normal file
29
Code/devterm_keyboard/glider.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#ifndef GLIDER_H
|
||||||
|
#define GLIDER_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
60
Code/devterm_keyboard/glider.ino
Normal file
60
Code/devterm_keyboard/glider.ino
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#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<int8_t>(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 };
|
||||||
|
}
|
||||||
@ -58,7 +58,7 @@ enum SKEYS {
|
|||||||
|
|
||||||
_VOLUME_M,
|
_VOLUME_M,
|
||||||
_VOLUME_P,
|
_VOLUME_P,
|
||||||
|
_TRACKBALL_BTN,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define DEF_LAYER 0x00
|
#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_RIGHT,_JOYSTICK_A,_JOYSTICK_B, \
|
||||||
_JOYSTICK_X,_JOYSTICK_Y,_LEFT_SHIFT_KEY,_FN_KEY,\
|
_JOYSTICK_X,_JOYSTICK_Y,_LEFT_SHIFT_KEY,_FN_KEY,\
|
||||||
_LEFT_CTRL_KEY,_CMD_KEY , _LEFT_ALT, \
|
_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)
|
uint8_t check_pd2(){ // if swtich 2 in back is set to on(HIGH)
|
||||||
@ -248,6 +248,7 @@ void keypad_action(DEVTERM*dv,uint8_t col,uint8_t mode) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
switch(k) {
|
switch(k) {
|
||||||
case _LEFT_SHIFT_KEY:
|
case _LEFT_SHIFT_KEY:
|
||||||
if(mode == KEY_PRESSED) {
|
if(mode == KEY_PRESSED) {
|
||||||
@ -420,6 +421,14 @@ void keypad_action(DEVTERM*dv,uint8_t col,uint8_t mode) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case _TRACKBALL_BTN:
|
||||||
|
if(mode == KEY_PRESSED) {
|
||||||
|
dv->state->pressMiddleClick();
|
||||||
|
}else {
|
||||||
|
dv->state->releaseMiddleClick();
|
||||||
|
dv->Mouse->click(MOUSE_MIDDLE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:break;
|
default:break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
# define KEY_DEBOUNCE 5
|
# define KEY_DEBOUNCE 5
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define KEYS_NUM 16
|
#define KEYS_NUM 17
|
||||||
|
|
||||||
|
|
||||||
void keys_task(DEVTERM*);
|
void keys_task(DEVTERM*);
|
||||||
|
|||||||
@ -2,12 +2,12 @@
|
|||||||
|
|
||||||
KEY_DEB keypad_debouncing;
|
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) */
|
/* keys state(1:on, 0:off) */
|
||||||
static uint16_t keys;
|
static uint32_t keys;
|
||||||
static uint16_t keys_debouncing;
|
static uint32_t keys_debouncing;
|
||||||
static uint16_t keys_prev;
|
static uint32_t keys_prev;
|
||||||
|
|
||||||
void init_keys(){
|
void init_keys(){
|
||||||
int i;
|
int i;
|
||||||
@ -19,7 +19,7 @@ void init_keys(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8_t scan_keys(){
|
uint8_t scan_keys(){
|
||||||
uint16_t data;
|
uint32_t data;
|
||||||
uint8_t s;
|
uint8_t s;
|
||||||
|
|
||||||
data = 0;
|
data = 0;
|
||||||
@ -69,9 +69,9 @@ void keys_task(DEVTERM*dv){
|
|||||||
|
|
||||||
scan_keys();
|
scan_keys();
|
||||||
|
|
||||||
uint16_t _mask =1;
|
uint32_t _mask =1;
|
||||||
uint16_t _change = 0;
|
uint32_t _change = 0;
|
||||||
uint16_t _pressed = 0;
|
uint32_t _pressed = 0;
|
||||||
|
|
||||||
_change = keys ^ keys_prev;
|
_change = keys ^ keys_prev;
|
||||||
|
|
||||||
|
|||||||
56
Code/devterm_keyboard/math.h
Normal file
56
Code/devterm_keyboard/math.h
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
#ifndef MATH_H
|
||||||
|
#define MATH_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <limits>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
uint32_t getDelta(uint32_t prev, uint32_t now);
|
||||||
|
uint32_t getDelta(uint32_t prev, uint32_t now, uint32_t max);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T sign(T value) {
|
||||||
|
if (value > 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (value < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename U>
|
||||||
|
T clamp(U value) {
|
||||||
|
if (value >= std::numeric_limits<T>().max()) {
|
||||||
|
return std::numeric_limits<T>().max();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value <= std::numeric_limits<T>().min()) {
|
||||||
|
return std::numeric_limits<T>().min();
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T min(T x, T y) {
|
||||||
|
if (x < y) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T max(T x, T y) {
|
||||||
|
if (x > y) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T hypot(T x, T y) {
|
||||||
|
return std::sqrt(x * x + y * y);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
23
Code/devterm_keyboard/math.ino
Normal file
23
Code/devterm_keyboard/math.ino
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#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<uint32_t>().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;
|
||||||
|
}
|
||||||
38
Code/devterm_keyboard/ratemeter.h
Normal file
38
Code/devterm_keyboard/ratemeter.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#ifndef RATEMETER_H
|
||||||
|
#define RATEMETER_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#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<uint16_t, CUTOFF_MS> cutoff;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
40
Code/devterm_keyboard/ratemeter.ino
Normal file
40
Code/devterm_keyboard/ratemeter.ino
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
}
|
||||||
34
Code/devterm_keyboard/state.h
Normal file
34
Code/devterm_keyboard/state.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#ifndef STATE_H
|
||||||
|
#define STATE_H
|
||||||
|
|
||||||
|
#include <bitset>
|
||||||
|
#include <array>
|
||||||
|
#include <USBComposite.h>
|
||||||
|
|
||||||
|
#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<uint16_t, MIDDLE_CLICK_TIMEOUT_MS> middleClickTimeout;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
36
Code/devterm_keyboard/state.ino
Normal file
36
Code/devterm_keyboard/state.ino
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#include <cassert>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
}
|
||||||
30
Code/devterm_keyboard/tickwaiter.h
Normal file
30
Code/devterm_keyboard/tickwaiter.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#ifndef TICKWAITER_H
|
||||||
|
#define TICKWAITER_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include "math.h"
|
||||||
|
|
||||||
|
template<uint32_t TargetInterval>
|
||||||
|
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
|
||||||
@ -12,64 +12,11 @@
|
|||||||
#define EXPONENTIAL_BASE 1.2
|
#define EXPONENTIAL_BASE 1.2
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define BTN_PIN KEY0
|
|
||||||
#define RIGHT_PIN HO3
|
#define RIGHT_PIN HO3
|
||||||
#define LEFT_PIN HO1
|
#define LEFT_PIN HO1
|
||||||
#define DOWN_PIN HO4
|
#define DOWN_PIN HO4
|
||||||
#define UP_PIN HO2
|
#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_init(DEVTERM*);
|
||||||
void trackball_task(DEVTERM*);
|
void trackball_task(DEVTERM*);
|
||||||
|
|||||||
@ -9,71 +9,103 @@
|
|||||||
|
|
||||||
#include <USBComposite.h>
|
#include <USBComposite.h>
|
||||||
|
|
||||||
|
|
||||||
#include "trackball.h"
|
#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);
|
|
||||||
|
|
||||||
TrackSpeed Normal_ts;
|
enum Axis: uint8_t {
|
||||||
TrackSpeed Detail_ts;
|
AXIS_X,
|
||||||
TrackSpeed *ts_ptr;
|
AXIS_Y,
|
||||||
|
AXIS_NUM,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int8_t distances[AXIS_NUM];
|
||||||
|
static RateMeter rateMeter[AXIS_NUM];
|
||||||
|
static Glider glider[AXIS_NUM];
|
||||||
|
|
||||||
|
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<Axis AXIS, int8_t Direction>
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void trackball_task(DEVTERM*dv) {
|
void trackball_task(DEVTERM*dv) {
|
||||||
|
int8_t x = 0, y = 0, w = 0;
|
||||||
if(dv-> Keyboard_state.fn_on > 0) {
|
noInterrupts();
|
||||||
ts_ptr = &Detail_ts;
|
rateMeter[AXIS_X].tick(dv->delta);
|
||||||
}else {
|
rateMeter[AXIS_Y].tick(dv->delta);
|
||||||
ts_ptr = &Normal_ts;
|
const auto mode = dv->state->moveTrackball();
|
||||||
}
|
switch(mode){
|
||||||
|
case TrackballMode::Mouse: {
|
||||||
x_direction.ts = ts_ptr;
|
const auto rX = glider[AXIS_X].glide(dv->delta);
|
||||||
y_direction.ts = ts_ptr;
|
const auto rY = glider[AXIS_Y].glide(dv->delta);
|
||||||
|
x = rX.value;
|
||||||
btn_read_state = digitalRead(BTN_PIN);
|
y = rY.value;
|
||||||
|
if (rX.stopped) {
|
||||||
if(btn_read_state != btn_state) {
|
glider[AXIS_Y].stop();
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
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();
|
dv->Mouse->move(x, y, -w);
|
||||||
y_move = y_direction.read_action();
|
|
||||||
if(x_move != 0 || y_move != 0) {
|
|
||||||
dv->Mouse->move(x_move, y_move, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void trackball_init(DEVTERM*){
|
void trackball_init(DEVTERM*){
|
||||||
pinMode(BTN_PIN,INPUT);
|
|
||||||
|
|
||||||
Normal_ts.bounce_interval = 30;
|
pinMode(LEFT_PIN, INPUT);
|
||||||
Normal_ts.base_move_pixels = 5;
|
pinMode(UP_PIN, INPUT);
|
||||||
Normal_ts.exponential_bound = 14;
|
pinMode(RIGHT_PIN, INPUT);
|
||||||
Normal_ts.exponential_base = 1.2;
|
pinMode(DOWN_PIN, INPUT);
|
||||||
|
|
||||||
Detail_ts.bounce_interval = 100;
|
|
||||||
Detail_ts.base_move_pixels = 3;
|
|
||||||
Detail_ts.exponential_bound = 10;
|
|
||||||
Detail_ts.exponential_base = 1.2;
|
|
||||||
|
|
||||||
|
attachInterrupt(LEFT_PIN, &interrupt<AXIS_X, -1>, ExtIntTriggerMode::CHANGE);
|
||||||
|
attachInterrupt(RIGHT_PIN, &interrupt<AXIS_X, 1>, ExtIntTriggerMode::CHANGE);
|
||||||
|
attachInterrupt(UP_PIN, &interrupt<AXIS_Y, -1>, ExtIntTriggerMode::CHANGE);
|
||||||
|
attachInterrupt(DOWN_PIN, &interrupt<AXIS_Y, 1>, ExtIntTriggerMode::CHANGE);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user