Created
October 3, 2012 15:04
-
-
Save houmei/3827425 to your computer and use it in GitHub Desktop.
USB HID keyboard memo (arduinoIDE1.0.1)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
http://arduino.cc/playground/Main/PS2Keyboard | |
http://arduino.cc/playground/Main/PS2KeyboardExt2 | |
http://msdn.microsoft.com/en-us/library/windows/hardware/gg463446.aspx | |
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
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 <avr/io.h> | |
#include <avr/interrupt.h> | |
#include <avr/pgmspace.h> | |
#include "Arduino.h" | |
#include "PS2kybd.h" | |
#include "binary.h" | |
#define HID_USAGEPAGE 0x0700 | |
// Scancode FIFO | |
#define BUFFER_SIZE 64 | |
static volatile uint8_t Buffer[BUFFER_SIZE]; | |
static volatile uint8_t Head, Tail; | |
// Modifier | |
#define PS2_MOD_CTRL_R 1 | |
#define PS2_MOD_SHIFT_R 2 | |
#define PS2_MOD_ALT_R 4 | |
#define PS2_MOD_META_R 8 | |
#define PS2_MOD_CTRL_L 16 | |
#define PS2_MOD_SHIFT_L 32 | |
#define PS2_MOD_ALT_L 64 | |
#define PS2_MOD_META_L 128 | |
#define PS2_MOD_CTRL (PS2_MOD_CTRL_L | PS2_MOD_CTRL_R) | |
#define PS2_MOD_SHIFT (PS2_MOD_SHIFT_L | PS2_MOD_SHIFT_R) | |
#define PS2_MOD_ALT (PS2_MOD_ALT_L | PS2_MOD_ALT_R) | |
#define PS2_MOD_META (PS2_MOD_META_L | PS2_MOD_META_R) | |
const uint8_t MODIFIERBIT[8] = { | |
PS2_MOD_CTRL_L,PS2_MOD_SHIFT_L,PS2_MOD_ALT_L,PS2_MOD_META_L, | |
PS2_MOD_CTRL_R,PS2_MOD_SHIFT_R,PS2_MOD_ALT_R,PS2_MOD_META_R | |
} ; | |
static volatile uint8_t Modifier; | |
// Lock status | |
#define PS2_LOCK_CAPS 1 | |
#define PS2_LOCK_NUM 2 | |
#define PS2_LOCK_SCR 4 | |
#define PS2_LOCK_KANA 8 | |
static volatile uint8_t Lock; | |
// scancode extend,release | |
static volatile uint8_t Extend; | |
static volatile uint8_t Release; | |
static volatile uint8_t PrtScr; | |
static volatile uint8_t Pause; | |
int ps2Keyboard_DataPin; | |
// variables used to remember information about key presses | |
volatile bool ps2Keyboard_shift; // indicates shift key is pressed | |
volatile bool ps2Keyboard_ctrl; // indicates the ctrl key is pressed | |
volatile bool ps2Keyboard_alt; // indicates the alt key is pressed | |
volatile bool ps2Keyboard_extend; // remembers a keyboard extended char received | |
volatile bool ps2Keyboard_release; // distinguishes key presses from releases | |
volatile bool ps2Keyboard_caps_lock; // remembers shift lock has been pressed | |
// vairables used in sending command bytes to the keyboard, eg caps_lock light | |
volatile bool cmd_in_progress; | |
volatile int cmd_count; | |
uint8_t cmd_value; | |
volatile uint8_t cmd_ack_value; | |
uint8_t cmd_parity; | |
volatile bool cmd_ack_byte_ok; | |
#define PS2TBLSIZE 256 | |
#define EXTTBLOFFSET 0x80 | |
const uint8_t PS2UID[PS2TBLSIZE] = { | |
// no Extend 00 - 8F 16x9 = 144bytes | |
0xFF,0x42,0x00,0x3e,0x3c,0x3a,0x3b,0x45, 0x68,0x43,0x41,0x3f,0x3d,0x2b,0x35,0x67, // "" F9 "" F5 F3 F1 F2 F12 F13 F10 F8 F6 F4 TAB ` K= | |
0x69,0xe2,0xe1,0x88,0xe0,0x14,0x1e,0x00, 0x6a,0x00,0x1d,0x16,0x04,0x1a,0x1f,0x00, // F14 ALT SFT "" CTRL Q 1 "" F15 "" Z S A W 2 "" | |
0x6b,0x06,0x1b,0x07,0x08,0x21,0x20,0x8c, 0x6c,0x2c,0x19,0x09,0x17,0x15,0x22,0x00, // F16 C X D E 4 3 PC98 F17 SPC V F T R S "" | |
0x6d,0x11,0x05,0x0b,0x0a,0x1c,0x23,0x00, 0x6e,0x00,0x10,0x0d,0x18,0x24,0x25,0x00, // F18 N B H G Y 6 "" F19 "" M J U 7 8 "" | |
0x6f,0x36,0x0e,0x0c,0x12,0x27,0x26,0x00, 0x70,0x37,0x38,0x0f,0x33,0x13,0x2d,0x00, // F20 ,< K I O 0 9 F21 .> /? L ;: P -_ "" | |
0x71,0x87,0x34,0x00,0x2f,0x2e,0x00,0x72, 0x39,0xe5,0x28,0x30,0x00,0x31,0x00,0x94, // F22 JP\ '" "" [{ =+ "" F23 Cap RSFT Ret ]} "" \ "" F24 | |
0x00,0x00,0x93,0x92,0x8a,0x00,0x2a,0x8b, 0x00,0x59,0x89,0x5c,0x5f,0x85,0x00,0x00, // "" "" "" "" HENKAN "" BS MUHEN "" K1 YEN K4 K7 "" "" "" | |
0x62,0x63,0x5a,0x5d,0x5e,0x60,0x29,0x53, 0x44,0x57,0x5b,0x56,0x55,0x61,0x47,0x00, // K0 K. K2 K5 K6 K8 ESC NumLk F11 K+ K3 K- K* K9 ScrLk | |
0xFE,0x00,0x00,0x40,0x46,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x48, // "" "" "" F7 Alt+PrtScn [0x8f=Pause] | |
// Extend E010 - E07F 16x7 = 112bytes | |
0x00,0xE6,0x00,0x00,0xe4,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe3, // "" ALT-R PrtScr(1) "" CTRL-R ... META-L | |
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe7, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x65, // META-R App | |
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // KeyboardPower | |
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x54,0x00,0x00,0x00,0x00,0x00, // K/ | |
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00, // Enter | |
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x4d,0x00,0x50,0x4a,0x00,0x00,0x00, // End L-arrow Home | |
0x49,0x4c,0x51,0x00,0x4f,0x52,0x00,0x00, 0x00,0x00,0x4e,0x00,0x46,0x4b,0x48,0x48 // Ins Del D-arrow R-arrow U-arrow PD PrtScn(2) UP Break | |
} ; | |
const uint8_t PS2LEONARDO[PS2TBLSIZE] = { | |
// no Extend 00 - 8F 16x9 = 144bytes | |
0xFF,0xCA,0x00,0xC6,0xC4,0xC2,0xC3,0xCD, 0xF0,0xCB,0xC9,0xC7,0xC5,0x09,0x60,0xEF, // "" F9 "" F5 F3 F1 F2 F12 F13 F10 F8 F6 F4 TAB ` K= | |
0xF1,0x82,0x81,0x88,0x80,0x71,0x31,0x00, 0xF2,0x00,0x7A,0x73,0x61,0x77,0x32,0x00, // F14 ALT SFT KATAHIRA CTRL Q 1 "" F15 "" Z S A W 2 "" | |
0xF3,0x63,0x78,0x64,0x65,0x34,0x33,0x00, 0xF4,0x20,0x76,0x66,0x74,0x72,0x35,0x00, // F16 C X D E 4 3 PC98 F17 SPC V F T R 5 "" | |
0xF5,0x6E,0x62,0x68,0x67,0x79,0x36,0x00, 0xF6,0x00,0x6D,0x6A,0x75,0x37,0x38,0x00, // F18 N B H G Y 6 "" F19 "" M J U 7 8 "" | |
0xF7,0x2C,0x6B,0x69,0x6F,0x30,0x39,0x00, 0xF8,0x2E,0x2F,0x6C,0x3B,0x70,0x2D,0x00, // F20 ,< K I O 0 9 "" F21 .> /? L ;: P -_ "" | |
0xF9,0x5C,0x27,0x00,0x5B,0x3D,0x00,0xFA, 0xC1,0x85,0x0A,0x5D,0x00,0x5C,0x00,0xFB, // F22 JP\ '" "" [{ =+ "" F23 Cap RSFT Ret ]} "" \ "" F24 | |
0x00,0x00,0x93,0x92,0x00,0x00,0x08,0x00, 0x00,0xE1,0x5C,0xE4,0xE7,0x85,0x00,0x00, // "" "" "" "" HENKAN "" BS MUHEN "" K1 YEN K4 K7 "" "" "" | |
0xEA,0xEB,0xE2,0xE5,0xE6,0xE8,0xB1,0xDB, 0xCC,0xDF,0xE3,0xDE,0xDD,0xE9,0xCF,0x00, // K0 K. K2 K5 K6 K8 ESC NumLk F11 K+ K3 K- K* K9 ScrLk | |
0xFE,0x00,0x00,0xC8,0xCE,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0xD0,0xD0, // "" "" "" F7 Alt+PrtScn [0x8f=Pause] | |
// Extend E010 - E07F 16x7 = 112bytes | |
0x00,0x86,0x00,0x00,0x84,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x83, // "" ALT-R PrtScr(1) "" CTRL-R ... META-L | |
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x87, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xED, // META-R App* | |
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // KeyboardPower* | |
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0xDC,0x00,0x00,0x00,0x00,0x00, // K/ | |
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0xE0,0x00,0x00,0x00,0x00,0x00, // Enter | |
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0xD5,0x00,0xD8,0xD2,0x00,0x00,0x00, // End L-arrow Home | |
0xD1,0xD4,0xD9,0x00,0xD7,0xDA,0x00,0x00, 0x00,0x00,0xD6,0x00,0xCE,0xD3,0xD0,0xD0 // Ins Del D-arrow R-arrow U-arrow PD PrtScn(2) UP Break | |
} ; | |
// Low level key report: up to 6 keys and shift, ctrl etc at once | |
/* | |
typedef struct | |
{ | |
uint8_t modifiers; | |
uint8_t reserved; | |
uint8_t keys[6]; | |
} KeyReport; | |
*/ | |
KeyReport _keyReport; | |
void sendReport(KeyReport* keys) | |
{ | |
HID_SendReport(2,keys,sizeof(KeyReport)); | |
} | |
size_t rawpress(uint8_t k) | |
{ | |
uint8_t i; | |
if (!k) { | |
// setWriteError(); | |
return 0; | |
} | |
// Add k to the key report only if it's not already present | |
// and if there is an empty slot. | |
if (_keyReport.keys[0] != k && _keyReport.keys[1] != k && | |
_keyReport.keys[2] != k && _keyReport.keys[3] != k && | |
_keyReport.keys[4] != k && _keyReport.keys[5] != k) { | |
for (i=0; i<6; i++) { | |
if (_keyReport.keys[i] == 0x00) { | |
_keyReport.keys[i] = k; | |
break; | |
} | |
} | |
if (i == 6) { | |
// setWriteError(); | |
return 0; | |
} | |
} | |
sendReport(&_keyReport); | |
return 1; | |
} | |
size_t release(uint8_t k) | |
{ | |
uint8_t i; | |
if (!k) { | |
return 0; | |
} | |
// Test the key report to see if k is present. Clear it if it exists. | |
// Check all positions in case the key is present more than once (which it shouldn't be) | |
for (i=0; i<6; i++) { | |
if (0 != k && _keyReport.keys[i] == k) { | |
_keyReport.keys[i] = 0x00; | |
} | |
} | |
sendReport(&_keyReport); | |
return 1; | |
} | |
void releaseAll(void) | |
{ | |
_keyReport.keys[0] = 0; | |
_keyReport.keys[1] = 0; | |
_keyReport.keys[2] = 0; | |
_keyReport.keys[3] = 0; | |
_keyReport.keys[4] = 0; | |
_keyReport.keys[5] = 0; | |
_keyReport.modifiers = 0; | |
sendReport(&_keyReport); | |
} | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
// subroutines | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
// odd parity | |
uint8_t odd_parity(uint8_t val) { | |
val ^= val>>4; val ^= val>>2; val ^= val>>1; return (~val) & 1; | |
} | |
void v_modifier_initialize() { | |
Extend = 0; | |
Release = 0; | |
Modifier = 0; | |
PrtScr = 0; // E0 12 E0 7C / E0 F0 7C E0 F0 12 | |
Pause = 0; // Pause - E1 14 77 E1 F0 14 F0 77 (stop keyboard repeat) | |
Lock = 0; // 0000:Kana:Caps:Num:Scroll | |
ps2Keyboard_shift = false; | |
ps2Keyboard_ctrl = false; | |
ps2Keyboard_alt = false; | |
ps2Keyboard_extend = false; | |
ps2Keyboard_release = false; | |
ps2Keyboard_caps_lock = false; | |
} | |
void v_initialize() { | |
v_modifier_initialize(); | |
// reset all the global variables | |
cmd_in_progress = false; | |
cmd_count = 0; | |
cmd_value = 0; | |
cmd_ack_value = 1; | |
} | |
static inline uint8_t get_scan_code(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; | |
} | |
// The ISR for the external interrupt | |
void 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(ps2Keyboard_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; | |
} | |
} | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
// | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
// | |
// uint16_t keystatus() | |
// [15]...release | |
// [14]...CapsLock | |
// [13]...ALT_R | |
// [12]...ALT_L | |
// [11]...SHIFT_R | |
// [10]...SHIFT_L | |
// [9]...CTRL_R | |
// [8]...CTRL_L | |
// | |
uint16_t keystatus() { | |
uint16_t b=0; | |
b |= (Release)?0x8000:0; | |
b |= (Lock&PS2_LOCK_CAPS)?0x4000:0; | |
b |= (Modifier&PS2_MOD_ALT_R)?0x2000:0; | |
b |= (Modifier&PS2_MOD_ALT_L)?0x1000:0; | |
b |= (Modifier&PS2_MOD_SHIFT_R)?0x0800:0; | |
b |= (Modifier&PS2_MOD_SHIFT_L)?0x0400:0; | |
b |= (Modifier&PS2_MOD_CTRL_R)?0x0200:0; | |
b |= (Modifier&PS2_MOD_CTRL_L)?0x0100:0; | |
return b; | |
} | |
#define MOD_UID 0xe0 | |
#define MOD_LEONARDO 0x80 | |
void set_modifier(uint8_t userid, uint8_t mod_base) { // E0-E7 L-CTRL:L-SFT:L-ALT:L-META:R-CTRL:R-SFT:R-ALT:R-META | |
uint8_t b; | |
if (userid>=mod_base && userid<=(mod_base+7)) { | |
b = MODIFIERBIT[userid-mod_base]; | |
Modifier = Release?(Modifier & ~b):(Modifier | b); | |
} | |
} | |
void set_lock(uint8_t userid,uint8_t mod_base) { // Caps,Num,Scroll | |
if (Release) return; | |
switch(mod_base) { | |
case MOD_UID: { | |
switch(userid) { | |
case 0x39: { | |
Lock ^= PS2_LOCK_CAPS; | |
break; | |
} | |
case 0x53: { | |
Lock ^= PS2_LOCK_NUM; | |
break; | |
} | |
case 0x47: { | |
Lock ^= PS2_LOCK_SCR; | |
break; | |
} | |
} | |
break; | |
} | |
case MOD_LEONARDO: { | |
switch(userid) { | |
case 0xC1: { | |
Lock ^= PS2_LOCK_CAPS; | |
break; | |
} | |
case 0xDB: { | |
Lock ^= PS2_LOCK_NUM; | |
break; | |
} | |
case 0xCF: { | |
Lock ^= PS2_LOCK_SCR; | |
break; | |
} | |
} | |
break; | |
} | |
} | |
} | |
void kbd_send_command(uint8_t val) { | |
// stop interrupt routine from receiving characters so that we can use it | |
// to send a byte | |
cmd_in_progress = true; | |
cmd_count = 0; | |
// set up the byte to shift out and initialise the ack bit | |
cmd_value = val; | |
cmd_ack_value = 1; // the kbd will clear this bit on receiving the byte | |
cmd_parity = odd_parity(val); | |
// set the data pin as an output, ready for driving | |
digitalWrite(ps2Keyboard_DataPin, HIGH); | |
pinMode(ps2Keyboard_DataPin, OUTPUT); | |
// drive clock pin low - this is going to generate the first | |
// interrupt of the shifting out process | |
pinMode(PS2_INT_PIN, OUTPUT); | |
digitalWrite(PS2_INT_PIN, LOW); | |
// wait at least one clock cycle (in case the kbd is mid transmission) | |
delayMicroseconds(60); | |
// set up the 0 start bit | |
digitalWrite(ps2Keyboard_DataPin, LOW); | |
// let go of clock - the kbd takes over driving the clock from here | |
digitalWrite(PS2_INT_PIN, HIGH); | |
pinMode(PS2_INT_PIN, INPUT); | |
// wait for interrupt routine to shift out byte, parity and receive ack bit | |
while (cmd_ack_value!=0) ; | |
// switch back to the interrupt routine receiving characters from the kbd | |
cmd_in_progress = false; | |
} | |
// val : bit_3=KANA_lock, bit_2=caps_lock, bit_1=num_lock, bit_0=scroll_lock | |
void kbd_set_lights(uint8_t val) { | |
// When setting the lights with the 0xED command the keyboard responds | |
// with an "ack byte", 0xFA. This is NOT the same as the "ack bit" that | |
// follows the succesful shifting of each command byte. See this web | |
// page for a good description of all this: | |
// http://www.beyondlogic.org/keyboard/keybrd.htm | |
cmd_ack_byte_ok = false; // initialise the ack byte flag | |
kbd_send_command(0xED); // send the command byte | |
while (!cmd_ack_byte_ok) ; // ack byte from keyboard sets this flag | |
kbd_send_command(val); // now send the data | |
} | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
// PS2Keyboard | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
PS2Keyboard::PS2Keyboard() { | |
// nothing to do here | |
} | |
void PS2Keyboard::reset() { | |
kbd_send_command(0xFF); // send the kbd reset code to the kbd: 3 lights | |
// should flash briefly on the kbd | |
v_initialize(); | |
} | |
void PS2Keyboard::begin(int dataPin) { | |
v_initialize(); | |
// Prepare the global variables | |
ps2Keyboard_DataPin = dataPin; | |
// initialize the pins | |
pinMode(PS2_INT_PIN, INPUT); | |
digitalWrite(PS2_INT_PIN, HIGH); | |
pinMode(dataPin, INPUT); | |
digitalWrite(dataPin, HIGH); | |
attachInterrupt(PS2_INT_NUM, ps2interrupt, FALLING); | |
#if 0 | |
// Global Enable INT1 interrupt | |
EIMSK |= ( 1 << INT1); | |
// Falling edge triggers interrupt | |
EICRA |= (0 << ISC10) | (1 << ISC11); | |
#endif | |
} | |
bool PS2Keyboard::available() { | |
if (Tail == Head) return 0; | |
return 1 ; | |
} | |
uint8_t PS2Keyboard::read_extra() { | |
return (ps2Keyboard_caps_lock<<3) | (ps2Keyboard_shift<<2) | (ps2Keyboard_alt<<1) | ps2Keyboard_ctrl; | |
} | |
uint8_t PS2Keyboard::rawread() { | |
return get_scan_code() ; | |
} | |
uint8_t PS2Keyboard::modifier() { | |
return Modifier ; | |
} | |
uint8_t PS2Keyboard::lock() { | |
return Lock ; | |
} | |
uint8_t PS2Keyboard::led(uint8_t lamp) { | |
kbd_set_lights(lamp) ; | |
} | |
// | |
// readuid() | |
// [15]...release | |
// [14:8]...0000111 (0x07) | |
// [7:0]...USB UserID | |
// | |
uint16_t PS2Keyboard::readuid() { | |
uint8_t c; | |
uint16_t uid; | |
uint8_t cc; | |
uid=0; | |
while(c=get_scan_code()){ | |
if (Pause>0) { | |
Pause--; | |
continue; | |
} | |
cc=c; | |
switch(c) { | |
case 0xF0: { // key Release | |
Release = 1; | |
break; | |
} | |
case 0xFA: { // command ACK_BYTE | |
cmd_ack_byte_ok = true; | |
break; | |
} | |
case 0xE0: { // key Extend | |
Extend = 1; | |
break; | |
} | |
case 0xE1: { // Pause - E1 14 77 E1 F0 14 F0 77 / (stop keyboard repeat) | |
Pause=7; | |
Extend=0; Release=0; | |
uid=0x48; | |
goto readuidexit; | |
} | |
case 0x7E: { // Ctrl-Pause - E0 7E E0 F0 7E (stop keyboard repeat) | |
if (Extend!=0 && Release!=0) { | |
Extend=0; Release=0 ; | |
return 0; | |
} | |
// thru default: | |
} | |
default: { // Lookup Table | |
if (c==0x12 && Extend!=0) { // PrtScr E0 12 E0 7C / E0 F0 7C E0 F0 12 | |
Extend=0; Release=0 ; | |
return 0; | |
} | |
if (Extend) c+=EXTTBLOFFSET; | |
uid=PS2UID[c]; | |
set_modifier(uid,MOD_UID); // CTRL,SHIFT,ALT,META status | |
set_lock(uid,MOD_UID); // KANA,Caps,Num,Scroll | |
if (Release) uid |= 0x8000; | |
Extend=0; | |
Release=0; | |
return uid?(uid | HID_USAGEPAGE):0x0000 ; // 0x5555:TABLE ERROR(for debug) | |
} | |
} | |
} | |
readuidexit: | |
return uid?(uid | HID_USAGEPAGE):0 ; | |
} | |
// | |
// uint16_t readext() | |
// [15]...release | |
// [14]...CapsLock | |
// [13]...ALT_R | |
// [12]...ALT_L | |
// [11]...SHIFT_R | |
// [10]...SHIFT_L | |
// [9]...CTRL_R | |
// [8]...CTRL_L | |
// [7:0]...USB Leonardo keyboard.press code | |
// | |
uint16_t PS2Keyboard::readext() { | |
uint8_t c; | |
uint16_t uid; | |
uint8_t cc; | |
uid=0; | |
while(c=get_scan_code()){ | |
if (Pause>0) { | |
Pause--; | |
continue; | |
} | |
cc=c; | |
switch(c) { | |
case 0xF0: { // key Release | |
Release = 1; | |
break; | |
} | |
case 0xFA: { // command ACK_BYTE | |
cmd_ack_byte_ok = true; | |
break; | |
} | |
case 0xE0: { // key Extend | |
Extend = 1; | |
break; | |
} | |
case 0xE1: { // Pause - E1 14 77 E1 F0 14 F0 77 / (stop keyboard repeat) | |
Pause=7; | |
Extend=0; Release=0; | |
uid=0x48+0x88; | |
goto readextexit; | |
} | |
case 0x7E: { // Ctrl-Pause - E0 7E E0 F0 7E (stop keyboard repeat) | |
if (Extend!=0 && Release!=0) { | |
Extend=0; Release=0 ; | |
return 0; | |
} | |
// thru default: | |
} | |
default: { // Lookup Table | |
if (c==0x12 && Extend!=0) { // PrtScr E0 12 E0 7C / E0 F0 7C E0 F0 12 | |
Extend=0; Release=0 ; | |
return 0; | |
} | |
if (Extend) c+=EXTTBLOFFSET; | |
uid=PS2LEONARDO[c]; | |
set_modifier(uid,MOD_LEONARDO); // CTRL,SHIFT,ALT,META status | |
set_lock(uid,MOD_LEONARDO); // KANA,Caps,Num,Scroll | |
if (Release) uid |= 0x8000; | |
Extend=0; | |
Release=0; | |
return uid?(uid|keystatus()):0x0000 ; // 0x5555:TABLE ERROR(for debug) | |
} | |
} | |
} | |
readextexit: | |
return uid?(uid|keystatus()):0 ; | |
} | |
uint8_t PS2Keyboard::read() { | |
uint8_t result; | |
// result = getfifo() ; // read the raw data from the keyboard | |
// Use a switch for the code to character conversion. | |
// This is fast and actually only uses 4 bytes per simple line | |
switch (result) { | |
case 0x58: | |
// setting the keyboard lights is done here. Ideally it would be done | |
// in the interrupt routine itself and the key codes associated wth | |
// caps lock key presses would never be passed on as characters. | |
// However it would make the interrupt routine very messy with lots | |
// of extra state associated with the control of a caps_lock | |
// key code causing a cmd byte to transmit, causing an ack_byte to | |
// be received, then a data byte to transmit. Much easier done here. | |
// The downside, however, is that the light going on or off at the | |
// right time relies on the calling program to be checking for | |
// characters on a regular basis. If the calling program stops | |
// polling for characters at any point pressing the caps lock key | |
// will not change the state of the caps lock light while polling | |
// is not happening. | |
result = ps2Keyboard_caps_lock? PS2_KC_CLON : PS2_KC_CLOFF; | |
if (ps2Keyboard_caps_lock) kbd_set_lights(4); | |
else kbd_set_lights(0); | |
break; | |
// Reset the shift counter for unexpected values, to get back into sink | |
// This allows for hot plugging a keyboard in and out | |
default: delay(500); // but wait a bit in case part way through a shift | |
v_modifier_initialize(); | |
} // end switch(result) | |
// shift a-z chars here (less code than in the switch statement) | |
if (((result>='a') && (result<='z')) && | |
((ps2Keyboard_shift && !ps2Keyboard_caps_lock) || | |
(!ps2Keyboard_shift && ps2Keyboard_caps_lock))) { | |
result = result + ('A'-'a'); | |
} | |
return(result); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef PS2Keyboard_h | |
#define PS2Keyboard_h | |
#include <avr/io.h> | |
#include <avr/interrupt.h> | |
#include <avr/pgmspace.h> | |
/* | |
* PS2 keyboard "make" codes to check for certain keys. | |
*/ | |
// Give these codes that aren't used by anything else | |
// Making all the control key codes above 0x80 makes it simple to check for | |
// printable characters at the calling level. | |
#define PS2_KC_BKSP 0x80 | |
#define PS2_KC_UP 0x81 | |
#define PS2_KC_DOWN 0x82 | |
#define PS2_KC_LEFT 0x83 | |
#define PS2_KC_RIGHT 0x84 | |
#define PS2_KC_PGDN 0x85 | |
#define PS2_KC_PGUP 0x86 | |
#define PS2_KC_END 0x87 | |
#define PS2_KC_HOME 0x88 | |
#define PS2_KC_INS 0x89 | |
#define PS2_KC_DEL 0x8A | |
#define PS2_KC_ESC 0x8B | |
#define PS2_KC_CLON 0x8C // caps_lock on | |
#define PS2_KC_CLOFF 0x8D // caps_lock off | |
#include "binary.h" | |
typedef uint8_t boolean; | |
/* | |
* This PIN is hardcoded in the init routine later on. If you change this | |
* make sure you change the interrupt initialization as well. | |
PS2_INT_PIN 2 / attachInterrupt(0, ps2interrupt, FALLING); | |
PS2_INT_PIN 3 / attachInterrupt(1, ps2interrupt, FALLING); | |
*/ | |
// #define PS2_INT_PIN 3 | |
// #define PS2_INT_NUM 1 | |
// for Leonardo IDE1.0.1 | |
#define PS2_INT_PIN 2 | |
#define PS2_INT_NUM 1 | |
/** | |
* Purpose: Provides an easy access to PS2 keyboards | |
* Author: Christian Weichel | |
*/ | |
class PS2Keyboard { | |
private: | |
int m_dataPin; | |
uint8_t m_charBuffer; | |
public: | |
/** | |
* This constructor does basically nothing. Please call the begin(int) | |
* method before using any other method of this class. | |
*/ | |
PS2Keyboard(); | |
/** | |
* Starts the keyboard "service" by registering the external interrupt. | |
* setting the pin modes correctly and driving those needed to high. | |
* The propably best place to call this method is in the setup routine. | |
*/ | |
void begin(int dataPin); | |
/** | |
* Returns true if there is a char to be read, false if not. | |
*/ | |
bool available(); | |
/** | |
* Sends a reset command to the keyboard and re-initialises all the control | |
* variables within the PS2Keybaord code. | |
*/ | |
void reset(); | |
/** | |
* Returns the char last read from the keyboard. If the user has pressed two | |
* keys between calls to this method, only the later one will be availble. Once | |
* the char has been read, the buffer will be cleared. | |
* If there is no char availble, 0 is returned. | |
*/ | |
uint8_t read(); | |
uint8_t rawread(); | |
uint8_t modifier(); | |
uint8_t lock(); | |
uint8_t led(uint8_t lamp); | |
/** | |
* Returns the status of the <ctrl> key, the <alt> key, the <shift> key and the | |
* caps_lock state. Note that shift and caps_lock are handled within the | |
* Ps2Keyboard code (and the return value from read() is already modified), but | |
* being able to read them here may be useful. | |
* This routine is optional BUT MUST ONLY be read after available() has returned | |
* true and BEFORE read() is called to retrieve the character. Reading it after | |
* the call to read() will return unpredictable values. | |
*/ | |
uint8_t read_extra(); | |
uint16_t readext(); | |
uint16_t readuid(); | |
}; | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment