Skip to content

Instantly share code, notes, and snippets.

@houmei
Created October 3, 2012 15:04
Show Gist options
  • Save houmei/3827425 to your computer and use it in GitHub Desktop.
Save houmei/3827425 to your computer and use it in GitHub Desktop.
USB HID keyboard memo (arduinoIDE1.0.1)
/*
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);
}
#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