Combining Pins as inputs

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

I am experimenting with a method to create a method to use fewer pins that there are switches, similar to Charlie Plexing. So I have 12 switches. 4 rows by 3 columns. Think kind of like a telephone keypad and to simplify it we will use the number pattern of the phone. I am using the method for debouncing by Peter Dannegger. In my mind, I think you should be able to use one port (8 pins) to monitor 16 buttons. I am only working with 12.

The theory is that there is a combination to each button and by decoding this combination it should work. My theory is that when a button is pressed, it is connected to 2 pins. The 2 pins create a unique combination. 

Button 1 is connected to pin0 and pin3 (Row 1, Column 1)

Button 2 is connected to pin1 and pin3 (Row 1, Column 2)

Button 3 is connected to pin2 and pin3 (Row 1, Column 3)

 

So I am trying to read the status of 2 pins but it isn't working. I only started off with the first 3 switches wired. When I ran the program, pressing the first two buttons did nothing....The third button would produce random results. It would randomly light up light up LED0 LED1 and LED2. After button 3 was pressed, I would get random results from the other buttons. I am fairly new to AVRs so maybe I am in left field with this theory.

 

Will this even work?

 

Here is my modified code:

/*
 * CoffeePuzzle.cpp
 *
 * Created: 1/3/2015 8:19:14 AM
 *  Author: RTS
 */ 


/**********************************************************************/
/*               */
/*                      Debouncing 8 Keys        */
/*   Sampling 4 Times         */
/*               */
/*              Author: Peter Dannegger        */
/*               */
/**********************************************************************/

#include <util\atomic.h>  // need "--std=c99"


typedef unsigned char u8;
typedef signed short s16;

#define XTAL  8e6  // 8MHz

#define KEY_PIN  PIND
#define KEY_PORT PORTD 
#define KEY_DDR  DDRD
#define KEYC1  0
#define KEYC2  1
#define KEYC3  2
#define KEYR1  3
#define KEYR2  4
#define KEYR3  5
#define KEYR4  6
#define KEYBREW  7

#define LED_DDR1 DDRC
#define LED_PORT1 PORTC
#define LED0  0
#define LED1  1
#define LED2  2
#define LED3  3
#define LED4  4
#define LED5  5
#define LED_DDR2 DDRB
#define LED_PORT2 PORTB
#define LED6  0
#define LED7  1
#define LED8  2
#define LED9  3
#define LED10  4
#define LED11  5
#define LED12B  6


u8 key_state;    // debounced and inverted key state:
// bit = 1: key pressed
u8 key_press;    // key press detect


ISR( TIMER0_COMPA_vect )  // every 10ms
{
 static u8 ct0 = 0xFF, ct1 = 0xFF; // 8 * 2bit counters
 u8 i;

 i = ~KEY_PIN;    // read keys (low active)
 i ^= key_state;   // key changed ?
 ct0 = ~( ct0 & i );   // reset or count ct0
 ct1 = ct0 ^ (ct1 & i);  // reset or count ct1
 i &= ct0 & ct1;   // count until roll over ?
 key_state ^= i;   // then toggle debounced state
 key_press |= key_state & i;  // 0->1: key press detect
}


u8 get_key_press( u8 key_mask )
{
 ATOMIC_BLOCK(ATOMIC_FORCEON){  // read and clear atomic !
  key_mask &= key_press;  // read key(s)
  key_press ^= key_mask;  // clear key(s)
 }
 return key_mask;
}


