GP2X Detecting Changes In /dev/gpio


A_SN

Active Member
Joined
Jun 8, 2006
Messages
899
ParkyDR and I were wondering if we could possibly tell when /dev/GPIO changes so that we save the changes and thus queue the key presses so that none could be missed, as you can miss key presses when you only look at the state of /dev/GPIO if the main loop is too slow.

He suggested to use select() in order to tell when a change would happen be tested without any success. So I don't really have any idea on how we could tell when a change happens, so if anyone has any ideas, that would be great. I think pretty much all games could more or less benefit from not ever missing a key press.
 
/dev/gpio is pretty simplistic, and the only way of guaranteeing not missing a single keypress would be using a timer to read the keys every 10ms or so directly from the hardware (via an interrupt on the 940 if you wanted to be funky enough). You'd also have to debounce them yourselves too, but even if you required the key down for 100ms before you regarded it a press, it would still be able to record pressing any single key 10 times per second, which should be enough for most people.
 
Squidge posted on Dec 21 2006 at 09:53 PM said:
/dev/gpio is pretty simplistic, and the only way of guaranteeing not missing a single keypress would be using a timer to read the keys every 10ms or so directly from the hardware (via an interrupt on the 940 if you wanted to be funky enough). You'd also have to debounce them yourselves too, but even if you required the key down for 100ms before you regarded it a press, it would still be able to record pressing any single key 10 times per second, which should be enough for most people.

Sounds good, know how to do that? :) I never messed with interrupts or multi-core programming, just so you know, but I'd gladly learn.

By the way, what's debouncing?
 
Last edited by a moderator:
I don't see the need for anything better than SDL's event system.

Debouncing is getting rid of any "false positives" from your input by having a set amount of time that a button has to be held down for to register.
 
Well, there's lots of examples of multi core programming on this board that can help you out here, but if you've only played with SDL, it's probably a bit out of your reach, and you'll probably be better off optimizing your main loop so it checks the SDL events more often.

If you play with any normal button, you'll often find that the button "bounces" due to the contacts used and mechanism/etc. For example, when you press the button, you may actually get several on's and off's, before it rests on it's final status, and the same when you release the button. If you write your program so it only regards a keypress or release that has been in that state for at least X amount of time, then you'll likely only get the actual state of the button. -> http://www.ganssle.com/debouncing.pdf
 
if you read in a polling fashion I think you don't have debounce this signal, the signal will be already well-set when you read it most likely

bouncing occurs for a very short period, like 0.1ms or (much) less, and in that state, chances are good that you get the correct signal even while boucing, so your chances are 1% to get the exact time when button is changing from on to off/vice-versa and still 50% at least of chance to get the wrong signal instead of the correct...
 
Lint, I think you need to read that PDF I referenced :)

But yeah, if you read through /dev/gpio, debouncing has already occured, and there's a certain amount of debouncing in hardware too by the looks of things.
 
Blah posted on Dec 22 2006 at 04:23 AM said:
I don't see the need for anything better than SDL's event system.

You would if you ever experience a not so great FPS.

Anyways, about debouncing, it's kind of funny, you guys basically say two things :

1). Reading GPIO too often is bad because then you have to debounce
2). You have to make your loop go fast enough and everything will be fine, you won't miss any key press and everything would be properly debounced

Well, I just have to read GPIO at the right frequency then, regardless the main loop speed then? I however don't see how could measuring less often without any kind of filtering could prevent you from getting any "false positive", only make it less likely. Anyways, I really gotta make measurements and stuff about that bouncing buttons thing, I find it intriguing.

This being said, I'm still interested in doing it on the 940T, i guess I'll have to look around for code examples.

Squidge posted on Dec 22 2006 at 01:28 PM said:
Well, there's lots of examples of multi core programming on this board that can help you out here, but if you've only played with SDL, it's probably a bit out of your reach, and you'll probably be better off optimizing your main loop so it checks the SDL events more often.

