3x3 keypad

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

Below I have the schematic and C code for contolling a 3x3 keypad. I have complied and ran this code on my ATMEGA48 using PORTD. All of the rows and columns work great except COL1. buttons 1,4,7 do not work while all the rest do. Maybe I have an error in the code? Also i am using these buttons to output USART command via PD0 (RX) and PD1 (TX), could this cause a conflict when initializong PORTD for the keypad?


#include 
#include 
#include 


#define keyport PORTD		//Keypad Port
#define keyportddr DDRD		//Data Direction Register
#define keyportpin PIND		//Keypad Port Pins
#define col1 PD2			//Column1 PortD2
#define col2 PD3			//Column2 PortD3
#define col3 PD4			//Column3 PortD4

int main(void);
void init_keypad(void);
int get_key(void);
void led_on(int);

int keyval;



int main(void) {


	init_keypad();			//init Keypad
	


	while(1){		
	
		keyval=get_key();
			_delay_ms(25);
		led_on(keyval);

		
	}

}




/*********************************************
* KEYPAD FUNCTION MODULE
*********************************************/

void init_keypad(void)
{
	keyportddr = 0xE0;
	keyport = 0x1F;
	DDRB = 0xFF;			//set all output for LEDs
	PORTB = 0xFF;			//all LEDs off (high)
}
	




int get_key(){
	
	int i,key=1;
	for(i=0;i<3;i++)
	{											//Loop for 4 rows
		keyport &=~(0x80>>i);					//Make rows low one by one
		if(!(keyportpin & (1<<col1)))
		{										//check COL1
			while(!(keyportpin & (1<<col1)));	//wait for release
			return key;							//return pressed key value
		}
		if(!(keyportpin & (1<<col2)))
		{										//Check COL2
			key += 1;							//Second key pressed
			while(!(keyportpin & (1<<col2)));	//wait for release
			return key;							//return key value
		}
		if(!(keyportpin & (1<<col3)))
		{										//Check COL3
			key += 2;							//Third key pressed
			while(!(keyportpin & (1<<col3)));	//Wait for release
			return key;							//Return value
		}
		key +=3;								//Next row
		keyport |= 0x80>>i;						//make read row high again
	}
	return 0;									//return false if no key pressed
}


void led_on(int l) 
{
	PORTB = ~l;									//low output to turn LED on
}




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

forgot attachment...

Attachment(s): 

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

When the uart is enabled, it controls the rx and tx pins. If you write to those bits in the output port, I guess they get set and reset, but they just dont go anywhere.

Imagecraft compiler user

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

Hmm... well when I first initialize the UART and then write the port for the keypad and then run it, it runs fine, the rx and tx work while the keypad works. I am just not sure why the COL1 doesnt work.

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

Are the portd pullups off at poweron reset? Try writing 0x1c to keyport before setting the DDR to turn them on.

Imagecraft compiler user

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

yea, i am pretty sure they are on. Writing keyport = 0x1C didn't change anything... When I reverse the pin assignments of COL1 and COL3, (COL1 = PD4 and COL3 = PD2), it causes COL3 not to work...

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

Thought I would just post my whole code:


/*********************************************
* Team ThumbStart       Univeristy of Idaho
* Chip type           : ATmega48
* Clock frequency     : 1.843200 MHz
*
* PD0 used for RX		PB6 used for XTAL1
* PD1 used for TX		PB7 used for XTAL2
* PD2-7 used for keypad
*********************************************/
#include 
#include 
#include 
#include 

#define F_CPU 1843200		//Clock Speed
#define baud 9600			//Baud Rate
#define baudnum F_CPU/16/baud-1

#define keyport PORTD		//Keypad Port
#define keyportddr DDRD		//Data Direction Register
#define keyportpin PIND		//Keypad Port Pins
#define col1 PD2			//Column1 PortD2
#define col2 PD3			//Column2 PortD3
#define col3 PD4			//Column3 PortD4

int main(void);
void usart_transmit(unsigned char);
unsigned char usart_recieve(void);
//void send_bytes(char,int);
void init_usart(unsigned int);
void init_keypad(void);
int get_key(void);
void led_on(int);

int keyval;
	
char enroll[] =   {0x41,0x01,0x00,0x05,0x00,0x00,0x00,
					   0x00,0x00,0x00,0x00,0x00,0x79,0xC0,0x0A},	//enroll
	 del[] =      {0x41,0x01,0x00,0x17,0x00,0x00,0x00,
		 		       0x00,0x00,0x00,0x00,0x00,0x00,0x59,0x0A},	//delete
	 identify[] = {0x41,0x01,0x00,0x11,0x00,0x00,0x00,
		 			   0x00,0x00,0x00,0x00,0x00,0x00,0x53,0x0A};	//identify
	 
