Long and Short Key presses- Please help.

Go To Last Post
16 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Guys,

I am trying to understand the coding in these project.
https://code.google.com/p/th9x/
https://code.google.com/p/er9x/

At this moment I am particularly interested in understanding the Key input subsystem where navigation keys and Select/Exit keys are scanned in such a way that each botton click can create several events based on how long its been pressed.
As I understand, in a very basic level the code works like this.ISR driven per10ms function calls Key::input() and that function somehow create an event based on how long the keys has been pressed and debouncing is also performed.
so the basic building blocks we can find,

Keys been defined as,

	#define INP_B_KEY_LFT 6
	#define INP_B_KEY_RGT 5
	#define INP_B_KEY_UP  4
	#define INP_B_KEY_DWN 3
	#define INP_B_KEY_EXT 2
	#define INP_B_KEY_MEN 1

and enumuration of Keys can be find as

	enum EnumKeys {
	KEY_MENU ,
	KEY_EXIT ,
	KEY_DOWN ,
	KEY_UP  ,
	KEY_RIGHT ,
	KEY_LEFT ,
	}

and some macros related to this

	#define EVT_KEY_BREAK(key) ((key)|0x20)
	#define EVT_KEY_FIRST(key) ((key)|_MSK_KEY_FIRST)
	#define EVT_KEY_REPT(key)  ((key)|_MSK_KEY_REPT)
	#define EVT_KEY_LONG(key)  ((key)|0x80)
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

And the following code is the base code to create an event based on the long or short key presses.

	Static uint8_t s_evt;
	void putEvent(uint8_t evt)
	{
	  s_evt = evt;
	}


	class Key
	{
	#define FFVAL 0x0f
	  uint8_t m_vals:4;
	  uint8_t m_cnt;
	public:
	  void input(bool val, EnumKeys enuk);
	  bool state();
	  void killEvents();
	};

	Key keys[NUM_KEYS];

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

And this Key::input() method is called periodically by per10ms function

	void per10ms()
	{
		uint8_t enuk = KEY_MENU;
		uint8_t    in = ~PINB;
		for(int i=1; i<7; i++)
		{
			//INP_B_KEY_MEN 1  .. INP_B_KEY_LFT 6
			keys[enuk].input(in & (1<<i),(EnumKeys)enuk);
			++enuk;
		}
	.
	.
	}

If you can shed some light on how the Key::input() method create these events based on the time that bottons has been pressed I appreciate it very much.
To visit the complete code of the project https://code.google.com/p/th9x/source/browse/trunk/src/drivers.cpp?spec=svn20&r=20
Related projects are Th9x,Er9x,Gruvin9x,Open9x..
Any responce is appreciated.

Thanks very much.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No, I have not looked at your code.

Normally, you scan a keyboard at regular intervals to detect whether any keys are pressed. e.g. every 10 ms.

If you detect any change, you check whether this new value is stable. e.g. same value for 50ms.
This is to cope with bouncing switches. i.e. when the key is pressed or released.

Obviously you can distinguish between long and short keypresses by simply counting the number of scans. Perhaps 5 scans to debounce and 100 scans to say 'key pressed for >= one second'.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For some debouncing code, wit handling of short and long key-presses, see: https://www.avrfreaks.net/index.p... (ultimately leading to http://www.mikrocontroller.net/a... )

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the input. I went through some denouncing and long and short key press tutorials before.

but in this case I could not find any similarity up to this point.

Anyway thanks for the feedback again.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But, surely, there aren't all that many ways that this can be done given a tick interval. There are two components, debounce and press duration. When a state change is indicated by the debounce, a timer ( a counter ) is started and when the counter is greater than a certain value, it is a long key press.

