add devterm_keyboard_mini init code

This commit is contained in:
cuu
2022-10-07 19:40:58 +08:00
parent 24d160c64b
commit 54a9333747
37 changed files with 15161 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
Arduino 1.8.13
http://dan.drown.org/stm32duino/package_STM32duino_index.json
STM32F1xx/GD32F1xx boards
by stm32duino version 2021.2.22
GENERIC STM32F103R series
gd32f1_generic_boot20_pc13.bin
generic_boot20_pc13.bin

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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

View 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;
}

View File

@@ -0,0 +1,42 @@
#ifndef DEVTERM_H
#define DEVTERM_H
#define KEY_LATENCY 1400
#include "state.h"
#include <USBComposite.h>
typedef struct key_debouncing{
bool deing;//debouncing
uint16_t de_time;
}KEY_DEB;
typedef struct keyboard_state{
uint8_t layer;
uint8_t prev_layer;
uint8_t fn_on;
uint8_t shift;
}KEYBOARD_STATE;
class DEVTERM {
public:
HIDKeyboard *Keyboard;
HIDMouse *Mouse;
HIDJoystick *Joystick;
HIDConsumer *Consumer;
KEYBOARD_STATE Keyboard_state;
USBCompositeSerial *_Serial;
//if not to use USBCompositeSerial,then use default Serial
//**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
#endif

View File

@@ -0,0 +1,71 @@
#include "keyboard.h"
#include "keys.h"
#include "trackball.h"
#include "devterm.h"
#include "tickwaiter.h"
#include <USBComposite.h>
#define SER_NUM_STR "20210531"
USBHID HID;
DEVTERM dev_term;
const uint8_t reportDescription[] = {
HID_CONSUMER_REPORT_DESCRIPTOR(),
HID_KEYBOARD_REPORT_DESCRIPTOR(),
HID_JOYSTICK_REPORT_DESCRIPTOR(),
HID_MOUSE_REPORT_DESCRIPTOR()
};
static const uint32_t LOOP_INTERVAL_MS = 0;
static TickWaiter<LOOP_INTERVAL_MS> waiter;
void setup() {
USBComposite.setManufacturerString("ClockworkPI");
USBComposite.setProductString("DevTerm");
USBComposite.setSerialString(SER_NUM_STR);
dev_term.Keyboard = new HIDKeyboard(HID);
dev_term.Joystick = new HIDJoystick(HID);
dev_term.Mouse = new HIDMouse(HID);
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;
dev_term.Keyboard_state.fn_on = 0;
dev_term.Keyboard_state.shift = 0;
dev_term._Serial = new USBCompositeSerial;
HID.begin(*dev_term._Serial,reportDescription, sizeof(reportDescription));
while(!USBComposite);//wait until usb port been plugged in to PC
keyboard_init(&dev_term);
keys_init(&dev_term);
trackball_init(&dev_term);
dev_term._Serial->println("setup done");
pinMode(PD2,INPUT);// switch 2 in back
delay(1000);
}
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);
}

View 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

View 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 };
}

View File

@@ -0,0 +1,19 @@
/*
*
*/
#ifndef HELPER_H
#define HELPER_H
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
uint8_t read_io(uint8_t io);
#endif

View File

