PDA

View Full Version : PC Keyboard question



HatchetHand
- 8th September 2007, 06:06
Hello,
I am new to PBP and PIC’s but I am trying to learn more about PIC projects. I need to build a PS2 Keyboard with only some of the keys of a standard keyboard, i.e. 24 to 28. I have a little experience with the 16F688 and if possible I would use it again for this project. I think it will communicate through the serial out of the chip.

Here are my questions; Is there any information on the interfacing of a PIC to the PS2 port on a PC. I have read and I believe I understand the scan codes, but what I do not fully understand is the communication to the PS2 port. Is it 9600 baud with 11 bits? Are there examples out there where someone has done this before too? Does the PBP serial I/O commands support this type of communication?

Communicating through the PS2 port seems very interesting and all types of things could be done, if I can get my head round the basic I/O.

Thanks for the help

Darrel Taylor
- 9th September 2007, 01:06
Have you seen this thread yet?

AT/PS2 Keybord - PIC Interface?
http://www.picbasic.co.uk/forum/showthread.php?t=2736
<br>

Kamikaze47
- 9th September 2007, 08:10
RE: the thread linked above: I did managed to get a keyboard interfaced to a PIC and it works great. Of course, what you want to do is the reverse (i.e. emulate a keyboard using a PIC).

I'm happy to post my code if you think it will be helpful.

HatchetHand
- 10th September 2007, 03:40
DT, and KamiKaze47,

Thanks for the info,
I have been going through the thread, it seems somewhat difficult to get the timing correct. It looks like you can use the HserIn and HserOut commands to clock the data to the PC and depending upon what you need to send it could be easier with PBP. Did you find the 16F84A was easy to use with the internal clock, or did you have to add an external to make it work?

If you do not mind your source would be of great help.

Hatchethand

Kamikaze47
- 10th September 2007, 07:23
In the end I didn't use a 16F84A, but purely because the code space wasn't big enough for what i wanted to do. Also, I never ended up using any special PBP commands, opting to write a subroutine using interrupts that would get the timing perfect. I used an 18F1320 with it's 8Mhz internal clock, but I think 4Mhz would work too.

Here's my code:


INCLUDE "DT_INTS-18.bas" ' Include base interrupt system
INCLUDE "ReEnterPBP-18.bas"

' Interrupt Aliases
INT0IE var INTCON.4 ' INT0 external interrupt enable bit
INTEDG0 var INTCON2.6 ' INT0 external interrupt edge select bit: 1=Rising Edge 0=Falling Edge

' I/O
kb_clock var PORTB.0 ' Keyboard clock pin
kb_data var PORTB.1 ' Keyboard data pin

' Constants
buffer_size CON 32 ' Keyboard buffer size

' Variables
packet var word bank0 ' Latest packet from the keyboard
packet_index var byte bank0 ' Current bit in keyboard packet
buffer var byte[buffer_size] bank0 ' Keyboard data buffer
buffer_i_index var byte bank0 ' Current buffer possition for inputs
buffer_o_index var byte bank0 ' Current buffer possition for outputs
data_received var byte bank0 ' Latest byte retrieved from the buffer
n var word bank0 ' Temporary variable for loops
temp var word bank0 ' General temporary variable
new_data var bit bank0 ' Flag to indicate there is new data from the keyboard
parity_bit var bit bank0 ' Parity bit used for PIC->keyboard communication
error var bit bank0 ' Flag to indicate an invalid packet

' Initialisation
INPUT kb_clock ' Keyboard Clock Pin = Input
INPUT kb_data ' Keyboard Data Pin = Input

ASM
INT_LIST macro ; IntSource Label Type Reset Flag?
INT_Handler INT0_INT, _int0_handler, PBP, yes
endm
INT_CREATE
ENDASM

INTEDG0=0 ' INT0 Interrupt to Trigger on Falling Edge

@ INT_ENABLE INT0_INT



main:
gosub check_for_new_data
IF new_data=1 then
' Do whatever you want with the byte we just received. It is stored in data_received.
ENDIF
goto main