But... the code. The class Key ( this is C++ ) has two data members. The first m_val is a bit field of four bits and the second m_count is an unsigned eight bit value. When the Key::input() method is called, the current pin state value is shifted into the m_val variable. If m_val has changed and is equal to 0x0F ( FFVAL ) m_cnt is cleared, if m_val is 0 then a break event ( switch released ) event is posted. Then m_cnt is incremented ( only when m_val == FFVAL ) and goes through a series of comparisons. If m_cnt == 1, that is a short press, if m_cnt > 30, that is a long press. There are other conditions for repeated button presses.

There are some other features of the code, but that's a basic outline. Just what would be expected from standard debounce implementations, though it is in a somewhat more complex framework here.

Martin Jay McKee

As with most things in engineering, the answer is an unabashed, "It depends."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Martin,

Thanks a lot. I agree with you with reference to the outline.
You should be right about the denouncing taken place in first part of the Key::input()method and counter thing.

But what I don't understand is that this member variables are not static and per one call only one increment is happen. so how come the "Counter" function to measure the interval is implemented here ?

And I am confused here since the s_evt is the single parameter which has been put as an event.
Its not private to Key. so how it can be distinguished in which key the event had taken place ?

Thanks again in advanced.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Each Key object ( stored in the array keys[] ) is it's own unit. Key is a class... it just declares what something looks like. You have to create instances of classes ( objects ) to actually define storage. The important thing about objects, however is that each has its own storage, in this case for m_cnt and m_val. Also, if you look at the per10ms() method, the input() method is called on every key with two values, the current state of the pin and 'enuk' which is a KeyEnum, value... long way of saying that it is an "ID" for each key.

So, question #1, why is the count in the input call? Because the Key object is where the count ( m_cnt ) is stored. The actual timing is a combination of how often input is called and the count threshold values, but the input() method has the counter implementation because the implementers decided that it made sense to put it there ( how's that for an answer ). In this case, I would agree. Given the structure, why not make the same object handle both debouncing and timing on a key.

And, question #2, how do you know which key it was? I'd have to dig deeper into the code to explain it fully ( and I really don't feel like doing so at the moment ), but do you notice the calls to putEvent() in the Key::input() method? What is actually being sent appears to be modified by a macro ( all caps ) or function, and it takes as a parameter enuk. For instance, EVT_KEY_FIRST(enuk) is the parameter to putEvent() for a short press ( count == 1 ). The answer appears to be that the events ( and the keys they relate to ) are encoded to the uint8_t value by these macros.

I just had another thought ( and my previous explanation may be superfluous ) that what you meant was that, as there is only the ability to store a single event value, multiple button events could be lost. If that's the question then, yes, it looks like a problem. The timer code does not process the events so more than a single button having an event during a single check cycle will result in at least one event being lost. Of course, there may be a design reason why it doesn't make sense to handle multiple events at once.

Martin Jay McKee

As with most things in engineering, the answer is an unabashed, "It depends."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Martin,

I really do apprecite your effort you put on this question and thanks a lot for taking your time to reply.

My question before was,

Quote:
And I am confused here since the s_evt is the single parameter which has been put as an event.
Its not private to Key. so how it can be distinguished in which key the event had taken place ?

as we can see in putEvent() and getEvent() functions it only deals with s_evt variable which not a member variable so diffentiate what event on what key would be a problem and yes your explanation for this at the end of your answer makes sence,

Quote:
I just had another thought ( and my previous explanation may be superfluous ) that what you meant was that, as there is only the ability to store a single event value, multiple button events could be lost. If that's the question then, yes, it looks like a problem. The timer code does not process the events so more than a single button having an event during a single check cycle will result in at least one event being lost. Of course, there may be a design reason why it doesn't make sense to handle multiple events at once.

but if you see the implementation of periodic call for Key::input() method, all the Key::input() methods in Key objects array (keys[]) being called

   void per10ms() 
   { 
      uint8_t enuk = KEY_MENU; 
      uint8_t    in = ~PINB; 
      for(int i=1; i<7; i++) 
      { 
         //INP_B_KEY_MEN 1  .. INP_B_KEY_LFT 6 
         keys[enuk].input(in & (1<<i),(EnumKeys)enuk); 
         ++enuk; 
      } 
   . 
   . 
   }

