"Sensitivity" of inputs

Discuss the development of new homebrew software, tools and libraries.

Moderators: cheriff, TyRaNiD

Post Reply
redflyingpig
Posts: 5
Joined: Thu Nov 03, 2005 12:55 am

"Sensitivity" of inputs

Post by redflyingpig »

Hi, I've written an initial testing program on PSP. I use the UP and DOWN buttons as input buttons, when hit, the character displayed changes. It works, only that the inputs are too sensitive. The character changes many times on one hit. Has anybody got the idea to improve this? What is the 'classic' or 'right' way to control input sensitivities? Here is part of my code:

Code: Select all

int main(void)
{
  SceCtrlData pad;
  char charList[CHAR_LEN] = {'0','1','2','3','4','5','6','7','8','9','.','+','-','*','/','(',')'};
  int curIndex = 0;

  pspDebugScreenInit();
  SetupCallbacks();

  sceCtrlSetSamplingCycle(0);
  sceCtrlSetSamplingMode(PSP_CTRL_MODE_DIGITAL);

  while(1) {
    pspDebugScreenSetXY(0,0);
    printf("Hit UP or DOWN button to change the character:");
    
    pspDebugScreenSetXY(10,10);
    printf("Input: %c", charList[curIndex]);
    
    sceCtrlReadBufferPositive(&pad,1);
    if(pad.Buttons & PSP_CTRL_UP) {
      curIndex = (curIndex + 1) % CHAR_LEN;
    }
    else
      if(pad.Buttons & PSP_CTRL_DOWN) {
	curIndex = (curIndex + CHAR_LEN -1) % CHAR_LEN;
      }

  }

  return 0;
}
weltall
Posts: 310
Joined: Fri Feb 20, 2004 1:56 am
Contact:

Post by weltall »

try putting the result from keys also in a second variable and check if it's equal to the current, so the selected button wont do it's action every time the content of the while is executed like this:

Code: Select all