' Keyboard interrupt handler: Retrieves packet from keyboard
int0_handler:
packet.11=kb_data ' Store bit
packet=packet>>1 ' Rotate the packet so it is ready for the next bit
packet_index=packet_index+1 ' Increment packet bit index
iF packet_index=11 then ' If packet is complete:
gosub check_packet_for_errors ' Check the packet for errors
if error=1 then ' If there was an error:
packet=0 ' Discard the packet
packet_index=0 ' Reset the packet bit index
goto int_exit ' Exit the interrupt
ENDIF
packet_index=0 ' Reset Packet Index
packet=packet>>1 ' Discard the start bit
buffer[buffer_i_index]=packet.byte0 ' Store just the recieved byte
buffer_i_index=buffer_i_index+1 ' Increment buffer input index
if buffer_i_index=buffer_size then buffer_i_index=0 ' If end of buffer has been reached, reset the index
IF buffer_i_index=buffer_o_index THEN ' If the buffer is full:
INT0IE=0 ' Disable Interrupt
OUTPUT kb_clock ' Set the keyboard's clock as output
low kb_clock ' Set the keyboard's clock line low (tell the keyboard to stall)
endif
endif
int_exit:
@ INT_RETURN



' Subroutine to check to see if there is any new data waiting in the keyboard buffer
check_for_new_data:
IF buffer_o_index!=buffer_i_index then ' If the buffer is not empty:
data_received=buffer[buffer_o_index] ' Get the next byte from the buffer
new_data=1 ' Set the new data flag
buffer_o_index=buffer_o_index+1 ' Increment buffer output index
if buffer_o_index=buffer_size then buffer_o_index=0 ' If end of buffer has been reached, reset the index
IF INT0IE=0 THEN ' If the buffer was full, its now got a space, so:
INPUT kb_clock ' Release the keyboard's clock line
INT0IE=1 ' Re-enable the interrupt
ENDIF
else
new_data=0 ' If the buffer is empty, clear the new_data flag
endif
return



' Subroutine to check the incoming packet for errors
check_packet_for_errors:
error=0 ' Reset error flag
parity_bit=1 ' Set parity bit for start of calculation
temp=packet ' Copy the packet to a temp variable so we can play with it
if packet.0=1 then ' Check that start bit = 0
error=1
return
endif
if packet.10=0 then ' Check that stop bit = 1
error=1
return
endif
temp=temp>>1 ' Discard the start bit
for n=0 to 7 ' Calculate what the bit parity should be
if packet.0=1 then toggle parity_bit
temp=temp>>1
next n
if packet.10!=parity_bit then ' Check the parity bit
error=1
return
endif
return

ultiblade
- 11th September 2007, 08:06
Hi Hatchedhand,

I did this (PIC -> PC PS2) a while ago to simulate keystrokes. I used a simple 12F509
and it worked ....

See thread : http://www.picbasic.co.uk/forum/showthread.php?t=1611

Best Regards,

UB

Kamikaze47
- 11th September 2007, 08:23
Your other option is to use USB.

It is quite trivial to set up a PIC keyboard with USB PIC chip. When you plug it in, your OS detects it as a generic USB keyboard. Include the USB keyboard definitions file and its a matter of 1 or 2 lines of code to send keystrokes. Much easier than PS2.

HatchetHand
- 16th September 2007, 06:41
Attached is a program I have tried. I see data going out of the data pin and the clock on the scope. But I do not get anything on the dos screen (DOS 6.22).

I strobe the switch looking for a High, and then set and LED on when I get it, for some reason I do not see any data on the screen.

I have tried all of the modes for the shiftout command but nothing seems to work.

I am using pins +5 and Ground on the PCB connector, and then 1 and 5 on the connector. I have even jumpered 1-2 and 5-6 thinking I am looking at the wrong end of the cable. but nothig works.

Any help would be appreciated.



USB would be great, but I need PS/2.
Thanks,
HH





' PIC 16F688
' PortC.5 - Data
' PortC.4 - Clk
' PortA.2 - Switch in, normal low, active high
' PortA.0 - LED Out, high if switch pressed

' Circuit description
' 10K pull up on PortC.5 - Data
' 10K pull up on PortC.4 - Clk
' 10K pull up on /MCLR
' 10K pulling down on switch input, PortA.2

'Goal of program, to send a chracter to the PC keyboard port when switch is pressed

INCLUDE "modedefs.bas"
DEFINE OSC 4 ' 4 Mhz

TRISA = %00000100 ' Set PORTA.2 to input, all others are output
TRISC = %00000000 ' set PORTC to outputs
CMCON0 = 7 ' turn off comparitors
ANSEL = %00000000 ' set all as digital ports, analog off
char var word
breakcode var word