PS: I am sorry if I didnt make myself clear when asking the quesions.

Thanks again.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
So, question #1, why is the count in the input call? Because the Key object is where the count ( m_cnt ) is stored. The actual timing is a combination of how often input is called and the count threshold values, but the input() method has the counter implementation because the implementers decided that it made sense to put it there ( how's that for an answer ). In this case, I would agree. Given the structure, why not make the same object handle both debouncing and timing on a key.

And if you can explain a bit, how the m_cnt is increased in each call that would be a great help too. I also assume it sould work that way and when I see the rest of the coding I guess thats how its been working. But frankly I could not understand how the m_cnt is increased each time the Key::input() is called.

I might sound dumb or missing something basic but I could not figure it out the implementation.

Thanks very much again.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, I wouldn't say it's dumb, but it is something pretty basic. Take a look at line 223 of the code you linked to. It simply says m_cnt++, that is, post-increment m_cnt ( return the value of m_cnt and then add 1 to it ). The code does this everytime that Key::input() is called and the m_val variable is equal to FFVAL ( pressed and debounced ). Myself, I'd have used pre-increment, ++m_cnt, but m_cnt += 1 and m_cnt = m_cnt + 1 would be equivalent too.

Martin Jay McKee

As with most things in engineering, the answer is an unabashed, "It depends."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Martin,

Thanks very much. I did see the m_cnt++ before but I had a wrong assumption that this m_cnt has the same scope and its not static. that assumption lead me in a wrong direction.

I wrote a small code to clarify this and now its clear. I hope I found the answer to my second question, I will clarify it again and post it here.

Happy Holidays.!!!!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Glad you got it figured out! I've actually been toying with the idea of getting myself a th9x compatible radio... kind of fun looking through the code-base.

Martin Jay McKee

As with most things in engineering, the answer is an unabashed, "It depends."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yeah.. that's very true.Code in this project is really nice to look at and been written by some well experienced guys I guess. people who are having this radio claim that they will never switch back to any other radio. (With all the functionality of >1000$ radio with the flexibility of MPX4000 and more, I think they are right)

Th9x and RadioClone are the father projects of almost all other related 9x projects and there are many other projects with many high end features.if you are interested, Er9x, Gruvin9x, ErSky9x(ARM Port) open9x, RadioClone and HeliClone are other similar projects.

And I figured out the single event bottleneck we talked before.

Quote:
And I am confused here since the s_evt is the single parameter which has been put as an event.
Its not private to Key. so how it can be distinguished in which key the event had taken place ?

How its been implemented ? well actually the high nibble of the event variable(s_evt) is used to store the event type and low nibble is used to store the key value. Ex :

putEvent(EVT_KEY_LONG(enuk));
#define EVT_KEY_LONG(key)  ((key)|0x80)

the EVT_KEY_LONG(enuk) sets the s_evt consisting of Event in one half of a byte and Key in the other part of the byte.

And in the implementation, as you said earlier at one particular time only one event is taken in to account.

ex:

void menuProc0(uint8_t event)
{
  switch(event)
  {
    case  EVT_KEY_LONG(KEY_MENU):

As we can see event is taken from getEvent() and its been passed to this menuProc0. there with a switch case at one time only one key event is taken in to processing.

Thanks again for your time you put on this in your holiday season. appreciate it very much.

PS: I wrote a small article about HeliClone input subsystem in my blog yesterday after failing to understand this.http://www.kanijaya.blogspot.com/?view=sidebar. you might find it interesting and now I can start my article again on Th9x input subsystem. :D

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Nice work on the write-ups ( I caught the th9x article too ). It's interesting to see how multiple projects have approached the same problem. Different solutions even beyond the different languages ( C vs. C++ ). Of course, there are things that I would change were I doing it...

Such a simple thing too...

Martin Jay McKee

As with most things in engineering, the answer is an unabashed, "It depends."