Towards a nice keyboard controller program

Iain_C

Member
Hi all,

It's been a while since I posted here; but once again my electronics experiments have led me to the PICAXE :)

I'm building a Z80-based computer, and I'm using a 14M2 as a PS/2 keyboard controller to feed ASCII to an SCC2691 UART; for now I'm debugging over the programming link.

Initially I tried a simple lookup-table approach as per the keyin.bas sample.

This worked, but gave me double-hits on all keys with no way to discard the keycodes resulting from key-up events (as the key-up keycode preamble doesn't make it through to PICAXE basic). I can't just ignore every other keycode received from kbin, as I want to handle fast overlapping-keys typing (eg when typing "the" at speed, the keycodes received may be t-h-t-h-e-e instead of simply t-t-h-h-e-e).

So, I added an "isPressed" buffer in RAM for each key. When I receive a given keycode, I check if that key's currently marked as pressed. If so, this must be a keyup event I can ignore (and reset the pressed flag). If not, this must be a keypress, so I send the character and mark the key as pressed. This RAM buffer uses an entire byte per key; lazy and wasteful I know, it should be a single bit per key.

I also wanted to be able to press Shift for uppercase and symbols, so I added a flag to keep track of the status of the shift keys and used the remaining EEPROM space to have different lookup tables for shifted and unshifted keys.

This is a lot closer to where I want to be, and is presented below in the hope that it's useful to someone.

It handles "overlapping" typing well and allows Shift for uppercase. Latency between key press and a byte heading out the serial port is acceptable. Supporting Caps Lock would be easy and I'll probably add that. It's also got better lookup tables (more keys) than the ones in the manual.

However, it's still not perfect. When typing at even moderate speed it either misses character completely or gets two swapped around out of sequence. Missing the character I can understand, assuming there's no FIFO that kbin consumes; swapping two seems stranger though.

Anyway, my queries are:

(1) Does anyone have a better strategy for handling these requirements on a PICAXE, or am I on the right track?
(2) Can anyone think of any way I can speed up the code so that there's less chance of missing characters? I've already disabled the keyboard LED flashing and am running at 32MHz.

Code:
; Keyboard controller firmware for PICAXE14M2
; Iain C, 18 April 2012
; Circuit: 14M2 with a programming interface, plus a PS/2 port connected as per circuit at bottom of p133, picaxe_manual2.pdf.

	symbol serpin = 0		; Pin to use for serial output
	symbol serbaud = N38400_32	; Serial baudrate to use for output
	symbol shifted = b2		; To store current shift state
	symbol isPressedStart = $1C	; Mem address of isPressed buffer (1 byte per key; wasteful!)
	
	kbled %10000010			; Turn on just numlock led and disable flash
	setfreq m32			; Let's go fast :)
	
	shifted = 0			; Set shifted flag to false intially
	
	b3 = isPressedStart
	for b0=0 to 127			; Init the isPressed array
		poke b3,0
		inc b3		
	next b0
	
main:
	kbin b0
	
	if b0=$12 or b0=$59 then 	; Shift pressed/depressed?
		if shifted=0 then	; Toggle shifted flag
			shifted=1
		else
			shifted=0
		endif
		
		goto main		; Loop back to main; don't actually send a keycode for shift
	endif
	
	b4=b0+$1C			; Find mem address of this key's isPressed flag
	peek b4,b3			; Check the flag location
	if b3=1 then			; If 1, it's been pressed -- this must be an onRelease hit...
		poke b4,0		; ... so reset the key's isPressed flag
		goto main		; ... and loop back to main
	else				; Otherwise, this must be an onPress hit...
		poke b4,1		; ... so set the isPressed flag and continue!
	endif
	
	if shifted=1 then		; Currently shifted? Amend b0 to use the shifted lookup table instead
		b0=b0+$80
	endif
	
	read b0,b1			; Do the table lookup	
	
	if b1="?" and b0!=$4A then 	; Try and prevent spurious undefined chars (not sure why these happen when typing
		goto main		;  at speed -- keycodes being partially received by kbin?)
	endif
	
	serout serpin,serbaud,(b1)	; Send the char
	goto main

; Unshifted keycodes
eeprom $00,("?9?5312c?a864?`?")		; Fn keys
eeprom $10,("?????q1???zsaw2?")		; Main keyboard keys
eeprom $20,("?cxde43?? vftr5?")
eeprom $30,("?nbhgy6???mju78?")
eeprom $40,("?,kio09??./l;p-?")
eeprom $50,("??'?[=?????]?#??")
eeprom $60,("?\\???????1?47???")	; Numpad keys
eeprom $70,("0.2568??b+3-*9??")

; Shifted keycodes
eeprom $80,("?9?5312C?A864?¬?")		; Function keys
eeprom $90,("?????Q!???ZSAW\"?")	; Main keyboard keys
eeprom $A0,("?CXDE$#?? VFTR%?")		; Sorry, no pound sign -- not part of ASCII! Another # instead.
eeprom $B0,("?NBHGY^???MJU&*?")
eeprom $C0,("?<KIO)(??>?L:P_?")		; Note the ACTUAL question mark is 4A :P
eeprom $D0,("??@?{+?????}?~??")
eeprom $E0,("?|???????1?47???")		; Numpad keys
eeprom $F0,("0.2568??B+3-*9??")
Great to see the PICAXE community as vibrant as ever :)