char = %11100011110 '/a
breakcode = %11111000000 '/F0H
'----- Main Loop --------------------------------
Start: ' loop start
low porta.0 ' turn off LED
if PORTA.2 = 1 then ' check switch, if pushed then LED on and Char our
high porta.0 ' LED on
call sendchar ' send characters
endif
goto start ' restart loop


sendchar:
char = %11100011110 '/a
shiftout portc.5,portc.4,4,[(char)\11] ' LSB first, Clk High
shiftout portc.5,portc.4,4,[(breakcode)\11]
shiftout portc.5,portc.4,4,[(char)\11]

char=%11010010100 '/CR
shiftout portc.5,portc.4,1,[(char)\11] ' MSB first, Clk low
shiftout portc.5,portc.4,1,[(breakcode)\11]
shiftout portc.5,portc.4,1,[(char)\11]

char=%11011001110 '/b
shiftout portc.5,portc.4,1,[char\11] ' MSB first, Clk low
shiftout portc.5,portc.4,1,[breakcode\11]
shiftout portc.5,portc.4,1,[char\11]

char=%11100101110 '/t
shiftout portc.5,portc.4,5,[(char)\11] ' MSB first, Clk high
shiftout portc.5,portc.4,5,[(breakcode)\11]
shiftout portc.5,portc.4,5,[(char)\11]

char=%10100101100 '/r
shiftout portc.5,portc.4,0,[(char)\11] ' LSB first, Clk low
shiftout portc.5,portc.4,0,[(breakcode)\11]
shiftout portc.5,portc.4,0,[(char)\11]


char=%10011101110 '/d
shiftout portc.5,portc.4,1,[char\11] ' MSB first, Clk low
shiftout portc.5,portc.4,1,[breakcode\11]
shiftout portc.5,portc.4,1,[char\11]

pause 100
return

Kamikaze47
- 16th September 2007, 06:53
I think your problem is that a normal keyboard will initialize with the PC at boot up. This involves the PC sending different commands to the keyboard, and the keyboard responding to them.

You might want to try plugging in a standard keyboard, boot the PC, and while its still on, unplug the keyboard and plug in your pic and see if it works then.

Heres an example of the 2 way communication that happens as the keyboard inits:


Keyboard: AA Self-test passed ;Keyboard controller init
Host: ED Set/Reset Status Indicators
Keyboard: FA Acknowledge
Host: 00 Turn off all LEDs
Keyboard: FA Acknowledge
Host: F2 Read ID
Keyboard: FA Acknowledge
Keyboard: AB First byte of ID
Host: ED Set/Reset Status Indicators ;BIOS init
Keyboard: FA Acknowledge
Host: 02 Turn on Num Lock LED
Keyboard: FA Acknowledge
Host: F3 Set Typematic Rate/Delay ;Windows init
Keyboard: FA Acknowledge
Host: 20 500 ms / 30.0 reports/sec
Keyboard: FA Acknowledge
Host: F4 Enable
Keyboard: FA Acknowledge
Host: F3 Set Typematic Rate/delay
Keyboard: FA Acknowledge
Host: 00 250 ms / 30.0 reports/sec
Keyboard: FA Acknowledge

The above is copied from: http://www.computer-engineering.org/ps2keyboard/

Kamikaze47
- 16th September 2007, 07:17
Also, the PS2 keyboard protocol expects data in the order shown below and the data is valid when the clock is low.

First Bit Sent -> Last Bit Sent
Start(0), Data0, Data2, Data3, Data4, Data5, Data6, Data7, Parity, Stop(1)

To press A you need to send $1C and then to release you need to send $F0, $1C

Which means the code for A is "0 00011100 0 1", but if you store that into a word variable it will be stored as 0000000001110001 which wont help us much becuase the first bit we want to send is bit10. So if you store the code for A in reverse like this:

char = %10001110000 '/a

you can then send the LSB first and you will get the correct bits in the correct order. It then follows that:

breakcode = %11000011110 '/F0

Kamikaze47
- 16th September 2007, 07:21
Not meaning to flood this thread, but just spotted something else:
"call sendchar" should be "gosub sendchar" as call is only for routines written in assembly.

Luciano
- 16th September 2007, 08:38
Hi,

You can use a built-in feature of Windows if the PC has an unused serial port.
This feature lets you send any kind of keystroke to the PC via serial port.

* * *

