Browse Source

Merge remote-tracking branch 'origin/master'

cuu 11 months ago
parent
commit
9cebd76441

+ 13 - 0
Code/uconsole_keyboard/README.md

@@ -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
+  
+  

+ 53 - 0
Code/uconsole_keyboard/debouncer.h

@@ -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 - 0
Code/uconsole_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;
+}

+ 60 - 0
Code/uconsole_keyboard/devterm.h

@@ -0,0 +1,60 @@
+#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_lock{
+  
+  uint16_t lock;//
+  uint16_t time;//
+  uint16_t begin;//
+    
+}KEYBOARD_LOCK;
+
+typedef struct keyboard_state{
+
+  uint8_t layer;
+  uint8_t prev_layer;
+  uint8_t fn_on;
+  uint16_t sf_on;//shift on
+  
+  uint8_t backlight;//0 1 2 3
+  uint8_t lock;//0 1
+
+  
+  KEYBOARD_LOCK ctrl;
+  KEYBOARD_LOCK shift;  
+  KEYBOARD_LOCK alt;
+  KEYBOARD_LOCK fn;
+      
+}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 0 // 1 for PULLUP, 0 FOR PULLDOWN
+#define KEYBOARD_LED_PWM_PERIOD 200
+
+#endif

+ 29 - 0
Code/uconsole_keyboard/glider.h

@@ -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 - 0
Code/uconsole_keyboard/glider.ino

@@ -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 };
+}

+ 19 - 0
Code/uconsole_keyboard/helper.h

@@ -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

+ 10 - 0
Code/uconsole_keyboard/helper.ino

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

+ 83 - 0
Code/uconsole_keyboard/keyboard.h

@@ -0,0 +1,83 @@
+#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
+
+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)
+  _JOYSTICK_L,
+  _JOYSTICK_R,
+  _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,
+};
+
+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

+ 204 - 0
Code/uconsole_keyboard/keyboard.ino

@@ -0,0 +1,204 @@
+#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)
+{
+ char buff[128];
+  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) { 
+      //sprintf(buff,"matrix_row: %d %d\n",matrix_row,matrix_prev[r]);
+      //dv->_Serial->print(buff);
+      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;
+  
+}

+ 544 - 0
Code/uconsole_keyboard/keymaps.ino