All the best,
Iain
 
Last edited:

pvdven777

New Member
Hi Iain,

Your story is indeed of use to someone. That'd be me for certain.
I've actually bought some picaxes. Was interested in them for a long time but never found the right excuse to buy and apply.

I'm seemingly in a very similar situation in terms of application anyway. I'm trying to attach a PS2 keyboard to an old home computer mainboard (which was donated to me but came without anything at all attached, not even a keyboard)

So I bought some different chips and when I started delving deeper into it I was left with the impression that the picaxe completely discards the incoming key-up event/code to a point where I might have to come up with another solution and/or abandon this particular project.

What has me puzzled is that your story seems to imply that the picaxe DOES in fact pass another code/character upon releasing the key. So based on this I experimented with the supplied original kbin saple code. I simply added a counter after the kbin command and watched to see if every single press/release action would indeed generate 2 codes being read from the keyboard.
That did not seem to be the case. I was hoping you could help me on understanding this part of working with kbin as you seem to have advanced quite a bit already.

Now I need to convert my PS2 codes not to ASCII but to yet another code to pass on to the other system. I'll have a good look at your code first however before I start asking silly questions ;o).

As for your you request for suggestions. I was thinking about a plan B myself. Not sure if it could somehow help but let me describe in any case.
Lets say I dont get to read the key-up event. In that case I cold simulate on myself. With a small built in time delay one could generate the key-up event. Now for keys like shift which are commonly used in combination with other keys you could make the up-event delay a bit longer. That way in my theory if you could tweak the delays just right you could still type at a decent rate while combinations are still possible. Kind of like the windows accessibilty type of features (one finger typing) if I recall correctly.
And like yourself in either case I am trying to find the smartest way to record/keep track of all this happening.

Would really like to hear your thoughts,

Greet's

PatrickV
 

Grogster

Senior Member
While we are on the subject, can someone help me understand how that lookup table actually works?
Does not seem to have any logical arrangement that I can see - I am sure it is just that I am a little blind in this respect... ;)
 

westaust55

Moderator
While we are on the subject, can someone help me understand how that lookup table actually works?
Does not seem to have any logical arrangement that I can see - I am sure it is just that I am a little blind in this respect... ;)
If you look at the key versus code table in PICAXE manual 2 page 130 (the kbin command) you will see that the codes for the characters are in a non linear order.
So "A" does not follow"B" by code values as occurs with the ASCII code.

The data in the EEPROM table in the kbin.bas example maps the characters ASCII code into the EEPROM location equal to the keyboard PS2 code.
"A" has a PS2 code value of $1C so the ASCII code for "A" is store at EEPROM location $1C

Then when the "A" key is pressed, the PS2 code received from the keyboard and reported by the kbin command is $1C.
The READ command fetches the actual ASCII value "A" (= $41 = 65dec) from EEPROM location $1C
 

Grogster

Senior Member
@ westaust55: Thanks for that - it suddenly makes much more sence to me now, how all that works. :)

