Matrix Switch Hooked to PIC16f886

Thread Starter

beeson76

Joined Apr 19, 2010
211
Included is my schematic.

Just a few questions about this setup. I would like to use the Internal Pullups on PORTB. I have just a small hobby box to put it in and really have no room for external resistors. Does this program look right for the schematic. The program seems to work on a PIC simulator (PIC simulator 1.3), but doesn't seem to work in the real world:)

I read from this forum that I must have MCLR enabled to use the weak pullups on PORTB. So that is why that is so in the configuration line for the chip.

The LCD is "wired: correctly and initializes and displays text so I'm pretty sure the problem is the program side of things. Can someone please look at this and critique it. I appreciate any help anyone can provide. Here is the code for the Main.c

Rich (BB code):
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//This program controls a Matrix Switch.  The Switch consists of 3 columns and 4 rows.
//Whenever a button is pushed, that button is displayed on an LCD Display.
//PIC16F886 Microcontroller from Microchip is being used.
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include <htc.h>
#include "lcd.h"

#define  KEY_PORT				PORTB								//Defining PORTB as KEY_PORT							
#define  KEY_SET(bits)			KEY_PORT |= (bits)					//Defining KEY_SET as 
#define  KEY_CLR				KEY_PORT &=~ (bits)					//Defining Key_CLR as KEY_PORT = KEY_PORT &~ (bits)
#define	 KEY_FLP(bits)			KEY_PORT ^= (bits)					//Defining KEY_FLP as KEY_PORT = KEY_PORT ^ (bits)
#define  KEY_IN(bits)			TRISB |= (bits)						//Defining KEY_IN as TRISB = TRISB | (bits)
#define  KEY_OUT(bits)			TRISB &=~ (bits)					//Defining KEY_OUT as TRISB = TRISB &~ (bits)
#define  KEY_COL				0b10000011							//Defining KEY_COL as high,low,low,low,low,low,high,high 
#define  KEY_ROW				0b01111000							//Defining KYE_ROW as low,high,high,high,high,low,low,low
#define  DelayS(T)				{unsigned char i; for (i = 0; i < T * 10; i++) __delay_ms(100);}	//Delay Macro
#define  _XTAL_FREQ				4000000								//Needs to be set for __delay_ms


/////CONFIGURATION FUSE//////////////////////////////////////////////////////////////////////////////////////////////////////////
//Master Clear Reset enabled & Internal RC No Clock & Watchdog Timer Disable & Power Up Timer On & Brown Out Reset Disabled &
// Low Voltage Porgramming Disabled & Code Unprotect
//
__CONFIG (MCLREN & INTIO & WDTDIS & PWRTEN & BORDIS & LVPDIS & UNPROTECT);
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////




unsigned char key_read(unsigned char in_bits, unsigned char out_bits)
	{
		KEY_IN(in_bits);
		KEY_SET(out_bits);
		KEY_OUT(out_bits);
		return KEY_PORT & in_bits;
	}

unsigned char key_detect(void)
	{
		unsigned char tmp1, tmp2;
		tmp2 = key_read(KEY_ROW, KEY_COL);
		tmp1 = key_read(KEY_COL, KEY_ROW);
		return tmp1 | tmp2;
	}

