PS2 Keyboard timing woes
#1
Posted 10 November 2010 - 12:15 PM
I ported over the Arduino PS2 keyboard driver but unfortunately I cannot seem to read the PS2 data line fast enough during the clock interrupt.
The same pinout etc. was tested and working on an Arduino so it seems to be an interop speed issue.
Has anyone else managed to get this working?
Thanks
-(e)
#2
Posted 14 November 2010 - 10:58 PM
private void clockPort_OnInterrupt(uint data1, uint data2, DateTime time) { this.Clock(this.data); } private void dataPort_OnInterrupt(uint data1, uint data2, DateTime time) { this.data = (data2 == 0) ? false : true; }
It almost works but it still drops bits, especially when something is happening in the background.
According to this page,
, which implies that it may be possible to implement some sort of protocol to pause the keyboard while some processing is happening, possibly "driving" the pins by setting the pull-up/pull-down resistors, eg:The KBD Clock line can be used as a Clear to Send line. If the host takes the KBD Clock line low, the keyboard will buffer any data until the KBD Clock is released
this.dataPort.Resistor = Port.ResistorMode.PullDown; this.clockPort.Resistor = Port.ResistorMode.PullDown;, Although this throws a runtime exception. Other dead-ends include creating an OutputPort on the same pin as the InterruptPort, or using TristatePorts with interrupt handlers. Its puzzling that this is even an issue considering the keyboard should only be transmitting at ~25kHz.
#3
Posted 15 November 2010 - 03:59 AM
I think the netduino processor only has pullup resistors and that would be why it would thow an error when you selected pulldown.Seem to be having some success (not alot though) using one interrupt per pin, eg:
private void clockPort_OnInterrupt(uint data1, uint data2, DateTime time) { this.Clock(this.data); } private void dataPort_OnInterrupt(uint data1, uint data2, DateTime time) { this.data = (data2 == 0) ? false : true; }
It almost works but it still drops bits, especially when something is happening in the background.
According to this page, , which implies that it may be possible to implement some sort of protocol to pause the keyboard while some processing is happening, possibly "driving" the pins by setting the pull-up/pull-down resistors, eg:this.dataPort.Resistor = Port.ResistorMode.PullDown; this.clockPort.Resistor = Port.ResistorMode.PullDown;, Although this throws a runtime exception. Other dead-ends include creating an OutputPort on the same pin as the InterruptPort, or using TristatePorts with interrupt handlers. Its puzzling that this is even an issue considering the keyboard should only be transmitting at ~25kHz.
#4
Posted 15 November 2010 - 07:33 AM
#5
Posted 15 November 2010 - 08:01 AM
It should be possible to implement PS/2 protocol using Netduino's USART in synchronous mode, unfortunately serial clock pins are not broken out and there is no support for enabling the synchronous mode from managed code. Additionally, TX and RX must be wired together and I am not sure I/O lines can be set to open-drain when used by USART, so it may need some external circuitry.I saw another page (can't remember where off-hand) which hinted that a serial port might do the job. On one hand the data looks like standard serial data (stop bit, start bit, parity bit), on the other hand there's no real handshaking so it would be a bit of a guess getting the right connection settings as to speed and so on.
#6
Posted 16 November 2010 - 08:50 AM
#7
Posted 20 November 2010 - 08:13 PM
SUCCESS!!!
12 hours of my life I'll never get back, a switch to the Keil compiler and a low-level firmware modification and I can read a PS2 keyboard.
At least it seems like it - I haven't got to the decoding routine yet.
During this exercise I discovered that GCC built firmware is horribly unreliable in interrupt delivery (at least I think it was a GCC issue).
A keyboard press / release should generate 33 clock pulses for a standard key (scan code, E0 and scan code again to match key down / key up).
Under GCC I was getting a random number of interrupts in the range 11 - 23... Under Keil I consistently get 33.
My firmware modification traps the state of 0 - 31 InputPorts at the time of the interrupt and passes this state via the data2 parameter of the interrupt. This is handled via a modification to InterruptPort and the low level ISR routine:
public sealed class InterruptPort : InputPort { //--// private uint[] m_inputPinWatch = null; public InterruptPort( Cpu.Pin portId, bool glitchFilter, ResistorMode resistor, InterruptMode interrupt, InputPort[] watches ) : this( portId, glitchFilter, resistor, interrupt ) { if ( watches != null ) { if ( watches.Length > 31 ) { // max 31 bits of resolution in data2 argument of the interrupt event handler throw new ArgumentException(); } m_inputPinWatch = new uint[ watches.Length ]; for ( int index = 0; index < watches.Length; index++ ) { m_inputPinWatch[ index ] = (uint) watches[index].Id; } } } ]
and
CLR_RT_HeapBlock_Array* inputPinWatchRef = pManagedPortObj[ Library_spot_hardware_native_Microsoft_SPOT_Hardware_Port::FIELD__m_inputPinWatch ].DereferenceArray(); if ( inputPinWatchRef != NULL ) { CLR_UINT32* inputPinIds = (CLR_UINT32*) inputPinWatchRef->GetFirstElement(); int len = inputPinWatchRef->m_numOfElements; for ( int i = 0; i < len; i++ ) { if ( ::CPU_GPIO_GetPinState( inputPinIds[i] ) ) { data2 |= ( 1 << ( i + 1 ) ); } } }
This seems to perform well but is an interesting alteration to the core framework and changes the meaning of the data2 / state parameter of the interrupt significantly.
I would be interested in any comments on this approach.
I'd also like to thank CW2 for various posts on getting the firmware compiled without which this wouldn't have happened.
Thanks
-(e)
0 user(s) are reading this topic
0 members, 0 guests, 0 anonymous users