Author Topic: Nspire: Keypad Input  (Read 13261 times)

0 Members and 1 Guest are viewing this topic.

Offline sammyMaX

  • LV5 Advanced (Next: 300)
  • *****
  • Posts: 204
  • Rating: +9/-0
    • View Profile
Nspire: Keypad Input
« on: September 06, 2011, 04:11:06 pm »
Since the Nspire is so fast, it is hard to program responsive, but not oversensitive keyboard input. I have a solution to this! (Hopefully you can help improve it too)

From what I've seen in most Ndless programs, the bulk of the main function is in something like this:
Code: [Select]
while(!isKeyPressed(NSPIRE_KEY_ESC))
{
// Code
}
If one just inserts things like if(isKeyPressed(KEY_NSPIRE_ENTER)), a single tap of the enter button may register 10 times, which is really bad if you're trying to type in numbers or words. However, if one does:
Code: [Select]
// Insert whatever value of x below
sleep(x);
if(isKeyPressed(someKey))
{
}
Then low values of x will result in input registering twice or three times, and high values of x resulting in the Nspire detecting nothing at all. I have tried this many times and couldn't find a "magic value" between the two. My new method uses a counter to see how many iterations it has gone through the while loop since the previous key press, and uses idle() instead of sleep(). A key will only register if it has been at least x iterations. In code:
Code: [Select]
// I used 5 as the minimum number of iterations here
int numIterations = 5;
while(!isKeyPressed(NSPIRE_KEY_ESC))
{
  // Optional, but saves power
  idle();
  if(isKeyPressed(someKey) && numIterations == 5)
  {
    numIterations = 0;
    // Stuff
  }
  // Prevents numIterations from going above 5
  if (numIterations < 5)
    numIterations++;
}
Does anyone have any other ideas? What do you do in your Ndless programs?
« Last Edit: September 07, 2011, 05:21:10 pm by sammyMaX »

Are you wondering who Sammy is? My avatar is Sammy.
   

Offline JustCause

  • CoT Emeritus
  • LV8 Addict (Next: 1000)
  • *
  • Posts: 810
  • Rating: +115/-5
    • View Profile
Re: Nspire: Keypad Input
« Reply #1 on: September 06, 2011, 04:22:57 pm »
That's one way of doing it. While I don't know much about nSpire Lua, it seems like you could also fix that by not checking for another keypress until the key's been released (i.e. not pressed). Not a bad method, though, and it's certainly less complicated than the logic for manual debouncing. :)
See you, space cowboy...

Offline sammyMaX

  • LV5 Advanced (Next: 300)
  • *****
  • Posts: 204
  • Rating: +9/-0
    • View Profile
Re: Nspire: Keypad Input
« Reply #2 on: September 06, 2011, 04:28:46 pm »
It's actually C :)

I don't know if there is something for key release, because that would be an event. It's hard to turn an event into a function some program calls. I have browsed the source of many Ndless programs, and all of them seem to be using isKeyPressed().
« Last Edit: September 06, 2011, 04:29:12 pm by sammyMaX »

Are you wondering who Sammy is? My avatar is Sammy.
   

Offline TravisE

  • LV4 Regular (Next: 200)
  • ****
  • Posts: 182
  • Rating: +33/-0
    • View Profile
    • ticalc.org
Re: Nspire: Keypad Input
« Reply #3 on: September 06, 2011, 09:41:56 pm »
My new method uses a counter to see how many iterations it has gone through the while loop since the previous key press, and uses idle() instead of sleep(). A key will only register if it has been at least x iterations.

Yep, I believe that method is essentially a form of key “debouncing”. I have no experience with Nspire, but in my experience, this actually applies to some extent to the TI-89/89t as well when reading the keyboard hardware directly in C or ASM. Many programs I've found that do this on that platform either tend to have spurious key repeats or have key responses that are much too slow—sometimes even both, as if there is no solution. But the real solution (which is what the OS does) is to wait for a key to be pressed or released for a certain minimum interval before accepting it. This makes a big difference in the quality of key response in programs.
ticalc.org staff member—http://www.ticalc.org/

