Created
September 16, 2017 05:40
-
-
Save lpereira/7934df291b7bf1d83e889445afe937a7 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
/* Based on code by user DorianRudolph on GitHub | |
https://gist.github.com/DorianRudolph/ca283dfdfd185bc812b7 | |
*/ | |
/* | |
TODO: | |
- use menu key as a Fn key to enable multimedia control | |
- Fn+F1 = toggle mute | |
- Fn+F2 = vol up | |
- Fn+F3 = vol dn | |
- Fn+F5 = prev track | |
- Fn+F6 = next track | |
- Fn+F12 = toggle mouse movement (mx+1, mx-1 every second) | |
*/ | |
#include <Mouse.h> | |
#include <Keyboard.h> | |
static const uint8_t PROGMEM keymap_tbl[256] = { | |
0, 66, 0, 62, 60, 58, 59, 69, 0, 67, 65, 63, 61, 43, 53, 0, 0, 226, | |
225, 0, 224, 20, 30, 0, 0, 0, 29, 22, 4, 26, 31, 0, 0, 6, 27, 7, | |
8, 33, 32, 0, 0, 44, 25, 9, 23, 21, 34, 0, 0, 17, 5, 11, 10, 28, | |
35, 0, 0, 0, 16, 13, 24, 36, 37, 0, 0, 54, 14, 12, 18, 39, 38, 0, | |
0, 55, 56, 15, 51, 19, 45, 0, 0, 0, 52, 0, 47, 46, 0, 0, 57, 229, | |
40, 48, 0, 49, 0, 0, 0, 100, 0, 0, 0, 0, 42, 0, 0, 89, 0, 92, | |
95, 0, 0, 0, 98, 99, 90, 93, 94, 96, 41, /*scrlk*/71, 68, 87, 91, 86, 85, 97, | |
83, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0 | |
}; | |
static const uint8_t PROGMEM keymap_ext_tbl[256] = { | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 0, | |
0, 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 227, 0, 0, 0, 0, 0, 0, | |
0, 231, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 77, 0, 80, 74, 0, 0, 0, 73, 76, | |
81, 0, 79, 82, 0, 0, 0, 0, 78, 0, 70, 75, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0 | |
}; | |
class RingBuffer { | |
private: | |
uint8_t buffer[16]; | |
uint8_t first{0}, last{0}, pop{0}; | |
public: | |
void put(uint8_t b) { | |
if (pop == sizeof(buffer)) return; | |
buffer[last] = b; | |
last = (last + 1) % sizeof(buffer); | |
pop++; | |
} | |
uint8_t get() { | |
if (!pop) return 0; | |
uint8_t v = buffer[first]; | |
first = (first + 1) % sizeof(buffer); | |
pop--; | |
return v; | |
} | |
uint8_t size() { | |
return pop; | |
} | |
bool empty() { | |
return pop == 0; | |
} | |
}; | |
class PS2Device { | |
private: | |
const int dataPin, clkPin; | |
uint32_t prev_ms{0}; | |
uint8_t sendBits{0}; | |
uint8_t bitcount{0}; | |
uint8_t incoming{0}; | |
volatile uint8_t msg{0}; | |
volatile uint8_t bitCount{0}; | |
volatile uint8_t setBits{0}; | |
protected: | |
RingBuffer buffer; | |
void beginBase(); | |
public: | |
PS2Device(int dataPin_, int clkPin_) | |
: dataPin(dataPin_) | |
, clkPin(clkPin_) {} | |
void handleInterruptImpl(); | |
uint8_t readByte() { | |
return buffer.get(); | |
} | |
void writeByte(uint8_t msg); | |
bool available() { | |
return !buffer.empty(); | |
} | |
}; | |
class PS2Keyboard : public PS2Device { | |
private: | |
KeyReport report; | |
uint8_t leds{0}; | |
bool send_leds{0}; | |
void addReport(uint8_t k); | |
void removeReport(uint8_t k); | |
public: | |
PS2Keyboard(int dataPin_, int clkPin_) : PS2Device(dataPin_, clkPin_) {} | |
void loop(); | |
void begin() { | |
beginBase(); | |
} | |
void toggleLED(int led); | |
static void handleInterrupt(); | |
}; | |
class PS2Mouse : public PS2Device { | |
private: | |
bool gotDataReportAck{false}; | |
char lastState{0}; | |
void controlUsbMouse(int button, int mask); | |
public: | |
PS2Mouse(int dataPin_, int clkPin_) : PS2Device(dataPin_, clkPin_) {} | |
void loop(); | |
void begin(); | |
static void handleInterrupt(); | |
}; | |
PS2Keyboard ps2Keyboard(4, 3); | |
PS2Mouse ps2Mouse(5, 2); | |
void setup() { | |
Serial.begin(9600); | |
ps2Keyboard.begin(); | |
Keyboard.begin(); | |
ps2Mouse.begin(); | |
Mouse.begin(); | |
} | |
void loop() { | |
if (ps2Keyboard.available()) | |
ps2Keyboard.loop(); | |
if (ps2Mouse.available()) | |
ps2Mouse.loop(); | |
} | |
void PS2Mouse::begin() { | |
beginBase(); | |
writeByte(0xF4); // Enable data reporting | |
} | |
void PS2Mouse::handleInterrupt() { | |
ps2Mouse.handleInterruptImpl(); | |
} | |
void PS2Mouse::loop() { | |
if (!gotDataReportAck) { | |
gotDataReportAck = readByte() == 0xFA; | |
return; | |
} | |
if (buffer.size() != 3) return; | |
int8_t btnState = readByte(); | |
int8_t mx = readByte(); | |
int8_t my = readByte(); | |
if (!(btnState & 1 << 3)) return; /* this bit should be always 1 */ | |
if (Mouse.isPressed(MOUSE_MIDDLE) && my != 0) | |
Mouse.move(0, 0, my); | |
else if (mx || my) | |
Mouse.move(mx, -my, 0); | |
if (btnState != lastState) { | |
lastState = btnState; | |
controlUsbMouse(MOUSE_LEFT, 1 << 0); | |
controlUsbMouse(MOUSE_RIGHT, 1 << 1); | |
controlUsbMouse(MOUSE_MIDDLE, 1 << 2); | |
} | |
} | |
void PS2Mouse::controlUsbMouse(int button, int mask) | |
{ | |
if (lastState & mask) | |
Mouse.press(button); | |
else if (Mouse.isPressed(button)) | |
Mouse.release(button); | |
} | |
void PS2Keyboard::handleInterrupt() { | |
ps2Keyboard.handleInterruptImpl(); | |
} | |
void PS2Keyboard::toggleLED(int led) | |
{ | |
leds ^= 1 << led; | |
send_leds = true; | |
writeByte(0xED); | |
} | |
void PS2Keyboard::loop() { | |
static bool brk, ext; | |
static uint8_t skip; | |
uint8_t k; | |
k = ps2Keyboard.readByte(); | |
if (!k) return; | |
if (skip) { | |
--skip; | |
return; | |
} | |
if (k == 0xE0) { | |
ext = true; | |
return; | |
} | |
if (k == 0xF0) { | |
brk = true; | |
return; | |
} | |
if (k == 0xFA) { | |
if (send_leds) { | |
send_leds = false; | |
writeByte(leds); | |
} | |
return; | |
} | |
if (k == 0xE1) { | |
k = 72; | |
skip = 7; | |
brk = true; | |
addReport(k); | |
Keyboard.sendReport(&report); | |
} else { | |
k = pgm_read_byte(ext ? &keymap_ext_tbl[k] : &keymap_tbl[k]); | |
} | |
if (k) { | |
if (brk) { | |
removeReport(k); | |
switch (k) { | |
case 71: toggleLED(0); break; | |
case 83: toggleLED(1); break; | |
case 57: toggleLED(2); break; | |
} | |
} else { | |
addReport(k); | |
} | |
Keyboard.sendReport(&report); | |
} | |
brk = false; | |
ext = false; | |
} | |
void PS2Keyboard::addReport(uint8_t k) { | |
if (k >= 224) { | |
report.modifiers |= 1 << (k - 224); | |
return; | |
} | |
for (uint8_t i = 0; i < 6; i++) { | |
if (report.keys[i] == k) return; | |
} | |
for (uint8_t i = 0; i < 6; ++i) { | |
if (report.keys[i] == 0) { | |
report.keys[i] = k; | |
break; | |
} | |
} | |
} | |
void PS2Keyboard::removeReport(uint8_t k) { | |
if (k >= 224) { | |
report.modifiers &= ~(1 << (k - 224)); | |
return; | |
} | |
for (uint8_t i = 0; i < 6; ++i) { | |
if (report.keys[i] == k) { | |
report.keys[i] = 0; | |
return; | |
} | |
} | |
} | |
void PS2Device::beginBase() | |
{ | |
if (this == &ps2Keyboard) | |
attachInterrupt(digitalPinToInterrupt(clkPin), PS2Keyboard::handleInterrupt, FALLING); | |
else if (this == &ps2Mouse) | |
attachInterrupt(digitalPinToInterrupt(clkPin), PS2Mouse::handleInterrupt, FALLING); | |
pinMode(clkPin, INPUT_PULLUP); | |
pinMode(dataPin, INPUT_PULLUP); | |
} | |
void PS2Device::writeByte(uint8_t m) { | |
noInterrupts(); | |
pinMode(clkPin, OUTPUT); | |
digitalWrite(clkPin, LOW); | |
delayMicroseconds(60); | |
pinMode(clkPin, INPUT_PULLUP); | |
msg = m; | |
bitCount = 0; | |
setBits = 0; | |
sendBits = 12; | |
pinMode(dataPin, OUTPUT); | |
digitalWrite(dataPin, LOW); | |
interrupts(); | |
} | |
void PS2Device::handleInterruptImpl() | |
{ | |
uint32_t now_ms; | |
uint8_t n, val; | |
if (!sendBits) { | |
val = digitalRead(dataPin); | |
now_ms = millis(); | |
if (now_ms - prev_ms > 1000) { | |
bitcount = 0; | |
incoming = 0; | |
} | |
prev_ms = now_ms; | |
n = bitcount - 1; | |
if (n <= 7) { | |
incoming |= (val << n); | |
} | |
bitcount++; | |
if (bitcount == 11) { | |
buffer.put(incoming); | |
bitcount = 0; | |
incoming = 0; | |
} | |
} else { | |
--sendBits; | |
uint8_t b = bitCount - 1; | |
if (b == 8) { | |
digitalWrite(dataPin, !(setBits & 1)); | |
} else if (b == 9) { | |
pinMode(dataPin, INPUT_PULLUP); | |
} else if (b < 8) { | |
bool bt = (msg >> b) & 1; | |
digitalWrite(dataPin, bt); | |
setBits += bt; | |
} | |
++bitCount; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment