Omnimaga

Calculator Community => TI Calculators => Calculator C => Topic started by: sammyMaX on September 06, 2011, 04:11:06 pm

Title: Nspire: Keypad Input
Post by: sammyMaX 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?
Title: Re: Nspire: Keypad Input
Post by: JustCause 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. :)
Title: Re: Nspire: Keypad Input
Post by: sammyMaX 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().
Title: Re: Nspire: Keypad Input
Post by: TravisE 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.
Title: Re: Nspire: Keypad Input
Post by: Lionel Debroux 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.
Title: Re: Nspire: Keypad Input
Post by: shrear 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.
Title: Re: Nspire: Keypad Input
Post by: fb39ca4 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.
Title: Re: Nspire: Keypad Input
Post by: sammyMaX 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)
Title: Re: Nspire: Keypad Input
Post by: shrear 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?
Title: Re: Nspire: Keypad Input
Post by: sammyMaX 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."
Title: Re: Nspire: Keypad Input
Post by: shrear 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 (http://hackspire.unsads.com/wiki/index.php/Memory-mapped_I/O_ports#90090000_-_Real-Time_Clock_.28RTC.29) 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 ;)
Title: Re: Nspire: Keypad Input
Post by: sammyMaX on September 10, 2011, 05:13:56 pm
Yes, I get it now :) Thanks.
Title: Re: Nspire: Keypad Input
Post by: ExtendeD 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.
Title: Re: Nspire: Keypad Input
Post by: sammyMaX 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?)
Title: Re: Nspire: Keypad Input
Post by: ExtendeD 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.
Title: Re: Nspire: Keypad Input
Post by: sammyMaX on September 15, 2011, 05:39:56 pm
I wanted such a method because it would save some code (not really that important). Right now I have ten if statements (for buttons for the numbers 0 to 9) and each calls the same function with its keycode as an argument, that then adds the character to a string for output. It would be cool if all these if statements could be grouped together since they call the same thing.
Title: Re: Nspire: Keypad Input
Post by: ExtendeD on September 16, 2011, 03:48:34 am
I remember Goplat posted once an OS function definition to directly read key events (and key codes). I can't find it back, I don't think it has been integrated to Ndless.
Title: Re: Nspire: Keypad Input
Post by: bsl on September 16, 2011, 10:04:22 am
I remember Goplat posted once an OS function definition to directly read key events (and key codes). I can't find it back, I don't think it has been integrated to Ndless.
http://ourl.ca/4852/112431
Title: Re: Nspire: Keypad Input
Post by: Lionel Debroux on September 16, 2011, 10:09:43 am
Interesting :)
Title: Re: Nspire: Keypad Input
Post by: bsl on September 16, 2011, 08:21:52 pm
To use this in Ndless 2.0:
Code: [Select]
static const unsigned get_event_addrs[] = {0x1016C354, 0x1016e36c, 0x101b2724, 0x101b2fec};  //OS 1.7 ,OS2.0.1.60
#define get_event SYSCALL_CUSTOM(get_event_addrs, int,  struct event *)
Title: Re: Nspire: Keypad Input
Post by: sammyMaX on September 19, 2011, 09:19:05 pm
I don't get what your code does, bsl, and I don't really get Goplat's either :( Could anyone explain to me what they do, and how to use them?
Title: Re: Nspire: Keypad Input
Post by: lkj on September 27, 2011, 11:54:16 am
This code lets you use an OS function to get the key code. When you call it, it waits until a key gets pressed and then gives you the key code.
To use it you have to copy paste Goplat's code into your project and replace #ifdef to #endif with the two codelines bsl posted.
Title: Re: Nspire: Keypad Input
Post by: sammyMaX on September 27, 2011, 05:37:28 pm
When you call it, it waits until a key gets pressed and then gives you the key code.
Okay, thanks. I'll try it out.