GP2X Detecting Changes In /dev/gpio


A_SN posted on Dec 26 2006 at 11:44 AM said:
Squidge posted on Dec 25 2006 at 05:46 PM said:
So your saying your main loop takes longer than ~33ms to execute?

Depends, but even if it only takes 30 ms I have key presses that must last as shortly as you can make em with your fingers, and that's probably less than that, and pressing again because the first time it didn't work isn't an option

Well, personally, I'd say any key held down for less than 30 ms is a glitch and needs filtering out. I doub't many people can press and release a button 30 times a second. I'd be suprised if they could do 10 times a second.

You could try threads/etc, but I think the only proper way of handling this is to simply make your main loop faster - perhaps split some things up so the happen on alternate frames.
 
Last edited by a moderator:
The point of the thread was that select() doesn't work, it always says that GPIO has changed even when it hasn't.

I've also noticed the delay is 10 microseconds (10000 nanoseconds) but the code just demonstrates threading it isn't a proper main loop.

Other devices could be added as other threads and the inputs converted to a standard event and queued.

Missed that sorry ...
 
Last edited by a moderator:
I doub't many people can press and release a button 30 times a second. I'd be suprised if they could do 10 times a second.

Ouch, you just made one awfully terrible assumption.

I was talking about how long key presses could last. You considered it was the same as how long it took to press, release and press again. One would have to make a test program to really be able to tell, but I think that while you must be able to press a button 5 times a second, not much more, you shall be able to keep a button down for a very few centiseconds.
 
Last edited by a moderator:
I see what you mean now.

I'm going on the basis that if you press and release the button too quickly, it could be taken as a glitch and filtered out by the debounce circuitry rather than actually being accepted. The shortest possible button press time is therefore the debounce time, or the button press time, whichever is longer. Reading from /dev/gpio will also introduce more time delays than reading the hardware directly. I wouldn't be suprised if the time between the button being pressed to /dev/gpio acknowledging is about 10ms (1cs).
 
I see what you mean now.

I'm going on the basis that if you press and release the button too quickly, it could be taken as a glitch and filtered out by the debounce circuitry rather than actually being accepted. The shortest possible button press time is therefore the debounce time, or the button press time, whichever is longer. Reading from /dev/gpio will also introduce more time delays than reading the hardware directly. I wouldn't be suprised if the time between the button being pressed to /dev/gpio acknowledging is about 10ms (1cs).

Well, I heard someone saying that /dev/gpio didn't actually debounce, but the best thing is yet to quote him

<DieMumie1> lol the /dev/GPIO kernel code is .. useless
<DieMumie1> it has a debounce function
<DieMumie1> but it is commented out
<DieMumie1> http://svn.gp2x.com/gp2x/tag/kernel/2.1.x/...har/mmsp2-key.c
 
Last edited by a moderator:
Figures. The debounce could be done in hardware, but from my tests, it's not.

I've just knocked up a simple button timer:

Ns:B st:5044575 et:5280331 d:235756 (t)
Ns: st:5280339 et:5280748 d:409 (t)
Ns:B st:5280754 et:5281676 d:922 (t)
Ns: st:5281682 et:5282196 d:514 (t)
Ns:B st:5282202 et:5282202 d:0 (t)
Ns: st:5282208 et:5282214 d:6 (t)

Ns:B st:5282219 et:5283022 d:803 (t)
Ns: st:5283028 et:5283660 d:632 (t)
Ns:B st:5283666 et:5284508 d:842 (t)
Ns: st:5284513 et:5285153 d:640 (t)
Ns:B st:5285159 et:5285960 d:801 (t)
Ns: st:5285966 et:5286706 d:740 (t)
Ns:B st:5286712 et:5287405 d:693 (t)
Ns: st:5287411 et:5288132 d:721 (t)
Ns:B st:5288137 et:5289048 d:911 (t)
Ns: st:5289053 et:5289614 d:561 (t)
Ns:B st:5289620 et:5290477 d:857 (t)
Ns: st:5290483 et:5291082 d:599 (t)
Ns:B st:5291088 et:5291915 d:827 (t)
Ns: st:5291921 et:5292479 d:558 (t)
Ns:B st:5292484 et:5293377 d:893 (t)
Ns: st:5293383 et:5293918 d:535 (t)
Ns:B st:5293923 et:5293925 d:2 (t)
Ns: st:5293931 et:5293931 d:0 (t)

