Go Down

Topic: ToeKeys USB macro keyboard for toes. (Read 408 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
 


Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

Arduino
via Egeo 16
Torino, 10131
Italy