int main(void)
{
  SceCtrlData pad;
  int old_buttons = 0;
  char charList[CHAR_LEN] = {'0','1','2','3','4','5','6','7','8','9','.','+','-','*','/','(',')'};
  int curIndex = 0;

  pspDebugScreenInit();
  SetupCallbacks();

  sceCtrlSetSamplingCycle(0);
  sceCtrlSetSamplingMode(PSP_CTRL_MODE_DIGITAL);
  sceCtrlReadBufferPositive(&pad,1);
  old_buttons = pad.buttons;
  while(1) {
    pspDebugScreenSetXY(0,0);
    printf("Hit UP or DOWN button to change the character:");
   
    pspDebugScreenSetXY(10,10);
    printf("Input: %c", charList[curIndex]);
   
    sceCtrlReadBufferPositive(&pad,1);
    if(pad.buttons != old_buttons)
    {
    old_buttons = pad.buttons;
    if(pad.Buttons & PSP_CTRL_UP) {
      curIndex = (curIndex + 1) % CHAR_LEN;
    }
    else
      if(pad.Buttons & PSP_CTRL_DOWN) {
   curIndex = (curIndex + CHAR_LEN -1) % CHAR_LEN;
      }
    }
  }
redflyingpig
Posts: 5
Joined: Thu Nov 03, 2005 12:55 am

Post by redflyingpig »

Thanks, weltall, but that does not give me the intended result. The character will not change any more when the same button is pressed repeatedly, if the code is modified like this, which is not what I intended to do.

I'm currenly thinking of using timestamps to make the input more 'stable'.
weltall
Posts: 310
Joined: Fri Feb 20, 2004 1:56 am
Contact:

Post by weltall »

you need to wait a cicle before pressing the button again or it wont work, else you need to check that the button was pressed before and if so check how much time it was pressed, if so trigger a faster scrolling.
you could use cputicks you can find the function to get it from psppower if i remember correctly
CyberBill
Posts: 86
Joined: Tue Jul 26, 2005 3:53 pm
Location: Redmond, WA

Post by CyberBill »

One cycle isnt enough.

The typical way to do key holding is to set a timer when you press the key down, and once that timer reaches some value (400ms or so) you signal the output again, and then set the timer back to something like 300ms, so that the next signal only takes 100ms... This is how you get the effect of holding a keyboard key down.

Try it out..

OOOOOOOOOOOOOOOOOOOOOOOOOOOO :D

(This means you'll have to implement a timer system, as a counter is totally inaccurate)
redflyingpig
Posts: 5
Joined: Thu Nov 03, 2005 12:55 am

Post by redflyingpig »

Could you give me more information about setting up a timer, like, which functions can be utilized to implement it?
Arwin
Posts: 426
Joined: Tue Jul 12, 2005 7:00 pm

Post by Arwin »

redflyingpig wrote:Could you give me more information about setting up a timer, like, which functions can be utilized to implement it?
You don't need a special function. You can just add a counter to the routine with which you check the controller now.
CyberBill
Posts: 86
Joined: Tue Jul 26, 2005 3:53 pm
Location: Redmond, WA

Post by CyberBill »

Dont use a counter.

Code: Select all

unsigned GetMilliseconds()
    {
        SceKernelSysClock clock;
        sceKernelGetSystemTime(&clock);

        unsigned time = clock.low / 1000;
        time += clock.hi * (0xFFFFFFFF / 1000);

        return time;
    }
This gets the time. When someone presses a Key, set somewhere what the current time is and what the key they pressed is, and maybe a flag saying a key is down. Then, every 'frame' or update, check the current time against that time:

Code: Select all

if ( KeyBeingHeld && ( GetMilliseconds() - KeyDownTime > REPEAT_WAIT ) )
{
  //Repeat the key event
  KeyDownTime -= REPEAT_INTERVAL;
}
Where REPEAT_WAIT is an unsigned int corresponding to the number of milliseconds it takes before the first repeat is done, and where REPEAT_INTERVAL is the time between subsequent repeats.

REPEAT_WAIT ~= 400; REPEAT_INTERVAL ~= 100; Those should give you decent results.[/code]
redflyingpig
Posts: 5
Joined: Thu Nov 03, 2005 12:55 am

Post by redflyingpig »

Thanks a lot, CyberBill. I know what you mean, it's a rather accurate solution, however, I've found another way, by simply adding a loop:

Code: Select all

for&#40;i=0; i<6; i++&#41; &#123;
      sceDisplayWaitVblankStart&#40;&#41;;
at the end of each iteration. In this way I set intervals between frames so that the input won't be updated that fast and thus the input becomes less sensitive. Currently it works fine but I don't know exactly what's going on. Could you explain the up/down sides of these solutions? And also, can anybody explain for me what the function 'sceDisplayWaitVblankState()' is doing?
CyberBill
Posts: 86
Joined: Tue Jul 26, 2005 3:53 pm
Location: Redmond, WA

Post by CyberBill »

sceDisplayWaitVBlankStart() waits until the VSynch gets called to the OS. Its tied into the screen refresh rate, the PSP refreshes 60 times a second. So calling it 6 times in a tight loop essentially is the same thing as doing a sceKernelDelayThread( 6 * (1000000/60) ); which should be 100 milliseconds or 100,000 microseconds..

The upside to this solution is that its easy and gets the job done. The bad side is that the code isnt going to port well if you plan on doing a game because some code needs to have full control of the graphics system and game loop, and this throws it out of whack. And dont even think about trying to do that in a multithreaded situation where you have some other system drawing, very strange results I'm sure!!
redflyingpig
Posts: 5
Joined: Thu Nov 03, 2005 12:55 am

Post by redflyingpig »

Thanks a lot for help!
JustChris
Posts: 21
Joined: Thu Nov 03, 2005 7:17 am

Post by JustChris »

I decided to ask the following here because it's related to the topic, instead of making another topic:

How do I detect when a button is released?

For example, I need to make an event happen only once when the button is pressed/held down. And maybe, I want something else to happen after the button is released.[/code]
weltall
Posts: 310
Joined: Fri Feb 20, 2004 1:56 am
Contact:

Post by weltall »

you could do something like that if it's only one button
sceCtrlReadBufferPositive(&pad,1);
if(pad.Buttons & PSP_CTRL_BUTTON)
{
//do action
}
else
{
//stop action
}
CyberBill
Posts: 86
Joined: Tue Jul 26, 2005 3:53 pm
Location: Redmond, WA

Post by CyberBill »

sceCtrlReadBufferPositive() will set the "buttons" flags with the current state of the buttons (if the flag is present then that means the button is pressed down, if its not present then the button is not pressed).

The code from weltall only handles half of this. What you really want to do is detect not when the button is not pressed, but when the button's state changes from not pressed to pressed and back.

So:

Code: Select all

static int oldstate = 0;
SceCtrlData state;
sceCtrlReadBufferPositive&#40;&state, 1&#41;;
for&#40;int i=0; i<32; i++&#41;
&#123;
  if &#40;&#40;state.Buttons & &#40;1<<i&#41;&#41; != &#40;oldstate & &#40;1<<i&#41;&#41;
  &#123;
    //Button 'i' was changed!!
    if &#40;state.Buttons & &#40;1<<i&#41;&#41;
    &#123;
       //Button i was PRESSED
    &#125;
    else
    &#123;
       //Button i was RELEASED
    &#125;
  &#125;
&#125;
oldstate = state.Buttons;
You can then compare 'i' to the different #defines (SCE_CTRL_SELECT, SCE_CTRL_UP, etc) to see what logical button it is. Also, clearly there is not 32 buttons... But the bits are all over the place. You could technically do different ifs for each button instead of a loop, but it wouldnt be good coding style.

Id also like to point out that there is latch functionality in the PSP to do some hardware switches that apparantly is more correct, and wont ever miss button presses (wheras this could potential miss a press if the app was slow enough). However I havnt looked into this... you may want to search and see how it works.

Good luck!
nerdcore
Posts: 1
Joined: Mon Nov 07, 2005 11:53 am
Location: Ottawa, ON, CA
Contact:

sceCtrlSetSamplingCycle?

Post by nerdcore »

One easy solution for slowing input would be to sleep the input thread using sceKernelDelayThread(int microsecs).

Code: Select all

while &#40;1&#41; &#123;
  sceCtrlReadBufferPositive&#40;&input, 1&#41;;
  if &#40;input.Buttons & PSP_CTRL_UP&#41; &#123; &#125;
  if &#40;input.Buttons & PSP_CTRL_DOWN&#41; &#123; &#125;

  sceKernelDelayThread&#40;333333&#41;; // Delay for about 1/3 second
&#125;
This is not as nice a solution as checking the time of input against the last input, but perhaps cleaner than assuming sceDisplayWaitVblankStart() takes any given length of time, because it is not dependant on the system refresh rate.

But my question would be this: What exactly does sceCtrlSetSamplingCycle(int arg) do? It sounds to me like this would be a sensible way to change the sampling rate of the controls, but the pspsdk doc suggests that this always be set to 0.
Post Reply