PIC Microcontrollers: Scan inputs on a 4x4 Keypad, using only Port C RC0-RC3 in C

Status
Not open for further replies.

James Child

Newbie level 3
Joined
Dec 10, 2014
Messages
4
Helped
1
Reputation
2
Reaction score
1
Trophy points
13
Location
Hatfield, Hertfordshire
Visit site
Activity points
56
I'm new to PIC Microcontrollers & C programming and I've been set the task of creating my own Keypad scanning method that works exclusively on Port C (16F877A Microcontroller). Specifically, the program that uses this method only uses digits 1, 2, 4 and 5- so to be efficient, the scanner method is to only use RC0, RC1, RC2 and RC3 as the designated input/ outputs (I'm not allowed to use RC4-RC7). This essentially turns the 4x4 keypad into a 2x2 keypad.

I understand that the concept on scanning has Port lines set to high on rows, and lines set as input columns- when a button is pressed, this reads the low nibble of the port to detect the button.

Unfortunately, I have no idea how to code this in C, especially using the Port lines specified above. Can anybody shed some light on the problem and point me in the right direction? Thanks!:shock:
 

If possible change the port or as bit level programing can be done so choose the three pins fron other port and make the mechanism as you have mentioned also or if you have more further restriction let me know ?
 
A standard keypad has this arrangement:

4 5
1 2

so you only need 2x2 and four bits of the port are sufficient. The principle is to set the specific bits driving the keypad to the states 01 then 10 while reading back on the other two pins. You may need pull-up or pull-down resistors on the inputs so they go to the inactive state when keys are not pressed. To make a pin high, you OR the port pins with a number where only the required bit is a '1'. To make the pin low you AND it with a number where only the required bit is low.

For example to make RC0 high you use PORTC | b'00000001'
For example to make RC1 high you use PORTC | b'00000010'
For example to make RC0 low you use PORTC & b'11111110'
For example to make RC1 low you use PORTC & b'11111101'

Note the positions of the '1' and '0' in the examples match the bit position in PORTC you want to change. You can do it by writing the whole byte to the port but using the method I show ensures all the other pins are unaffected.

Knowing which of the bits is high when you read a number back from the keypad lets you work out which of the keys conducted the signal back to the port inputs.

Brian.
 

I did start with Defining TRISC = 0x3; and PORTC = 0xC;, then in the "readkeypad()" method that would return the lowest nibble on PORTC, defined as simply as return PORTC & TRISC; .This has allowed me to stay within my restrictions and use RC2 and RC3 as outputs (please correct me if I'm wrong, I get confused).
Anyway, the problem is, running this in my main method + simulator, I get the same nibble reading for buttons 1 & 3 = 1 and 2 & 4 = 2. I need a way to differentiate between all 4 buttons, so that I can set a specific action for each.
 
Yes, General keypad can be used but the addition resistors will help in converting the keypad to adc keypad. Different keypress will give out different voltage to adc input pin and adc code will decide which key pressed. For 4X4 keypad you only need 1 adc input pin instead of 8 pins.
 
Reactions: embRTS

    embRTS

    Points: 2
    Helpful Answer Positive Rating
An old project from 2011:

https://www.edaboard.com/threads/211782/

Written by me in Proton Basic (and ASM), but I think it can be easily converted to C (if you familiar with the asm : -).
Uses the high nibble of PORTB (RB4..RB7), the "pin change IT" to sense keystrokes and the timer2 to debouncing.

When it's simplified for only 4 keys only 2 cheap diode have to used.

Tried only in Proteus!
 
@milan - So the debouncing of the key stroke with the resistor placed will give different variation of ADC (say 10 bit value)

So with the possible ranges we can make/identify the key's action pressed,

Great !!
 

Yes, additionally you can use a while(condition) loop for every key press if(condition) so that key press is registered only if button is pressed and released.

- - - Updated - - -

Use this keypad.

http://www.aliexpress.com/item/AD-Keypad-16-Push-Buttons-4x4-Accessory-Board-Matrix-Buttons-Controlled-ADC-AD-Port-Keyboard-Free/32244542811.html


It interface with 3 pins. VCC, GND and ADC out pin.
 

Ok, so I'm still aboslutely stumped! I've had a go at coding something from what's been discussed, however, it's not working. Let me show you my thought process!

First of, my (what is essentially a 2x2 keypad) scanning method is to return a BYTE representation of the key pressed, which is called by another function in the main method, displaying the value to a GLCD. The GLCD works fine, it's defininately my poor attempt at making a keyscanner. Anyway;


Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
BYTE getKeypad(){
    TRISC = 0b00000011; //RC0, RC1 = Col1, Col2
    PORTC = 0b00001100; //RC2, RC3 = Row1, Row2
 
    return PORTC&TRISC; //returns the low nibble of PORTC 
}
 
When called in my GLCD_out function, and tested on my emulator, the value output is;
Key 1 = 1        |1| |2| |3...  //very crude representation of the section of keypad 
Key 2 = 2        |4| |5| |6...  //that I am using. 
Key 4 = 1
Key 5 = 2
 
Obviously, you can see my problem here, I need a different bit value for each key, not the repeated nibble values for the keys on Row 2. So I tried another method variation to try and distinguish between the possible bit patterns. See;
 
BYTE getkeypad(){
    BYTE key;
    PORTC = 0b00001100; //rows
    TRISC = 0b00000011; //colomns
 
    //These are the four possible bit pattern combinations from a key being pressed
    if((PORTC&TRISC) == 0b00000101) return key = 1; 
    if((PORTC&TRISC) == 0b00001010) return key = 2;
    if((PORTC&TRISC) == 0b00001001) return key = 5;
    if((PORTC&TRISC) == 0b00000110) return key = 6;
}



However, this returns the value 128 to the GLCD and the buttons do not respond- fun!
Please bare with me, as I've been trying to figure out this problem for the past 3 days and I'm probably missing something obvious!
Thanks for the help.
 
Last edited by a moderator:

Without a computer at the moment so I can't write and debug code but the 'flow' is like this:

1. initialize the ports so POTC bits 0 and 1 are inputs, bits 2 and 3 are outputs.
2. start a loop
3. set bit 2 = 1 and bit 3 = 0
4. read PORTC then logic AND the result with 0x03 (so only the input bits remain)
5. if the result is zero, no key is pressed, if it is 0x01 then '1' was pressed, otherwise it was '4' that was pressed. Display your result.
6. set bit 2 = 0 and bit 3 = 1
7. read PORTC then logic AND the result with 0x03 (so only the input bits remain)
8. if the result is zero, no key is pressed, if it is 0x01 then '2' was pressed, otherwise it was '5' that was pressed. Display your result.
9. go back to step 3.

I think I got the numbers right, if the rows or columns are swapped it will still work and you can edit the values to reflect the correct keys numbers.

Brian.
 

Thank you- I finally understand! My University example was totally throwing me off, as they had Initialized both rows on PORTC throughout. It finally works- albiet, a bit buggy, but I think that's something to do with needing to debounce. Anyway, here's my code (this is called in a main functions that declares PORTC 0b00001100 and TRISC 0b00000011 and then loops forever):

Code:
BYTE getkeypad(){
int q, w;
    PORTC = 0b00000100;     //Set RC2 High
    for(q=0;q<2;q++){       //Cycle through row RC2
        if((PORTC & 0x3)==0x1) return 1;
        if((PORTC & 0x3)==0x2) return 2;
    }
    PORTC = 0b00001000;    //Set RC3 High
    for(w=0;w<2;w++){      //Cycle through row RC3
       if((PORTC & 0x3)==0x1) return 4;
       if((PORTC & 0x3)==0x2) return 5;
    }
    if((PORTC & 0x3)==0) return 0;  //Returns 0 if nothing is pressed
}

It's does some strange things when I first launch the program on the emulator- it's initial value starts at 128, until I start hitting the buttons then it returns to expected behaviour after cycling through 5, 4, 2, 1. Any thoughts?
 

That's almost right.

You don't need 'q' or 'w' though, they actually do nothing except waste a fw uS of time as their loops complete.

If you actually want to wait UNTIL a key is pressed, in other words the routine never returns until 1,2,4 or 5 is pressed, wrap the whole code in a:
do
{

}while((PORTC & 0x03) != 0);

so it keeps repeating until a non-zero value is returned.

As mentioned before, there is a potential problem with writing a value directly to a port, when you use

PORTC = 0b00000100; //Set RC2 High and
PORTC = 0b00001000; //Set RC3 High

you are also setting all the other port bits to zero at the same time. There is nothing wrong with that if they are not used for any other purpose but if they were, you may have changed the bits from '1' to '0' and stopped something else working properly. Instead use :
PORTC |= 0b00000100; // set RC2 high and RC3 low
PORTC &= 0b11110111;
and
PORTC |= 0b00001000; // set RC3 high and RC2 low
PORTC &= 0b11111011

which only changes the state of RC2 and RC3 without touching the other bits in PORTC.

You missed one point I made earlier, look at the combinations of possible values you can return from PORTC as you drive each output line, it can only be 0,1,2 or 0,4,5 so by rearranging your instructions you can avoid three checks, if it isn't one or the other it has to be the third.

Note that in the future, if you want to expand the number of rows and columns you can still use exactly the same technique but there is a better way of driving only one line at a time in sequence. We will come to that later....

Brian.
 
Status
Not open for further replies.

Similar threads

Cookies are required to use this site. You must accept them to continue using the site. Learn more…