/* Heavily modified by WS PS2Keyboard.cpp - PS2Keyboard library Copyright (c) 2007 Free Software Foundation. All right reserved. Written by Christian Weichel ** Mostly rewritten Paul Stoffregen 2010, 2011 ** Modified for use beginning with Arduino 13 by L. Abraham Smith, * ** Modified for easy interrup pin assignement on method begin(datapin,irq_pin). Cuningan ** for more information you can read the original wiki in arduino.cc at http://www.arduino.cc/playground/Main/PS2Keyboard or http://www.pjrc.com/teensy/td_libs_PS2Keyboard.html Version 2.3-Ctrl-Enter (September 2012) - Add Ctrl+Enter (Ctrl+J) as PS2_LINEFEED, code 10 - Move PS2_DOWNARROW to code 12 Version 2.3-Ctrl (June 2012) - Reintroduce Ctrl Version 2.3 (October 2011) - Minor bugs fixed Version 2.2 (August 2011) - Support non-US keyboards - thanks to Rainer Bruch for a German keyboard :) Version 2.1 (May 2011) - timeout to recover from misaligned input - compatibility with Arduino "new-extension" branch - TODO: send function, proposed by Scott Penrose, scooterda at me dot com Version 2.0 (June 2010) - Buffering added, many scan codes can be captured without data loss if your sketch is busy doing other work - Shift keys supported, completely rewritten scan code to ascii - Slow linear search replaced with fast indexed table lookups - Support for Teensy, Arduino Mega, and Sanguino added This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "PS2Keyboard.h" extern uint8_t system_mode; #define MODE_MENU 0 #define MODE_EMU 1 #define BUFFER_SIZE 45 #define BREAK 0x01 #define MODIFIER 0x02 #define SHIFT_L 0x04 #define SHIFT_R 0x08 #define ALTGR 0x10 #define CTRL 0x20 static volatile uint8_t buffer[BUFFER_SIZE]; static volatile uint8_t head, tail; static uint8_t DataPin; static uint8_t CharBuffer=0; static uint8_t UTF8next=0; static const PS2Keymap_t *keymap=NULL; extern uint8_t lowmemRAM[0x600]; extern uint16_t portram[256]; void ICACHE_RAM_ATTR ps2interrupt(void) { static uint8_t bitcount=0; static uint8_t incoming=0; static uint32_t prev_ms=0; uint32_t now_ms; uint8_t n, val; val = digitalRead(DataPin); now_ms = millis(); if (now_ms - prev_ms > 250) { bitcount = 0; incoming = 0; } prev_ms = now_ms; n = bitcount - 1; if (n <= 7) { incoming |= (val << n); } bitcount++; if (bitcount == 11) { uint8_t i = head + 1; if (i >= BUFFER_SIZE) i = 0; if (i != tail) { buffer[i] = incoming; head = i; } bitcount = 0; incoming = 0; } } static inline uint8_t get_raw_data(void) { uint8_t c, i; i = tail; if (i == head) return 0; i++; if (i >= BUFFER_SIZE) i = 0; c = buffer[i]; tail = i; return c; } const PROGMEM PS2Keymap_t PS2Keymap_US = { // without shift {0, 0x43, 0, 0x3F, 0x3D, 0x3B, 0x3C, 0 /*PS2_F12*/, 0, 0x44, 0x42, 0x40, 0x3E, 0x0F, 0x29, 0, 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, 0x10, 0x02, 0, 0, 0, 0x2C, 0x1F, 0x1E, 0x11, 0x03, 0, 0, 0x2E, 0x2D, 0x20, 0x12, 0x05, 0x04, 0, 0, 0x39, 0x2F, 0x21, 0x14, 0x13, 0x06, 0, 0, 0x31, 0x30, 0x23, 0x22, 0x15, 0x07, 0, 0, 0, 0x32, 0x24, 0x16, 0x08, 0x09, 0, 0, 0x33, 0x25, 0x17, 0x18, 0x0B, 0x0A, 0, 0, 0x34, 0x35, 0x26, 0x27, 0x19, 0x0C, 0, 0, 0, 0x28, 0, 0x1A, 0x0D, 0, 0, 0 /*CapsLock*/, 0 /*Rshift*/, 0x1C, 0x1B, 0, 0x2B, 0, 0, 0, 0, 0, 0, 0, 0, 0x0E, 0, 0, 0xcf /*Gray 1*/, 0, 0xcb /*Gray 4*/, 0xc7 /*Gray 7*/, 0, 0, 0, 0xd2 /*Gray 0*/, 0x34, 0xd0 /*Gray 2*/, 0xcc /*Gray 5*/, 0xcd /*Gray 6*/, 0xc8 /*Gray 8*/, 0x01, 0 /*NumLock*/, 0 /*PS2_F11*/, 0x4E, 0xd1 /*Gray 3*/, 0x4A, 0x37, 0xc9 /*Gray 9*/, 0 /*PS2_SCROLL*/, 0, 0, 0, 0, 0x41}, // with shift {0, 0x5C, 0, 0x58, 0x56, 0x54, 0x55, 0 /*PS2_F12*/, 0, 0x5D, 0x5B, 0x59, 0x57, 0x0F, 0x29, 0, 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, 0x10, 0x02, 0, 0, 0, 0x2C, 0x1F, 0x1E, 0x11, 0x03, 0, 0, 0x2E, 0x2D, 0x20, 0x12, 0x05, 0x04, 0, 0, 0x39 /*sh+space*/, 0x2F, 0x21, 0x14, 0x13, 0x06, 0, 0, 0x31, 0x30, 0x23, 0x22, 0x15, 0x07, 0, 0, 0, 0x32, 0x24, 0x16, 0x08, 0x09, 0, 0, 0x33, 0x25, 0x17, 0x18, 0x0B, 0x0A, 0, 0, 0x34, 0x35, 0x26, 0x27, 0x19, 0x0C, 0, 0, 0, 0x28, 0, 0x1A, 0x0D, 0, 0, 0 /*CapsLock*/, 0 /*Rshift*/, 0x1C /*Enter*/, 0x1B, 0, 0x2B, 0, 0, 0, 0, 0, 0, 0, 0, 0x0E, 0, 0, '1', 0, '4', '7', 0, 0, 0, '0', '.', '2', '5', '6', '8', 0x01, 0 /*NumLock*/, 0 /*PS2_F11*/, 0x0D, '3', 0x0C, 0x37, '9', 0 /*PS2_SCROLL*/, 0, 0, 0, 0, 0x5A }, 0 }; static char get_scancode(void) { static uint8_t state=0; uint8_t s; char c; while (1) { s = get_raw_data(); if (!s) return 0; if (s == 0xF0) { state |= BREAK; return 0x80; // retrun break?? } else if (s == 0xE0) { state |= MODIFIER; } else { if (state & BREAK) { if (s == 0x12) { ///0x12 is left shift key pressed state &= ~SHIFT_L; } else if (s == 0x59) { state &= ~SHIFT_R; /// 0x59 is right shift } else if (s == 0x14) { /// 0x14 is ctrl key pressed state &= ~CTRL; } else if (s == 0x11) { ///0x11 is alt key pressed state &= ~ALTGR; } state &= ~(BREAK | MODIFIER); continue; } if (s == 0x12) { state |= SHIFT_L; continue; } else if (s == 0x59) { state |= SHIFT_R; continue; } else if (s == 0x14) { state |= CTRL; continue; } else if (s == 0x11) { state |= ALTGR; continue; } c = 0; if (state & MODIFIER) { switch (s) { case 0x70: c = 0x52; break; case 0x6C: c = 0x47; break; case 0x7D: c = 0x49; break; case 0x71: c = 0x53; break; case 0x69: c = 0x4F; break; case 0x7A: c = 0x51; break; case 0x75: c = 0x48; break; case 0x6B: c = 0x4B; break; case 0x72: c = 0x50; break; case 0x74: c = 0x4D; break; case 0x4A: c = 0x35; break; case 0x5A: c = 0x1C; break; default: break; } } else if (state & (SHIFT_L | SHIFT_R)) { if (s < PS2_KEYMAP_SIZE) c = pgm_read_byte(keymap->shift + s); } else { if (s < PS2_KEYMAP_SIZE) c = pgm_read_byte(keymap->noshift + s); } state &= ~(BREAK | MODIFIER); switch (state) { case 0x04: lowmemRAM[0x417]= 0x02; break; case 0x08: lowmemRAM[0x417]= 0x01; break; case 0x0c: lowmemRAM[0x417]= 0x03; break; case 0x10: lowmemRAM[0x417]= 0x08; break; case 0x14: lowmemRAM[0x417]= 0x0a; break; case 0x18: lowmemRAM[0x417]= 0x09; break; case 0x1c: lowmemRAM[0x417]= 0x0b; break; case 0x20: lowmemRAM[0x417]= 0x04; break; case 0x24: lowmemRAM[0x417]= 0x06; break; case 0x28: lowmemRAM[0x417]= 0x05; break; case 0x2c: lowmemRAM[0x417]= 0x07; break; case 0x30: lowmemRAM[0x417]= 0x0c; break; case 0x34: lowmemRAM[0x417]= 0x0e; break; case 0x38: lowmemRAM[0x417]= 0x0d; break; case 0x3c: lowmemRAM[0x417]= 0x0f; break; default: lowmemRAM[0x417]= 0x00; break; } if ((c == 0x53) && (lowmemRAM[0x417]&0x0c)) { c =0x34; } if (c > 127) { c = c - 0x80; lowmemRAM[0x417] |= 0x20; } if (c) { portram[0x60] = c; return c; } } } } bool PS2Keyboard::available() { if (CharBuffer || UTF8next) return true; CharBuffer = get_scancode(); if (CharBuffer) return true; return false; } int PS2Keyboard::read() { uint8_t result; result = UTF8next; if (result) { UTF8next = 0; } else { result = CharBuffer; if (result) { CharBuffer = 0; } else { result = get_scancode(); } } if (!result) return -1; return result; } void PS2Keyboard::begin(uint8_t data_pin, uint8_t irq_pin, const PS2Keymap_t &map) { uint8_t irq_num=0; DataPin = data_pin; irq_num=irq_pin; keymap = ↦ pinMode(irq_pin, INPUT_PULLUP); digitalWrite(irq_pin, HIGH); pinMode(data_pin, INPUT_PULLUP); digitalWrite(data_pin, HIGH); head = 0; tail = 0; attachInterrupt(digitalPinToInterrupt(irq_num), ps2interrupt, FALLING); }