@@ -0,0 +1,544 @@
+#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
+
+
+#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_BACKSPACE,KEY_RETURN,_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_DELETE,KEY_RETURN,_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] = {
+ 
+   [DEF_LAYER] = {KEY_UP_ARROW,KEY_DOWN_ARROW, KEY_LEFT_ARROW,KEY_RIGHT_ARROW, \
+                 _JOYSTICK_A,_JOYSTICK_B, _JOYSTICK_X,_JOYSTICK_Y, \
+                 _LEFT_SHIFT_KEY,KEY_RIGHT_SHIFT,_LEFT_CTRL_KEY, KEY_RIGHT_CTRL, \
+                 _LEFT_ALT,  _MOUSE_LEFT,  KEY_RIGHT_ALT,_MOUSE_RIGHT,  \
+                 _TRACKBALL_BTN },
+                 
+   [FN_LAYER] = {_FN_KEY_UP_ARROW,_FN_KEY_DOWN_ARROW, _FN_KEY_LEFT_ARROW, _FN_KEY_RIGHT_ARROW, \
+                 _JOYSTICK_A,_JOYSTICK_B, _JOYSTICK_X,_JOYSTICK_Y, \
+                 _LEFT_SHIFT_KEY,KEY_RIGHT_SHIFT,_LEFT_CTRL_KEY, KEY_RIGHT_CTRL, \
+                 _CMD_KEY,  _MOUSE_LEFT,  KEY_RIGHT_ALT,_MOUSE_RIGHT,  \
+                 _TRACKBALL_BTN },
+                     
+ };
+
+
+                                      
+const uint16_t backlight_vals[3] = {0,500,2000};
+
+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 press_any_key_to_release_lock(DEVTERM*dv, KEYBOARD_LOCK*lock, uint16_t k,uint8_t mode) {
+
+
+  if( lock->lock > 0 ) {
+    if(mode == KEY_RELEASED
+        && k != _LEFT_CTRL_KEY && k!= KEY_RIGHT_CTRL
+        && k != _LEFT_ALT && k!= KEY_RIGHT_ALT
+        && k != _LEFT_SHIFT_KEY && k!= KEY_RIGHT_SHIFT
+        && k != _FN_KEY) {
+      lock->lock = 0;
+      
+      if(lock->begin!= _FN_KEY) {
+	 dv->Keyboard->release(lock->begin);
+      }
+      lock->begin = 0;
+      lock->time = 0;
+      //dv->_Serial->println("ctrl lock released");
+    }
+  }
+}
+
+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;
+  }
+  
+  if(k != _FN_KEY && k != _FN_LOCK_KEYBOARD && dv->Keyboard_state.lock == 1) {
+    return;
+  }
+
+  switch(k) {
+    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) {
+        if(dv->Keyboard_state.sf_on > 0){
+           dv->Consumer->press(HIDConsumer::VOLUME_UP); 
+           dv->Keyboard->release(dv->Keyboard_state.sf_on);
+           dv->Keyboard_state.sf_on = 0;
+        }else{
+          dv->Consumer->press(HIDConsumer::VOLUME_DOWN);
+        }
+      }else {
+        dv->Consumer->release();
+      }
+    }break;
+    case _VOLUME_MUTE:{
+      if(mode == KEY_PRESSED) {
+        dv->Consumer->press(HIDConsumer::MUTE);
+      }else {
+        dv->Consumer->release();
+      }
+    }break;    
+    case _FN_LOCK_KEYBOARD:{
+      if(mode == KEY_PRESSED) {
+        dv->Keyboard_state.lock = dv->Keyboard_state.lock ^ 1;
+      }
+    }break;
+    case _FN_LIGHT_KEYBOARD: {
+      //dv->_Serial->println("light keyboard");
+      
+      if(mode == KEY_PRESSED) {
+         dv->Keyboard_state.backlight ++;
+         if(dv->Keyboard_state.backlight >= 3) {
+          dv->Keyboard_state.backlight = 0;
+         }
+         timer.resume();
+         pwmWrite(PA8,backlight_vals[ dv->Keyboard_state.backlight ] );
+      }
+    }break;
+    case  _FN_KEY:
+      if(mode == KEY_PRESSED){
+	      if(dv->Keyboard_state.fn.lock == 0){
+          dv->Keyboard_state.fn_on = FN_LAYER;
+	        dv->Keyboard_state.fn.begin = k;
+	      }
+        }else if(mode == KEY_RELEASED ) {
+	      if(dv->Keyboard_state.fn.lock == 0){
+          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;
+        }
+
+	      dv->Keyboard_state.fn.begin = 0;
+        dv->Keyboard_state.fn.time = 0;
+       }
+    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;
+  }
+  /*
+  press_any_key_to_release_lock(dv,&dv->Keyboard_state.ctrl,k,mode);
+  press_any_key_to_release_lock(dv,&dv->Keyboard_state.alt,k,mode);
+  press_any_key_to_release_lock(dv,&dv->Keyboard_state.shift,k,mode);
+  press_any_key_to_release_lock(dv,&dv->Keyboard_state.fn,k,mode);
+  */
+}
+
+
+void keypad_action(DEVTERM*dv,uint8_t col,uint8_t mode) {
+
+  uint16_t k;
+   
+  if(dv->Keyboard_state.fn_on > 0){
+    k = keys_maps[dv->Keyboard_state.fn_on][col];
+  }else {
+    k = keys_maps[dv->Keyboard_state.layer][col];
+  }
+  
+  if(k == EMP){
+    return;
+  }
+
+  if(dv->Keyboard_state.lock == 1) {
+    return;
+  }
+
+  switch(k) {
+    case _LEFT_SHIFT_KEY:
+    case KEY_RIGHT_SHIFT:
+      if(mode == KEY_PRESSED) {
+        dv->Keyboard_state.sf_on = k;
+	      if(dv->Keyboard_state.shift.lock == 0){
+          dv->Keyboard->press(k);
+	        dv->Keyboard_state.shift.begin=k;
+	      }
+      }else if(mode == KEY_RELEASED) {
+        dv->Keyboard_state.sf_on = 0;
+        if(dv->Keyboard_state.shift.lock == 0){
+          dv->Keyboard->release(k);
+	        dv->Keyboard_state.shift.begin = 0;
+          dv->Keyboard_state.shift.time = 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 KEY_RIGHT_CTRL:
+      if(mode == KEY_PRESSED){
+        if(dv->Keyboard_state.ctrl.lock == 0){
+          dv->Keyboard->press(k);
+          dv->Keyboard_state.ctrl.begin = k;
+        }
+      }else {
+        if(dv->Keyboard_state.ctrl.lock == 0){
+          dv->Keyboard->release(k);
+          dv->Keyboard_state.ctrl.begin = 0;
+          dv->Keyboard_state.ctrl.time = 0;
+        }
+      }
+    break;
+
+    case _LEFT_ALT:
+    case KEY_RIGHT_ALT:
+      if(mode == KEY_PRESSED){
+	      if(dv->Keyboard_state.alt.lock == 0){ 
+	        dv->Keyboard->press(k);
+	        dv->Keyboard_state.alt.begin=k;
+	      }
+      }else {
+	    if(dv->Keyboard_state.alt.lock == 0){
+          dv->Keyboard->release(k);
+	        dv->Keyboard_state.alt.begin = 0;
+	        dv->Keyboard_state.alt.time = 0;
+	      }
+      }
+    break;
+
+    case _CMD_KEY:
+      if(mode == KEY_PRESSED){
+        dv->Keyboard->press(k);
+      }else {
+        dv->Keyboard->release(k);
+      }
+    break;
+
+    case _TRACKBALL_BTN:
+      if(mode == KEY_PRESSED){
+        dv->Mouse->press(MOUSE_MIDDLE);
+      }else if(mode == KEY_RELEASED){
+        dv->Mouse->release(MOUSE_MIDDLE);
+      }
+    break;
+    default:
+       if(mode == KEY_PRESSED) {
+        dv->Keyboard->press(k);
+      }else if(mode == KEY_RELEASED) {
+        dv->Keyboard->release(k);
+      }
+    break;
+    
+  }
+  /*
+  press_any_key_to_release_lock(dv,&dv->Keyboard_state.ctrl,k,mode);
+  press_any_key_to_release_lock(dv,&dv->Keyboard_state.alt,k,mode);
+  press_any_key_to_release_lock(dv,&dv->Keyboard_state.shift,k,mode);
+  press_any_key_to_release_lock(dv,&dv->Keyboard_state.fn,k,mode); 
+  */
+}

+ 27 - 0
Code/uconsole_keyboard/keys.h

@@ -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

+ 106 - 0
Code/uconsole_keyboard/keys.ino

@@ -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;
+  
+}

+ 49 - 0
Code/uconsole_keyboard/keys_io_map.h

@@ -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

+ 56 - 0
Code/uconsole_keyboard/math.h

@@ -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 - 0
Code/uconsole_keyboard/math.ino

@@ -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 - 0
Code/uconsole_keyboard/ratemeter.h

@@ -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

+ 47 - 0
Code/uconsole_keyboard/ratemeter.ino

@@ -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;
+  }
+}

