Instances of this class can be used to interface with a PS2 keyboard. This class does not decode the keystroke protocol, but rather provides the data from the keyboard in a raw form. You either need to write code that directly understands what the PS2 is providing, or use one of the provided translator classes (e.g. AnsiTranslator).
More...
|
| Keyboard (Diagnostics &diagnostics= *Diagnostics::defaultInstance()) |
|
void | begin () |
|
bool | awaitStartup (uint16_t timeoutInMillis=750) |
| After the keyboard gets power, the PS2 keyboard sends a code that indicates successful or unsuccessful startup. This waits for that to happen. More...
|
|
KeyboardOutput | readScanCode () |
| Returns the last code sent by the keyboard. More...
|
|
bool | sendLedStatus (KeyboardLeds ledStatus) |
| Sets the keyboard's onboard LED's. More...
|
|
bool | reset (uint16_t timeoutInMillis=1000) |
| Resets the keyboard and returns true if the keyboard appears well, false otherwise. More...
|
|
uint16_t | readId () |
| Returns the device ID returned by the keyboard - according to the documentation, this will always be 0xab83. In the event of an error, this will return 0xffff. More...
|
|
ScanCodeSet | getScanCodeSet () |
| Gets the current scancode set. More...
|
|
bool | setScanCodeSet (ScanCodeSet newScanCodeSet) |
| Sets the current scancode set. More...
|
|
bool | echo () |
| Sends the "Echo" command to the keyboard, which should send an "Echo" in return. This can be used to verifies that a keyboard is connected and working properly. More...
|
|
bool | setTypematicRateAndDelay (TypematicRate rate, TypematicStartDelay startDelay) |
| Sets the typematic rate (how fast presses come) and the delay (the time between key down and the start of auto-repeating. More...
|
|
bool | resetToDefaults () |
| Restores scan code set, typematic rate, and typematic delay. More...
|
|
bool | enable () |
| Allows the keyboard to start sending data again. More...
|
|
bool | disable () |
| Makes it so the keyboard will no longer responsd to keystrokes. More...
|
|
bool | enableBreakAndTypematic () |
| Re-enables typematic and break (unmake, key release) for all keys. More...
|
|
bool | disableBreakCodes () |
| Makes the keyboard no longer send "break" or "unmake" events when keys are released. More...
|
|
bool | disableBreakCodes (const byte *specificKeys, int numKeys) |
| Instructs the keyboard to stop sending "break" codes for some keys (that is, it disables the notifications that come when a key is released.) More...
|
|
bool | disableTypematic () |
| Makes the keyboard no longer send multiple key down events while a key is held down. More...
|
|
bool | disableBreakAndTypematic () |
| Re-enables typematic and break for all keys. More...
|
|
bool | disableTypematic (const byte *specificKeys, int numKeys) |
| Instructs the keyboard to disable typematic (additional key down events when the user presses and holds a key) for a given set of keys. More...
|
|
bool | disableBreakAndTypematic (const byte *specificKeys, int numKeys) |
| Instructs the keyboard to disable typematic (additional key down events when the user presses and holds a key) and break sequences (events that tell you when a key has been released) for a given set of keys. More...
|
|
template<int DataPin, int ClockPin, int BufferSize = 16, typename Diagnostics = NullDiagnostics>
class ps2::Keyboard< DataPin, ClockPin, BufferSize, Diagnostics >
Instances of this class can be used to interface with a PS2 keyboard. This class does not decode the keystroke protocol, but rather provides the data from the keyboard in a raw form. You either need to write code that directly understands what the PS2 is providing, or use one of the provided translator classes (e.g. AnsiTranslator).
- Template Parameters
-
DataPin | The pin number of the pin that's connected to the PS2 data wire. |
ClockPin | The pin number of the pin that's connected to the PS2 clock wire. This pin must be one that supports interrupts on your board. |
BufferSize | The size of the internal buffer that stores the bytes coming from the keyboard between the time they're received from the ClockPin-based interrupts and the time they're consumed by calls to readScanCode. If you are calling that method very frequently (many times per millisecond) then 1 is enough. Expect each keystroke to eat about 4 bytes, so 16 can hold up to 4 keystrokes. There's nothing wrong with larger numbers, but you probably want some amount of responsiveness to user commands. |
Diagnostics | A class that will record any unpleasantness that befalls your program such as the aforementioned buffer overflows. This is purely a debugging aid. If you don't need to be doing any debugging, you can stick wit the default, NullDiagnostics, which has no-op implementations for all the event handlers. The C++ compiler will optimize empty implementations completely away, so you pay no penalty for having this debug code in your project if you don't use it. The SimpleDiagnostics implementation provides an implementation that records all the events. |
A great source of information about the PS2 keyboard can be found here: http://www.computer-engineering.org/ps2keyboard/. This documentation assumes you have a basic grasp of that.
Most programs will use this class like this:
void setup() {
ps2Keyboard.begin();
ps2Keyboard.reset();
}
void loop() {
respondTo(scanCode);
};
}
In this example, the keyboard is wired up with the data pin connected to pin 4 and the clock pin connected to pin 2. The setup function should start up the keyboard. In many cases, you really don't have to do anything other than call 'begin' to get going. Here, it calls 'reset' to undo whatever mode the keyboard might already be in. In the real world, this is unlikely to ever be necessary, but there you are. A more likely move would be to set the keyboard up in the PS/2 scan code set and perhaps disabling typematic or enabling unmake codes to make it easier to interpret the keyboard's protocol.
You should poll the keyboard from your loop implementation, as illustrated here.
With all things Arduino, you should understand the performance. readScanCode takes only a few machine instructions to complete. The methods that push data to the keyboard take longer (on the order of milliseconds) because of the handshake between the two devices and the throttling of sending one bit at a time at 10KHz.
All of the keyboard setup methods return a bool that reports success or not; you can test them if you feel it necessary, but few applications will really need to.
The biggest source of legitimate error is long-running interrupts which cause the PS2 clock interrupt to be skipped. The response to the clock pin must be swift and consistent, else you'll get garbled messages. The protocol is just robust enough to know that a failure has happened, but not robust enough to fully recover. If you're going to have another interrupt source in your project, see to it that its interrupt handler is as quick as possible. For its part, the PS2 handler is just a few instructions in all cases.
template<int DataPin, int ClockPin, int BufferSize = 16, typename Diagnostics = NullDiagnostics>
bool ps2::Keyboard< DataPin, ClockPin, BufferSize, Diagnostics >::awaitStartup |
( |
uint16_t |
timeoutInMillis = 750 | ) |
|
|
inline |
After the keyboard gets power, the PS2 keyboard sends a code that indicates successful or unsuccessful startup. This waits for that to happen.
- Parameters
-
timeoutInMillis | The number of milliseconds to wait for the keyboard to send its success/fail message. |
All this function does is wait for a proscribed amount of time (750ms, as suggested by the documentation), for a batSuccessful code. That's straightforward. The thing to be careful of is this - if you call this function in your begin() and then upload the program to your Arduino, all this is going to do is delay for 750ms and then throw a diagnostic code - that's because the keyboard has power throughout the event and so doesn't power-on.
But once you get your device into the field, you really can count on the proper behavior (well, unless you have, say, a reset button that resets the Arduino).
There are two reasonable ways to deal with this - first, if you need to set up the keyboard, e.g. set up a scan code set and all that, you need to call this method before you get up to your shennanigans - the keyboard just won't respond until it's done booting. If that's you, and you're keen on having Diagnostic data, you can call SimpleDiagnostic::reset right after this to clean it up. If you're really keen on diagnostics, then you'll set up a "RELEASE" preprocessor symbol and skip the reset when you compile in that mode.
If you don't actually need to do any setup, then you can just skip calling this method. Yes, the keyboard will send that batSuccessful (hopefully) signal later on, but readScanCode specifically looks for that code and drops it on the floor when it arrives.
template<int DataPin, int ClockPin, int BufferSize = 16, typename Diagnostics = NullDiagnostics>
Returns the last code sent by the keyboard.
You should call this function frequently, in your loop implementation most likely. The more often you call it, the more responsive your device will be. If you cannot call it frequently, make sure you have an appropriately-sized buffer (BufferSize).
If there is nothing to read, this method will return 'none'.
It can also return 'garbled' if there's been a framing error. A retry will be attempted, but that's far from a sure-thing. If you get this result, it likely indicates a collision with one of your interrupt handlers. Look into reducing the amount of processing you do in your interrupt handlers.
In any case, if you see a 'garbled' result, you might be losing some keystrokes, so do what you can to make sure that it doesn't impact the device too badly.