int main( void )
{
 TCCR0A = 1<<WGM01;   // T0 Mode 2: CTC
 TCCR0B = 1<<CS02^1<<CS00;  // divide by 1024
 OCR0A = XTAL / 1024.0 * 10e-3 -1; // 10ms
 TIMSK0 = 1<<OCIE0A;   // enable T0 interrupt

 KEY_DDR = 0;    // input
 KEY_PORT = 0xFF;   // pullups on
 LED_DDR1 = 0xFF;   // LED output
 LED_DDR2 = 0xFF;   // LED output
 LED_PORT1 = 0xFF;   // LEDs off (low active)
 LED_PORT2 = 0XFF;   // LEDs off (low active)
 
 key_state = ~KEY_PIN;   // no action on keypress during reset
 sei();

 for(;;){     // main loop
  if (( get_key_press( 1<<KEYC1 )) && ( get_key_press( 1<<KEYR1 )))
  LED_PORT1 ^= 1<<LED0;  // toggle LED on keypress

  if (( get_key_press( 1<<KEYC2 )) && ( get_key_press( 1<<KEYR1 )))
  LED_PORT1 ^= 1<<LED1;

  if (( get_key_press( 1<<KEYC3 )) && ( get_key_press( 1<<KEYR1 )))
  LED_PORT1 ^= 1<<LED2;

  if (( get_key_press( 1<<KEYC1 )) && ( get_key_press( 1<<KEYR2 )))
  LED_PORT1 ^= 1<<LED3;

  if (( get_key_press( 1<<KEYC2 )) && ( get_key_press( 1<<KEYR2 )))
  LED_PORT1 ^= 1<<LED4;

  if (( get_key_press( 1<<KEYC3 )) && ( get_key_press( 1<<KEYR2 )))
  LED_PORT1 ^= 1<<LED5;

  if (( get_key_press( 1<<KEYC1 )) && ( get_key_press( 1<<KEYR3 )))
  LED_PORT2 ^= 1<<LED6;

  if (( get_key_press( 1<<KEYC2 )) && ( get_key_press( 1<<KEYR3 )))
  LED_PORT2 ^= 1<<LED7;
  
  if (( get_key_press( 1<<KEYC3 )) && ( get_key_press( 1<<KEYR3 )))
  LED_PORT2 ^= 1<<LED8;
  
  if (( get_key_press( 1<<KEYC1 )) && ( get_key_press( 1<<KEYR4 )))
  LED_PORT2 ^= 1<<LED9;
  
  if (( get_key_press( 1<<KEYC2 )) && ( get_key_press( 1<<KEYR4 )))
  LED_PORT2 ^= 1<<LED10;
  
  if (( get_key_press( 1<<KEYC3 )) && ( get_key_press( 1<<KEYR4 )))
  LED_PORT2 ^= 1<<LED11;
  
    
 }
}

 

 

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

This looks a bit strange to me and I'm also quite new to avr's. You set all key ports to input with pull-ups activated and that will not work.

Instead you have to set the colums to Tri state and then "scan" each column by setting the column port to output and then set it low. That way you can read each button.

This is a good read: http://extremeelectronics.co.in/avr-tutorials/4x3-matrix-keypad-interface-avr-tutorial/

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

Try this

 

1. Set row-pins as output, columns as input with pull-ups.

2. Set row1 to 0, other rows to 1.

3. Read column pins. The column with  pressed buton is 0, others are 1.

 

4. Set row2 to 0, other rows to 1.

5. Read column pins.

 

And so on for all rows. Once you find the button, you can stop further testing.

 

 

 

 

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

Another method:

 

1. Set rows to outputs, cols to inputs with pull-ups.

2. Read value of cols nibble into var A.

3. Set cols to outputs, rows to inputs with pull-ups.

4. Read value of rows nibble into var B.

 

Now you can detect the pressed buton from values A, B.

Not suitable for more than one button pressed.

 

/* Keyboard 4x4

    PB0 PB1 PB2 PB3 

PB4  0   1   2   3

PB5  4   5   6   7

PB6  8   9   10  11

PB7  12  13  14  15

*/


#include <avr/io.h>             

unsigned char getkbd(void)
{
 unsigned char A, B, key;
      DDRB  = 0x0f;
      PORTB = 0xf0;           // set columns = 0
      asm("nop");
      A = 14 - (PINB >> 4);   // read rows (PB7-PB4)

      DDRB  = 0xf0;
      PORTB = 0x0f;           // set rows = 0
      asm("nop");
      B = 14 - (PINB & 0x0f); // read columns (PB3-PB0)

      if(A == 3)  A = 2;      // some calc
      if(A == 7)  A = 3;
      if(B == 3)  B = 2;
      if(B == 7)  B = 3;

      _delay_ms(30);           // debounce
      key = A * 4 + B;
      while((A = (PINB & 0x0f)) != 0)
         ;                     // wait for key released
      _delay_ms(30);           // debounce

return key;

}

 