+ 37 - 0
Code/uconsole_keyboard/state.h

@@ -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

+ 47 - 0
Code/uconsole_keyboard/state.ino

@@ -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;
+  }
+}

+ 30 - 0
Code/uconsole_keyboard/tickwaiter.h

@@ -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

+ 25 - 0
Code/uconsole_keyboard/trackball.h

@@ -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             HO2
+#define LEFT_PIN              HO4
+#define DOWN_PIN              HO3
+#define UP_PIN                HO1
+
+
+void trackball_init(DEVTERM*);
+void trackball_task(DEVTERM*);
+
+
+#endif

+ 130 - 0
Code/uconsole_keyboard/trackball.ino

@@ -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);
+  
+}

+ 139 - 0
Code/uconsole_keyboard/uconsole_keyboard.ino

@@ -0,0 +1,139 @@
+#include "keyboard.h"
+#include "keys.h"
+#include "trackball.h"
+#include "devterm.h"
+#include "tickwaiter.h"
+
+#include <USBComposite.h>
+
+#define SER_NUM_STR "20230307"
+
+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;
+
+HardwareTimer timer(1);
+//HardwareTimer ctrl_timer(4);
+
+void setup() {
+  USBComposite.setManufacturerString("ClockworkPI");
+  USBComposite.setProductString("uConsole");
+  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.sf_on = 0;
+  
+  //dev_term.Keyboard_state.shift = 0;
+  dev_term.Keyboard_state.backlight = 0;
+  dev_term.Keyboard_state.lock = 0;
+  
+  dev_term.Keyboard_state.ctrl.lock = 0;
+  dev_term.Keyboard_state.ctrl.time = 0;
+  dev_term.Keyboard_state.ctrl.begin = 0;
+
+  dev_term.Keyboard_state.shift.lock = 0;
+  dev_term.Keyboard_state.shift.time = 0;
+  dev_term.Keyboard_state.shift.begin = 0;
+
+  dev_term.Keyboard_state.alt.lock = 0;
+  dev_term.Keyboard_state.alt.time = 0;
+  dev_term.Keyboard_state.alt.begin = 0;
+      
+  dev_term.Keyboard_state.fn.lock = 0;
+  dev_term.Keyboard_state.fn.time = 0;
+  dev_term.Keyboard_state.fn.begin = 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 
+
+  timer.setPeriod(KEYBOARD_LED_PWM_PERIOD);
+  timer.resume();
+
+  /*
+  ctrl_timer.setPeriod(20*1000);
+  ctrl_timer.attachInterrupt(1,ctrl_timer_handler);
+  ctrl_timer.refresh();
+  ctrl_timer.resume();
+  */
+  pinMode(PA8,PWM);
+  pwmWrite(PA8,0);
+
+  
+  delay(1000);
+}
+
+#define LOCK_TIME 50
+
+//DO NOT USE dev_term._Serial->println(""); in timer interrupt function,will block 
+void check_keyboard_lock(KEYBOARD_LOCK*lock){
+   if( lock->begin >0) {
+    lock->time++;
+
+    if( lock->time>=LOCK_TIME && lock->time<200){
+      lock->lock = 1;
+    }
+    
+    if( lock->time > 200){
+     if(lock->begin != _FN_KEY) {
+      	dev_term.Keyboard->release(lock->begin);
+      }
+      lock->time = 0;
+      lock->lock = 0;
+      lock->begin = 0;
+    }
+  } 
+}
+
+#define LOCK_TIME 50
+void ctrl_timer_handler(void) {
+  
+  check_keyboard_lock(&dev_term.Keyboard_state.ctrl);
+  check_keyboard_lock(&dev_term.Keyboard_state.shift);
+  check_keyboard_lock(&dev_term.Keyboard_state.alt);
+  check_keyboard_lock(&dev_term.Keyboard_state.fn);
+
+}
+
+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);
+  
+
+}