21 #if defined(ARDUINO) && ARDUINO >= 100 27 #include <util/atomic.h> 120 template<
int DataPin,
int ClockPin,
int BufferSize = 16,
typename Diagnostics = NullDiagnostics>
122 static const uint8_t immediateResponseTimeInMilliseconds = 10;
124 Diagnostics *diagnostics;
129 uint8_t bitCounter = 0;
130 uint32_t failureTimeMicroseconds = 0;
131 uint32_t lastReadInterruptMicroseconds = 0;
133 bool receivedHasFramingError =
false;
136 KeyboardOutputBuffer<BufferSize, Diagnostics> inputBuffer;
140 enum class ps2CommandCode : uint8_t {
143 disableBreakAndTypematicForSpecificKeys = 0xfd,
144 disableTypematicForSpecificKeys = 0xfc,
145 disableBreaksForSpecificKeys = 0xfb,
146 enableBreakAndTypeMaticForAllKeys = 0xfa,
147 disableBreakAndTypematicForAllKeys = 0xf9,
148 disableTypematicForAllKeys = 0xf8,
149 disableBreaksForAllKeys = 0xf7,
150 useDefaultSettings = 0xf6,
153 setTypematicRate = 0xf3,
155 setScanCodeSet = 0xf0,
160 void readInterruptHandler() {
169 uint8_t dataPinValue = (*portInputRegister(digitalPinToPort(DataPin)) & digitalPinToBitMask(DataPin)) ? 1 : 0;
171 lastReadInterruptMicroseconds = micros();
188 if (dataPinValue == 0) {
189 receivedHasFramingError =
false;
192 this->diagnostics->packetDidNotStartWithZero();
197 receivedHasFramingError =
true;
198 failureTimeMicroseconds = lastReadInterruptMicroseconds;
213 ioByte |= (1 << (bitCounter - 1));
219 if (parity != (
Parity)dataPinValue) {
220 this->diagnostics->parityError();
221 receivedHasFramingError =
true;
222 failureTimeMicroseconds = lastReadInterruptMicroseconds;
227 if (dataPinValue == 0) {
228 this->diagnostics->packetDidNotEndWithOne();
229 receivedHasFramingError =
true;
230 failureTimeMicroseconds = lastReadInterruptMicroseconds;
233 if (!receivedHasFramingError) {
234 this->diagnostics->receivedByte(ioByte);
242 void writeInterruptHandler() {
258 valueToSend = (this->ioByte & (1 << (bitCounter - 1))) ? HIGH : LOW;
259 digitalWrite(DataPin, valueToSend);
260 parity ^= valueToSend;
264 digitalWrite(DataPin, (uint8_t)parity);
268 pinMode(DataPin, INPUT_PULLUP);
272 if (digitalRead(DataPin) != LOW) {
275 this->diagnostics->sendFrameError();
278 enableReadInterrupts();
283 void sendByte(uint8_t byte1) {
287 detachInterrupt(digitalPinToInterrupt(ClockPin));
290 pinMode(ClockPin, OUTPUT);
291 digitalWrite(ClockPin, LOW);
292 delayMicroseconds(120);
297 this->receivedHasFramingError =
false;
298 this->inputBuffer.clear();
299 this->bitCounter = 0;
301 this->ioByte = byte1;
302 attachInterrupt(digitalPinToInterrupt(ClockPin), Keyboard::staticWriteInterruptHandler, FALLING);
305 pinMode(DataPin, OUTPUT);
306 digitalWrite(DataPin, LOW);
307 pinMode(ClockPin, INPUT_PULLUP);
310 void enableReadInterrupts() {
311 this->receivedHasFramingError =
false;
312 this->bitCounter = 0;
315 this->inputBuffer.clear();
317 attachInterrupt(digitalPinToInterrupt(ClockPin), Keyboard::staticReadInterruptHandler, FALLING);
322 static void staticReadInterruptHandler() {
323 instance->readInterruptHandler();
326 static void staticWriteInterruptHandler() {
327 instance->writeInterruptHandler();
336 KeyboardOutput expectResponse(uint16_t timeoutInMilliseconds = immediateResponseTimeInMilliseconds)
338 unsigned long startMilliseconds = millis();
339 unsigned long stopMilliseconds = startMilliseconds + timeoutInMilliseconds;
340 unsigned long nowMilliseconds;
344 actualResponse = this->inputBuffer.peek();
350 this->receivedHasFramingError =
false;
352 nowMilliseconds = millis();
353 }
while (actualResponse ==
KeyboardOutput::none && (nowMilliseconds < stopMilliseconds || (stopMilliseconds < startMilliseconds && startMilliseconds <= nowMilliseconds)));
358 return actualResponse;
365 bool expectResponse(
KeyboardOutput expectedResponse, uint16_t timeoutInMilliseconds = immediateResponseTimeInMilliseconds) {
366 KeyboardOutput actualResponse = expectResponse(timeoutInMilliseconds);
372 else if ( actualResponse != expectedResponse ) {
373 this->diagnostics->incorrectResponse(actualResponse, expectedResponse);
377 this->inputBuffer.pop();
386 bool sendCommand(ps2CommandCode command) {
387 return this->sendData((byte)command);
390 bool sendCommand(ps2CommandCode command, byte data) {
391 return this->sendCommand(command)
392 && this->sendData(data);
395 bool sendCommand(ps2CommandCode command,
const byte *data,
int numBytes) {
396 if (!this->sendCommand(command)) {
399 for (
int i = 0; i < numBytes; ++i) {
400 if (!this->sendData(data[i])) {
407 bool sendData(byte data) {
408 this->diagnostics->sentByte(data);
409 this->sendByte(data);
410 bool isOk = this->expectAck();
413 this->enableReadInterrupts();
419 this->diagnostics->sentByte((byte)ps2CommandCode::resend);
420 this->sendByte((byte)ps2CommandCode::resend);
424 Keyboard(Diagnostics &diagnostics = *Diagnostics::defaultInstance())
425 : inputBuffer(diagnostics)
427 this->diagnostics = &diagnostics;
437 pinMode(ClockPin, INPUT_PULLUP);
438 pinMode(DataPin, INPUT_PULLUP);
442 digitalRead(DataPin);
444 this->enableReadInterrupts();
514 if (failureTimeMicroseconds + 200 > micros()) {
517 if (this->bitCounter > 3) {
521 this->diagnostics->clockLineGlitch(this->bitCounter);
522 this->receivedHasFramingError =
false;
523 this->bitCounter = 0;
537 code = this->inputBuffer.pop();
540 diagnostics->startupFailure();
541 code = this->inputBuffer.pop();
556 return sendCommand(ps2CommandCode::setLeds, (byte)ledStatus);
566 bool reset(uint16_t timeoutInMillis = 1000) {
567 this->inputBuffer.clear();
568 this->sendCommand(ps2CommandCode::reset);
576 this->sendCommand(ps2CommandCode::readId);
581 this->inputBuffer.pop();
582 uint16_t
id = ((uint8_t)response) << 8;
583 response = this->expectResponse();
587 this->inputBuffer.pop();
588 id |= (uint8_t)response;
595 if (!this->sendCommand(ps2CommandCode::setScanCodeSet, 0)) {
600 this->inputBuffer.pop();
612 return this->sendCommand(ps2CommandCode::setScanCodeSet, (byte)newScanCodeSet);
621 this->sendByte((byte)ps2CommandCode::echo);
630 byte combined = (byte)rate | (((byte)startDelay) << 4);
631 return this->sendCommand(ps2CommandCode::setTypematicRate, combined);
639 return this->sendCommand(ps2CommandCode::useDefaultSettings);
645 bool enable() {
return this->sendCommand(ps2CommandCode::enable); }
650 bool disable() {
return this->sendCommand(ps2CommandCode::disable); }
656 return this->sendCommand(ps2CommandCode::enableBreakAndTypeMaticForAllKeys);
663 return this->sendCommand(ps2CommandCode::disableBreaksForAllKeys);
679 return this->sendCommand(ps2CommandCode::disableBreaksForSpecificKeys, specificKeys, numKeys);
686 return this->sendCommand(ps2CommandCode::disableTypematicForAllKeys);
693 return this->sendCommand(ps2CommandCode::disableBreakAndTypematicForAllKeys);
708 return this->sendCommand(ps2CommandCode::disableTypematicForSpecificKeys, specificKeys, numKeys);
725 return this->sendCommand(ps2CommandCode::disableBreakAndTypematicForSpecificKeys, specificKeys, numKeys);
729 template<
int DataPin,
int ClockPin,
int BufferSize,
typename Diagnostics>
KeyboardOutput readScanCode()
Returns the last code sent by the keyboard.
Definition: ps2_Keyboard.h:493
bool resetToDefaults()
Restores scan code set, typematic rate, and typematic delay.
Definition: ps2_Keyboard.h:637
bool disableBreakAndTypematic(const byte *specificKeys, int numKeys)
Instructs the keyboard to disable typematic (additional key down events when the user presses and hol...
Definition: ps2_Keyboard.h:724
Keyboard(Diagnostics &diagnostics= *Diagnostics::defaultInstance())
Definition: ps2_Keyboard.h:424
bool awaitStartup(uint16_t timeoutInMillis=750)
After the keyboard gets power, the PS2 keyboard sends a code that indicates successful or unsuccessfu...
Definition: ps2_Keyboard.h:472
bool reset(uint16_t timeoutInMillis=1000)
Resets the keyboard and returns true if the keyboard appears well, false otherwise.
Definition: ps2_Keyboard.h:566
bool disableTypematic(const byte *specificKeys, int numKeys)
Instructs the keyboard to disable typematic (additional key down events when the user presses and hol...
Definition: ps2_Keyboard.h:707
ScanCodeSet getScanCodeSet()
Gets the current scancode set.
Definition: ps2_Keyboard.h:594
Definition: ps2_AnsiTranslator.h:24
bool disableBreakCodes()
Makes the keyboard no longer send "break" or "unmake" events when keys are released.
Definition: ps2_Keyboard.h:662
bool enableBreakAndTypematic()
Re-enables typematic and break (unmake, key release) for all keys.
Definition: ps2_Keyboard.h:655
bool setTypematicRateAndDelay(TypematicRate rate, TypematicStartDelay startDelay)
Sets the typematic rate (how fast presses come) and the delay (the time between key down and the star...
Definition: ps2_Keyboard.h:629
bool disableBreakCodes(const byte *specificKeys, int numKeys)
Instructs the keyboard to stop sending "break" codes for some keys (that is, it disables the notifica...
Definition: ps2_Keyboard.h:678
ScanCodeSet
Definition: ps2_ScanCodeSet.h:22
bool enable()
Allows the keyboard to start sending data again.
Definition: ps2_Keyboard.h:645
Parity
Definition: ps2_Parity.h:22
bool disableBreakAndTypematic()
Re-enables typematic and break for all keys.
Definition: ps2_Keyboard.h:692
KeyboardOutput
Byte-codes sent back from the Ps2 keyboard to the host.
Definition: ps2_KeyboardOutput.h:31
bool disableTypematic()
Makes the keyboard no longer send multiple key down events while a key is held down.
Definition: ps2_Keyboard.h:685
bool disable()
Makes it so the keyboard will no longer responsd to keystrokes.
Definition: ps2_Keyboard.h:650
KeyboardLeds
The LED's available on a standard PS2 keyboard.
Definition: ps2_KeyboardLeds.h:23
bool sendLedStatus(KeyboardLeds ledStatus)
Sets the keyboard's onboard LED's.
Definition: ps2_Keyboard.h:554
TypematicRate
Definition: ps2_TypematicRate.h:22
TypematicStartDelay
Definition: ps2_TypematicStartDelay.h:22
void begin()
Definition: ps2_Keyboard.h:436
bool setScanCodeSet(ScanCodeSet newScanCodeSet)
Sets the current scancode set.
Definition: ps2_Keyboard.h:611
uint16_t readId()
Returns the device ID returned by the keyboard - according to the documentation, this will always be ...
Definition: ps2_Keyboard.h:575
Instances of this class can be used to interface with a PS2 keyboard. This class does not decode the ...
Definition: ps2_Keyboard.h:121
bool echo()
Sends the "Echo" command to the keyboard, which should send an "Echo" in return. This can be used to ...
Definition: ps2_Keyboard.h:620