I don't know what you mean by "only played with SDL" (are you talking about buttons handling? game programming? general programming?), but suggesting that something may be out of my reach is the best way to make me want to do it ;)
 
Last edited by a moderator:
You seem to be confused - we are talking about two seperate things...

If you optimize your main code so the loop runs fast enough, you can use SDL events, or read /dev/gpio and not get any missed keypresses, and all keys will be debounced for you.

If you ignore the above and read from the hardware directly, you will have to do some debouncing yourself - you can't just read less often, you have to sample constantly and at well defined time constants. Otherwise you might think a key has been pressed twice, where in reality it has only been pressed once (for example).
 
Squidge posted on Dec 23 2006 at 04:41 PM said:
You seem to be confused - we are talking about two seperate things...

If you optimize your main code so the loop runs fast enough, you can use SDL events, or read /dev/gpio and not get any missed keypresses, and all keys will be debounced for you.

If you ignore the above and read from the hardware directly, you will have to do some debouncing yourself - you can't just read less often, you have to sample constantly and at well defined time constants. Otherwise you might think a key has been pressed twice, where in reality it has only been pressed once (for example).

Wow, that's right, I really didn't get it. What's the difference between reading from /dev/GPIO and "reading from the hardware directly?" and how do you even do that, and why would I even wanna do that?
 
Last edited by a moderator:
The main reason (and advantage) for hitting the hardware directly rather than using well established methods is overhead. Eg. reading from the hardware will always be quicker than reading a kernel provided file, or SDL event. The disadvantage is that it's more complex and requires more code on your part.
 
Squidge posted on Dec 24 2006 at 09:18 PM said:
The main reason (and advantage) for hitting the hardware directly rather than using well established methods is overhead. Eg. reading from the hardware will always be quicker than reading a kernel provided file, or SDL event. The disadvantage is that it's more complex and requires more code on your part.

I don't wanna get into that, I mean I don't see the advantage, unless that by overhead you mean more than 10 milliseconds. Wouldn't you lose that advantage anyways by debouncing on your own? Anyways like I said, I just want to make sure I don't miss an actual keypress by checking often and queuing, so using GPIO will be good enough for that I'm sure. I just need to figure out how to work it out with the second core.
 
Last edited by a moderator:
The difference will be less than 1ms, but if you want to use the second core, you have to hit the hardware directly - you don't have a choice. SDL and /dev/gpio don't work on the second core.
 
Squidge posted on Dec 24 2006 at 10:30 PM said:
The difference will be less than 1ms, but if you want to use the second core, you have to hit the hardware directly - you don't have a choice. SDL and /dev/gpio don't work on the second core.

Sounds over-complicated, at least for me. If anyone wants to do it, great, but I think I'll go the quick and dirty way by creating an array of int's and regularly store the content of GPIO in between the most time consuming parts of my main loop in order to have it polled often enough, and at the beginning of the loop I'd deal with all I gathered. Can't think of any simpler way to do it, provided that we consider that polling once per loop iteration isn't sufficient.
 
Last edited by a moderator:
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
 
Last edited by a moderator:
An obvious way to handle this is to use threads

Here's an example I prepared earlier :)

Code:
#include <pthread.h>
#include <fcntl.h>
#include <iostream>
#include <deque>
#include <time.h>
#include <unistd.h>
 
using namespace std;
 
deque<unsigned int> button_queue;
 
pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER;
 
 
void* poll_buttons(void *arg)
{
		int fd = open("/dev/GPIO", O_RDONLY);
		unsigned int last_value = 0xFFFFFFFF;
		unsigned int value = 0;
		timespec delay;
		delay.tv_sec = 0;
		delay.tv_nsec = 10000;
 
		while (true)
		{
			// Read gpio
			read(fd, &value, 4); 
 
			// If changed queue it
			if (value != last_value)
			{
				// Lock out other threads
				pthread_mutex_lock(&queue_mutex);
 
				// Queue value
				last_value = value;
				button_queue.push_back(value);
				cout << "Thread queued " << value << endl;
 
				// Release mutex
				pthread_mutex_unlock(&queue_mutex);
			}
			// Delay 10 msec
			nanosleep(&delay, NULL);
		}
 
		return 0;
}
 