char recieve[16];		//recieve array


int main(void) {


	init_usart(baudnum);	//init USART
	init_keypad();			//init Keypad


	//sei();				//enable interrupts

	//send_bytes(identify, 16);	


	while(1){		
	
		keyval=get_key();
		_delay_ms(25);
		led_on(keyval);


 		switch (keyval){                  
    		case 3:
        		send_bytes(identify,16);             
        		break;
   			case 9:
     			send_bytes(enroll,16);             
       			break;
    		case 6:
      			send_bytes(del,16);            
        		break;
    		default: 0;
    		}
			
 

	/*	for(int i=1;i<17;i++)
			{
			recieve[i] = usart_recieve();
			}*/
	}

}





/*********************************************
* USART COMMUNICATION MODULE
*********************************************/

void init_usart(unsigned int ubrr) {
	
	//Set baud rate 
	UBRR0H = (unsigned char)(ubrr>>8);
	UBRR0L = (unsigned char)ubrr;

	// Enable receiver and transmitter; enable RX interrupt
	UCSR0B = (1 << RXEN0) | (1 << TXEN0); // | (1 << RXCIE);

	//asynchronous 8 data, 2 stop
	UCSR0C = (1 << USBS0)| (3 << UCSZ00);

}

void send_bytes(char *s, int length) {
	while (length--) {
		usart_transmit(*s);
		s++;
	}
}

void usart_transmit(unsigned char c) {
  	while(!(UCSR0A & (1 << UDRE0)));	// wait until UDR ready
	UDR0 = c;							// send character
}

unsigned char usart_recieve(void)
{

	while (!(UCSR0A & (1<<RXC0)));	//wait for data to be recieved

	return UDR0;	//return data from buffer
}





/*********************************************
* KEYPAD FUNCTION MODULE
*********************************************/

void init_keypad(void)
{
	keyportddr = 0xE0;
	keyport = 0x1F;
	DDRB = 0xFF;			//set all output for LEDs
	PORTB = 0xFF;			//all LEDs off (high)
}
	




int get_key(){
	
	int i,key=1;
	for(i=0;i<3;i++)
	{											//Loop for 4 rows
		keyport &=~(0x80>>i);					//Make rows low one by one
		if(!(keyportpin & (1<<col1)))
		{										//check COL1
			while(!(keyportpin & (1<<col1)));	//wait for release
			return key;							//return pressed key value
		}
		if(!(keyportpin & (1<<col2)))
		{										//Check COL2
			key += 1;							//Second key pressed
			while(!(keyportpin & (1<<col2)));	//wait for release
			return key;							//return key value
		}
		if(!(keyportpin & (1<<col3)))
		{										//Check COL3
			key += 2;							//Third key pressed
			while(!(keyportpin & (1<<col3)));	//Wait for release
			return key;							//Return value
		}
		key +=3;								//Next row
		keyport |= 0x80>>i;						//make read row high again
	}
	return 0;									//return false if no key pressed
}


void led_on(int l) 
{
	PORTB = ~l;									//low output to turn LED on
}


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

Try adding a short delay between setting keyport and the first time you read keypin.

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

Awesome, that it. I added a 5ms delay where you said and all 9 buttons work! Thanks a lot!

What would be the best way to store, say a 5 button presses, in an array?

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

There is another problem, 2 AVR outputs become connected together if 2 buttons in the same column are pressed, if one is low and the other high this may destroy the AVR.

You should therefore never set the row outputs high, instead change them between low and high impedance using keyportddr.

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

OK, I kind of understand what you are saying...but don't completely get it. Can you give me an example in my code where I shoukd modify it?

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

robtotoreb wrote:
OK, I kind of understand what you are saying...but don't completely get it. Can you give me an example in my code where I shoukd modify it?
The two lines which change keyport should be changed to modify keyportddr instead. The initialization of keyportddr should also be changed. The column ports should all be inputs by default and then you switch them to low driving outputs one at a time.

Assuming you never press two keys at the same time, it would never be an issue, but Timothy makes a good point and ideally it should be changed.

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

Thanks for all the help with the keypad! It works great.