Ns:B st:5293937 et:5294900 d:963 (t)
Ns: st:5294906 et:5295336 d:430 (t)
Ns:B st:5295342 et:5296228 d:886 (t)
Ns: st:5296234 et:5296784 d:550 (t)
Ns:B st:5296790 et:5297821 d:1031 (t)
Ns: st:5297826 et:5298292 d:466 (t)
Ns:B st:5298297 et:5299189 d:892 (t)
Ns: st:5299194 et:5299654 d:460 (t)
Ns:B st:5299659 et:5300695 d:1036 (t)
Ns: st:5300700 et:5301084 d:384 (t)
Ns:B st:5301090 et:5302127 d:1037 (t)
Ns: st:5302132 et:5302506 d:374 (t)
Ns:B st:5302512 et:5303570 d:1058 (t)
Ns: st:5303576 et:5303951 d:375 (t)
Ns:B st:5303956 et:5304989 d:1033 (t)
Ns: st:5304995 et:5305322 d:327 (t)
Ns:B st:5305328 et:5306327 d:999 (t)
Ns: st:5306332 et:5306739 d:407 (t)
Ns:B st:5306745 et:5307741 d:996 (t)
Ns: st:5307747 et:5308143 d:396 (t)
Ns:B st:5308149 et:5309103 d:954 (t)
Ns: st:5309109 et:5309594 d:485 (t)
Ns:B st:5309599 et:5310563 d:964 (t)
Ns: st:5310569 et:5310962 d:393 (t)
Ns:B st:5310968 et:5311847 d:879 (t)
Ns: st:5311853 et:5312399 d:546 (t)
Ns:B st:5312404 et:5313379 d:975 (t)
Ns: st:5313385 et:5313828 d:443 (t)
Ns:B st:5313833 et:5313834 d:1 (t)
Ns: st:5313839 et:5313840 d:1 (t)

Ns:B st:5313845 et:5314899 d:1054 (t)
Ns: st:5314905 et:5315380 d:475 (t)
Ns:B st:5315385 et:5316360 d:975 (t)
Ns: st:5316366 et:5316999 d:633 (t)
Ns:B st:5317004 et:5317004 d:0 (t)
Ns: st:5317010 et:5317010 d:0 (t)


