Go Down

Topic: ToeKeys USB macro keyboard for toes. (Read 336 times) previous topic - next topic

matmanj

Sep 01, 2014, 07:27 am Last Edit: Sep 01, 2014, 07:31 am by matmanj Reason: 1
ToeKeys is an 8 button interface that applies transformations to
 button state before passing the state to a function to send USB
 keystrokes.
 
 Requirements:
   - Arduino Micro. May work on other atmega32u4 based products
   - 8x double throw switches
   - 16 IO pins connected as shown in toekeys_schematic.png
 Features:
   - Fast, reliable and trivial debounce code made possible
     by the use of double throw switches.
   - Fast button handling. Polling all buttons and applying all
     transformations happens within 4µs.
   - Supported button state transformations: reverse, toggle,
     repeat, strike/hold.
   - Send up to 6 keys per button.

Code: [Select]
/*  ToeKeys v0.05: USB macro keyboard : For Arduino Micro  */

#include <Arduino.h>

void setup()
{
 pinSetup();
 Keyboard.begin();
}

void loop()
{
 word button_state = pinModify(pinRead());
 runMacros(button_state);
}
/* ==============Abstract===============
 ToeKeys is an 8 button interface that applies transformations to
 button state before passing the state to a function to send USB
 keystrokes.
 
 Requirements:
   - Arduino Micro. May work on other atmega32u4 based products
   - 8x double throw switches
   - 16 IO pins connected as shown in toekeys_schematic.png
 Features:
   - Fast, reliable and trivial debounce code made possible
     by the use of double throw switches.
   - Fast button handling. Polling all buttons and applying all
     transformations happens within 4µs.
   - Supported button state transformations: reverse, toggle,
     repeat, strike/hold.
   - Send up to 6 keys per button.
*/
// ================Macros==================
const word repeat_rate = 200;  //milliseconds
const byte key_hold    = B00000100;
/* key_hold is standard keyboard behaviour with keydown
  and keyup coinciding with button press and release.
  If not key_hold both key reports will be sent on button press*/
/* A maximum of 6 keys can be held simultaneously (not including
  shift, ctrl, alt). Support for sending much longer key
  sequences is on the wishlist but not by increasing the size of
  this array. */
const char key_code[8][7] = {{'a'},
                            {' '},
                            {KEY_LEFT_CTRL},
                            {'f'},
                            {'g'},
                            {'h'},
                            {"groovy"},
                            {"kool\n"}
                           };

void runMacros(word pin_state) {
 if (!(pin_state >> 8)) return;
 //quick exit if nothing changed
 for (byte i=0;i<8;i++){
   if ((pin_state >> (8+i)) & 1){
     if (!((key_hold >> i) & 1)){
       if ((pin_state >> i) & 1)
         macroStrike(i);
     }
     else {
       macroHold(i, (pin_state >> i) & 1);
     }
   }
 }
}

void macroStrike(byte id) {
 Keyboard.print(key_code[id]);
}

void macroHold(byte id, byte pressed) {
 for (byte i=0;i<6;i++){
   if (pressed)
     Keyboard.press(key_code[id][i]);
   else
     Keyboard.release(key_code[id][i]);
 }
}

// ===============Pins===================
/* Port masks for reading IO ports directly .. low byte is
  primary state data. High byte is opposite throw of switches
  for debounce code  */
#define PB_MASK B11111100  // MO,MI,D11-D8  .. high byte
#define PF_MASK B11110011  // A5-A0         .. low byte
#define PD_MASK B11000011  // D3,D2,D12,D6  .. shifted << 2

const byte mod_reverse = B00000001;
const byte mod_toggle  = B00000010;
const byte mod_repeat  = B10000010;

void pinSetup()
{
 DDRB  &= ~PB_MASK;
 DDRD  &= ~PD_MASK;
 DDRF  &= ~PF_MASK;
 /*  using 3 ports to rally 16 input pins   */
 PORTB |= PB_MASK;
 PORTD |= PD_MASK;
 PORTF |= PF_MASK;
 /*  The combination of INPUT_PULLUP and reading normally
     closed switches means 1 = "on".  Yay.  */
}

word pinRead()
{
 word pin_read = (((PINB & PB_MASK) << 8)
                | ((PIND & PD_MASK) << 2)
                |  (PINF & PF_MASK));
 byte bounce_mask = (pin_read >> 8) ^ pin_read;
 /*  If nether side of a double throw switch shows a closed
     circuit it's read is masked and we treat that button
     as if it hasn't changed since last read.  */
 static byte last_read = 0;
 byte pin_changed = (pin_read ^ last_read) & bounce_mask;
 last_read = (pin_read & bounce_mask)
           | (last_read & ~bounce_mask);
 return last_read | (pin_changed << 8);
 /*  Button state is passed around this sketch in this format.
     High byte is change flags.  Low byte is current status. */
}

word pinModify(word mod_state)
{
 mod_state ^= mod_reverse;
 // up is down
 mod_state = modToggle(mod_state);
 // on state is toggled at button press
 mod_state = modRepeat(mod_state);
 /* a shared timer periodically asserts changed flag while
    button is down */
 return mod_state;
}

word modToggle(word mod_state)
{
 static byte tglon_state = 0;
 tglon_state ^= mod_state & (mod_state >> 8);
 mod_state &= (mod_state | ~mod_toggle) << 8 | 0xFF;
 mod_state = (mod_state & ~mod_toggle)
           | (tglon_state & mod_toggle);
 return mod_state;
}

word modRepeat(word mod_state)
{
 static unsigned long timer = 0;
 unsigned long now = millis();
 if ((timer + repeat_rate) < now) {
     timer = now;
     mod_state |= (mod_state & mod_repeat) << 8;
   }
 return mod_state;
}

Go Up