What I need to do now is store a sequence of button presses and record them into an array and then compare it to another array. I want to make a password for my program where the user enters say a 5-digit combination to gain access to the rest of my program. I have been messing around with it and I can't seem to figure out the best way to store the keypad presses. Here is what I have so far:

int pin[2];         //array for user pin input
int pass={1,2};     //default password

int main(void) 
{ 
	init();	    //initialize ports

	for(int i=0;i<2;i++)
		{
		pin[i]=get_key();
		}
		
	if(pin==pass)
		{
		led_on(7);
		}
	else led_on(1);
}

I think I am on the right path with this code, but it needs some help...Any ideas?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
if(pin==pass)

You cannot compare two arrays directly like this. In C, when you use an array name without an index, you get a pointer to the first array element. Therefore, your code is comparing the addresses of pin and pass which will always be false.

Instead, you need to compare each element separately or you could use the memcmp function:

if(memcmp(pin, pass, sizeof(pin)) == 0)

Also, your password declaration should look more like this:

int pass[2]={1,2}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

here a lib.wish you enjoy it.



#ifndef KEY_BUFF_SIZE
    # define KEY_BUFF_SIZE     8
#endif
#ifndef KEY_NULL
    # define KEY_NULL         0xff
#endif

#ifndef KEY_PRESS_DELAY
    # define KEY_PRESS_DELAY  50
#endif

#ifndef _USE_KEY_LONG_PRESS_DELAY_TIME
    # define _USE_KEY_LONG_PRESS_DELAY_TIME     1000
#endif
#ifndef _USE_KEY_LONG_KEY_FASTEST_SPEED_LEVEL
    # define _USE_KEY_LONG_KEY_FASTEST_SPEED_LEVEL 2
#endif

#if _USE_KEY_LONG_KEY_FASTEST_SPEED_LEVEL == 0
    # define _USE_KEY_LONG_PRESS_FASTEST_SPEED 0x03ff
#elif _USE_KEY_LONG_KEY_FASTEST_SPEED_LEVEL == 1
    # define _USE_KEY_LONG_PRESS_FASTEST_SPEED 0x01ff
#elif _USE_KEY_LONG_KEY_FASTEST_SPEED_LEVEL == 2
    # define _USE_KEY_LONG_PRESS_FASTEST_SPEED 0x00ff
#elif _USE_KEY_LONG_KEY_FASTEST_SPEED_LEVEL == 3
    # define _USE_KEY_LONG_PRESS_FASTEST_SPEED 0x007f
#elif _USE_KEY_LONG_KEY_FASTEST_SPEED_LEVEL == 4
    # define _USE_KEY_LONG_PRESS_FASTEST_SPEED 0x003f
#else
    # define _USE_KEY_LONG_PRESS_FASTEST_SPEED 0x00ff 
#endif

static void _Key_Scan(void);

void Add_Key_Code(void);
UINT8 Get_Key_Code(void);
void Clear_Key_Buffer(void);


#ifndef _USE_KEY_PRESS_SCAN_FUNC_INTERFACE
    # error Need for _USE_KEY_PRESS_SCAN_FUNC_INTERFACE
#else
    extern unsigned char _USE_KEY_PRESS_SCAN_FUNC_INTERFACE(void);
#endif

UINT8 g_cReturnKeyNum = KEY_NULL;
UINT8 g_cReturnLongPressKeyNum = KEY_NULL;
UINT16 g_wKeyPressTimeCounter = 0;
UINT16 g_wKeyPressDelayCounter = 0;

static UINT8 s_cKeyBUFF[KEY_BUFF_SIZE][2];
static UINT8 s_cKeyBUFFCounter = 0;
static UINT8 s_cKeyBUFFHeadPoint = 0;
static UINT8 s_cKeyBUFFTailPoint = 0;

static UINT8 s_cLongKeyPressNum = KEY_NULL;
static UINT8 s_cKeyNum = KEY_NULL;

void Clear_Key_Buffer(void)
{
    s_cKeyBUFFCounter = 0;
    s_cKeyBUFFCounter = 0;
    s_cKeyBUFFHeadPoint = 0;
    s_cKeyBUFFTailPoint = 0;
}