Offline Lionel Debroux

  • LV11 Super Veteran (Next: 3000)
  • ***********
  • Posts: 2135
  • Rating: +290/-45
    • View Profile
    • TI-Chess Team
Re: Nspire: Keypad Input
« Reply #4 on: September 07, 2011, 01:50:04 am »
Yes, key debouncing and anti-repeat can be done by either taking into account the key only after a number of iterations, or just waiting for 100-200ms after each keypress taken into account. The waiting period can be performed by e.g. a CPU busy-wait loop, or by an idle loop until some timer expires / some interrupt fires once or several times.
Member of the TI-Chess Team.
Co-maintainer of GCC4TI (GCC4TI online documentation), TILP and TIEmu.
Co-admin of TI-Planet.

Offline shrear

  • LV4 Regular (Next: 200)
  • ****
  • Posts: 193
  • Rating: +17/-0
    • View Profile
Re: Nspire: Keypad Input
« Reply #5 on: September 07, 2011, 12:05:07 pm »
well this is how I do it in nwriter

Code: [Select]
int key_pressed(t_key is_pressed)
{

    if ( isKeyPressed(is_pressed) )
    {
        while ( isKeyPressed(is_pressed)
        {
     //loop to prevent more than one reaction per key-press
}
return 1 ;
    }
    return 0 ;

}
It ensures that there are as many "reactions" as key-presses, drawback is that you can't "hold" a key.
« Last Edit: September 07, 2011, 12:05:21 pm by shrear »

Offline fb39ca4

  • LV10 31337 u53r (Next: 2000)
  • **********
  • Posts: 1749
  • Rating: +60/-3
    • View Profile
Re: Nspire: Keypad Input
« Reply #6 on: September 07, 2011, 04:46:21 pm »
That is a very good solution, the only problem I see with it is that it holds up the whole program while the key is held down which may not be desirable in some situations.

Offline sammyMaX

  • LV5 Advanced (Next: 300)
  • *****
  • Posts: 204
  • Rating: +9/-0
    • View Profile
Re: Nspire: Keypad Input
« Reply #7 on: September 07, 2011, 05:31:29 pm »
I really like your solution, Shrear. What it does is basically start the actual function when the key is released, right? I will probably implement that in my program, or perhaps expand that to allow "holding." (I would do this by adding a character every 15 or so times through the inner loop)

Are you wondering who Sammy is? My avatar is Sammy.
   

Offline shrear

  • LV4 Regular (Next: 200)
  • ****
  • Posts: 193
  • Rating: +17/-0
    • View Profile
Re: Nspire: Keypad Input
« Reply #8 on: September 08, 2011, 12:36:29 pm »
I really like your solution
That is a very good solution
Thanks  :)