@ Technical: Thanks very much for the link. I will read up on that idea, as I need KB input on my next project, so would love to make it as painless as possible code-wise!!! :D This project will use the 40X2 chip(new one based on 18F45K22)

From the link:

To overcome some of these issues the #variable option has been added to M2 parts. In this case the ASCII character of the keyboard letter is loaded into the variable.
So am I correct in that with my intended PICAXE being the 40X2, I use the table method?
Or does the #variable option apply to the X2 parts too?

...I assume it does NOT, as there is no mention of this applying to X2 parts, but just to clarify one way or the other...
 
Last edited:

hippy

Ex-Staff (retired)
So am I correct in that with my intended PICAXE being the 40X2, I use the table method?
Or does the #variable option apply to the X2 parts too?
Yes, it's an M2 command only so you will have to use TABLE on the X2 and others.
 

pvdven777

New Member
In my case I still need to use a table since I dont want to get ASCII values but rather another code to suit the receiving system.
Trying to figure out how to most easily get this table built since it'll be alot of numbers rather than one string of characters.

I was doing some experiments (read simulations) the other day and noticed another little potential pitfall.
After pressing a few different buttons on the keyboard I tried hitting the ALT buttons.
The right one gives me the expected response. After pressing the left ALT button however my simulation just quit running with a message "Key not recognised and so not simulated!"
Now I really wonder how my actual hardware will respond a) give me the actual key code so I can handle it b) just quit operating ?? Haven't had a chance to run it on an actual picaxe just yet. Hopefully I can try tonight.
Also it makes me wonder what else I'm going to bump into along the way.

Any idea's why this keyboard implementation appears to be so 'half implemented' ? Its a real shame.
Is there any way to tell the picaxe to just give me the 'raw codes' or would I need to then implement the whole protocol (date + clocl) myself on another set of pins ?
 

hippy

Ex-Staff (retired)
Any idea's why this keyboard implementation appears to be so 'half implemented' ? Its a real shame.
Most likely because 'keyin' was designed to allow users to easily use something with a large number of keys and to be able detect which of those was pressed, rather than for full keyboard processing.

Is there any way to tell the picaxe to just give me the 'raw codes' or would I need to then implement the whole protocol (date + clocl) myself on another set of pins ?
The key detection is all done within the firmware and there's no way I know to get the raw control codes. The protocol is also high-speed and may be difficult to implement but I haven't really looked at that.

An alternative is to rip a keyboard apart, take out the controller chip which generates PS/2 signals, replace with a PICAXE scanning the keyboard matrix, and generate ASCII data from that.
 

westaust55

Moderator
(2) Can anyone think of any way I can speed up the code so that there's less chance of missing characters? I've already disabled the keyboard LED flashing and am running at 32MHz.
One way to achieve a small speed improvement is to use TABLE memory rather than EEPROM memory.
TABLE memory is part of the main program space and the M2 parts have 512 bytes available.
The speed gain achieved comes from the fact that the program memory can be access faster than EEPROM memory.
See the TABLE and READTABLE commands
 

erco

Senior Member
An alternative is to rip a keyboard apart, take out the controller chip which generates PS/2 signals, replace with a PICAXE scanning the keyboard matrix, and generate ASCII data from that.
That's a lot of work. Heck, it's easier to use the M2 as intended and pull off the keys that don't respond! :)
 

pvdven777

New Member
Thanks everyone for sharing their ideas, experience and all !

Anyway. For my project I do have another option. The home computer also still has a header (flexcable) to connect the original built in keyboard. But yeah, then I will have to deal with approx 31 matrix/signal lines. I'm not even sure if even the biggest picaxe can handle that. Besides that, as noted already its not a very practical approach.
I've tried testing the other ALT key response on a running setup rahter than through simulation. However somehow I was getting confusing readout from debug and on top I started with some connection issues (mechanical) which threw me off a bit. So anyway I'll have to find a bit more time and do the experiments again.

From what I gather though I understood that PS/2 keyboard was manageable for picaxe but the flood of X/Y data from a mouse wasn't going to work. Maybe I misunderstood. Haven't done all the maths to be honest.

P.S> Oh and I've got plenty of keyboards to rip apart by the way but I'm looking to read rahter than generate the PS/2 signal then send out another (not ASCII) protocol out the other end.
 
Top