int main(int argc, char** argv)
{
	// Create thread
	pthread_t thread_id;
	pthread_create(&thread_id, NULL, poll_buttons, NULL);
 
	while (true)
	{
		cout << "Doing time consuming stuff" << endl;
		sleep(2);
 
		// Read stuff from queue
 
		// Lock out other threads
		pthread_mutex_lock(&queue_mutex);
 
		// Get queued values
		while (!button_queue.empty())
		{
			cout << "Value = " << button_queue.front() << endl;
			button_queue.pop_front();
		}
 
		// Release mutex
		pthread_mutex_unlock(&queue_mutex);
	}
 
	return 0;
}
 
An obvious way to handle this is to use threads

Here's an example I prepared earlier :)

Code:
#include <pthread.h>
#include <fcntl.h>
#include <iostream>
#include <deque>
#include <time.h>
#include <unistd.h>
 
using namespace std;
 
deque<unsigned int> button_queue;
 
pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER;
 
 
void* poll_buttons(void *arg)
{
		int fd = open("/dev/GPIO", O_RDONLY);
		unsigned int last_value = 0xFFFFFFFF;
		unsigned int value = 0;
		timespec delay;
		delay.tv_sec = 0;
		delay.tv_nsec = 10000;
 
		while (true)
		{
			// Read gpio
			read(fd, &value, 4); 
 
			// If changed queue it
			if (value != last_value)
			{
				// Lock out other threads
				pthread_mutex_lock(&queue_mutex);
 
				// Queue value
				last_value = value;
				button_queue.push_back(value);
				cout << "Thread queued " << value << endl;
 
				// Release mutex
				pthread_mutex_unlock(&queue_mutex);
			}
			// Delay 10 msec
			nanosleep(&delay, NULL);
		}
 
		return 0;
}
 
int main(int argc, char** argv)
{
	// Create thread
	pthread_t thread_id;
	pthread_create(&thread_id, NULL, poll_buttons, NULL);
 
	while (true)
	{
		cout << "Doing time consuming stuff" << endl;
		sleep(2);
 
		// Read stuff from queue
 
		// Lock out other threads
		pthread_mutex_lock(&queue_mutex);
 
		// Get queued values
		while (!button_queue.empty())
		{
			cout << "Value = " << button_queue.front() << endl;
			button_queue.pop_front();
		}
 
		// Release mutex
		pthread_mutex_unlock(&queue_mutex);
	}
 
	return 0;
}

Could you use the standard "select" call to wait until there is actual data to read or does read stall till there is data. I don't know the way /dev/GPIO behaves as an I/O device but I assume that the read will stall so the nano sleep would not be needed. If of course, if it always returns the current value and does not stall the read then the nano sleep is needed.
 
Last edited by a moderator:
A_SN posted on Dec 21 2006 at 09:40 PM said:
ParkyDR and I were wondering if we could possibly tell when /dev/GPIO changes so that we save the changes and thus queue the key presses so that none could be missed, as you can miss key presses when you only look at the state of /dev/GPIO if the main loop is too slow.

Reorganize your main loop. Put /dev/GPIO and maybe some other devices like /dev/js0 or /dev/js1 (if you also want to support usb joysticks) into the read fd_set of select, and manage a list of time points. Take the smallest time point of that list and use the smallest_time_point - now() as timeout for select. After select is done you have to check if a file descriptor is set or the timeout happened. Depending on your application you will react on these input events with redraw events, or you will just update some data structures.. For games you would probably use the timeout events to update your game state and then redraw. If you have non animated menu you would simply wait for the next input that changes something on screen, before you redraw.
 
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.
 
Back
Top