@@ -0,0 +1,10 @@
#include "helper.h"
uint8_t read_io(uint8_t io) {
if(digitalRead(io) == LOW ){
return 0;
}else {
return 1;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,54 @@
#ifndef KEYBOARD_H
#define KEYBOARD_H
/*
* clockworkpi devterm keyboard test2
* able to correct scan the 8x8 keypads re-action
*/
#include "devterm.h"
#include "keys_io_map.h"
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#define MATRIX_ROWS 8
#define MATRIX_COLS 8
#define MATRIX_KEYS 64 // 8*8
#ifndef DEBOUNCE
# define DEBOUNCE 20
#endif
void init_rows();
void init_cols();
uint8_t read_io(uint8_t io);
void matrix_init();
uint8_t matrix_scan(void);
bool matrix_is_on(uint8_t row, uint8_t col);
uint8_t matrix_get_row(uint8_t row) ;
//void matrix_print(void);
void keyboard_task(DEVTERM*);
void keyboard_init(DEVTERM*);
#define KEY_PRESSED 1
#define KEY_RELEASED 0
#define KEY_PRNT_SCRN 0xCE //Print screen - 0x88 == usb hut1_12v2.pdf keyboard code
#define KEY_PAUSE 0xd0 // - 0x88 == usb hut1_12v2.pdf keyboard code
#define KEY_VOLUME_UP 0x108 // - 0x88 == usb hut1_12v2.pdf keyboard code
#define KEY_VOLUME_DOWN 0x109 // - 0x88 == usb hut1_12v2.pdf keyboard code
#endif

View File

@@ -0,0 +1,203 @@
#include "keyboard.h"
#include "helper.h"
KEY_DEB keyboard_debouncing;
uint8_t matrix_rows[ MATRIX_ROWS ]= {ROW1,ROW2,ROW3,ROW4,ROW5,ROW6,ROW7,ROW8};
uint8_t matrix_cols[ MATRIX_COLS ] = {COL1,COL2,COL3,COL4,COL5,COL6,COL7,COL8};
/* matrix state(1:on, 0:off) */
static uint8_t matrix[MATRIX_ROWS];
static uint8_t matrix_debouncing[MATRIX_COLS];
static uint8_t matrix_prev[MATRIX_ROWS];
uint8_t read_kbd_io(uint8_t io) {
#if defined KEYBOARD_PULL && KEYBOARD_PULL == 0
if(digitalRead(io) == LOW ){
return 0;
}else {
return 1;
}
#elif defined KEYBOARD_PULL && KEYBOARD_PULL == 1
if(digitalRead(io) == LOW ){
return 1;
}else {
return 0;
}
#endif
}
void init_rows(){
int i;
for(i=0;i<8;i++) {
#if defined KEYBOARD_PULL && KEYBOARD_PULL == 0
pinMode(matrix_rows[i],OUTPUT);
digitalWrite(matrix_rows[i],LOW);
pinMode(matrix_rows[i],INPUT_PULLDOWN);
#elif defined KEYBOARD_PULL && KEYBOARD_PULL == 1
pinMode(matrix_rows[i],INPUT_PULLUP);
#endif
}
}
void init_cols() {
int i;
for(i=0;i<8;i++){
pinMode(matrix_cols[i],OUTPUT);
digitalWrite(matrix_cols[i],LOW);
}
}
void matrix_init() {
init_cols();
init_rows();
for (uint8_t i=0; i < MATRIX_ROWS; i++) {
matrix[i] = 0;
matrix_debouncing[i] = 0;
matrix_prev[i] = 0;
}
delay(500);
}
uint8_t matrix_scan(void) {
uint8_t data;
for(int col = 0; col < MATRIX_COLS;col++){
data = 0;
#if defined KEYBOARD_PULL && KEYBOARD_PULL == 1
digitalWrite(matrix_cols[col],LOW);
#elif defined KEYBOARD_PULL && KEYBOARD_PULL == 0
digitalWrite(matrix_cols[col],HIGH);
#endif
delayMicroseconds(700);
data =(
( read_kbd_io(matrix_rows[0]) << 0 ) |
( read_kbd_io(matrix_rows[1]) << 1 ) |
( read_kbd_io(matrix_rows[2]) << 2 ) |
( read_kbd_io(matrix_rows[3]) << 3 ) |
( read_kbd_io(matrix_rows[4]) << 4 ) |
( read_kbd_io(matrix_rows[5]) << 5 ) |
( read_kbd_io(matrix_rows[6]) << 6 ) |
( read_kbd_io(matrix_rows[7]) << 7 )
);
#if defined KEYBOARD_PULL && KEYBOARD_PULL == 1
digitalWrite(matrix_cols[col],HIGH);
#elif defined KEYBOARD_PULL && KEYBOARD_PULL == 0
digitalWrite(matrix_cols[col],LOW);
#endif
if (matrix_debouncing[col] != data) {
matrix_debouncing[col] = data;
keyboard_debouncing.deing = true;
keyboard_debouncing.de_time = millis();
}
}
if (keyboard_debouncing.deing == true && ( (millis() - keyboard_debouncing.de_time) > DEBOUNCE )) {
for (int row = 0; row < MATRIX_ROWS; row++) {
matrix[row] = 0;
for (int col = 0; col < MATRIX_COLS; col++) {
matrix[row] |= ((matrix_debouncing[col] & (1 << row) ? 1 : 0) << col);
}
}
keyboard_debouncing.deing = false;
}else{
delay(1);
}
return 1;
}
bool matrix_is_on(uint8_t row, uint8_t col) {
return (matrix[row] & (1<<col));
}
uint8_t matrix_get_row(uint8_t row) {
return matrix[row];
}
void matrix_press(DEVTERM*dv,uint8_t row,uint8_t col) {
char buff[128];
if(matrix_is_on(row,col) == true ){
sprintf(buff,"%d %d M%d pressed\n",row,col,(row+1)*10+col+1);
//dv->_Serial->print(buff);
keyboard_action(dv,row,col,KEY_PRESSED);
}
}
void matrix_release(DEVTERM*dv,uint8_t row,uint8_t col) {
char buff[128];
if(matrix_is_on(row,col) == false ){
sprintf(buff,"%d %d M%d released\n",row,col,(row+1)*10+col+1);
//dv->_Serial->print(buff);
keyboard_action(dv,row,col,KEY_RELEASED);
}
}
void keyboard_task(DEVTERM*dv)
{
uint8_t matrix_row = 0;
uint8_t matrix_change = 0;
uint8_t pressed = 0;
matrix_scan();
for (uint8_t r = 0; r < MATRIX_ROWS; r++) {
matrix_row = matrix_get_row(r);
matrix_change = matrix_row ^ matrix_prev[r];
if (matrix_change) {
uint8_t col_mask = 1;
for (uint8_t c = 0; c < MATRIX_COLS; c++, col_mask <<= 1) {
if (matrix_change & col_mask) {
pressed = (matrix_row & col_mask);
if(pressed != 0) {
matrix_press(dv,r,c);
}else {
matrix_release(dv,r,c);
}
matrix_prev[r] ^= col_mask;
}
}
}
}
}
void keyboard_init(DEVTERM*){
matrix_init();
keyboard_debouncing.deing=false;
keyboard_debouncing.de_time = 0;
}

View File

@@ -0,0 +1,443 @@
#include "devterm.h"
#include "keyboard.h"
#include "keys.h"
#define EMP 0XFFFF
/*
B1 joystick up
B2 joystick down
B3 joystick left
B4 joystick right
B5 joystick A
B6 joystick B
B7 joystick X
B8 joystick Y
B9 left shift
B10 Fn
B11 left Ctrl
B12 Cmd
B13 left Alt
B14 mouse left
B15 mouse mid
B16 mouse right
*/
#define _PRINT_KEY KEY_PRNT_SCRN
#define _PAUSE_KEY KEY_PAUSE
#define _LEFT_SHIFT_KEY KEY_LEFT_SHIFT
#define _LEFT_CTRL_KEY KEY_LEFT_CTRL
#define _CMD_KEY KEY_RIGHT_GUI
#define _LEFT_ALT KEY_LEFT_ALT
#define _FN_KEY_UP_ARROW KEY_PAGE_UP
#define _FN_KEY_DOWN_ARROW KEY_PAGE_DOWN
#define _FN_KEY_LEFT_ARROW KEY_HOME
#define _FN_KEY_RIGHT_ARROW KEY_END
enum SKEYS {
_SELECT_KEY =0xe8, //Joystick.button(n)
_START_KEY, //Joystick.button(n)
_JOYSTICK_UP, //B1 //Joystick.Y()
_JOYSTICK_DOWN, //Joystick.Y()
_JOYSTICK_LEFT, //Joystick.X()
_JOYSTICK_RIGHT, //Joystick.X()
_JOYSTICK_A, //Joystick.button(1)
_JOYSTICK_B, //Joystick.button(2)
_JOYSTICK_X, //Joystick.button(3)
_JOYSTICK_Y, //Joystick.button(4)
_FN_KEY,
_MOUSE_LEFT, // Mouse.press(1)
_MOUSE_MID, // Mouse.press(2)
_MOUSE_RIGHT, // Mouse.press(3)
_FN_BRIGHTNESS_UP, //USB Consumer brightness up https://github.com/torvalds/linux/blob/7fe10096c1508c7f033d34d0741809f8eecc1ed4/drivers/hid/hid-input.c#L903
_FN_BRIGHTNESS_DOWN, //USB Consumer brightness down
_VOLUME_M,
_VOLUME_P,
_VOLUME_MUTE, //https://github.com/torvalds/linux/blob/7fe10096c1508c7f033d34d0741809f8eecc1ed4/drivers/hid/hid-input.c#L956
_TRACKBALL_BTN,
_FN_LOCK_KEYBOARD,
_FN_LIGHT_KEYBOARD,
};
#define DEF_LAYER 0x00
#define FN_LAYER 0x01
/*
* keyboard_maps
* M11 - M18
* M21 - M28
* M31 - M38
* M41 - M48
* M51 - M58
* M61 - M68
* M71 - M78
* M81 - M88
*/
const uint16_t keyboard_maps[][MATRIX_KEYS] = {
[DEF_LAYER] = { _SELECT_KEY,_START_KEY,_VOLUME_M,'`','[',']','-','=', \
'1','2','3','4','5','6','7','8',\
'9','0',KEY_ESC,KEY_TAB,EMP,EMP,EMP,EMP, \
'q','w','e','r','t','y','u','i', \
'o','p','a','s','d','f','g','h',\
'j','k','l','z','x','c','v','b', \
'n','m',',','.','/','\\',';','\'', \
KEY_RETURN,KEY_BACKSPACE,_FN_KEY,_FN_KEY,' ',EMP,EMP,EMP},
[FN_LAYER] = { _PRINT_KEY,_PAUSE_KEY,_VOLUME_MUTE,'`','[',']',KEY_F11,KEY_F12, \
KEY_F1,KEY_F2,KEY_F3,KEY_F4,KEY_F5,KEY_F6,KEY_F7,KEY_F8,\
KEY_F9,KEY_F10,_FN_LOCK_KEYBOARD,KEY_CAPS_LOCK,EMP,EMP,EMP,EMP, \
'q','w','e','r','t','y',KEY_PAGE_UP,KEY_INSERT, \
'o','p','a','s','d','f','g',KEY_HOME,\
KEY_END,KEY_PAGE_DOWN,'l','z','x','c','v','b', \
'n','m',_FN_BRIGHTNESS_DOWN,_FN_BRIGHTNESS_UP,'/','\\',';','\'', \
KEY_RETURN,KEY_DELETE,_FN_KEY,_FN_KEY,_FN_LIGHT_KEYBOARD,EMP,EMP,EMP}
};
static uint8_t fn_actions[MATRIX_KEYS]= {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
const uint16_t keys_maps[KEYS_NUM] = {_JOYSTICK_UP,_JOYSTICK_DOWN, _JOYSTICK_LEFT, \
_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,_TRACKBALL_BTN};
uint8_t check_pd2(){ // if swtich 2 in back is set to on(HIGH)
return digitalRead(PD2);
}
void dt_kbd_set_layer(DEVTERM*dv,uint8_t new_layer) {
if( dv->Keyboard_state.layer != new_layer) {
dv->Keyboard_state.prev_layer = dv->Keyboard_state.layer;
dv->Keyboard_state.layer = new_layer;
}
}
void dt_kbd_restore_layer(DEVTERM*dv) {
dv->Keyboard_state.layer = dv->Keyboard_state.prev_layer;
}
void keyboard_action(DEVTERM*dv,uint8_t row,uint8_t col,uint8_t mode) {
uint16_t k;
uint8_t addr;
addr = row*MATRIX_COLS+col;
if(dv->Keyboard_state.fn_on > 0){
k = keyboard_maps[dv->Keyboard_state.fn_on][addr];
fn_actions[addr] = 1;
}else {
k = keyboard_maps[dv->Keyboard_state.layer][addr];
}
if(k == EMP){
return;
}
switch(k) {
case KEY_RIGHT_SHIFT:{
if(mode == KEY_PRESSED) {
dv->Keyboard->press(k);
}else {
dv->Keyboard->release(k);
}
}break;
case KEY_CAPS_LOCK:
if(mode == KEY_PRESSED) {
dv->Keyboard->press(k);
dv->Keyboard->setAdjustForHostCapsLock(true);
}else if(mode == KEY_RELEASED) {
dv->Keyboard->setAdjustForHostCapsLock(false);
dv->Keyboard->release(k);
}
break;
case _SELECT_KEY:
if(check_pd2() == HIGH) {
k = ' ';
if(mode == KEY_PRESSED) {
dv->Keyboard->press(k);
}else if(mode == KEY_RELEASED) {
dv->Keyboard->release(k);
}
}else {
dv->Joystick->button(9,mode);
}
break;
case _START_KEY:
if(check_pd2() == HIGH) {
k = KEY_RETURN;
if(mode == KEY_PRESSED) {
dv->Keyboard->press(k);
}else if(mode == KEY_RELEASED) {
dv->Keyboard->release(k);
}
}else {
dv->Joystick->button(10,mode);
}
break;
case _FN_BRIGHTNESS_UP:
if(mode == KEY_PRESSED) {
dv->Consumer->press(HIDConsumer::BRIGHTNESS_UP);
}else {
dv->Consumer->release();
}
break;
case _FN_BRIGHTNESS_DOWN:
if(mode == KEY_PRESSED) {
dv->Consumer->press(HIDConsumer::BRIGHTNESS_DOWN);
}else {
dv->Consumer->release();
}
break;
case _VOLUME_P:{
if(mode == KEY_PRESSED) {
dv->Consumer->press(HIDConsumer::VOLUME_UP);
}else {
dv->Consumer->release();
}
}break;
case _VOLUME_M:{
if(mode == KEY_PRESSED) {
dv->Consumer->press(HIDConsumer::VOLUME_DOWN);
}else {
dv->Consumer->release();
}
}break;
default:
if(mode == KEY_PRESSED) {
dv->Keyboard->press(k);
}else if(mode == KEY_RELEASED) {
dv->Keyboard->release(k);
if(dv->Keyboard_state.fn_on > 0){
fn_actions[addr] = 0;
}
}
break;
}
}
void keypad_action(DEVTERM*dv,uint8_t col,uint8_t mode) {
uint16_t k;
k = keys_maps[col];
if(k == EMP){
return;
}
switch(k) {
case _LEFT_SHIFT_KEY:
if(mode == KEY_PRESSED) {
dv->Keyboard->press(k);
}else if(mode == KEY_RELEASED) {
dv->Keyboard->release(k);
}
break;
case _FN_KEY:
if(mode == KEY_PRESSED){
dv->Keyboard_state.fn_on = FN_LAYER;
}else if(mode == KEY_RELEASED ) {
//release all pressed fn keys if they still been pressing
for(int i=0;i<64;i++) {
if(fn_actions[i] !=0) {
k = keyboard_maps[dv->Keyboard_state.fn_on][i];
dv->Keyboard->release(k);
fn_actions[i] = 0;
}
}
dv->Keyboard_state.fn_on = 0;
}
break;
case _JOYSTICK_UP:
if(check_pd2() == HIGH) {
k = KEY_UP_ARROW;
if(mode == KEY_PRESSED) {
dv->Keyboard->press(k);
}else if(mode == KEY_RELEASED) {
dv->Keyboard->release(k);
}
}else {
if(mode == KEY_RELEASED){
dv->Joystick->Y(511);
}else {
dv->Joystick->Y(0);
}
}
break;
case _JOYSTICK_DOWN:
if(check_pd2() == HIGH) {
k = KEY_DOWN_ARROW;
if(mode == KEY_PRESSED) {
dv->Keyboard->press(k);
}else if(mode == KEY_RELEASED) {
dv->Keyboard->release(k);
}
}else {
if(mode == KEY_RELEASED){
dv->Joystick->Y(511);
}else {
dv->Joystick->Y(1023);
}
}
break;
case _JOYSTICK_LEFT:
if(check_pd2() == HIGH) {
k = KEY_LEFT_ARROW;
if(mode == KEY_PRESSED) {
dv->Keyboard->press(k);
}else if(mode == KEY_RELEASED) {
dv->Keyboard->release(k);
}
}else {
if(mode == KEY_RELEASED){
dv->Joystick->X(511);
}else {
dv->Joystick->X(0);
}
}
break;
case _JOYSTICK_RIGHT:
if(check_pd2() == HIGH) {
k = KEY_RIGHT_ARROW;
if(mode == KEY_PRESSED) {
dv->Keyboard->press(k);
}else if(mode == KEY_RELEASED) {
dv->Keyboard->release(k);
}
}else {
if(mode == KEY_RELEASED){
dv->Joystick->X(511);
}else {
dv->Joystick->X(1023);
}
}
break;
case _JOYSTICK_A:
if(check_pd2() == HIGH) {
k = 'j';
if(mode == KEY_PRESSED) {
dv->Keyboard->press(k);
}else if(mode == KEY_RELEASED) {
dv->Keyboard->release(k);
}
}else {
dv->Joystick->button(2,mode);
}
break;
case _JOYSTICK_B:
if(check_pd2() == HIGH) {
k = 'k';
if(mode == KEY_PRESSED) {
dv->Keyboard->press(k);
}else if(mode == KEY_RELEASED) {
dv->Keyboard->release(k);
}
}else {
dv->Joystick->button(3,mode);
}
break;
case _JOYSTICK_X:
if(check_pd2() == HIGH) {
k = 'u';
if(mode == KEY_PRESSED) {
dv->Keyboard->press(k);
}else if(mode == KEY_RELEASED) {
dv->Keyboard->release(k);
}
}else {
dv->Joystick->button(1,mode);
}
break;
case _JOYSTICK_Y:
if(check_pd2() == HIGH) {
k = 'i';
if(mode == KEY_PRESSED) {
dv->Keyboard->press(k);
}else if(mode == KEY_RELEASED) {
dv->Keyboard->release(k);
}
}else {
dv->Joystick->button(4,mode);
}
break;
case _MOUSE_LEFT:
if(mode == KEY_PRESSED){
dv->Mouse->press(1);
}else if(mode == KEY_RELEASED){
dv->Mouse->release(1);
}
break;
case _MOUSE_MID:
if(mode == KEY_PRESSED) {
dv->state->pressMiddleClick();
}else {
if(dv->state->getScrolled() == false){
//if no scrolling happend ,do as a normal mid mouse key click
dv->Mouse->click(MOUSE_MIDDLE);
}
dv->state->releaseMiddleClick();
}
break;
case _MOUSE_RIGHT:
if(mode == KEY_PRESSED){
dv->Mouse->press(2);
}else if(mode == KEY_RELEASED){
dv->Mouse->release(2);
}
break;
//_LEFT_CTRL_KEY,_CMD_KEY , _LEFT_ALT
case _LEFT_CTRL_KEY:
case _CMD_KEY:
case _LEFT_ALT:
if(mode == KEY_PRESSED){
dv->Keyboard->press(k);
}else {
dv->Keyboard->release(k);
}
break;
case _TRACKBALL_BTN:
if(mode == KEY_PRESSED){
dv->Mouse->press(1);
}else if(mode == KEY_RELEASED){
dv->Mouse->release(1);
}
break;
default:break;
}
}

View File

@@ -0,0 +1,27 @@
#ifndef KEYS_H
#define KEYS_H
/*
* keys include the joystick and mouse left/mid/right keys
*/
#include "devterm.h"
#include "keys_io_map.h"
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#ifndef KEY_DEBOUNCE
# define KEY_DEBOUNCE 5
#endif
#define KEYS_NUM 17
void keys_task(DEVTERM*);
void keys_init(DEVTERM*);
#endif

View File

@@ -0,0 +1,106 @@
#include "keys.h"
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,KEY0};
/* keys state(1:on, 0:off) */
static uint32_t keys;
static uint32_t keys_debouncing;
static uint32_t keys_prev;
void init_keys(){
int i;
for(i=0;i<KEYS_NUM;i++) {
pinMode( keys_io[i],INPUT_PULLUP);
}
}
uint8_t scan_keys(){
uint32_t data;
uint8_t s;
data = 0;
delayMicroseconds(30);
for(int i = 0;i < KEYS_NUM;i++) {
s = read_io(keys_io[i]);
s ^= 1;
data |= s << i;
}
if ( keys_debouncing != data ) {
keys_debouncing = data;
keypad_debouncing.deing = true;
keypad_debouncing.de_time = millis();
}
if (keypad_debouncing.deing == true && ( (millis() - keypad_debouncing.de_time) > KEY_DEBOUNCE )) {
keys = keys_debouncing;
keypad_debouncing.deing = false;
}else {
delay(1);
}
return 1;
}
void print_keys(DEVTERM*dv) {
char buff[128];
for (int i = 0; i < KEYS_NUM; i++) {
if( keys & (1<< i) ){
sprintf(buff,"B%d pressed\n",i+1);
dv->_Serial->print(buff);
}
}
}
void keys_task(DEVTERM*dv){
scan_keys();
uint32_t _mask =1;
uint32_t _change = 0;
uint32_t _pressed = 0;
_change = keys ^ keys_prev;
if(_change) {
for(uint8_t c=0;c < KEYS_NUM;c++,_mask <<=1) {
if (_change & _mask) {
_pressed = keys & _mask;
if(_pressed) {
keypad_action(dv,c,KEY_PRESSED);
}else {
keypad_action(dv,c,KEY_RELEASED);
}
keys_prev ^= _mask;
}
}
}
}
void keys_init(DEVTERM*dv){
init_keys();
//center the position
dv->Joystick->X(511);
dv->Joystick->Y(511);
keypad_debouncing.deing = false;
keypad_debouncing.de_time = 0;
}

View File

@@ -0,0 +1,49 @@
#ifndef KEYS_IO_MAP_H
#define KEYS_IO_MAP_H
#define ROW1 PA0
#define ROW2 PA1
#define ROW3 PA2
#define ROW4 PA3
#define ROW5 PA4
#define ROW6 PA5
#define ROW7 PA6
#define ROW8 PA7
#define COL1 PC0
#define COL2 PC1
#define COL3 PC2
#define COL4 PC3
#define COL5 PC4
#define COL6 PC5
#define COL7 PC6
#define COL8 PC7
#define KEY1 PB0 //
#define KEY2 PB1
#define KEY3 PB2
#define KEY4 PB3
#define KEY5 PB4
#define KEY6 PB5
#define KEY7 PB6
#define KEY8 PB7
#define KEY9 PB8
#define KEY10 PB9
#define KEY11 PB10
#define KEY12 PB11
#define KEY13 PB12
#define KEY14 PB13
#define KEY15 PB14
#define KEY16 PB15 //
//ball
#define HO1 PC8
#define HO2 PC9
#define HO3 PC10
#define HO4 PC11
#define KEY0 PC12
#endif

View 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

View 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;
}

View 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

View File

@@ -0,0 +1,47 @@
#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);
if (!cutoff.get()) {
averageDelta += delta;
}
}
void RateMeter::expire() {
cutoff.expire();
}
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;
}
}