the only problem I see with it is that it holds up the whole program while the key is held down which may not be desirable in some situations.
I can imagine that this will be problematic in games etc. but for menus or typing text I haven't yet found a better solution. (or none I think better)
For games I think you could do like the following:
(this is just an idea I have right now, never tested it)
Code: [Select]
int (key_pressed(t_key is_pressed, int* block_value)
{

    if ( *block_value < VALUE_A && isKeyPressed(is_pressed) )
         {
           *block_value += VALUE_B ;
           return 1 ;
         }
   return 0 ;
}

int main()
{

     int block_value = 0 ;
     while( SOMETHING ) //main loop
     {
            if ( key_pressed(KEY_NSPIRE_WHATEVER, &block_value)
            {
                   //do what you want
            }
             //etc.
            if ( block_value > 0 )
                   block_value -= VALUE_C ; //or any other way to reduce "block_value", can probably done in a more controlled fashion like it gets updated when an action finishes or so...
      }
      return 0 ;
}
What do you think of this?

Offline sammyMaX

  • LV5 Advanced (Next: 300)
  • *****
  • Posts: 204
  • Rating: +9/-0
    • View Profile
Re: Nspire: Keypad Input
« Reply #9 on: September 09, 2011, 06:15:30 pm »
Edit: I deleted my old silly response for something more useful.
I don't understand your code; specifically, I don't get block_value, VALUE_A, and VALUE_B. I'll just state my opinions on the most comfortable "key holding", though: (this is oriented for typing, not games) it shouldn't be like "add another character every second held" or anything like that; there should be a pretty long pause between the first character typed and when the holding begins happening. For example, it should be like "after two seconds of the key being held, add another character every half second."
« Last Edit: September 09, 2011, 08:18:07 pm by sammyMaX »

Are you wondering who Sammy is? My avatar is Sammy.
   

Offline shrear

  • LV4 Regular (Next: 200)
  • ****
  • Posts: 193
  • Rating: +17/-0
    • View Profile
Re: Nspire: Keypad Input
« Reply #10 on: September 10, 2011, 05:44:26 am »
I don't understand your code; specifically, I don't get block_value, VALUE_A, and VALUE_B.
As said it was fast written down... I thought it still comprehensible, seems not to be the case :(
Ok I explain it in words:

You have a "global"(it is declared in main and passed by pointer) value "block_value" which causes key_presses to be ignored
if its value is to high ( higher than "VALUE_A", who I didn't want to give a specific value, maybe that's where the confusion comes from.)

"block_value" is incremented each time a key is pressed and not ignored. (in fact, with a bit more code you could assign different "increment" values to different keys.)
Now you can decrement ( or increment) "block_value" outside the "key" function, either always in the main loop or if certain other conditions are meet.

Basically this method, or something similar gives you a fine grained control (as may be needed in games where there can be a lot of factors) if a key results in an action or not.


I wonder right now if my english is more understandable...lol.

In case now passed seconds is the single factor, there is a Clock in the nspire.

Code: [Select]
int key_pressed(t_key is_pressed)
{

if ( isKeyPressed(is_pressed) )
{
unsigned time_start = *(volatile unsigned*)0x90090000 ; //get the time when key is first pressed down
while ( *(volatile unsigned*)0x90090000 < time_start+SECONDS_TO_WAIT && isKeyPressed(is_pressed) )  //compare additionally to the current time
{
//loop to prevent more than one reaction per key-press or per "SECONDS_TO_WAIT" seconds.
}
return 1 ;
}
else
return 0 ;

}
I hope this code is understandable ;)
« Last Edit: September 10, 2011, 05:44:49 am by shrear »

Offline sammyMaX

  • LV5 Advanced (Next: 300)
  • *****
  • Posts: 204
  • Rating: +9/-0
    • View Profile
Re: Nspire: Keypad Input
« Reply #11 on: September 10, 2011, 05:13:56 pm »
Yes, I get it now :) Thanks.

Are you wondering who Sammy is? My avatar is Sammy.
   

Offline ExtendeD

  • CoT Emeritus
  • LV8 Addict (Next: 1000)
  • *
  • Posts: 825
  • Rating: +167/-2
    • View Profile
Re: Nspire: Keypad Input
« Reply #12 on: September 13, 2011, 12:42:10 pm »
Ndless's wait_no_key_pressed() and any_key_pressed() may also help you: http://hackspire.unsads.com/wiki/index.php/Libndls#Keyboard
You can have a look at the keypad testing program in arm/tests of Ndless source code.
« Last Edit: September 13, 2011, 12:44:04 pm by ExtendeD »
Ndless.me with the finest TI-Nspire programs

Offline sammyMaX

  • LV5 Advanced (Next: 300)
  • *****
  • Posts: 204
  • Rating: +9/-0
    • View Profile
Re: Nspire: Keypad Input
« Reply #13 on: September 14, 2011, 08:05:53 pm »
Thanks, ExtendeD. If I use something like any_key_pressed(), is there any way to see which key was pressed when it returns true? (Without using a lot of if statements?)

Are you wondering who Sammy is? My avatar is Sammy.
   

Offline ExtendeD

  • CoT Emeritus
  • LV8 Addict (Next: 1000)
  • *
  • Posts: 825
  • Rating: +167/-2
    • View Profile
Re: Nspire: Keypad Input
« Reply #14 on: September 15, 2011, 03:41:49 am »
No, you need to get through your if-list.
Anyway if such a function returned the key code, wouldn't you also need to use all these if? Also several key can be pressed at a time, so this couldn't really be done.
Ndless.me with the finest TI-Nspire programs