main ()											
{																				

PORTA = 0x00;										//PORTA is cleared and set low
PORTB = 0x00; 										//PORTB is cleared and set low
PORTC = 0x00;										//PORTA is cleared and set low
TRISA = 0x00;										//Set PORTA to outputs
TRISB = 0xFF;										//Set PORTB to inputs
TRISC = 0x00;										//Set PORTC to outputs
RBPU = 0;											//Internal PORTB Pullups enabled
ANSEL = 0;											//Initialize A/D Ports off
ANSELH = 0;											//Initialize ........
CM1CON0 = 0;										//Initialize Comparator 1 off
CM2CON0 = 0;										//Initialize Comparator 2 off

OPTION = 0b01010101;								//OPTION REG
													//xbxxxxx101  1:64
													//xbxxxx0xxx  Prescaler set to Timer0
													//xbxxx1xxxx  (T0SE) set to Increment on high-to-low transition on T0CKI pin
													//xbxx0xxxxx  (T0CS) Internal instruction cycle clock
													//xbx1xxxxxx  (INTEDG) Interrupt on rising edge of INT pin
													//xb0xxxxxxx  (RBPU) PORTB pull-ups are enabled by individual PORT latch values

lcd_init();											//LCD Display is Initialized...See if LCD.C file

/////IGNORE THIS -- ONLY A TEST//////////////////////////////////////////////////////////////////////////////////////////////////
//Only a test to see if LCD is Initialized and Display Text
//while (1 == 1)
//{
//	
//	lcd_clear();
//	lcd_goto(0);
//	lcd_puts("Hello World");
//	__delay_ms(150);
//	lcd_goto(40);
//	lcd_puts("LCD Iniatlized");
//	__delay_ms(150);
//}
//}													//Everything After this would be commented out
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

char key;											//Creating a local Variable called "key" to hold value of key is pressed

while (key_detect() == 1)  							//Loops When Key is Pressed
continue;
{

key = key_detect();									//Referring to key_detect() function.  Returns key pressed.

switch (key)										//Switch Statement for "key"
	{
		case 0b00100001:							//Column 1 Row 4--Switch 12
			lcd_goto(0);
			lcd_puts("Switch 1");
			//DelayS(1);
			lcd_clear();
			break;
		case 0b01000001:							//Column 1 Row 3--Switch 11
			lcd_goto(0);
			lcd_puts("Switch 2");
			//DelayS(1);
			lcd_clear();
			break;
		case 0b00010001:							//Column 1 Row 2--Switch 10
			lcd_goto(0);
			lcd_puts("Switch 3");
			//DelayS(1);
			lcd_clear();
			break;
		case 0b00001001:							//Column 1 Row 1--Switch 9
			lcd_goto(0);
			lcd_puts("Switch 4");
			//DelayS(1);
			lcd_clear();
			break;
		case 0b00100010:							//Column 2 Row 4--Switch 8
			lcd_goto(0);
			lcd_puts("Switch 5");
			//DelayS(1);
			lcd_clear();
			break;
		case 0b01000010:							//Column 2 Row 3--Switch 7
			lcd_goto(0);
			lcd_puts("Switch 6");
			//DelayS(1);
			lcd_clear();
			break;
		case 0b00010010:							//Column 2 Row 2--Switch 6
			lcd_goto(0);
			lcd_puts("Switch 7");
			//DelayS(1);
			lcd_clear();
			break;
		case 0b00001010:							//Column 2 Row 1--Switch 5
			lcd_goto(0);
			lcd_puts("Switch 8");
			//DelayS(1);
			lcd_clear();
			break;
		case 0b10100000:							//Column 3 Row 4--Switch 4
			lcd_goto(0);
			lcd_puts("Switch 9");
			//DelayS(1);
			lcd_clear();
			break;
		case 0b11000000:							//Column 3 Row 3--Switch 3
			lcd_goto(0);
			lcd_puts("Switch 10");
			//DelayS(1);
			lcd_clear();
			break;
		case 0b10010000:							//Column 3 Row 2--Switch 2
			lcd_goto(0);
			lcd_puts("Switch 11");
			//DelayS(1);
			lcd_clear();
			break;
		case 0b10001000:							//Column 3 Row 1--Switch 1
			lcd_goto(0);
			lcd_puts("Switch 12");
			//DelayS(1);
			lcd_clear();
			break;
	}


}
}
Thanks
 

Attachments

Last edited:

Thread Starter

beeson76

Joined Apr 19, 2010
211
For the PORTB to be used, should I have all of PORTB as inputs and set high or set low? Thanks for any replies.

Mike
 

Thread Starter

beeson76

Joined Apr 19, 2010
211
Can anyone provide sample code for what I am trying to do with this project.

I have read much, but have found no code example in C. That would probably help me the most.

Thanks.
 
For the PORTB to be used, should I have all of PORTB as inputs and set high or set low? Thanks for any replies.

Mike
I don't know C so I'm not 100% sure of what you are doing, but TRISB shouldn't all be inputs. It's been a little while since I wrote/used my assembly keypad code, but I think you need to have 3 outputs for the columns and 4 inputs for the rows.

You set high all rows but one and then have the program go down the rows looking for a low input (which will have been generated if the button in that row is pressed while to column is cleared low). Then set that column's pin and clear the next.
 