Use an Alternative Input Device Instead of a Keyboard or Mouse

SerialKeys is accessibility feature designed for people who have difficulty
using the computer's standard keyboard or mouse. SerialKeys provides support
so that alternative input devices, such as single switch or puff and sip
devices can be plugged into the computer's serial port.

https://www.microsoft.com/windowsxp/using/accessibility/serialkeys.mspx

How to Set Up and Use SerialKeys in Windows:
http://support.microsoft.com/?kbid=260517

Best regards,

Luciano

HatchetHand
- 18th September 2007, 05:17
Sorry for the delay, I have been working on trying different things. I am starting the PC with a regular keyboard and then swaping it with the PIC replacement.

I have tried a 16F627A and I have moved back to the 16F688

Below is the code I am testing, I am sending the data twice trying to send it both LSB and MSB first. Sometimes I get a beep from the PC when I change the PauseUS delay and other times I do not.

' PIC 16F688
' PortC.5 - Data
' PortC.4 - Clk
' PortA.2 - Switch in, normal low, active high
' PortA.4 - LED Out, high if switch pressed

' Circuit description
' 10K pull up on PortC.5 - Data
' 10K pull up on PortC.4 - Clk
' 10K pull up on /MCLR
' 10K pulling down on switch input, PortA.2

'Goal of program, to send a chracter to the PC keyboard port when switch is pressed
@ device INTRC_OSC_NOCLKOUT
INCLUDE "modedefs.bas"
DEFINE SHIFT_PAUSEUS 25
DEFINE OSC 8 ' 8 Mhz

TRISA = %00000100 ' Set PORTA.2 to input, all others are output
TRISC = %00000000 ' set PORTC to outputs
CMCON0 = 7 ' turn off comparitors
ANSEL = %00000000 ' set all as digital ports, analog off
WPUA = %00000000

char var word
breakcode var word

clkout var portc.4
switch1 var portA.2
ledout1 var portA.4
dataout var portc.5

'char = %10101101010 ' 'CR' mode 0
'breakcode = %11000011110 '/F0H mode 0
char = %01010110101
breakcode = %01111000011

'----- Main Loop --------------------------------
Start: ' loop start
low ledout1 ' turn off LED
if switch1 = 1 then ' check switch, if pushed then LED on and Char our
pause 50
if switch1 =1 then
high ledout1 ' LED on
shiftout dataout,clkout,4,[char\11]
shiftout dataout,clkout,4,[breakcode\11]
shiftout dataout,clkout,4,[char\11]
PAUSE 1
shiftout dataout,clkout,5,[char\11]
shiftout dataout,clkout,5,[breakcode\11]
shiftout dataout,clkout,5,[char\11]
endif
endif
goto start ' restart loop
'----- Main Loop --------------------------------

I have tried the character to send and the breakcode both ways, but they respond the same.

I see the characters and clock on the scope, all six characters come across but nothing on the PC.

I am driving the keyboard direct from the chip, no transistors. Do you think the open-collector could be an issue with the driving? Could the Clock and the timing be an issue?

I would think that something would appear with both combinations of send's but i seem to be going in circles.

USB would be great but it's not an option, I am not talking to a Windows OS. I have to use the PS/2 port to send data to the terminal.

Any thoughts would be great, my goal is to send about 24 pre-defined single characters on switches to the port. I have been

Thanks,
HH

Kamikaze47
- 18th September 2007, 06:03
2 problems that i can see:

First, looks like ur clock is too fast. PS2 keyboards generate a clock between 10khz-16khz. I'd be changing the shift pause to:

DEFINE SHIFT_PAUSEUS 40

Second, The "mode 0" as you call it is the only one with any chance of working, but "%10101101010" corresponds to a scancode of $AD which as far as i can tell is not a valid scancode.

Try using these:

char = %10001110000 '$1C = 'a'
breakcode = %11000011110 '$F0

shiftout dataout,clkout,4,[char\11]
shiftout dataout,clkout,4,[breakcode\11]
shiftout dataout,clkout,4,[char\11]


Something else you may want to try is this:

shiftout dataout,clkout,4,[char\11]
pauseus 100
shiftout dataout,clkout,4,[breakcode\11]
pauseus 100
shiftout dataout,clkout,4,[char\11]
pauseus 100

*edit* Check out this page for all the scancodes: http://www.computer-engineering.org/ps2keyboard/scancodes2.html