View File

@@ -0,0 +1,37 @@
#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();
bool getScrolled();
void setScrolled();
TrackballMode moveTrackball();
private:
bool middleClick;
bool scrolled;
Timeout<uint16_t, MIDDLE_CLICK_TIMEOUT_MS> middleClickTimeout;
};
#endif

View File

@@ -0,0 +1,47 @@
#include <cassert>
#include <algorithm>
#include <limits>
#include "state.h"
State::State()
: fn(false),
middleClick(false),
scrolled(false)
{
}
void State::tick(millis_t delta)
{
middleClickTimeout.updateTime(delta);
}
void State::setScrolled() {
if(middleClick==true){
scrolled = true;
}
}
bool State::getScrolled() {
return scrolled;
}
void State::pressMiddleClick() {
middleClick = true;
middleClickTimeout.reset();
}
bool State::releaseMiddleClick() {
middleClick = false;
scrolled = false;
const auto timeout = middleClickTimeout.get();
return !timeout;
}
TrackballMode State::moveTrackball() {
middleClickTimeout.expire();
if (middleClick) {
return TrackballMode::Wheel;
} else {
return TrackballMode::Mouse;
}
}

View 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

View File

@@ -0,0 +1,25 @@
#ifndef TRACKBALL_H
#define TRACKBALL_H
#include "devterm.h"
#include "keys_io_map.h"
/*
#define BOUNCE_INTERVAL 30
#define BASE_MOVE_PIXELS 5
#define EXPONENTIAL_BOUND 15
#define EXPONENTIAL_BASE 1.2
*/
#define RIGHT_PIN HO3
#define LEFT_PIN HO1
#define DOWN_PIN HO4
#define UP_PIN HO2
void trackball_init(DEVTERM*);
void trackball_task(DEVTERM*);
#endif