Potato Pudding

Joined Jun 11, 2010
688
Actually I think the whole point of weak pull ups is that they are a high outputlevel&impedance layered on an input. They are meant for a switch to pull them low.

I think you need to have 3 outputs for the columns and 4 inputs for the rows.
That is almost exactly what the weak pull ups would avoid - maybe.

I haven't gotten the hang of C either so I didn't look close at your code. I will try and work out how I think they could be used.

What a matrix does normally is sequentially strobe the outputs (3 columns) as mentioned by Wanna and then you check the inputs (4 rows) and matching which column was strobed with which input is switched low tells you which key was pressed.

They weak pull ups will allow a similar type of system but resistors or diodes will be needed.

Instead of the strobe you have the B inputs and interrupts.

You do need to isolate the different columns of switches somehow so that the column pulled low does not pull all of the other columns low. Those were strobed outputs but now they are all inputs with a weak pullup.

The rows are connected to all the columns in the strobed matrix switch, and you can't exactly do that the same way when all columns are inputs.

If you only need 9 buttons in your keypad then just connect one to each of the Port B inputs with the pullup and one to the E3 pin which also has a pullup.

That is the only way to get away without any resistors or diodes for crosstalk isolation.

But I don't care how small your case is. Putting some surface mount resistors or diodes in the signal paths will not hurt your space requirements. Just your eyes when you are working with them.
 
Identical problem for me, same MCU. Solution was to pull the PicKit2 program data and clock wires off the breadboard during testing. They were over-powering the pull-up resistors causing nonsense keypad codes. Noticed it by running debug within MPLAB X and looking at the tristate for the keypad port. It showed bits set differently than my TRISB = 0xF0 code.
 

John P

Joined Oct 14, 2008
2,026
Here's my code for reading a 3x4 keypad in a past project. In this case the outputs are the rows, on pins C0-C3, and the inputs are columns sampled via A0, A1 and A3. (I needed A2 for some other function, I forget what.) All 3 of these pins need to be in input mode (TRIS bit high) and have their pullups enabled. Then the Port C pins are all set low, but only one output is enabled (TRIS bit low) at a time, and then the input is sampled. If no pin of Port A is found low, the next row is tried. The result is turned into a 4-bit code via a routine lookup_button() which I haven't shown, but the codes need to be something other than 0xF, which is "No button pressed". The output "button" is a global variable, so the function doesn't need to return anything. The top 4 bits of Port C are used for other things, so just ignore those TRIS bits.

Note that it's possible to enter special codes by pushing 2 or 3 buttons at a time. With this routine, that only works with buttons in the same row, because the function returns as soon as it finds any button(s) pressed in a given row, and lower rows aren't checked.

Sorry about the formatting, but the interface here doesn't seem to respect my spacing.

Code:
void read_keypad(void)
{
  byte i, j;

  trisc = 0b01101110;  // Top row of keypad is driven
  portc &= 0xF0;       // All 4 output bits (Portc 0-3) are low, but only one is set as an output
  i = 0;
  j = porta & 0b00001011;     // Data comes in on bits 0, 1, 3 of PortA
  if (j == 0b00001011)  // Any bits of Porta low? (3 bits are checked: 0, 1 and 3)
  {
  trisc = 0b01101101;  // No, change Tris so second row is driven
  j = porta & 0b00001011;     // 2nd row drives any column?
  if (j == 0b00001011)  // No
  {
  trisc = 0b01101011;
  j = porta & 0b00001011;
  if (j == 0b00001011)     // 3rd row?
  {
   trisc = 0b01100111;
   j = porta & 0b00001011;
  if (j == 0b00001011)     // 4th row?
     {
  button = 0x0F;  // This is the no-button code
    goto QE;  // Failed, go to quick exit
   }
   else
    i = 24;
  }
  else
     i = 16;
  }
  else
  i = 8;
  }
  if (j.F3)
  j.F2 = 1;
  j.F3 = 0;         // Data is now on bits 0, 1, 2 of J
  i += j;
  i &= 0x1F;         // Must be in range 0-1F
  lookup_button(i);       // This returns with button value in w register
  asm movwf _button
QE:   portc = portc_shadow;
  trisc = 0b01100000;
}
 
Last edited:
Top