Ns is the button being pressed (blank if not buttons pressed) and 'd' states the time since the last event. It's measured in 100's of microseconds (so 10,000 is 1 second). The keys are pressed normally as you would in a game. I managed to get values of ~100 when flicking the buttons, but the usual values were more between 400 and 800. You can see several values about of less than 10 (I've marked some of them in bold), I can only assume these were bounces.

These tests were done by reading the hardware directly, I'll leave it to someone else to try the SDL and/or /dev/gpio routes.
 
Since the /dev/gpio code is pretty useless to keep track of input. I wrote a proper joystick
input module. The code uses a timer, i think it would be simple to turn that into an interupt handler, but I wasnt sure how the interrupt handling works on the mmsp.

KERNEL/drivers/char/joystick/gp2x.c:
Code:
/*
 *  Copyright (c) Andreas Pokorny
 *
 *  Based on the work of:
 *	Vojtech Pavlik
 */

/*
 * Gamepark Holding GP2X-F100 dpad and buttons
 */

/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or 
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 * Should you need to contact me, the author, you can do so by e-mail 
 * mail your message to <andreas.pokorny@gmail.com>
 */


#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/kernel.h>

/* This code is restricted to mmsp2 architectures:*/
#include <asm/hardware.h>
#include <asm/arch/mmsp20.h>
#include <asm/arch/hardware.h>
#include <asm/arch/mmsp20_type.h>

MODULE_AUTHOR("Andreas Pokorny <andreas.pokorny@gmail.com>");
MODULE_LICENSE("GPL");

#define GP2X_GROUP_1	  GPIO_C8
#define GP2X_GROUP_2	  GPIO_M0
#define GP2X_VOLUME_UP		GPIO_D7
#define GP2X_VOLUME_DOWN  GPIO_D6
#define GP2X_STICK_BUTTON	GPIO_D11
#define GP2X_KEYS_PER_GROUP 8

static unsigned int open_count = 0;

void gp2x_gamepad_timer_fun( unsigned long );

static struct timer_list gp2x_gamepad_timer;
static struct input_dev gp2x_gamepad_dev = {0};

void gp2x_gamepad_timer_fun( unsigned long data ) 
{
  MMSP20_GPIO * base;
  unsigned short buttons = 0, direction = 0, volumeStick = 0;
  int i = 0;

  base = MMSP20_GetBase_GPIO();

  buttons = ~((base->GPIOPINLVL[GRP_NUM(GP2X_GROUP_1)]>>(GP2X_GROUP_1&0xf)) &0xFF);
  volumeStick = ~(base->GPIOPINLVL[GRP_NUM(GP2X_VOLUME_DOWN)]);
  direction = ~base->GPIOPINLVL[GRP_NUM(GP2X_GROUP_2)];

  input_report_key(&gp2x_gamepad_dev,BTN_START,   buttons & 1);
  input_report_key(&gp2x_gamepad_dev,BTN_SELECT, (buttons>>1) & 1);
  input_report_key(&gp2x_gamepad_dev,BTN_TL,	 (buttons>>2) & 1);
  input_report_key(&gp2x_gamepad_dev,BTN_TR,	 (buttons>>3) & 1);
  input_report_key(&gp2x_gamepad_dev,BTN_A,	  (buttons>>4) & 1);
  input_report_key(&gp2x_gamepad_dev,BTN_B,	  (buttons>>5) & 1);
  input_report_key(&gp2x_gamepad_dev,BTN_X,	  (buttons>>6) & 1);
  input_report_key(&gp2x_gamepad_dev,BTN_Y,	  (buttons>>7) & 1);
  input_report_key(&gp2x_gamepad_dev, BTN_TL2,	(volumeStick >> (GP2X_VOLUME_DOWN &0xf))&1); /* volume down */
  input_report_key(&gp2x_gamepad_dev, BTN_TR2,	(volumeStick >> (GP2X_VOLUME_UP   &0xf))&1); /* volume up */
  input_report_key(&gp2x_gamepad_dev, BTN_TRIGGER,(volumeStick >> (GP2X_STICK_BUTTON&0xf))&1);


  switch( direction&255 )
  {
	case 0: 
	  input_report_abs(&gp2x_gamepad_dev, ABS_X,0);
	  input_report_abs(&gp2x_gamepad_dev, ABS_Y,0);
	  break;
	case 1: /* North*/
	  input_report_abs(&gp2x_gamepad_dev, ABS_X,0);
	  input_report_abs(&gp2x_gamepad_dev, ABS_Y,2);
	  break;
	case 3: /* NorthNorthWest*/
	  input_report_abs(&gp2x_gamepad_dev, ABS_X,-1);
	  input_report_abs(&gp2x_gamepad_dev, ABS_Y,2);
	  break;
	case 2: /* NorthWest*/
	  input_report_abs(&gp2x_gamepad_dev, ABS_X,-2);
	  input_report_abs(&gp2x_gamepad_dev, ABS_Y,2);
	  break;
	case 5: /* NorthWestWest*/
	  input_report_abs(&gp2x_gamepad_dev, ABS_X,-2);
	  input_report_abs(&gp2x_gamepad_dev, ABS_Y,1);
	  break;
	case 4: /* West*/
	  input_report_abs(&gp2x_gamepad_dev, ABS_X,-2);
	  input_report_abs(&gp2x_gamepad_dev, ABS_Y,0);
	  break;
	case 12:/* SouthWestWest*/
	  input_report_abs(&gp2x_gamepad_dev, ABS_X,-2);
	  input_report_abs(&gp2x_gamepad_dev, ABS_Y,-1);
	  break;
	case 8: /* SouthWest  */
	  input_report_abs(&gp2x_gamepad_dev, ABS_X,-2);
	  input_report_abs(&gp2x_gamepad_dev, ABS_Y,-2);
	  break;
	case 24:/* SouthSouthWest */
	  input_report_abs(&gp2x_gamepad_dev, ABS_X,-1);
	  input_report_abs(&gp2x_gamepad_dev, ABS_Y,-2);
	  break;
	case 16: /* South */
	  input_report_abs(&gp2x_gamepad_dev, ABS_X,0);
	  input_report_abs(&gp2x_gamepad_dev, ABS_Y,-2);
	  break;
	case 48: /* SouthSouthEast */
	  input_report_abs(&gp2x_gamepad_dev, ABS_X,1);
	  input_report_abs(&gp2x_gamepad_dev, ABS_Y,-2);
	  break;
	case 32: /* SouthEast */ 
	  input_report_abs(&gp2x_gamepad_dev, ABS_X,2);
	  input_report_abs(&gp2x_gamepad_dev, ABS_Y,-2);
	  break;
	case 96: /* SouthEastEast   */
	  input_report_abs(&gp2x_gamepad_dev, ABS_X,2);
	  input_report_abs(&gp2x_gamepad_dev, ABS_Y,-1);
	  break;
	case 64: /* East */
	  input_report_abs(&gp2x_gamepad_dev, ABS_X,2);
	  input_report_abs(&gp2x_gamepad_dev, ABS_Y,0);
	  break;
	case 192: /* NorthEastEast   */
	  input_report_abs(&gp2x_gamepad_dev, ABS_X,2);
	  input_report_abs(&gp2x_gamepad_dev, ABS_Y,1);
	  break;
	case 128: /* NorthEast	   */
	  input_report_abs(&gp2x_gamepad_dev, ABS_X,2);
	  input_report_abs(&gp2x_gamepad_dev, ABS_Y,2);
	  break;
	case 129: /* NorthNorthEast */
	  input_report_abs(&gp2x_gamepad_dev, ABS_X,1);
	  input_report_abs(&gp2x_gamepad_dev, ABS_Y,2);
	  break;
  }

  mod_timer( & gp2x_gamepad_timer, jiffies + 2 );
}

int open_gp2x_gamepad_dev(struct input_dev *dev)
{
  if( 0 == open_count )
  {
	init_timer( & gp2x_gamepad_timer );
	gp2x_gamepad_timer.expires = jiffies + HZ;
	gp2x_gamepad_timer.data = 0; 
	gp2x_gamepad_timer.function = gp2x_gamepad_timer_fun;
	add_timer( & gp2x_gamepad_timer );
  }

  ++open_count;
  return 0;
}


void close_gp2x_gamepad_dev(struct input_dev *dev)
{
  if( open_count == 1 )
  {
	del_timer( & gp2x_gamepad_timer );
	--open_count;
  }
  else if ( open_count != 0 )
  {
	--open_count;
  }
}


int __init gp2x_gamepad_init(void)
{
  int i;

  for( i=0; i<8; i++) 
  {
	set_gpio_ctrl( GPIO_C8 +i , GPIOMD_IN, GPIOPU_EN );
	set_gpio_ctrl( GPIO_M0 +i , GPIOMD_IN, GPIOPU_EN );
  }

  set_gpio_ctrl(GP2X_VOLUME_UP,GPIOMD_IN,GPIOPU_EN);
  set_gpio_ctrl(GP2X_VOLUME_DOWN,GPIOMD_IN,GPIOPU_EN);
  set_gpio_ctrl(GP2X_STICK_BUTTON,GPIOMD_IN,GPIOPU_EN);


  gp2x_gamepad_dev.evbit[0] = BIT(EV_ABS)|BIT(EV_KEY);
  gp2x_gamepad_dev.keybit[LONG(KEY_VOLUMEDOWN)] = BIT(KEY_VOLUMEDOWN) | BIT(KEY_VOLUMEUP);
  gp2x_gamepad_dev.keybit[LONG(BTN_JOYSTICK)] |= BIT(BTN_TRIGGER);
  gp2x_gamepad_dev.keybit[LONG(BTN_GAMEPAD)] |= BIT(BTN_A) 
	| BIT(BTN_B) 
	| BIT(BTN_X) 
	| BIT(BTN_Y) 
	| BIT(BTN_TL) 
	| BIT(BTN_TR) 
	| BIT(BTN_TL2) 
	| BIT(BTN_TR2) 
	| BIT(BTN_SELECT) 
	| BIT(BTN_START) 
   ;
	gp2x_gamepad_dev.absmax[ABS_X]=2;
	gp2x_gamepad_dev.absmax[ABS_Y]=2;
	gp2x_gamepad_dev.absmin[ABS_X]=-2;
	gp2x_gamepad_dev.absmin[ABS_Y]=-2;
	gp2x_gamepad_dev.absfuzz[ABS_X]=0;
	gp2x_gamepad_dev.absfuzz[ABS_Y]=0;
	gp2x_gamepad_dev.absflat[ABS_X]=0;
	gp2x_gamepad_dev.absflat[ABS_Y]=0;


  gp2x_gamepad_dev.absbit[LONG(ABS_X)] |= BIT(ABS_X) | BIT(ABS_Y); 
  gp2x_gamepad_dev.name = "GP2X-F100 gamepad";
  gp2x_gamepad_dev.open = open_gp2x_gamepad_dev;
  gp2x_gamepad_dev.close = close_gp2x_gamepad_dev;
  input_register_device(&gp2x_gamepad_dev);
  return 0;
}

void __exit gp2x_gamepad_exit(void)
{
	input_unregister_device(&gp2x_gamepad_dev);
}

module_init(gp2x_gamepad_init)
module_exit(gp2x_gamepad_exit)

You can tweak the code a bit by raising the value added to the jiffies.
Code:
   mod_timer( & gp2x_gamepad_timer, jiffies + 2 );


To add the code above to your kernel add the following to your drivers/char/joystick/Config.in:
Code:
   ....
else
   # Must explicitly set to n for drivers/sound/Config.in
   define_tristate CONFIG_INPUT_GAMEPORT n
   comment 'Input core support is needed for gameports'
fi

if [ "$CONFIG_INPUT" != "n" ]; then
   comment 'Joysticks'

   dep_tristate '  Classic PC analog joysticks and gamepads' CONFIG_INPUT_ANALOG $CONFIG_INPUT $CONFIG_INPUT_GAMEPORT
   dep_tristate '  Gamepark Holding GP2X-F100 gamepad' CONFIG_INPUT_GP2X $CONFIG_ARCH_MMSP2 $CONFIG_INPUT 
   dep_tristate '  Assasin 3D and MadCatz Panther devices' CONFIG_INPUT_A3D $CONFIG_INPUT $CONFIG_INPUT_GAMEPORT
   ....

And the Makefile in the same directory simple needs that line:
Code:
obj-$(CONFIG_INPUT_GP2X)	+= gp2x.o

Somehow it seems like a debouncer is not required. I tried to make "fast" inputs, and I neither lost an input nor experienced any unstable events.
 
Back
Top