View File

@@ -0,0 +1,130 @@
/*
* clockworkpi devterm trackball
*/
#include "keys_io_map.h"
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <USBComposite.h>
#include "trackball.h"
#include "ratemeter.h"
#include "glider.h"
#include "math.h"
enum Axis: uint8_t {
AXIS_X,
AXIS_Y,
AXIS_NUM,
};
static TrackballMode lastMode;
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.4);
return std::abs(input) / 30;
}
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, std::sqrt(rateMeter[AXIS_X].delta()));
glider[AXIS_Y].updateSpeed(vy);
} else {
glider[AXIS_X].updateSpeed(vx);
glider[AXIS_Y].update(vy, std::sqrt(rateMeter[AXIS_Y].delta()));
}
}
void trackball_task(DEVTERM*dv) {
int8_t x = 0, y = 0, w = 0;
noInterrupts();
const auto mode = dv->state->moveTrackball();
if (lastMode != mode) {
rateMeter[AXIS_X].expire();
rateMeter[AXIS_Y].expire();
wheelBuffer = 0;
}
else {
rateMeter[AXIS_X].tick(dv->delta);
rateMeter[AXIS_Y].tick(dv->delta);
}
lastMode = mode;
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;
if(w != 0){
dv->state->setScrolled();
}
break;
}
}
distances[AXIS_X] = 0;
distances[AXIS_Y] = 0;
interrupts();
if(x !=0 || y != 0 || -w!=0) {
dv->Mouse->move(x, y, -w);
}
}
void trackball_init(DEVTERM*dv){
pinMode(LEFT_PIN, INPUT);
pinMode(UP_PIN, INPUT);
pinMode(RIGHT_PIN, INPUT);
pinMode(DOWN_PIN, INPUT);
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);
}