Last Edited: Fri. Jan 9, 2015 - 07:49 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Reading 4x3 keyboard matrices using AVRs is as old as the hills. Visovian has given you the general technique but it's well worth searching out some of the hundreds of prior threads and projects about this. It's seldom worth reinventing the wheel completely from scratch as you may find your one ends up with square corners.

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

clawson wrote:
Reading 4x3 keyboard matrices using AVRs is as old as the hills.

Reading 4x3 keyboard was already as old as the hills before the AVR was even invented!

 

Quote:
It's seldom worth reinventing the wheel

Certainly not before you've studied the current state of the art, and have some specific improvements to offer

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As others have said,   a 4x3 keypad with 12 switches is very straightforward.

 

Personally,   I only 'enable' one column at a time i.e. as an output low.   All other columns are inputs (with pull-up).

This means that you can press any combination of keys at any time.    e.g. 1-2-3 is a different combination to 1

 

In theory this would give you 143 valid key-presses,   but I can't think of anyone that would want to operate like this.

I used to use the * key as a 'shift key'.    So I got 22 combinations from a 4x3 keypad.

 

Alternatively,  you can mimic the behaviour of texting on a phone.   e.g. press '2' key 3 times to get a 'C'

 

You just have to choose what would be most intuitive for your punter.

 

Yes,   I suppose that you can get several combinations from a 2x1 keypad.   e.g. with legends: 12,  you have 1, 12, 2

 

Charlieplexing drives LEDs with few pins.   Your firmware handles the electrics.  The punter can't tell the difference.

With any keypress strategy,   the punter has to know the rules.   e.g. shift-a or ctrl-h or alt-z

 

David.

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

david.prentice wrote:
texting on a phone.   e.g. press '2' key 3 times to get a 'C'

You might have to explain that for some of the younger members...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

By the way, what you're proposing isn't Charlieplexing, it is 'normal' multiplexing.

 

Charlieplexing can be used with switches instead of LEDs, but each switch must be fitted with a series diode.  To access 12 buttons you would need only 4 input pins.
 

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"Read a lot.  Write a lot."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

Now I don't know what you shall use the switches for, but remember that you will need diodes if more than 1 switch are used at the time.(if two are on at the same time it will look like 4 keys are pressed!)

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

I like to try and figure things out....I searched quite some time for multi key debounce and this was the best routine I found but with the limitation of one port. I had seen the LED Charlieplexing and thought something similiar could be done with inputs...but I didn't search for the right thing... If I knew what multiplexing was, I might have found it...LOL..

I will read through this information and see if I can make it work when I get home.... I appreciate your help!

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

Quite honestly,   if you want 12 physical keys,   conventional 4x3 keypad uses 7 GPIO.

If you have not got enough GPIO pins,   go for a different AVR.

 

This will be easier (and cheaper) than adding diodes or extra decoder chip.

 

My suggestion of less physical keys and using a "mode" button is less intuitive.

Humans are accustomed to setting a digital watch with 3 buttons.

Or texting with a 12-key numeric pad.

 

It does not mean that they like the arrangement.

 

David.

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

.I searched quite some time for multi key debounce and this was the best routine I found but with the limitation of one port.

      _delay_ms(30);           // debounce
      key = A * 4 + B;
      while((A = (PINB & 0x0f)) != 0)
         ;                     // wait for key released
      _delay_ms(30);           // debounce

Wow--that's the best you found?  No real app would be structured that way.  Besides waiting "forever" for a key to be released there is a hard 60ms in there.  Consider if you have a UART link or similar--buffers can overrun and such.

 

What I, and many, do is to sample and run the debounce periodically.  My typical for buttons and such is every 10ms, with 3x to 5x debounce.  After each pass, the latest debounced value (and edges if needed) are available to the application.  Never any sitting and waiting.

 

Indeed, with a multiplexed row/column setup those are often done with a faster tick -- say 2.5ms.  All the column values are gathered together and after the 10ms period a composite reading is entered into the debounce engine.

 

(BTW, there is an early AVR app note that had a setup for a 4x3 "telephone" keypad--perhaps hunt that out)

 

For prior parallel-debouncing discussions, on the old site it was recommended to search for "debounce danni lee".  That seems to work somewhat here as well.

 