static void _Key_Scan(void)
{
    static UINT8 OldKeyCode = KEY_NULL;
    static UINT8 IfLongKeyPress = FALSE;
    static UINT16  LongPressAcceleration = 0;
	 
    UINT8 NowKeyCode = _USE_KEY_PRESS_SCAN_FUNC_INTERFACE();
    s_cKeyNum = KEY_NULL;
	
    if ((OldKeyCode != NowKeyCode) && (g_wKeyPressDelayCounter == 0))
    {
        g_wKeyPressTimeCounter = 0;
        LongPressAcceleration = 0x03ff;
          
        if ((NowKeyCode == KEY_NULL) && (IfLongKeyPress == FALSE))
        {
            s_cKeyNum = OldKeyCode;
        }
		  
        g_wKeyPressDelayCounter = KEY_PRESS_DELAY;
		  
        OldKeyCode = NowKeyCode;
        IfLongKeyPress = FALSE;
    }
    else
    {
        s_cKeyNum = KEY_NULL;
        if ((g_wKeyPressTimeCounter > _USE_KEY_LONG_PRESS_DELAY_TIME) && (IfLongKeyPress == FALSE))
        {
            #ifdef _USE_KEY_LONG_PRESS_REPEAT
            if (LongPressAcceleration > _USE_KEY_LONG_PRESS_FASTEST_SPEED)
            {
                LongPressAcceleration >>= 1;
            }
            #endif
            s_cLongKeyPressNum = NowKeyCode;
            IfLongKeyPress = TRUE;
        }
        else
        {
            #ifdef _USE_KEY_LONG_PRESS_REPEAT
            if (!(g_wKeyPressTimeCounter & LongPressAcceleration))
            {
                IfLongKeyPress = FALSE;
            }
            else
            {
                s_cLongKeyPressNum = KEY_NULL;
            }
            #else
            s_cLongKeyPressNum = KEY_NULL;    
            #endif
        }
    }
	 
}

void Add_Key_Code(void)
{
    _Key_Scan();
    
	if ((s_cKeyNum == KEY_NULL) && (s_cLongKeyPressNum == KEY_NULL))
	{
	    return ;
	}
	
	if ((s_cKeyBUFFHeadPoint == s_cKeyBUFFTailPoint) && (s_cKeyBUFFCounter != NULL))
	{
	    return ;
	}
	
	s_cKeyBUFF[s_cKeyBUFFTailPoint][0] = s_cKeyNum;
	s_cKeyBUFF[s_cKeyBUFFTailPoint][1] = s_cLongKeyPressNum;
	
	s_cKeyBUFFTailPoint ++;
	if (s_cKeyBUFFTailPoint == KEY_BUFF_SIZE)
	{
	    s_cKeyBUFFTailPoint = 0;
	}
	s_cKeyBUFFCounter ++;
}

UINT8 Get_Key_Code(void)
{
    g_cReturnKeyNum = KEY_NULL;
    g_cReturnLongPressKeyNum = KEY_NULL;
	
	#ifdef _USE_KEY_MANUL_REFRESH
	    Add_Key_Code();
	#endif
	
	if ((s_cKeyBUFFHeadPoint == s_cKeyBUFFTailPoint) && (s_cKeyBUFFCounter == NULL))
	{
	    return FALSE;
	}

    g_cReturnKeyNum = s_cKeyBUFF[s_cKeyBUFFHeadPoint][0];
	g_cReturnLongPressKeyNum = s_cKeyBUFF[s_cKeyBUFFHeadPoint][1];
			
	s_cKeyBUFFCounter--;
	s_cKeyBUFFHeadPoint++;
	if (s_cKeyBUFFHeadPoint == KEY_BUFF_SIZE)
	{
	    s_cKeyBUFFHeadPoint = 0;
	}
			
    return TRUE;
}

Gorgon Meducer

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

Thanks for that code Gorgon, but I am not sure exactly what it is doing....

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

Kevin, I have modified my code from what you told me and it is working now. Although when no key is pressed, my keypad scanning function will continously return a 0. I do not want my for loop to assign any 0's into the pin[] array. When its like this, pin[] will always be {0,0}. I know what needs to be done, i'm just not sure how to code it...

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

There are many ways in which you could ignore the zero values. On looking at your code again, it seems another issue would be when a key is pressed, the whole array will be filled with that key press as there is nothing to wait for the key to be released before reading it again. You may want to create another function to handle both these issues at once. For example, something like this:

int get_key_press(void)
{
   int key = 0;

   // wait for key to be pressed
   while(!key)
   {
      key = get_key();
   }

   // wait for key to be released
   while(get_key() == key);

   // return value of key that was pressed
   return key;
}

Then use the above function instead of get_key in your loop that populates pin.

Hopefully my example doesn't have any mistakes, but it should at least give you an idea of one way it could be done.