![]() |
Forum Index : Microcontroller and PC projects : MM: Help with INKEY$
Page 1 of 2 ![]() ![]() |
|||||
Author | Message | ||||
WhiteWizzard Guru ![]() Joined: 05/04/2013 Location: United KingdomPosts: 2944 |
This is not the first time that this topic has been brought up on TBS - but searching through the history unfortunately has not revealed an answer for me. The issue I have is with trying to accurately detect a single key press (on a PS2 keyboard) at very specific times; and NOT to read a buffered key press from an earlier point in time. The setup I have is a MMBasic program (B31 on a 64-pin MM+) that is effectively a big repeating loop; continuously doing various hardware and software things depending on the state of certain I/O pin inputs. Part of the big loop also 'interegates' a PS2 keyboard to see if any (single) keys are pressed - if so then the program branches off to do whatever is required (i.e. turn a pin on or off). I am only needing to capture single key presses so I naturally used INKEY$ to try achieve this. NOTE: I am NOT entering data and hence am not using (or wanting to use) INPUT as this requires 'Enter' to be pressed. Also, I can not wait for a key to be pressed as the loop needs to be continually cycled through - hence if a key is pressed when interrogated then do 'something', else move on . . . The issue is that the INKEY$ function returns a character from the keyboard buffer which potentially means that it is NOT necessarily being pressed at the time INKEY$ is invoked. I thought it would be a simple matter of clearing the 'keyboard buffer prior to using INKEY$ but using INKEY$ (to clear) and then another INKEY$ to read a key does not return anything. I assume this is not a bug as it often appears to be the situation if you refer to Google! So does anyone have any ideas as to how to approach this? I do not want to use switches on I/O pins - it MUST be keys on a PS2 keyboard. Timers, SetTick ? ? ? Appreciate any responses, guidance, or ideas . . . . . . but please, no 'C' solutions ![]() WW |
||||
Chris Roper Senior Member ![]() Joined: 19/05/2015 Location: South AfricaPosts: 280 |
You could possibly do it with the PEEK function to read the U1RXREG (or U2RXREG - I am not sure which one is the console) directly. However, if the serial buffer is interrupt driven which it most likely is, the buffer may well catch the character before you do and the hardware has its own 8 level deep Rx Buffer too. Would it be possible to hook into the Receive Interrupt? In the long run a CFunction probably would be easier, but if you have your reasons not to use one, then Delving into the Source code and looking for a back door past the INKEY$ function is probably your best bet. Probably not too help full but I throw the ideas out there for comment. Cheers Chris http://caroper.blogspot.com/ |
||||
Grogster![]() Admin Group ![]() Joined: 31/12/2012 Location: New ZealandPosts: 9610 |
First thought that springs to mind, and bear in mind it is 3:10AM here, is to use a diode on the clock or data line from the PS/2 keyboard, to any interrupt capable I/O pin. As the PS/2 DTA and CLK lines are pulled high, then a pin set low with the PULLDOWN option or an external pulldown resistor on the INT I/O pin, cathod of diode to this I/O pin, anode of the diode to either CLK or DTA - probably CLK. Any time the CLK line goes low, it SHOULD cause an interrupt jump on the INT pin, at which point you KNOW there was a keypress right then and there, and can use INKEY$ right at the moment it is pressed. Totally untested, and at this time of the morning, I could be dreaming, but that is my first thought.... Smoke makes things work. When the smoke gets out, it stops! |
||||
Chris Roper Senior Member ![]() Joined: 19/05/2015 Location: South AfricaPosts: 280 |
Reading Grogster's reply I realise I was chasing the wrong horse ![]() http://caroper.blogspot.com/ |
||||
WhiteWizzard Guru ![]() Joined: 05/04/2013 Location: United KingdomPosts: 2944 |
Thanks guys for your ideas. @Chris - I don't mind using a CFunction - just haven't got my head round them yet (even with the brilliant tuorials from the two Peters). So I would be relying on someone else's code (prefer not to do this if I can avoid it). Time doesn't allow me learning CFunctions; I have tried on numerous occasions but must have some 'phobia; against them! @Grogs One thing I should have made clearer is that I do not need to 'guarantee' that I see a key press; I just want to know whether or not it is being pressed at the time I 'interrogate' it. So in my big loop I want to achieve something like: .
. . IF KEY$ = "C" THEN goto . . IF KEY$ = "Q" THEN goto . . . IF KEY$ = "P" THEN goto . . . . . . . The loop takes a varying number of milliseconds (from about 50 to over 800). If every time it was around 50mS then I can seem to capture the depresses reasonably accurately, but with 250mS or more then there is considerable 'delay' resulting in mis-interpretation of the key being pressed. Will keep pondering this one . . . |
||||
WhiteWizzard Guru ![]() Joined: 05/04/2013 Location: United KingdomPosts: 2944 |
Here is a piece of code to kind of demonstrate the issue: Do
a$=INKEY$ IF a$="w" THEN PRINT "W "; Pause 200 Loop Run this code; and repeatedly press random keys (but not 'W' or Ctrl-C!). This will put some 'keypresses' into the keyboard buffer which is what INKEY$ 'refers' to when invoked. Then immediately press the W key and see how long it takes to be displayed. Now shorten the PAUSE to 25 and do the same thing. W is now captured everytime. This would imply using another a$=INKEY to flush the buffer out, but when you do this it doesn't see the 'W' key most of the time ![]() Variations on the above short program infer that INKEY$ is not the way forward. ![]() |
||||
Chris Roper Senior Member ![]() Joined: 19/05/2015 Location: South AfricaPosts: 280 |
Total untested but what about something like this? [code] MyKey$ = "wcqp" ' Keys you wish to catch Do a$ = lcase$(INKEY$) IF instr$(MyKey$, a$) then ' if it is a key we want to trap rem Process a$ else do a$ = INKEY$ loop until a$ = "" ' Clear the buffer end if Pause 200 Loop [/code] or this: [code] do do a$ = lcase$(INKEY$) IF instr$(MyKey$, a$) then Process(a$) loop untill a$ = "" loop [/code] the above would process all valid keys but if you only want the first valid key then flush the buffer in process(a$). Not sure if my syntax matches MMBasic as I am currently working in GCBasic and keep mixing the two up ![]() Cheers Chris http://caroper.blogspot.com/ |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 10310 |
I'm probably misunderstanding but I don't think it can work the way you describe. A PS2 keyboard sends a data packet as a key is pressed and another when the key is released. That way the host software can do things like interpret multiple key presses (CTRL/ALT/DELETE). The keyboard may also have an auto-repeat set so that it sends the "pressed" code repeatedly at a pre-defined rate but not the "release" code until there is an actual release. It would not be normal for the host software to expose a status that says the key had been pressed but not yet released which seems to be what you want. The host software will know this as it determines the position in the state machine but doesn't expose it. You can run a loop to empty the keyboard buffer and note the last key pressed before you get the buffer empty code ("") but this does not tell you when it was pressed or if it is still pressed. The only way to see if it is still pressed is to empty the buffer and then poll continuously for the auto-repeat timeout (typically 500msec) before deciding. |
||||
WhiteWizzard Guru ![]() Joined: 05/04/2013 Location: United KingdomPosts: 2944 |
Thanks for your comments. Let me try a very simple analogy with a digital I/O pin: SETPIN(22),din . MLoop: . . . IF PIN(22) THEN GOTO xyz: . . . . xyz: ' do something. . . . . . goto MLoop The above has a one-line check to see if PIN 22 is high, and if so, the code branches off to label xyz. Now the line that reads the state of PIN 22 returns the 'current' state of PIN22; it does not return the state of the pin at some previous time in the past (which 'could' then clearly have been a different value to the current state). I simply want to do the same but reading the state of a key on a PS2 keyboard. There use to be a 'KEYDOWN' command in various BASICs which would resolve this issue. Most people seem to incorrectly use INKEY$ (and it works perfectly if your code can 'hang around' such as waiting for a 'Y' or 'N' keypress in response to a 'Yes'/ 'No' question). Fully aware about code sets, and their make & break codes (and extended codes) which strengthens my doubts that it can actually be done at all. ![]() Please tell me that I am missing something really stupid and that this seemingly simple task can somehow be achieved? |
||||
vegipete![]() Guru ![]() Joined: 29/01/2013 Location: CanadaPosts: 1132 |
Does MMBasic still have the KeyDown function? ky = KeyDown should put the ascii code of what was (last?) pressed into the variable ky. Probably up to you to test if it's a new press or one you already saw. Visit Vegipete's *Mite Library for cool programs. |
||||
cosmic frog Guru ![]() Joined: 09/02/2012 Location: United KingdomPosts: 302 |
KEYDOWN was available on the MaxiMite but unfortunately not on the MicroMite. |
||||
WhiteWizzard Guru ![]() Joined: 05/04/2013 Location: United KingdomPosts: 2944 |
Hi Pete, Unfortunately the KeyDown feature is not included in MicroMite+ MMBasic (please can someone prove me wrong i.e. Geoff ![]() However, I do seem to recall that it was included within the MaxiMite MMBasic. WW |
||||
TassyJim![]() Guru ![]() Joined: 07/08/2011 Location: AustraliaPosts: 6283 |
Would this work ' TassyJim ' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' keep reading the keyboard buffer until it is empty. ' use the last key pressed only DO x$="" DO a$=INKEY$ IF a$="" THEN EXIT DO x$=a$ LOOP PRINT x$ PAUSE 500 LOOP I don't have a keyboard connected to any device to test here. Jim VK7JH MMedit |
||||
TassyJim![]() Guru ![]() Joined: 07/08/2011 Location: AustraliaPosts: 6283 |
Had you tried ON KEY target You can enable it as required and disable when you don't want the interruption. If you do disable, I think that you would need to clear the buffer before you re-enable it. Jim VK7JH MMedit |
||||
robert.rozee Guru ![]() Joined: 31/12/2012 Location: New ZealandPosts: 2442 |
do you wish multiple keys to be allowed to be down at any one time? and what timeframe would be acceptable for responsiveness? would 1/2 a second be ok? cheers, rob :-) |
||||
WhiteWizzard Guru ![]() Joined: 05/04/2013 Location: United KingdomPosts: 2944 |
@TassyJim - You maybe onto something with ON KEY Just tried a simple test program and signs look good. At first I was doubtful as the User Manual implies this reads the 'serial console buffer' i.e. not the keyboard buffer. Anyway, will play more with this . . . Thanks Jim |
||||
WhiteWizzard Guru ![]() Joined: 05/04/2013 Location: United KingdomPosts: 2944 |
@rob - sorry your comments came in while replying to TassyJim. Multiple keys not required (but are ok). It is a first-come, first-served in terms of checking keys. So for example, if "1" then goto label1, if "2" then goto label2, if "3" then goto label3. Responsiveness needs to be instant at the line of code enquiring if a key is pressed. I am going to different parts of the program depending on which key is detected first. I do not need to 'go back' to see if the 'next' key is pressed. I will simply pick up any pressed keys at the next time I go through the loop. If none are pressed then this is fine - as long as my loop is continually executed. Using the analogy of checking digital input pins should help i.e. at the time I check a pin, if it is high then branch off. If low then check the next pin. If this pin is high then branch off somewhere else, otherwise check the next pin. If a pin is high then the program will deal with that pin, and then eventually the loop will go around again and reach the 'pin check' section again. So I will only check all pins IF all are low; otherwise the first one being high will 'deviate' the program flow. Now all I need is keys rather than pins . . . I need to make it clear that potentially lots of keys may be continually 'bashed' throughout my program loop which means an INKEY$ approach will not work as the keyboard buffer would rapidly get filled. Only at the time of checking for a key press do I want to see if a certain key is pressed i.e. NOT read from a buffer that contains 'old' information |
||||
WhiteWizzard Guru ![]() Joined: 05/04/2013 Location: United KingdomPosts: 2944 |
Could there be a PEEK to somewhere maybe that could simplify things ![]() This is way over my level of understanding so need an 'expert' to comment here . . . |
||||
robert.rozee Guru ![]() Joined: 31/12/2012 Location: New ZealandPosts: 2442 |
you have a couple of options if using a PS/2 keyboard is essential: 1. ask geoff to expose the scancodes returned from a PS/2 keyboard via an interrupt. these have the top bit cleared on a key going down, and the top bit set on the same key going up. you then set up an array of 128 booleans, one element for each key/scancode. upon seeing the down code for a given key, the interrupt service routine sets the appropriate boolean true. upon seeing the up code, it sets is false. in your main program, scan through the array for true values - these correspond to the keys currently 'down'. 2. write your own PS/2 input routine. this could be done with a custom function, or in basic code. once you have done this, do the same as in 1. above to the scan codes read. if you just want a keyboard that uses a single pin, have a limited number of keys to detect (less than a coupe of dozen), and are happy that the user is only allowed to press a single key at any time: 3. implement an analog keyboard, where each key causes a different resistance to be present at the output. there were some threads about doing this quite some time ago - i shall dig back through the archives if this is an suitable solution. cheers, rob :-) |
||||
robert.rozee Guru ![]() Joined: 31/12/2012 Location: New ZealandPosts: 2442 |
found it. see: http://www.thebackshed.com/forum/forum_posts.asp?TID=6418 |
||||
Page 1 of 2 ![]() ![]() |
![]() |
![]() |
The Back Shed's forum code is written, and hosted, in Australia. | © JAQ Software 2025 |