Let me see if I can find a thread from a few months back that discussed this "fast tick" and gathering the "composite"...

I can't find the one I was looking for, but this thread and links therein should give some ideas:

http://legacy.avrfreaks.net/inde...

https://www.avrfreaks.net/comment...

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

david.prentice wrote:
This will be easier (and cheaper) than adding diodes or extra decoder chip.
Not recommending the technique, just clarifying terminology for the OP.

 

And with Charlieplexed inputs you cannot handle multi-button presses.  With 'normal' multiplexing you can, with the addition of a series diode with each button.  In Charlieplexed input matrices, the diodes are required for a one-button-at-a-time scheme, and do nothing to permit multi-button reads... it's just not possible.

 

I too recommend sticking with the 4x3 = 7 GPIO method.  Much simpler and in the end cheaper.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"Read a lot.  Write a lot."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

Just to mention that the(*) other way to get multiple buttons in one pin is ADC and a resistor network.

 

(* well OK, "an")

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

theusch...the code you posted is not the best I found...that is what someone else posted as a solution...I avoided any routines that had the delayms() for the very reason you stated....

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

Thanks for all of your advice....Now that I know the correct terminology and your advice, I was able to figure it out. I really appreciate all of your input.

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

theusch...the code you posted is not the best I found...

???  Didn't I quote correctly?  Didn't >>you<< say:

I searched quite some time for multi key debounce and this was the best routine I found

I'm out.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

joeymorin wrote:

By the way, what you're proposing isn't Charlieplexing, it is 'normal' multiplexing.

 

Charlieplexing can be used with switches instead of LEDs, but each switch must be fitted with a series diode.  To access 12 buttons you would need only 4 input pins.
 

But you can easily read 6 switches with 4 I/Os and no diodes.

 

Quebracho seems to be the hardest wood.

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

Yes, I did say I searched for quite some time but then you posted some code that was not what I had posted and commented that the code >>you<< posted had many flaws. The code I posted did not have the delays you spoke of. Mine was interrupt driven and based on Peter Dannegger's  code which from my research many people on this forum praise. If my code had the delays you were speaking of, please show me so I can correct or improve my code. Maybe I wasn't understanding your post. I am here to learn and take any advice I can get. I know you are way more knowledgeable than I am and maybe I missed something.

 

Thanks for your assistance.

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

John_A_Brown wrote:
But you can easily read 6 switches with 4 I/Os and no diodes.
This is true ;)

 

If you used series resistors, then this 'half-charlie' arrangement would permit unambiguous detection of multi-button presses.

 

A summary of the tradeoffs:

 

     I/O pins: | 1  2  3  4  5  6  7  8  |  Multi-press    |  sleep-friendly
---------------|-------------------------|-----------------|----------------
       direct: | 1  2  3  4  5  6  7  8  |     yes         |       yes
    multiplex: | -  -  -  -  6  9 12 16  | requires diodes |       yes
 half-charlie: | -  -  3  6 10 15 21 28  | requires diodes |        no
  charlieplex: | -  -  6 12 20 30 42 56  |      no         |        no

---------------|-------------------------|-----------------|----------------

                    Number of Buttons

 

 

Re: 'sleep-friendliness', I mean the suitability of the approach for use in low power apps.  Whereas direct and multiplexed arrays can be configured to detect any pressed key using an external pin-change interrupt (since the row/column arrangement is fixed), with the two charlieplex approaches a full scan requires changing the role of each I/O line from input to output and back again.  This makes it harder to use in an app that wants to sleep most of the time.  You can configure the charliepex array to detect some, but not all button presses while sleeping.  For example, were I to use a 4-I/O half-charlie with 6 buttons, I could configure one I/O as the low output and the remaining three as the inputs.  The three buttons which match that configuration could trigger a pin-change interrupt to wake from sleep, but the remaining 3 buttons could not.  Similarly, a 4-I/O full-charlie could wake from 3 buttons, but not from the remaining 9.  This may not pose a problem if the user knows that to wake a the device they must press a specific button.  You simply built your hardware and software appropriately.

 

Sorry for the diversion ;)

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"Read a lot.  Write a lot."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Sat. Jan 10, 2015 - 07:21 PM